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 = !is_const
427            && ty.is_some()
428            && matches!(self.peek().kind, TokenKind::Semicolon | TokenKind::Comma);
429        let value = if let (true, Some(name)) = (implicit, &name) {
430            // `let x: T;` starts as `undefined`; the typechecker requires `T` to allow it.
431            let value = parse_arena_result(
432                self.ast.try_push_expr(Expr {
433                    kind: ExprKind::Identifier(Ident {
434                        name: "undefined".to_string(),
435                        span: name.span,
436                    }),
437                    span: name.span,
438                }),
439                &mut self.fatal,
440            )?;
441            self.ast.implicit_initializers.insert(value);
442            self.ast.synthetic_undefined.insert(value);
443            value
444        } else {
445            if !matches!(self.peek().kind, TokenKind::Equals) {
446                let (msg, help) = if is_const {
447                    (
448                        "`const` declaration requires an initializer",
449                        vec!["const x: T = expr;".to_string()],
450                    )
451                } else {
452                    (
453                        "`let` declaration requires an initializer",
454                        vec![
455                            "let x: T = expr;".to_string(),
456                            "or declare one that starts empty: `let x: T | undefined;`".to_string(),
457                        ],
458                    )
459                };
460                self.error_at_peek_with_help(msg, help);
461                return None;
462            }
463            self.advance();
464            self.parse_expression()?
465        };
466
467        Some(match (binding, name) {
468            (Some(binding), _) => {
469                if is_const {
470                    StmtKind::ConstPattern {
471                        binding,
472                        ty,
473                        value,
474                        doc,
475                    }
476                } else {
477                    StmtKind::LetPattern {
478                        binding,
479                        ty,
480                        value,
481                        doc,
482                    }
483                }
484            }
485            (None, Some(name)) => {
486                if is_const {
487                    StmtKind::Const {
488                        name,
489                        ty,
490                        value,
491                        doc,
492                    }
493                } else {
494                    StmtKind::Let {
495                        name,
496                        ty,
497                        value,
498                        doc,
499                    }
500                }
501            }
502            (None, None) => return self.invariant_failure("either binding or name must be Some"),
503        })
504    }
505
506    // One level only; nested patterns are rejected.
507    fn parse_binding(&mut self) -> Option<Binding> {
508        match self.peek().kind {
509            TokenKind::LeftBrace => self.parse_object_binding(),
510            TokenKind::LeftBracket => self.parse_array_binding(),
511            _ => {
512                self.error_at_peek("expected `{` or `[` to start a destructuring pattern");
513                None
514            }
515        }
516    }
517
518    fn parse_object_binding(&mut self) -> Option<Binding> {
519        let open = self.advance();
520        let mut fields: Vec<ObjectPatternField> = Vec::new();
521        let mut rest: Option<Ident> = None;
522
523        if matches!(self.peek().kind, TokenKind::RightBrace) {
524            self.error_at_peek("empty object destructuring pattern");
525            return None;
526        }
527
528        loop {
529            if matches!(self.peek().kind, TokenKind::DotDotDot) {
530                let dots = self.advance();
531                let name_tok = self.expect_binding_identifier("expected identifier after `...`")?;
532                let name = self.ident_from_token(&name_tok);
533                if !matches!(self.peek().kind, TokenKind::RightBrace) {
534                    self.error_at_peek_with_help(
535                        "rest element must be the last element in a destructuring pattern",
536                        vec!["move `...` after every other binding".to_string()],
537                    );
538                    return None;
539                }
540                rest = Some(Ident {
541                    name: name.name,
542                    span: self.span(dots.span.start, name.span.end),
543                });
544                break;
545            }
546
547            let source_tok = self.expect_property_name("expected field name in object pattern")?;
548            let source = self.ident_from_token(&source_tok);
549            let field_span_start = source.span.start;
550
551            let (local, field_end) = if matches!(self.peek().kind, TokenKind::Colon) {
552                self.advance();
553                match self.peek().kind {
554                    TokenKind::LeftBrace | TokenKind::LeftBracket => {
555                        self.error_at_peek_with_help(
556                                "nested destructuring is not supported",
557                                vec![
558                                    "destructure once and access nested fields explicitly: const { a } = obj; const inner = a.b;"
559                                        .to_string(),
560                                ],
561                            );
562                        return None;
563                    }
564                    _ => {
565                        let local_tok = self.expect_binding_identifier(
566                            "expected identifier after `:` in pattern",
567                        )?;
568                        let local = self.ident_from_token(&local_tok);
569                        let end = local.span.end;
570                        (local, end)
571                    }
572                }
573            } else {
574                if !matches!(source_tok.kind, TokenKind::Identifier) {
575                    self.error_at_peek("expected `:` after keyword field name in object pattern");
576                    return None;
577                }
578                self.reject_strict_mode_reserved_word(&source_tok);
579                self.reject_eval_or_arguments(source_tok.span);
580                let end = source.span.end;
581                (source.clone(), end)
582            };
583
584            let default = if matches!(self.peek().kind, TokenKind::Equals) {
585                self.advance();
586                Some(self.parse_expression()?)
587            } else {
588                None
589            };
590            let field_end = match default {
591                Some(value) => {
592                    parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?
593                        .span
594                        .end
595                }
596                None => field_end,
597            };
598
599            fields.push(ObjectPatternField {
600                source,
601                local,
602                default,
603                span: self.span(field_span_start, field_end),
604            });
605
606            match self.peek().kind {
607                TokenKind::Comma => {
608                    self.advance();
609                    if matches!(self.peek().kind, TokenKind::RightBrace) {
610                        break;
611                    }
612                }
613                TokenKind::RightBrace => break,
614                _ => {
615                    self.error_at_peek("expected `,` or `}` in object pattern");
616                    return None;
617                }
618            }
619        }
620
621        if !matches!(self.peek().kind, TokenKind::RightBrace) {
622            self.error_at_peek("expected `}` to close object pattern");
623            return None;
624        }
625        let close = self.advance();
626
627        Some(Binding::Object {
628            fields,
629            rest,
630            span: self.span(open.span.start, close.span.end),
631        })
632    }
633
634    fn parse_array_binding(&mut self) -> Option<Binding> {
635        let open = self.advance();
636        let mut elems: Vec<Option<Ident>> = Vec::new();
637        let mut defaults = Vec::new();
638        let mut rest: Option<Ident> = None;
639
640        if matches!(self.peek().kind, TokenKind::RightBracket) {
641            self.error_at_peek("empty array destructuring pattern");
642            return None;
643        }
644
645        loop {
646            match self.peek().kind {
647                TokenKind::Comma => {
648                    elems.push(None);
649                    defaults.push(None);
650                    self.advance();
651                    if matches!(self.peek().kind, TokenKind::RightBracket) {
652                        break;
653                    }
654                    continue;
655                }
656                TokenKind::RightBracket => break,
657                TokenKind::DotDotDot => {
658                    let dots = self.advance();
659                    let name_tok =
660                        self.expect_binding_identifier("expected identifier after `...`")?;
661                    let name = self.ident_from_token(&name_tok);
662                    if !matches!(self.peek().kind, TokenKind::RightBracket) {
663                        self.error_at_peek_with_help(
664                            "rest element must be the last element in a destructuring pattern",
665                            vec!["move `...` after every other binding".to_string()],
666                        );
667                        return None;
668                    }
669                    rest = Some(Ident {
670                        name: name.name,
671                        span: self.span(dots.span.start, name.span.end),
672                    });
673                    break;
674                }
675                TokenKind::LeftBrace | TokenKind::LeftBracket => {
676                    self.error_at_peek_with_help(
677                        "nested destructuring is not supported",
678                        vec![
679                            "destructure once and access nested elements explicitly: const [a] = arr; const inner = a[0];"
680                                .to_string(),
681                        ],
682                    );
683                    return None;
684                }
685                _ => {
686                    let name_tok =
687                        self.expect_binding_identifier("expected identifier in array pattern")?;
688                    let name = self.ident_from_token(&name_tok);
689
690                    let default = if matches!(self.peek().kind, TokenKind::Equals) {
691                        self.advance();
692                        Some(self.parse_expression()?)
693                    } else {
694                        None
695                    };
696                    defaults.push(default);
697                    elems.push(Some(name));
698
699                    match self.peek().kind {
700                        TokenKind::Comma => {
701                            self.advance();
702                            if matches!(self.peek().kind, TokenKind::RightBracket) {
703                                break;
704                            }
705                        }
706                        TokenKind::RightBracket => break,
707                        _ => {
708                            self.error_at_peek("expected `,` or `]` in array pattern");
709                            return None;
710                        }
711                    }
712                }
713            }
714        }
715
716        if !matches!(self.peek().kind, TokenKind::RightBracket) {
717            self.error_at_peek("expected `]` to close array pattern");
718            return None;
719        }
720        let close = self.advance();
721
722        Some(Binding::Array {
723            elems,
724            defaults,
725            rest,
726            span: self.span(open.span.start, close.span.end),
727        })
728    }
729
730    fn parse_function_decl(&mut self) -> Option<StmtId> {
731        let doc = self.take_leading_doc();
732        let kw = self.advance();
733
734        let name_tok = self.expect_binding_identifier("expected function name")?;
735        let name = self.ident_from_token(&name_tok);
736        let is_main = self.block_depth == 0 && name.name == "main";
737        let decl = self.parse_function_decl_after_name(doc, kw, name);
738        if decl.is_none() && is_main {
739            self.ast.unparsed_main = true;
740        }
741        decl
742    }
743
744    fn parse_function_decl_after_name(
745        &mut self,
746        doc: Option<crate::DocComment>,
747        kw: Token,
748        name: Ident,
749    ) -> Option<StmtId> {
750        let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
751            self.parse_generic_param_list()?
752        } else {
753            Vec::new()
754        };
755
756        if !matches!(self.peek().kind, TokenKind::LeftParen) {
757            self.error_at_peek("expected `(` after function name");
758            return None;
759        }
760        self.advance();
761
762        let params = self.parse_param_list(false)?;
763        self.advance(); // `)` — parse_param_list left us on it
764
765        if !matches!(self.peek().kind, TokenKind::Colon) {
766            self.error_at_peek_with_help(
767                "expected `:` and return type",
768                vec![format!(
769                    "write the return type: `function {}(…): T {{ … }}`, with `void` when it returns nothing",
770                    name.name
771                )],
772            );
773            // Read the body anyway, so its own errors are reported and recovery
774            // does not resume inside it and misread its closing `}`.
775            if matches!(self.peek().kind, TokenKind::LeftBrace) {
776                let _ = self.parse_outer_this_boundary(Self::parse_block);
777            }
778            return None;
779        }
780        self.advance();
781        let (return_type, type_predicate) =
782            self.parse_predicate_or_return_type(TypePos::Anywhere)?;
783
784        // A declaration nested in a method has no receiver of its own, and
785        // must not see the method's.
786        let body = self.parse_outer_this_boundary(Self::parse_block)?;
787        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
788            .span
789            .end;
790
791        parse_arena_result(
792            self.ast.try_push_stmt(Stmt {
793                kind: StmtKind::Function {
794                    name,
795                    generics,
796                    params,
797                    return_type,
798                    type_predicate,
799                    body,
800                    doc,
801                },
802                span: self.span(kw.span.start, body_end),
803            }),
804            &mut self.fatal,
805        )
806    }
807
808    fn parse_class_decl(&mut self) -> Option<StmtId> {
809        let doc = self.take_leading_doc();
810        let kw = self.advance();
811        // Classes are collected from the module's top level only; a nested one
812        // would otherwise be dropped unchecked.
813        if self.block_depth > 0 {
814            self.error_at_with_help(
815                kw.span,
816                "a `class` must be declared at the top level of the module",
817                vec!["move this `class` outside any block or function body".to_string()],
818            );
819        }
820        let name = self.parse_type_decl_name("a", "class")?;
821
822        let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
823            self.parse_generic_param_list()?
824        } else {
825            Vec::new()
826        };
827
828        let extends = if matches!(self.peek().kind, TokenKind::Extends) {
829            self.advance();
830            Some(self.parse_type_annotation()?)
831        } else {
832            None
833        };
834
835        let mut implements: Vec<crate::TypeAnnotation> = Vec::new();
836        if matches!(self.peek().kind, TokenKind::Implements) {
837            self.advance();
838            loop {
839                implements.push(self.parse_type_annotation()?);
840                if matches!(self.peek().kind, TokenKind::Comma) {
841                    self.advance();
842                    continue;
843                }
844                break;
845            }
846        }
847
848        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
849            self.error_at_peek("expected `{` after class header");
850            return None;
851        }
852        self.advance();
853
854        let mut members: Vec<crate::ClassMember> = Vec::new();
855        let mut member_failed = false;
856        // After a failed member, a body missing its own `}` may have taken the
857        // class's: a declaration or import there is the next top-level item, not a
858        // member, so the class stops and reports the missing `}`.
859        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
860            && !(member_failed && self.peek_starts_top_level_item())
861        {
862            // Stray `;` between members is accepted (TS allows it).
863            if matches!(self.peek().kind, TokenKind::Semicolon) {
864                self.advance();
865                continue;
866            }
867            match self.parse_class_member() {
868                Some(member) => members.push(member),
869                None if std::mem::take(&mut self.resume_at_next_member) => member_failed = true,
870                None => return None,
871            }
872        }
873
874        if !matches!(self.peek().kind, TokenKind::RightBrace) {
875            self.error_at_peek("expected `}` to close class");
876            return None;
877        }
878        let close = self.advance();
879        if member_failed {
880            return None;
881        }
882
883        parse_arena_result(
884            self.ast.try_push_stmt(Stmt {
885                kind: StmtKind::ClassDecl {
886                    name,
887                    generics,
888                    extends,
889                    implements,
890                    members,
891                    doc,
892                },
893                span: self.span(kw.span.start, close.span.end),
894            }),
895            &mut self.fatal,
896        )
897    }
898
899    fn parse_class_member(&mut self) -> Option<crate::ClassMember> {
900        let doc = self.take_leading_doc();
901        let member_start = self.peek().span.start;
902
903        // Excluded modifiers — reject before anything else, naming the fix.
904        if self.peek_identifier_text_is("protected") {
905            let tok = self.advance();
906            self.error_at_with_help(
907                tok.span,
908                "`protected` is not supported",
909                vec!["use `private` or `public`".to_string()],
910            );
911            return None;
912        }
913        if self.peek_identifier_text_is("abstract") {
914            let tok = self.advance();
915            self.error_at_with_help(
916                tok.span,
917                "abstract classes are not supported",
918                vec!["provide a concrete implementation instead of `abstract`".to_string()],
919            );
920            return None;
921        }
922
923        let modifiers = self.parse_class_modifiers();
924        self.reject_async_member_modifier();
925
926        // Getter/setter: `get`/`set` <name> ( … ). A member literally named `get`/`set`
927        // (followed directly by `(`) is an ordinary method and stays accepted.
928        for (kw, kind) in [
929            ("get", crate::AccessorKind::Get),
930            ("set", crate::AccessorKind::Set),
931        ] {
932            if self.peek_identifier_text_is(kw)
933                && is_property_name(&self.peek_at(1).kind)
934                && matches!(self.peek_at(2).kind, TokenKind::LeftParen)
935            {
936                if let Some(span) = modifiers.readonly {
937                    self.reject_readonly_modifier(span);
938                }
939                if let Some(span) = modifiers.static_span {
940                    self.error_at_with_help(
941                        span,
942                        "static accessors are not supported",
943                        vec!["use a static method: `static name(): T { … }`".to_string()],
944                    );
945                    return None;
946                }
947                return self.parse_accessor(member_start, modifiers, kind, doc);
948            }
949        }
950
951        // Constructor.
952        if self.peek_identifier_text_is("constructor")
953            && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
954        {
955            if let Some(span) = modifiers.static_span {
956                self.error_at_with_help(
957                    span,
958                    "a constructor cannot be `static`",
959                    vec![
960                        "remove `static` — the constructor already belongs to the class, not \
961                         instances"
962                            .to_string(),
963                    ],
964                );
965                return None;
966            }
967            if let Some(span) = modifiers.readonly {
968                self.error_at_with_help(
969                    span,
970                    "a constructor cannot be `readonly`",
971                    vec!["remove `readonly`; it applies to fields".to_string()],
972                );
973                return None;
974            }
975            return self.parse_constructor(member_start, modifiers.visibility, doc);
976        }
977
978        let name = self.expect_property_ident("expected class member name")?;
979        let optional_span = if matches!(self.peek().kind, TokenKind::Question) {
980            Some(self.advance().span)
981        } else {
982            None
983        };
984        let optional = optional_span.is_some();
985
986        // Method.
987        if matches!(self.peek().kind, TokenKind::LessThan | TokenKind::LeftParen) {
988            if let Some(span) = modifiers.readonly {
989                self.reject_readonly_modifier(span);
990            }
991            let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
992                self.parse_generic_param_list()?
993            } else {
994                Vec::new()
995            };
996            if !matches!(self.peek().kind, TokenKind::LeftParen) {
997                self.error_at_peek("expected `(` to start the method parameter list");
998                return None;
999            }
1000            self.advance();
1001            let params = self.parse_class_member_params(false)?;
1002            self.advance(); // `)` — parse_param_list left us on it
1003            if !matches!(self.peek().kind, TokenKind::Colon) {
1004                self.error_at_peek_with_help(
1005                    "expected `:` and return type",
1006                    vec![format!(
1007                        "write the return type: `{}(…): T {{ … }}`, with `void` when it returns nothing",
1008                        name.name
1009                    )],
1010                );
1011                // As for a function: read the body so recovery resumes after it.
1012                if matches!(self.peek().kind, TokenKind::LeftBrace)
1013                    && self.parse_class_member_body().is_some()
1014                {
1015                    self.resume_at_next_member = true;
1016                }
1017                return None;
1018            }
1019            self.advance();
1020            let return_type = self.parse_type_annotation()?;
1021            if self.at_inserted_semicolon() && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1022            {
1023                self.advance();
1024            }
1025            if optional
1026                && matches!(
1027                    self.peek().kind,
1028                    TokenKind::Semicolon | TokenKind::RightBrace
1029                )
1030            {
1031                if !generics.is_empty() {
1032                    self.error_at(
1033                        name.span,
1034                        "generic optional method declarations are not supported",
1035                    );
1036                    return None;
1037                }
1038                let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1039                    self.advance().span.end
1040                } else {
1041                    return_type.span.end
1042                };
1043                let ty = self.method_declaration_type(
1044                    params,
1045                    return_type,
1046                    self.span(name.span.start, end),
1047                )?;
1048                return Some(crate::ClassMember::Field {
1049                    name,
1050                    modifiers,
1051                    optional: true,
1052                    method_signature: true,
1053                    ty,
1054                    initializer: None,
1055                    span: self.span(member_start, end),
1056                    doc,
1057                });
1058            }
1059            let body = self.parse_class_member_body()?;
1060            let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1061                .span
1062                .end;
1063            return Some(crate::ClassMember::Method {
1064                name,
1065                optional,
1066                modifiers,
1067                generics,
1068                params,
1069                return_type,
1070                body,
1071                span: self.span(member_start, body_end),
1072                doc,
1073            });
1074        }
1075
1076        // Field.
1077        if let Some(span) = optional_span
1078            && modifiers.static_span.is_some()
1079        {
1080            self.error_at_with_help(
1081                span,
1082                "a static field cannot be optional",
1083                vec!["a static field must be initialized; give it a value with `= …`".to_string()],
1084            );
1085            return None;
1086        }
1087        if !matches!(self.peek().kind, TokenKind::Colon) {
1088            if matches!(self.peek().kind, TokenKind::Equals) {
1089                self.error_at_peek_with_help(
1090                    "class fields require a type annotation",
1091                    vec!["add `: T` before the initializer, e.g. `name: string = …`".to_string()],
1092                );
1093            } else {
1094                self.error_at_peek("expected `:` and a type for the class field");
1095            }
1096            return None;
1097        }
1098        self.advance();
1099        let ty = self.parse_type_annotation()?;
1100        let initializer = if matches!(self.peek().kind, TokenKind::Equals) {
1101            self.advance();
1102            // A field initializer may read `this` (the typechecker binds it); gate
1103            // it like a member body so `this.x` parses.
1104            self.class_member_body_depth += 1;
1105            let init = self.parse_expression();
1106            self.class_member_body_depth -= 1;
1107            Some(init?)
1108        } else {
1109            None
1110        };
1111        if !matches!(self.peek().kind, TokenKind::Semicolon) {
1112            self.error_at_peek("expected `;` after class field");
1113            return None;
1114        }
1115        let semi = self.advance();
1116        Some(crate::ClassMember::Field {
1117            name,
1118            modifiers,
1119            optional,
1120            method_signature: false,
1121            ty,
1122            initializer,
1123            span: self.span(member_start, semi.span.end),
1124            doc,
1125        })
1126    }
1127
1128    fn parse_constructor(
1129        &mut self,
1130        member_start: u32,
1131        visibility: crate::Visibility,
1132        doc: Option<crate::DocComment>,
1133    ) -> Option<crate::ClassMember> {
1134        self.advance(); // `constructor`
1135        self.advance(); // `(` — guaranteed by the caller
1136        let params = self.parse_class_member_params(true)?;
1137        self.advance(); // `)` — parse_param_list left us on it
1138        if matches!(self.peek().kind, TokenKind::Colon) {
1139            let colon = self.advance();
1140            let _ = self.parse_type_annotation();
1141            self.error_at_with_help(
1142                colon.span,
1143                "a constructor cannot declare a return type",
1144                vec![
1145                    "drop the `: T` — a constructor always returns the class instance".to_string(),
1146                ],
1147            );
1148        }
1149        let body = self.parse_class_member_body()?;
1150        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1151            .span
1152            .end;
1153        Some(crate::ClassMember::Constructor {
1154            visibility,
1155            params,
1156            body,
1157            span: self.span(member_start, body_end),
1158            doc,
1159        })
1160    }
1161
1162    /// `get name(): T { … }` / `set name(v: T) { … }`. The caller has confirmed the
1163    /// `get`/`set` keyword, a property name, and a `(` follow.
1164    fn parse_accessor(
1165        &mut self,
1166        member_start: u32,
1167        modifiers: crate::ClassModifiers,
1168        kind: crate::AccessorKind,
1169        doc: Option<crate::DocComment>,
1170    ) -> Option<crate::ClassMember> {
1171        self.advance(); // `get` / `set`
1172        let name = self.expect_property_ident("expected accessor name")?;
1173        self.advance(); // `(` — guaranteed by the caller
1174        let mut params = self.parse_class_member_params(false)?;
1175        self.advance(); // `)`
1176        let param = match kind {
1177            crate::AccessorKind::Get => {
1178                if !params.is_empty() {
1179                    self.error_at(name.span, "a getter cannot declare parameters");
1180                }
1181                None
1182            }
1183            crate::AccessorKind::Set => {
1184                if params.len() != 1 {
1185                    self.error_at(name.span, "a setter must declare exactly one parameter");
1186                    return None;
1187                }
1188                let param = params.remove(0);
1189                // An assignment always passes the value, so a setter's parameter
1190                // can't be omitted or collect several.
1191                if param.rest {
1192                    self.error_at(
1193                        param.name.span,
1194                        "a setter's parameter can't be a rest parameter",
1195                    );
1196                } else if param.default.is_some() {
1197                    self.error_at(name.span, "a setter's parameter can't have a default value");
1198                } else if param.optional {
1199                    self.error_at(param.name.span, "a setter's parameter can't be optional");
1200                }
1201                Some(Box::new(param))
1202            }
1203        };
1204        let return_type = if matches!(self.peek().kind, TokenKind::Colon) {
1205            self.advance();
1206            Some(self.parse_type_annotation()?)
1207        } else {
1208            // A getter's type is its return type, which, as for a method, is
1209            // written rather than inferred. The body still parses, so the
1210            // rest of the class is checked.
1211            if kind == crate::AccessorKind::Get {
1212                self.error_at_peek_with_help(
1213                    "expected `:` and return type",
1214                    vec!["get name(): T { … }".to_string()],
1215                );
1216            }
1217            None
1218        };
1219        let body = self.parse_class_member_body()?;
1220        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1221            .span
1222            .end;
1223        Some(crate::ClassMember::Accessor {
1224            name,
1225            modifiers,
1226            kind,
1227            param,
1228            return_type,
1229            body,
1230            span: self.span(member_start, body_end),
1231            doc,
1232        })
1233    }
1234
1235    /// `public`/`private`/`static`/`readonly`. Duplicate or conflicting modifiers are
1236    /// diagnosed but parsing continues so the member shape is still recovered.
1237    /// Visibility must precede `static` and `readonly` (the TypeScript order);
1238    /// `readonly` is accepted on either side of `static` — no semantic difference
1239    /// under our subset.
1240    fn parse_class_modifiers(&mut self) -> crate::ClassModifiers {
1241        let mut visibility = crate::Visibility::Public;
1242        let mut visibility_span: Option<Span> = None;
1243        let mut readonly: Option<Span> = None;
1244        let mut static_span: Option<Span> = None;
1245        loop {
1246            if self.peek_word_is_class_modifier("public")
1247                || self.peek_word_is_class_modifier("private")
1248            {
1249                let (kind, word) = if self.peek_identifier_text_is("private") {
1250                    (crate::Visibility::Private, "private")
1251                } else {
1252                    (crate::Visibility::Public, "public")
1253                };
1254                let tok = self.advance();
1255                if visibility_span.is_some() {
1256                    self.error_at_with_help(
1257                        tok.span,
1258                        "a class member may have at most one visibility modifier",
1259                        vec!["keep a single `public` or `private`".to_string()],
1260                    );
1261                    continue;
1262                }
1263                // TypeScript reports one misplaced modifier per member; `static` is
1264                // named first when both precede.
1265                let preceding = static_span
1266                    .map(|_| "static")
1267                    .or(readonly.map(|_| "readonly"));
1268                if let Some(preceding) = preceding {
1269                    self.error_at_with_help(
1270                        tok.span,
1271                        format!("`{word}` must come before `{preceding}`"),
1272                        vec![format!("write `{word} {preceding} <name>`")],
1273                    );
1274                }
1275                visibility = kind;
1276                visibility_span = Some(tok.span);
1277                continue;
1278            }
1279            if self.peek_word_is_class_modifier("static") {
1280                let tok = self.advance();
1281                if static_span.is_some() {
1282                    self.error_at(tok.span, "duplicate `static` modifier");
1283                } else {
1284                    static_span = Some(tok.span);
1285                }
1286                continue;
1287            }
1288            if self.peek_word_is_class_modifier("readonly") {
1289                let tok = self.advance();
1290                if readonly.is_some() {
1291                    self.error_at(tok.span, "duplicate `readonly` modifier");
1292                } else {
1293                    readonly = Some(tok.span);
1294                }
1295                continue;
1296            }
1297            break;
1298        }
1299        crate::ClassModifiers {
1300            visibility,
1301            visibility_span,
1302            readonly,
1303            static_span,
1304        }
1305    }
1306
1307    /// A contextual modifier keyword counts as a modifier only when another member token
1308    /// (the real name, or a further modifier) follows — otherwise the word is the member
1309    /// name itself (e.g. a field named `private`). Mirrors `eat_readonly_property_modifier`.
1310    /// As in TypeScript, only `static` may be followed by a line break; in a class body
1311    /// ASI already ends the member there, but a constructor parameter list has no ASI.
1312    fn peek_word_is_class_modifier(&self, word: &str) -> bool {
1313        if !self.peek_identifier_text_is(word) {
1314            return false;
1315        }
1316        if word != "static" && self.line_break_after_peek() {
1317            return false;
1318        }
1319        let next = &self.peek_at(1).kind;
1320        is_property_name(next) || matches!(next, TokenKind::StringLiteral(_))
1321    }
1322
1323    fn parse_class_member_params(
1324        &mut self,
1325        allow_param_properties: bool,
1326    ) -> Option<Vec<ParamDecl>> {
1327        let saved = self.class_member_body_depth;
1328        self.class_member_body_depth = 1;
1329        let params = self.parse_param_list(allow_param_properties);
1330        self.class_member_body_depth = saved;
1331        params
1332    }
1333
1334    fn parse_class_member_body(&mut self) -> Option<StmtId> {
1335        self.class_member_body_depth += 1;
1336        let body = self.parse_block();
1337        self.class_member_body_depth -= 1;
1338        body
1339    }
1340
1341    fn parse_interface_decl(&mut self) -> Option<StmtId> {
1342        let doc = self.take_leading_doc();
1343        let kw = self.advance();
1344        let name = self.parse_type_decl_name("an", "interface")?;
1345
1346        let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1347            self.parse_generic_param_list()?
1348        } else {
1349            Vec::new()
1350        };
1351
1352        let mut extends = Vec::new();
1353        if matches!(self.peek().kind, TokenKind::Extends) {
1354            self.advance();
1355            loop {
1356                extends.push(self.parse_type_annotation()?);
1357                if !matches!(self.peek().kind, TokenKind::Comma) {
1358                    break;
1359                }
1360                self.advance();
1361            }
1362        }
1363        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1364            self.error_at_peek("expected `{` after interface name");
1365            return None;
1366        }
1367        self.advance();
1368
1369        let mut members: Vec<crate::InterfaceMember> = Vec::new();
1370        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
1371            && !self.peek_starts_top_level_item()
1372        {
1373            // Call signatures `(params): ret;` are stored under the sentinel name `@call`.
1374            // The `@` prefix is not a valid identifier start, so collisions with user methods
1375            // are impossible.
1376            let member_doc = self.take_leading_doc();
1377            if matches!(self.peek().kind, TokenKind::LeftParen) {
1378                let open = self.advance();
1379                let params = self.parse_param_list(false)?;
1380                self.advance();
1381                if !matches!(self.peek().kind, TokenKind::Colon) {
1382                    self.error_at_peek("expected `:` and return type");
1383                    return None;
1384                }
1385                self.advance();
1386                let return_type = self.parse_type_annotation()?;
1387                let end = self.finish_interface_member(return_type.span.end)?;
1388                let span = self.span(open.span.start, end);
1389                let sentinel = Ident {
1390                    name: "@call".to_string(),
1391                    span: open.span,
1392                };
1393                members.push(crate::InterfaceMember::Method {
1394                    name: sentinel,
1395                    optional: false,
1396                    generics: Vec::new(),
1397                    params,
1398                    return_type,
1399                    span,
1400                    doc: member_doc,
1401                });
1402                continue;
1403            }
1404            if matches!(self.peek().kind, TokenKind::LessThan) {
1405                self.error_at_peek("generic call signatures are not yet supported");
1406                return None;
1407            }
1408            let member_start = self.peek().span.start;
1409            let readonly = self.eat_readonly_property_modifier();
1410            if self.peek_is_parameterless_index_signature() {
1411                self.skip_parameterless_index_signature(member_start)?;
1412                self.finish_interface_member(self.prev_token_end())?;
1413                continue;
1414            }
1415            if matches!(self.peek().kind, TokenKind::LeftBracket) {
1416                let signature = self.parse_index_signature(readonly)?;
1417                self.finish_interface_member(signature.span.end)?;
1418                members.push(crate::InterfaceMember::IndexSignature(signature));
1419                continue;
1420            }
1421            let member_name = self.expect_property_ident("expected interface member name")?;
1422            let optional = matches!(self.peek().kind, TokenKind::Question);
1423            if optional {
1424                self.advance();
1425            }
1426            if matches!(self.peek().kind, TokenKind::Colon) {
1427                self.advance();
1428                let ty = self.parse_type_annotation()?;
1429                let end = self.finish_interface_member(ty.span.end)?;
1430                let span = self.span(member_name.span.start, end);
1431                members.push(crate::InterfaceMember::Property {
1432                    name: member_name,
1433                    ty,
1434                    optional,
1435                    readonly,
1436                    span,
1437                    doc: member_doc,
1438                });
1439                continue;
1440            }
1441            if readonly {
1442                self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
1443            }
1444            let m_generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1445                self.parse_generic_param_list()?
1446            } else {
1447                Vec::new()
1448            };
1449            if !matches!(self.peek().kind, TokenKind::LeftParen) {
1450                self.error_at_peek(
1451                    "expected `(` to start a method signature or `:` to start a property",
1452                );
1453                return None;
1454            }
1455            self.advance();
1456            let params = self.parse_param_list(false)?;
1457            self.advance();
1458            if !matches!(self.peek().kind, TokenKind::Colon) {
1459                self.error_at_peek("expected `:` and return type");
1460                return None;
1461            }
1462            self.advance();
1463            let return_type = self.parse_type_annotation()?;
1464            let end = self.finish_interface_member(return_type.span.end)?;
1465            let span = self.span(member_name.span.start, end);
1466            if optional {
1467                if !m_generics.is_empty() {
1468                    self.error_at(
1469                        member_name.span,
1470                        "generic optional method declarations are not supported",
1471                    );
1472                    return None;
1473                }
1474                let ty = self.method_declaration_type(params, return_type, span)?;
1475                members.push(crate::InterfaceMember::Property {
1476                    name: member_name,
1477                    ty,
1478                    optional: true,
1479                    readonly,
1480                    span,
1481                    doc: member_doc,
1482                });
1483            } else {
1484                members.push(crate::InterfaceMember::Method {
1485                    name: member_name,
1486                    optional,
1487                    generics: m_generics,
1488                    params,
1489                    return_type,
1490                    span,
1491                    doc: member_doc,
1492                });
1493            }
1494        }
1495
1496        if !matches!(self.peek().kind, TokenKind::RightBrace) {
1497            self.error_at_peek("expected `}` to close interface");
1498            return None;
1499        }
1500        let close = self.advance();
1501
1502        parse_arena_result(
1503            self.ast.try_push_stmt(Stmt {
1504                kind: StmtKind::InterfaceDecl {
1505                    name,
1506                    generics,
1507                    extends,
1508                    members,
1509                    doc,
1510                },
1511                span: self.span(kw.span.start, close.span.end),
1512            }),
1513            &mut self.fatal,
1514        )
1515    }
1516
1517    fn method_declaration_type(
1518        &mut self,
1519        params: Vec<ParamDecl>,
1520        return_type: TypeAnnotation,
1521        span: Span,
1522    ) -> Option<TypeAnnotation> {
1523        let mut fields = Vec::new();
1524        for param in params {
1525            if param.pattern.is_some() || param.default.is_some() {
1526                self.error_at(
1527                    param.name.span,
1528                    "a method declaration cannot contain a parameter pattern or default value",
1529                );
1530                return None;
1531            }
1532            let Some(ty) = param.ty else {
1533                return self.invariant_failure("method parameter annotation was not retained");
1534            };
1535            fields.push(TypeAnnotationField {
1536                name: param.name,
1537                ty,
1538                optional: param.optional,
1539                readonly: false,
1540                rest: param.rest,
1541                method: false,
1542            });
1543        }
1544        Some(TypeAnnotation {
1545            kind: TypeAnnotationKind::Function {
1546                params: fields,
1547                return_type: Box::new(return_type),
1548            },
1549            span,
1550        })
1551    }
1552
1553    fn finish_interface_member(&mut self, body_end: u32) -> Option<u32> {
1554        self.finish_type_member(body_end, "expected `;`, `,`, or `}` after interface member")
1555    }
1556
1557    /// Consume the separator after an interface or type-literal member and return
1558    /// where the member ends. `;` and `,` are interchangeable and the last member's
1559    /// is optional. A line break also ends a member, as in TypeScript: ASI inserts
1560    /// `;` there, but keeps some tokens, such as `[` and `(`, attached. `body_end` is
1561    /// where the member ends when it carries no separator of its own.
1562    fn finish_type_member(&mut self, body_end: u32, message: &str) -> Option<u32> {
1563        match self.peek().kind {
1564            TokenKind::Semicolon | TokenKind::Comma => Some(self.advance().span.end),
1565            TokenKind::RightBrace => Some(body_end),
1566            _ if self.line_break_before_peek() => Some(body_end),
1567            _ => {
1568                self.error_at_peek(message);
1569                None
1570            }
1571        }
1572    }
1573
1574    fn parse_type_alias_decl(&mut self) -> Option<StmtId> {
1575        let doc = self.take_leading_doc();
1576        let kw = self.advance();
1577        let name = self.parse_type_decl_name("a", "type alias")?;
1578
1579        let generics: Vec<Ident> = if matches!(self.peek().kind, TokenKind::LessThan) {
1580            self.parse_generic_param_list()?
1581        } else {
1582            Vec::new()
1583        };
1584
1585        if !matches!(self.peek().kind, TokenKind::Equals) {
1586            self.error_at_peek("expected `=` after type alias name");
1587            return None;
1588        }
1589        self.advance();
1590
1591        let ty = self.parse_type_annotation()?;
1592
1593        // ASI keeps a `[` on the next line attached, but the type already ended at
1594        // the line break (see `parse_type_array_inner`), so the break ends the alias.
1595        let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1596            self.advance().span.end
1597        } else if self.line_break_before_peek() {
1598            self.prev_token_end()
1599        } else {
1600            self.error_at_peek("expected `;` after type alias body");
1601            return None;
1602        };
1603
1604        parse_arena_result(
1605            self.ast.try_push_stmt(Stmt {
1606                kind: StmtKind::TypeAliasDecl {
1607                    name,
1608                    generics,
1609                    ty,
1610                    doc,
1611                },
1612                span: self.span(kw.span.start, end),
1613            }),
1614            &mut self.fatal,
1615        )
1616    }
1617
1618    // Parser enforces kind-coherence: an enum cannot mix numeric and string initializers.
1619    fn parse_enum_decl(&mut self) -> Option<StmtId> {
1620        #[derive(Copy, Clone)]
1621        enum Kind {
1622            Number,
1623            String,
1624        }
1625        let doc = self.take_leading_doc();
1626        let kw = self.advance();
1627        let name = self.parse_type_decl_name("an", "enum")?;
1628
1629        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1630            self.error_at_peek("expected `{` after enum name");
1631            return None;
1632        }
1633        self.advance();
1634
1635        let mut members: Vec<EnumMember> = Vec::new();
1636        let mut first_explicit: Option<(Kind, Span)> = None;
1637        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1638            // ASI inserts `;` after `,` on a newline — tolerate it so multi-line enums parse.
1639            if matches!(self.peek().kind, TokenKind::Semicolon) {
1640                self.advance();
1641                continue;
1642            }
1643            let member_doc = self.take_leading_doc();
1644            let member_name_tok = self.expect_identifier_name("expected enum member name")?;
1645            let member_name = self.ident_from_token(&member_name_tok);
1646
1647            let value = if matches!(self.peek().kind, TokenKind::Equals) {
1648                self.advance();
1649                let init = self.parse_enum_initializer()?;
1650                let (this_kind, this_span) = match &init {
1651                    EnumInitializer::Number { span, .. } => (Kind::Number, *span),
1652                    EnumInitializer::String { span, .. } => (Kind::String, *span),
1653                };
1654                match first_explicit {
1655                    None => first_explicit = Some((this_kind, this_span)),
1656                    Some((prev, _)) => {
1657                        let mismatched = matches!(
1658                            (prev, this_kind),
1659                            (Kind::Number, Kind::String) | (Kind::String, Kind::Number)
1660                        );
1661                        if mismatched {
1662                            self.error_at(
1663                                this_span,
1664                                "mixed numeric and string enum members are not allowed",
1665                            );
1666                        }
1667                    }
1668                }
1669                Some(init)
1670            } else {
1671                None
1672            };
1673
1674            let end_span = value
1675                .as_ref()
1676                .map_or(member_name.span, super::ast::EnumInitializer::span);
1677            let member_span = self.span(member_name.span.start, end_span.end);
1678            members.push(EnumMember {
1679                name: member_name,
1680                value,
1681                span: member_span,
1682                doc: member_doc,
1683            });
1684
1685            match self.peek().kind {
1686                // ASI supplies the `;` before a closing `}` and after a newline, so an
1687                // enum body sees one wherever a comma was omitted.
1688                TokenKind::Comma | TokenKind::Semicolon => {
1689                    self.advance();
1690                }
1691                TokenKind::RightBrace | TokenKind::Eof => {}
1692                _ => {
1693                    self.error_at_peek("expected `,` or `}` after enum member");
1694                    return None;
1695                }
1696            }
1697        }
1698
1699        if !matches!(self.peek().kind, TokenKind::RightBrace) {
1700            self.error_at_peek("expected `}` to close enum");
1701            return None;
1702        }
1703        let close = self.advance();
1704
1705        parse_arena_result(
1706            self.ast.try_push_stmt(Stmt {
1707                kind: StmtKind::EnumDecl { name, members, doc },
1708                span: self.span(kw.span.start, close.span.end),
1709            }),
1710            &mut self.fatal,
1711        )
1712    }
1713
1714    /// `export` on a top-level declaration. Two forms (multi-file.md §3):
1715    /// Form 1 marks a declaration (`export function …`) — the declaration is
1716    /// parsed normally and recorded in `exported_decls`; Form 2 is a re-export
1717    /// (`export { a, b as c } from "./util";` / `export { x };`) parsed into an
1718    /// `ExportFrom` node. `export` is top-level only; inside a function body it
1719    /// is a parse error. `export default` stays rejected.
1720    fn parse_export(&mut self) -> Option<StmtId> {
1721        let export_tok = self.advance();
1722        let export_span = export_tok.span;
1723
1724        let nested = self.block_depth > 0;
1725        if nested {
1726            self.error_at_with_help(
1727                export_span,
1728                "`export` statements must appear at the top of the file",
1729                vec!["move this `export` to the top level of the module".to_string()],
1730            );
1731            // Fall through to parse the declaration / list anyway so recovery
1732            // stays in sync, but return None below so it isn't recorded.
1733        }
1734
1735        // `export type { … }` — TS's type-only re-export, accepted as a no-op
1736        // synonym: exports carry both value and type spaces here.
1737        if self.peek_identifier_text_is("type")
1738            && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1739        {
1740            self.advance();
1741        }
1742
1743        self.reject_async_modifier();
1744        let declares = matches!(
1745            self.peek().kind,
1746            TokenKind::Function
1747                | TokenKind::Class
1748                | TokenKind::Const
1749                | TokenKind::Let
1750                | TokenKind::Interface
1751                | TokenKind::Enum
1752        ) || self.at_type_alias_head();
1753        if declares {
1754            // Carry any doc comment that attached to `export` onto the
1755            // declaration token so the declaration parser still sees it.
1756            if export_tok.leading_doc.is_some() && self.peek().leading_doc.is_none() {
1757                if let Some(token) = self.tokens.get_mut(self.pos) {
1758                    token.leading_doc = export_tok.leading_doc;
1759                } else {
1760                    return self.invariant_failure("export cursor is outside token stream");
1761                }
1762            }
1763            let id = self.parse_statement()?;
1764            if nested {
1765                let detached: Vec<StmtId> = std::iter::once(id)
1766                    .chain(self.take_trailing_declarators())
1767                    .collect();
1768                self.discard_detached_patterns(&detached);
1769                return None;
1770            }
1771            // The caller places the trailing declarators; each is exported too.
1772            for stmt in std::iter::once(id).chain(self.trailing_declarators.clone()) {
1773                self.ast
1774                    .exported_decls
1775                    .push(ExportedDecl { stmt, export_span });
1776            }
1777            return Some(id);
1778        }
1779
1780        match self.peek().kind {
1781            TokenKind::LeftBrace => {
1782                // A doc comment attaches to the `export` keyword, which we've
1783                // already consumed — carry it onto the re-export node.
1784                let doc = export_tok
1785                    .leading_doc
1786                    .as_ref()
1787                    .and_then(|raw| self.parse_doc(raw))
1788                    .or_else(|| self.take_leading_doc());
1789                self.parse_export_from(export_span, nested, doc)
1790            }
1791            TokenKind::Default => {
1792                self.error_at_peek_with_help(
1793                    "`export default` is not supported",
1794                    vec![
1795                        "default exports are out of scope; drop `default` — a named \
1796                         `export` marks the declaration visible to sibling modules"
1797                            .to_string(),
1798                    ],
1799                );
1800                None
1801            }
1802            _ => {
1803                self.error_at_peek_with_help(
1804                    "expected a declaration after `export`",
1805                    vec![
1806                        "`export` may precede `function`, `class`, `const`, `let`, `type`, \
1807                         `interface`, or `enum`, or take the form `export { … }`"
1808                            .to_string(),
1809                    ],
1810                );
1811                None
1812            }
1813        }
1814    }
1815
1816    /// Parse Form 2 re-export: `export { a, b as c } from "./util";` or the
1817    /// bare `export { x };`. The leading `export` has already been consumed.
1818    fn parse_export_from(
1819        &mut self,
1820        export_span: Span,
1821        nested: bool,
1822        doc: Option<crate::DocComment>,
1823    ) -> Option<StmtId> {
1824        let (specs, open_span) = self.parse_specifier_list(SpecifierList::Export)?;
1825        if specs.is_empty() {
1826            self.error_at_with_help(
1827                open_span,
1828                "empty export specifier list",
1829                vec!["name what you re-export: `export { name } from \"./util\";`".to_string()],
1830            );
1831            return None;
1832        }
1833
1834        let source = if self.peek_identifier_text_is("from") {
1835            self.advance();
1836            let module_tok = self.peek().clone();
1837            let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1838                s.clone()
1839            } else {
1840                self.error_at_peek("expected module specifier string after `from`");
1841                return None;
1842            };
1843            self.advance();
1844            Some((module, module_tok.span))
1845        } else {
1846            None
1847        };
1848
1849        if !matches!(self.peek().kind, TokenKind::Semicolon) {
1850            self.error_at_peek("expected `;` after export statement");
1851            return None;
1852        }
1853        let semi = self.advance();
1854
1855        if nested {
1856            return None;
1857        }
1858
1859        parse_arena_result(
1860            self.ast.try_push_stmt(Stmt {
1861                kind: StmtKind::ExportFrom { specs, source, doc },
1862                span: self.span(export_span.start, semi.span.end),
1863            }),
1864            &mut self.fatal,
1865        )
1866    }
1867
1868    fn parse_import_decl(&mut self) -> Option<StmtId> {
1869        let doc = self.take_leading_doc();
1870        let kw = self.advance();
1871        let kw_span = kw.span;
1872
1873        let nested = self.block_depth > 0;
1874        if nested {
1875            self.error_at_with_help(
1876                kw_span,
1877                "`import` statements must appear at the top of the file",
1878                vec!["move this `import` outside any block / function body".to_string()],
1879            );
1880            // Continue parsing to consume the whole import so recovery isn't confused.
1881        }
1882
1883        let kind = self.parse_import_kind()?;
1884
1885        if !self.peek_identifier_text_is("from") {
1886            self.error_at_peek("expected `from` after import specifier list");
1887            return None;
1888        }
1889        self.advance();
1890
1891        let module_tok = self.peek().clone();
1892        let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1893            s.clone()
1894        } else {
1895            self.error_at_peek("expected module specifier string after `from`");
1896            return None;
1897        };
1898        let module_span = module_tok.span;
1899        self.advance();
1900
1901        if !matches!(self.peek().kind, TokenKind::Semicolon) {
1902            self.error_at_peek("expected `;` after import statement");
1903            return None;
1904        }
1905        let semi = self.advance();
1906
1907        if nested {
1908            return None;
1909        }
1910
1911        parse_arena_result(
1912            self.ast.try_push_stmt(Stmt {
1913                kind: StmtKind::Import {
1914                    module,
1915                    module_span,
1916                    kind,
1917                    doc,
1918                },
1919                span: self.span(kw_span.start, semi.span.end),
1920            }),
1921            &mut self.fatal,
1922        )
1923    }
1924
1925    fn parse_import_kind(&mut self) -> Option<ImportKind> {
1926        // `import type …` — TS's type-only import, accepted as a no-op synonym:
1927        // imports carry both value and type spaces here. `import type from "m"`
1928        // stays a namespace import named `type`, so `type` is a modifier only
1929        // when what follows can't be a complete import clause without it.
1930        if self.peek_identifier_text_is("type")
1931            && match self.peek_at(1).kind {
1932                TokenKind::LeftBrace | TokenKind::Star => true,
1933                TokenKind::Identifier => {
1934                    !(self.peek_at_is_word(1, "from")
1935                        && matches!(self.peek_at(2).kind, TokenKind::StringLiteral(_)))
1936                }
1937                _ => false,
1938            }
1939        {
1940            self.advance();
1941        }
1942
1943        match self.peek().kind {
1944            TokenKind::LeftBrace => self.parse_named_imports(),
1945            TokenKind::Identifier => {
1946                let local_tok = self.advance();
1947                let local_name = self.ident_from_token(&local_tok);
1948                if matches!(self.peek().kind, TokenKind::Comma) {
1949                    self.error_at_peek_with_help(
1950                        "combining default and named imports is not supported",
1951                        vec!["use two separate `import` statements".to_string()],
1952                    );
1953                    return None;
1954                }
1955                Some(ImportKind::Namespace { local_name })
1956            }
1957            // `import * as ns from "pkg"` — forgiveness synonym for `import ns from "pkg"`.
1958            // Both are identical since we have no default exports.
1959            TokenKind::Star => {
1960                self.advance();
1961                if !self.peek_identifier_text_is("as") {
1962                    self.error_at_peek_with_help(
1963                        "expected `as <name>` after `import *`",
1964                        vec![
1965                            "name the namespace binding: \
1966                             `import * as ns from \"pkg\";` (or use the \
1967                             short form: `import ns from \"pkg\";`)"
1968                                .to_string(),
1969                        ],
1970                    );
1971                    return None;
1972                }
1973                self.advance();
1974                let local_tok = self.expect_identifier("expected namespace name after `as`")?;
1975                let local_name = self.ident_from_token(&local_tok);
1976                Some(ImportKind::Namespace { local_name })
1977            }
1978            TokenKind::StringLiteral(_) => {
1979                self.error_at_peek_with_help(
1980                    "side-effect-only imports are not supported",
1981                    vec![
1982                        "name what you import: `import { name } from \"pkg\";` \
1983                         or `import ns from \"pkg\";`"
1984                            .to_string(),
1985                    ],
1986                );
1987                None
1988            }
1989            _ => {
1990                self.error_at_peek("expected `{` or namespace name after `import`");
1991                None
1992            }
1993        }
1994    }
1995
1996    fn parse_named_imports(&mut self) -> Option<ImportKind> {
1997        let (specs, open_span) = self.parse_specifier_list(SpecifierList::Import)?;
1998        if specs.is_empty() {
1999            self.error_at_with_help(
2000                open_span,
2001                "empty import specifier list",
2002                vec!["name what you import: `import { name } from \"pkg\";`".to_string()],
2003            );
2004            return None;
2005        }
2006        Some(ImportKind::Named(specs))
2007    }
2008
2009    /// Parse a `{ a, b as c }` specifier list shared by `import` and re-`export`.
2010    /// `list` says whether the specifiers bind local names (an import) and names the
2011    /// construct in diagnostics. Returns the specifiers (possibly
2012    /// empty — callers reject empty with construct-specific help) and the span of
2013    /// the opening brace.
2014    fn parse_specifier_list(
2015        &mut self,
2016        list: SpecifierList,
2017    ) -> Option<(Vec<ImportSpecifier>, Span)> {
2018        let what = list.noun();
2019        let open = self.advance();
2020        let mut specs: Vec<ImportSpecifier> = Vec::new();
2021        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2022            // Inline type-only modifier (`{ type X }`), accepted as a no-op
2023            // synonym. `{ type as t }` instead names the binding `type`.
2024            if self.peek_identifier_text_is("type")
2025                && matches!(self.peek_at(1).kind, TokenKind::Identifier)
2026                && !self.peek_at_is_word(1, "as")
2027            {
2028                self.advance();
2029            }
2030            let imported_tok =
2031                self.expect_identifier_name(&format!("expected {what} specifier name"))?;
2032            let imported_name = self.ident_from_token(&imported_tok);
2033
2034            let binds_local = list == SpecifierList::Import;
2035            let local_name = if self.peek_identifier_text_is("as") {
2036                self.advance();
2037                let local_tok = self.expect_identifier_name("expected local name after `as`")?;
2038                if binds_local {
2039                    self.reject_strict_mode_reserved_word(&local_tok);
2040                }
2041                self.ident_from_token(&local_tok)
2042            } else {
2043                if binds_local {
2044                    self.reject_strict_mode_reserved_word(&imported_tok);
2045                }
2046                imported_name.clone()
2047            };
2048
2049            specs.push(ImportSpecifier {
2050                imported_name,
2051                local_name,
2052            });
2053
2054            match self.peek().kind {
2055                TokenKind::Comma => {
2056                    self.advance();
2057                }
2058                TokenKind::RightBrace | TokenKind::Eof => {}
2059                _ => {
2060                    self.error_at_peek(format!("expected `,` or `}}` after {what} specifier"));
2061                    return None;
2062                }
2063            }
2064        }
2065
2066        if !matches!(self.peek().kind, TokenKind::RightBrace) {
2067            self.error_at_peek(format!("expected `}}` to close {what} specifier list"));
2068            return None;
2069        }
2070        self.advance();
2071
2072        Some((specs, open.span))
2073    }
2074
2075    fn parse_enum_initializer(&mut self) -> Option<EnumInitializer> {
2076        let neg = if matches!(self.peek().kind, TokenKind::Minus) {
2077            Some(self.advance())
2078        } else {
2079            None
2080        };
2081        let tok = self.advance();
2082        match &tok.kind {
2083            TokenKind::NumberLiteral(n) => {
2084                let value = if neg.is_some() { -n } else { *n };
2085                // tsc constant-folds enum initializers, which turns `-0` into `0`.
2086                let value = if value == 0.0 { 0.0 } else { value };
2087                let start = neg.as_ref().map_or(tok.span.start, |t| t.span.start);
2088                Some(EnumInitializer::Number {
2089                    value,
2090                    span: self.span(start, tok.span.end),
2091                })
2092            }
2093            TokenKind::StringLiteral(s) => {
2094                if let Some(neg_tok) = &neg {
2095                    self.error_at(
2096                        self.span(neg_tok.span.start, tok.span.end),
2097                        "cannot negate a string enum initializer",
2098                    );
2099                    return None;
2100                }
2101                Some(EnumInitializer::String {
2102                    value: s.clone(),
2103                    span: tok.span,
2104                })
2105            }
2106            _ => {
2107                let span = neg.map_or(tok.span, |t| self.span(t.span.start, tok.span.end));
2108                self.error_at(
2109                    span,
2110                    "expected a number or string literal for enum initializer",
2111                );
2112                None
2113            }
2114        }
2115    }
2116
2117    /// Whether the token at `index` is `operator` (`keyof`, `readonly`) used as a
2118    /// prefix type operator.
2119    ///
2120    /// Both are contextual keywords — valid as ordinary identifiers in TypeScript and
2121    /// Submilli — so they are only operators when a type follows. A bare type name is
2122    /// never followed by another type name, so requiring one separates `keyof T` from
2123    /// a type literally named `keyof`.
2124    fn is_contextual_type_operator(&self, index: usize, operator: &str) -> bool {
2125        let Some(tok) = self.tokens.get(index) else {
2126            return false;
2127        };
2128        if !self.token_is_word(tok, operator) {
2129            return false;
2130        }
2131        matches!(
2132            self.tokens.get(index + 1).map(|t| &t.kind),
2133            Some(
2134                TokenKind::Identifier
2135                    | TokenKind::Void
2136                    | TokenKind::LeftBrace
2137                    | TokenKind::LeftParen
2138                    | TokenKind::LeftBracket
2139                    | TokenKind::StringLiteral(_)
2140                    | TokenKind::NumberLiteral(_)
2141                    | TokenKind::BooleanLiteral(_)
2142                    | TokenKind::NullLiteral
2143            )
2144        )
2145    }
2146
2147    /// The lexer already emits shifts as separate `>` tokens. Only `>=`
2148    /// needs splitting in a type context; keep `=` at the cursor for the caller.
2149    fn consume_type_close(&mut self) -> Option<Token> {
2150        if matches!(self.peek().kind, TokenKind::GreaterThan) {
2151            return Some(self.advance());
2152        }
2153        let original = self.peek().clone();
2154        if !matches!(original.kind, TokenKind::GreaterEquals) {
2155            return self.invariant_failure("type closing token must start with `>`");
2156        }
2157        let middle = original.span.start + 1;
2158        let close = Token::new(
2159            TokenKind::GreaterThan,
2160            self.span(original.span.start, middle),
2161        );
2162        let remaining = Token::new(TokenKind::Equals, self.span(middle, original.span.end));
2163        self.split_type_tokens.push((self.pos, original));
2164        *self.tokens.get_mut(self.pos)? = remaining;
2165        Some(close)
2166    }
2167
2168    fn restore_type_tokens(&mut self, checkpoint: usize) {
2169        for (index, original) in self.split_type_tokens.drain(checkpoint..).rev() {
2170            // Splitting replaces tokens in place; the token vector never shrinks.
2171            self.tokens[index] = original;
2172        }
2173    }
2174
2175    fn parse_type_argument_list(&mut self) -> Option<(Vec<TypeAnnotation>, u32)> {
2176        let lt = self.advance();
2177        if matches!(
2178            self.peek().kind,
2179            TokenKind::GreaterThan | TokenKind::GreaterEquals
2180        ) {
2181            self.error_at(lt.span, "empty generic argument list `<>` is not allowed");
2182            return None;
2183        }
2184        let mut args = Vec::new();
2185        loop {
2186            args.push(self.parse_type_annotation()?);
2187            match self.peek().kind {
2188                TokenKind::Comma => {
2189                    self.advance();
2190                    if matches!(
2191                        self.peek().kind,
2192                        TokenKind::GreaterThan | TokenKind::GreaterEquals
2193                    ) {
2194                        let gt = self.consume_type_close()?;
2195                        return Some((args, gt.span.end));
2196                    }
2197                }
2198                TokenKind::GreaterThan | TokenKind::GreaterEquals => {
2199                    let gt = self.consume_type_close()?;
2200                    return Some((args, gt.span.end));
2201                }
2202                _ => {
2203                    self.error_at(
2204                        lt.span,
2205                        "expected `,` or `>` to close generic argument list",
2206                    );
2207                    return None;
2208                }
2209            }
2210        }
2211    }
2212
2213    /// The name a `class`, `interface`, `type` or `enum` declares, `noun` naming
2214    /// which. A built-in type's name is reported, as in TypeScript: `class number {}`
2215    /// would read as the built-in in some positions and the class in others.
2216    fn parse_type_decl_name(&mut self, article: &str, noun: &str) -> Option<Ident> {
2217        let name_tok = self.expect_identifier(&format!("expected {noun} name"))?;
2218        let name = self.ident_from_token(&name_tok);
2219        if BUILT_IN_TYPE_NAMES.contains(&name.name.as_str()) {
2220            self.error_at_with_help(
2221                name.span,
2222                format!(
2223                    "`{}` is a built-in type and can't be used as {article} {noun} name",
2224                    name.name
2225                ),
2226                vec![format!("rename the {noun}")],
2227            );
2228        }
2229        Some(name)
2230    }
2231
2232    fn parse_generic_param_list(&mut self) -> Option<Vec<Ident>> {
2233        let lt = self.advance();
2234        if matches!(
2235            self.peek().kind,
2236            TokenKind::GreaterThan | TokenKind::GreaterEquals
2237        ) {
2238            self.error_at(lt.span, "empty generic parameter list `<>` is not allowed");
2239            return None;
2240        }
2241        let mut generics = Vec::new();
2242        loop {
2243            let name_tok = self.expect_identifier("expected generic parameter name")?;
2244            let name = self.ident_from_token(&name_tok);
2245            // A duplicate is reported and dropped, as `tsc` drops it: the first
2246            // parameter keeps the name, and the type arguments count against the
2247            // parameters that remain.
2248            if generics.iter().any(|g: &Ident| g.name == name.name) {
2249                self.error_at_with_help(
2250                    name.span,
2251                    format!("duplicate type parameter `{}`", name.name),
2252                    vec!["give each type parameter its own name".to_string()],
2253                );
2254            } else {
2255                generics.push(name);
2256            }
2257            match self.peek().kind {
2258                TokenKind::Comma => {
2259                    self.advance();
2260                    if matches!(
2261                        self.peek().kind,
2262                        TokenKind::GreaterThan | TokenKind::GreaterEquals
2263                    ) {
2264                        self.consume_type_close()?;
2265                        return Some(generics);
2266                    }
2267                }
2268                TokenKind::GreaterThan | TokenKind::GreaterEquals => {
2269                    self.consume_type_close()?;
2270                    return Some(generics);
2271                }
2272                _ => {
2273                    self.error_at_peek("expected `,` or `>`");
2274                    return None;
2275                }
2276            }
2277        }
2278    }
2279
2280    fn parse_param_list(&mut self, allow_param_properties: bool) -> Option<Vec<ParamDecl>> {
2281        if matches!(self.peek().kind, TokenKind::RightParen) {
2282            return Some(Vec::new());
2283        }
2284        let mut params = Vec::new();
2285        loop {
2286            let param = self.parse_param(allow_param_properties)?;
2287            let is_rest = param.rest;
2288            params.push(param);
2289            match self.peek().kind {
2290                TokenKind::Comma => {
2291                    if is_rest {
2292                        let comma = self.advance();
2293                        self.error_at(comma.span, "rest parameter must be the last parameter");
2294                        return None;
2295                    }
2296                    self.advance();
2297                    if matches!(self.peek().kind, TokenKind::RightParen) {
2298                        break;
2299                    }
2300                }
2301                TokenKind::RightParen => break,
2302                _ => {
2303                    self.error_at_peek("expected `,` or `)`");
2304                    return None;
2305                }
2306            }
2307        }
2308        self.check_parameter_order(params.iter().map(ParamOrder::of_param));
2309        Some(params)
2310    }
2311
2312    fn parse_param(&mut self, allow_param_properties: bool) -> Option<ParamDecl> {
2313        // Parameter properties: `constructor(public x: T)` declares + assigns a
2314        // field. A modifier keyword counts only when a parameter name follows it
2315        // (so a param literally named `public`/`readonly` still parses).
2316        let modifiers = if self.peek_word_is_class_modifier("public")
2317            || self.peek_word_is_class_modifier("private")
2318            || self.peek_word_is_class_modifier("readonly")
2319        {
2320            let span = self.peek().span;
2321            let parsed = self.parse_class_modifiers();
2322            if allow_param_properties {
2323                Some(parsed)
2324            } else {
2325                self.error_at_with_help(
2326                    span,
2327                    "parameter properties are only allowed in a constructor",
2328                    vec![
2329                        "move the `public`/`private`/`readonly` modifier to a constructor parameter"
2330                            .to_string(),
2331                    ],
2332                );
2333                None
2334            }
2335        } else {
2336            None
2337        };
2338
2339        let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
2340            self.advance();
2341            true
2342        } else {
2343            false
2344        };
2345        if modifiers.is_some() && rest {
2346            self.error_at_peek("a parameter property cannot be a rest parameter");
2347            return None;
2348        }
2349
2350        // Placeholder `name` is the empty identifier at the pattern's span;
2351        // the pre-infer lowering pass replaces it with a fresh `#pattern_p_N`.
2352        let (name, pattern) = match self.peek().kind {
2353            TokenKind::LeftBrace | TokenKind::LeftBracket => {
2354                if rest {
2355                    self.error_at_peek("rest parameter cannot be destructured");
2356                    return None;
2357                }
2358                if modifiers.is_some() {
2359                    self.error_at_peek("a parameter property cannot be destructured");
2360                    return None;
2361                }
2362                let binding = self.parse_binding()?;
2363                let placeholder = Ident {
2364                    name: String::new(),
2365                    span: binding.span(),
2366                };
2367                (placeholder, Some(binding))
2368            }
2369            _ => {
2370                let name_tok = self.expect_binding_identifier(if rest {
2371                    "expected identifier after `...`"
2372                } else {
2373                    "expected parameter name"
2374                })?;
2375                let name = self.ident_from_token(&name_tok);
2376                (name, None)
2377            }
2378        };
2379
2380        let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
2381        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2382            self.advance();
2383            Some(self.parse_type_annotation()?)
2384        } else if !rest && matches!(self.peek().kind, TokenKind::Equals) {
2385            None
2386        } else {
2387            if rest {
2388                self.error_at_peek_with_help(
2389                    "rest parameter requires a type annotation",
2390                    vec!["function name(...args: T[]): R { … }".to_string()],
2391                );
2392            } else {
2393                self.error_at_peek_with_help(
2394                    "parameter requires a type annotation",
2395                    vec!["add a type (`x: T`) or a default initializer (`x = value`)".to_string()],
2396                );
2397            }
2398            return None;
2399        };
2400        let default = self.parse_parameter_default(rest, optional)?;
2401        Some(ParamDecl {
2402            name,
2403            ty,
2404            default,
2405            optional,
2406            pattern,
2407            rest,
2408            modifiers,
2409        })
2410    }
2411
2412    fn parse_parameter_optional(&mut self, rest: bool, pattern: bool) -> Option<bool> {
2413        if !matches!(self.peek().kind, TokenKind::Question) {
2414            return Some(false);
2415        }
2416        let question = self.advance();
2417        if rest || pattern {
2418            self.error_at(
2419                question.span,
2420                if rest {
2421                    "a rest parameter cannot be optional"
2422                } else {
2423                    "a destructured parameter cannot be optional; use a default value"
2424                },
2425            );
2426            return None;
2427        }
2428        Some(true)
2429    }
2430
2431    /// The `= value` after a parameter, if any. An optional parameter's default is
2432    /// reported but still parsed, so the rest of the list is checked too.
2433    fn parse_parameter_default(&mut self, rest: bool, optional: bool) -> Option<Option<ExprId>> {
2434        if !matches!(self.peek().kind, TokenKind::Equals) {
2435            return Some(None);
2436        }
2437        if rest {
2438            self.error_rest_parameter_default();
2439            return None;
2440        }
2441        if optional {
2442            self.error_at_peek_with_help(
2443                "an optional parameter cannot have a default value",
2444                vec!["drop the `?`: a parameter with a default is already optional".to_string()],
2445            );
2446        }
2447        self.advance();
2448        Some(Some(self.parse_expression()?))
2449    }
2450
2451    /// Rest's default is the empty array, so an explicit one is rejected.
2452    fn error_rest_parameter_default(&mut self) {
2453        self.error_at_peek_with_help(
2454            "rest parameter cannot have a default value",
2455            vec!["omit the `= …`; an unspecified rest defaults to `[]`".to_string()],
2456        );
2457    }
2458
2459    /// A caller cannot skip an optional argument and still pass a later one, so a
2460    /// required parameter after an optional one is reported. Only the first is, and
2461    /// parsing continues.
2462    fn check_parameter_order<'p>(&mut self, params: impl IntoIterator<Item = ParamOrder<'p>>) {
2463        let mut follows_optional = false;
2464        for param in params {
2465            if param.optional {
2466                follows_optional = true;
2467                continue;
2468            }
2469            if !follows_optional || !param.required {
2470                continue;
2471            }
2472            let label = match param.name {
2473                Some(name) => format!("parameter `{name}`"),
2474                None => "destructured parameter".to_string(),
2475            };
2476            self.error_at_with_help(
2477                param.span,
2478                format!("required {label} cannot follow an optional parameter"),
2479                vec!["make it optional too (`x?: T`), give it a default (`x: T = …`), or move it before the optional parameters".to_string()],
2480            );
2481            return;
2482        }
2483    }
2484
2485    /// Drop a `;` that ASI inserted directly before `next`, where the grammar requires
2486    /// `next` and so no statement can have ended. ASI is a token-stream pre-pass with no
2487    /// parser feedback, so it cannot see that a `}` ends a `do` body rather than a
2488    /// statement, or that the `{` after a signature's return annotation opens its body.
2489    fn eat_asi_semicolon_before(&mut self, next: TokenKind) {
2490        if matches!(self.peek().kind, TokenKind::Semicolon) && self.peek_at(1).kind == next {
2491            self.advance();
2492        }
2493    }
2494
2495    fn parse_block(&mut self) -> Option<StmtId> {
2496        self.eat_asi_semicolon_before(TokenKind::LeftBrace);
2497        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2498            self.error_at_peek("expected `{`");
2499            return None;
2500        }
2501        let open = self.advance();
2502        self.block_depth += 1;
2503
2504        let mut stmts = Vec::new();
2505        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2506            if matches!(self.peek().kind, TokenKind::Semicolon) {
2507                self.advance();
2508                continue;
2509            }
2510            let pos_before = self.pos;
2511            if let Some(id) = self.parse_statement() {
2512                stmts.push(id);
2513                stmts.extend(self.take_trailing_declarators());
2514            } else {
2515                self.recover();
2516                if self.pos == pos_before && !self.is_at_eof() {
2517                    self.advance();
2518                }
2519            }
2520        }
2521
2522        self.block_depth -= 1;
2523        if !matches!(self.peek().kind, TokenKind::RightBrace) {
2524            self.error_at_peek("expected `}`");
2525            return None;
2526        }
2527        let close = self.advance();
2528
2529        let span = self.span(open.span.start, close.span.end);
2530        parse_arena_result(
2531            self.ast.try_push_stmt(Stmt {
2532                kind: StmtKind::Block(stmts),
2533                span,
2534            }),
2535            &mut self.fatal,
2536        )
2537    }
2538
2539    // Control-flow bodies accept either a braced block or a single statement;
2540    // `if (c) return x;` is identical to `if (c) { return x; }` under our type
2541    // system (forgiveness principle). Downstream passes assume a `Block`, so a
2542    // braceless statement is wrapped in one.
2543    fn parse_control_body(&mut self) -> Option<StmtId> {
2544        if matches!(self.peek().kind, TokenKind::LeftBrace) {
2545            return self.parse_block();
2546        }
2547        // A binding here would go out of scope as soon as it was made. TypeScript
2548        // or JavaScript rejects each of these, so it is reported and then parsed
2549        // as usual. Only a declaration is reported: a `let` followed by
2550        // anything else already fails to parse.
2551        let keyword = match self.peek().kind {
2552            TokenKind::Let => Some(("a", "let")),
2553            TokenKind::Const => Some(("a", "const")),
2554            TokenKind::Function => Some(("a", "function")),
2555            // A nested `class` is reported wherever it is: see `parse_class_decl`.
2556            TokenKind::Interface => Some(("an", "interface")),
2557            TokenKind::Enum => Some(("an", "enum")),
2558            _ if self.peek_identifier_text_is("type") => Some(("a", "type")),
2559            _ => None,
2560        };
2561        // After `let`/`const`, a bracket or brace starts a destructuring
2562        // pattern; after `type`, which is also an ordinary identifier, it is an
2563        // index or nothing a declaration can be.
2564        let declares = match self.peek_at(1).kind {
2565            TokenKind::Identifier => true,
2566            TokenKind::LeftBracket | TokenKind::LeftBrace => !matches!(keyword, Some((_, "type"))),
2567            _ => false,
2568        };
2569        if let Some((article, keyword)) = keyword
2570            && declares
2571        {
2572            self.error_at_peek_with_help(
2573                format!(
2574                    "{article} `{keyword}` declaration can't be the body of a statement without braces"
2575                ),
2576                vec![format!("wrap it in braces: `{{ {keyword} … }}`")],
2577            );
2578        }
2579        self.block_depth += 1;
2580        let stmt = self.parse_statement();
2581        self.block_depth -= 1;
2582        let stmt = stmt?;
2583        let mut stmts = vec![stmt];
2584        stmts.extend(self.take_trailing_declarators());
2585        let first = parse_arena_result(self.ast.try_stmt(stmt), &mut self.fatal)?.span;
2586        let last_id = stmts.last().copied().unwrap_or(stmt);
2587        let last = parse_arena_result(self.ast.try_stmt(last_id), &mut self.fatal)?.span;
2588        let span = self.span(first.start, last.end);
2589        parse_arena_result(
2590            self.ast.try_push_stmt(Stmt {
2591                kind: StmtKind::Block(stmts),
2592                span,
2593            }),
2594            &mut self.fatal,
2595        )
2596    }
2597
2598    /// A loop body may also be a lone `;`: `while (advance());` does all its work in
2599    /// the header. Branches do not accept it; see `parse_branch_body`.
2600    fn parse_loop_body(&mut self) -> Option<StmtId> {
2601        if !matches!(self.peek().kind, TokenKind::Semicolon) {
2602            return self.parse_control_body();
2603        }
2604        self.empty_body_at_semicolon()
2605    }
2606
2607    /// An `if` or `else` body. Unlike a loop, a branch cannot be a lone `;`: `if (c);`
2608    /// silently detaches the block meant to follow it. The `;` is reported, then
2609    /// consumed as an empty body so an `else` after it doesn't cascade into a second error.
2610    fn parse_branch_body(&mut self, keyword: &str) -> Option<StmtId> {
2611        if !matches!(self.peek().kind, TokenKind::Semicolon) {
2612            return self.parse_control_body();
2613        }
2614        self.error_at_peek_with_help(
2615            format!("`{keyword}` has an empty body"),
2616            vec![format!("remove the `;` after `{keyword}`")],
2617        );
2618        self.empty_body_at_semicolon()
2619    }
2620
2621    /// Consumes a lone `;` as an empty block spanning it.
2622    fn empty_body_at_semicolon(&mut self) -> Option<StmtId> {
2623        let semi = self.advance();
2624        parse_arena_result(
2625            self.ast.try_push_stmt(Stmt {
2626                kind: StmtKind::Block(vec![]),
2627                span: semi.span,
2628            }),
2629            &mut self.fatal,
2630        )
2631    }
2632
2633    fn parse_if(&mut self) -> Option<StmtId> {
2634        self.with_recursion_limit(Self::parse_if_inner)
2635    }
2636
2637    fn parse_if_inner(&mut self) -> Option<StmtId> {
2638        let kw = self.advance();
2639        let condition = self.parse_paren_condition()?;
2640        let then_block = self.parse_branch_body("if")?;
2641
2642        let else_block = if matches!(self.peek().kind, TokenKind::Else) {
2643            self.advance();
2644            if matches!(self.peek().kind, TokenKind::If) {
2645                Some(self.parse_if()?)
2646            } else {
2647                Some(self.parse_branch_body("else")?)
2648            }
2649        } else {
2650            None
2651        };
2652
2653        let end = match else_block {
2654            Some(id) => {
2655                parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
2656                    .span
2657                    .end
2658            }
2659            None => {
2660                parse_arena_result(self.ast.try_stmt(then_block), &mut self.fatal)?
2661                    .span
2662                    .end
2663            }
2664        };
2665
2666        parse_arena_result(
2667            self.ast.try_push_stmt(Stmt {
2668                kind: StmtKind::If {
2669                    condition,
2670                    then_block,
2671                    else_block,
2672                },
2673                span: self.span(kw.span.start, end),
2674            }),
2675            &mut self.fatal,
2676        )
2677    }
2678
2679    fn parse_while(&mut self) -> Option<StmtId> {
2680        let kw = self.advance();
2681        let condition = self.parse_paren_condition()?;
2682        let body = self.parse_loop_body()?;
2683        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2684            .span
2685            .end;
2686
2687        parse_arena_result(
2688            self.ast.try_push_stmt(Stmt {
2689                kind: StmtKind::While { condition, body },
2690                span: self.span(kw.span.start, body_end),
2691            }),
2692            &mut self.fatal,
2693        )
2694    }
2695
2696    // Disambiguates C-style for and for-of via non-consuming lookahead for `of`.
2697    fn parse_for(&mut self) -> Option<StmtId> {
2698        let kw = self.advance();
2699        // `for await (…)`: there are no promises to wait for, so name the
2700        // rewrite and read the loop as a plain `for`.
2701        if self.peek_identifier_text_is("await")
2702            && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
2703        {
2704            let tok = self.advance();
2705            self.error_at_with_help(
2706                tok.span,
2707                "`for await` is not supported",
2708                vec![
2709                    "calls return their values directly: remove `await` and write \
2710                     `for (const e of …)`"
2711                        .to_string(),
2712                ],
2713            );
2714        }
2715        if !matches!(self.peek().kind, TokenKind::LeftParen) {
2716            self.error_at_peek("expected `(` after `for`");
2717            return None;
2718        }
2719        self.advance();
2720
2721        if self.scan_for_of_shape() {
2722            self.parse_for_of_tail(kw)
2723        } else {
2724            self.parse_c_for_tail(kw)
2725        }
2726    }
2727
2728    // Counts bracket/angle depth to skip past optional type annotations.
2729    fn scan_for_of_shape(&self) -> bool {
2730        if !matches!(self.peek().kind, TokenKind::Let | TokenKind::Const) {
2731            return false;
2732        }
2733        // Object patterns are tentatively accepted here; `parse_for_of_tail` rejects them.
2734        let (start, mut depth) = match self.peek_at(1).kind {
2735            TokenKind::Identifier => match self.peek_at(2).kind {
2736                TokenKind::Identifier if self.peek_at_is_word(2, "of") => return true,
2737                TokenKind::Colon => (3, 0i32),
2738                _ => return false,
2739            },
2740            TokenKind::LeftBracket | TokenKind::LeftBrace => (2, 1i32),
2741            _ => return false,
2742        };
2743        let mut i = start;
2744        loop {
2745            match self.peek_at(i).kind {
2746                TokenKind::Eof => return false,
2747                TokenKind::LessThan
2748                | TokenKind::LeftParen
2749                | TokenKind::LeftBracket
2750                | TokenKind::LeftBrace => {
2751                    depth += 1;
2752                }
2753                TokenKind::GreaterThan
2754                | TokenKind::RightParen
2755                | TokenKind::RightBracket
2756                | TokenKind::RightBrace => {
2757                    if depth == 0 {
2758                        return false;
2759                    }
2760                    depth -= 1;
2761                }
2762                // A user type literally named `of` in the annotation would
2763                // misclassify here and error loudly downstream — acceptable.
2764                TokenKind::Identifier if depth == 0 && self.peek_at_is_word(i, "of") => {
2765                    return true;
2766                }
2767                TokenKind::Equals | TokenKind::Semicolon if depth == 0 => return false,
2768                _ => {}
2769            }
2770            i += 1;
2771        }
2772    }
2773
2774    // The trailing `;` of init-statement parsers doubles as the first `;` of the for header.
2775    fn parse_c_for_tail(&mut self, kw: Token) -> Option<StmtId> {
2776        let init = match self.peek().kind {
2777            TokenKind::Semicolon => {
2778                self.advance();
2779                Vec::new()
2780            }
2781            TokenKind::Let | TokenKind::Const => {
2782                self.trailing_declarators.clear();
2783                let is_const = matches!(self.peek().kind, TokenKind::Const);
2784                let mut init = vec![self.parse_let_or_const(is_const)?];
2785                init.extend(self.take_trailing_declarators());
2786                init
2787            }
2788            _ => self.parse_for_init_expressions()?,
2789        };
2790        let for_stmt = self.finish_c_for(kw, init.clone());
2791        if for_stmt.is_none() {
2792            self.discard_detached_patterns(&init);
2793        }
2794        for_stmt
2795    }
2796
2797    fn finish_c_for(&mut self, kw: Token, init: Vec<StmtId>) -> Option<StmtId> {
2798        let condition = if matches!(self.peek().kind, TokenKind::Semicolon) {
2799            None
2800        } else {
2801            Some(self.parse_expression()?)
2802        };
2803        if !matches!(self.peek().kind, TokenKind::Semicolon) {
2804            self.error_at_peek("expected `;` after `for` condition");
2805            return None;
2806        }
2807        self.advance();
2808
2809        let update = if matches!(self.peek().kind, TokenKind::RightParen) {
2810            None
2811        } else {
2812            Some(self.parse_for_update_stmt()?)
2813        };
2814        if !matches!(self.peek().kind, TokenKind::RightParen) {
2815            self.error_at_peek("expected `)` after `for` update");
2816            return None;
2817        }
2818        self.advance();
2819
2820        let body = self.parse_loop_body()?;
2821        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2822            .span
2823            .end;
2824        parse_arena_result(
2825            self.ast.try_push_stmt(Stmt {
2826                kind: StmtKind::For {
2827                    init,
2828                    condition,
2829                    update,
2830                    body,
2831                },
2832                span: self.span(kw.span.start, body_end),
2833            }),
2834            &mut self.fatal,
2835        )
2836    }
2837
2838    /// Pattern lowering expands a destructuring declaration only where a block, the
2839    /// program or a `for` head holds it, so one parsed into the arena whose
2840    /// statement then failed (a `for` loop, a later declarator, a nested `export`)
2841    /// would otherwise reach inference unlowered.
2842    fn discard_detached_patterns(&mut self, stmts: &[StmtId]) {
2843        for &id in stmts {
2844            let Some(stmt) = parse_arena_result(self.ast.try_stmt_mut(id), &mut self.fatal) else {
2845                return;
2846            };
2847            if matches!(
2848                stmt.kind,
2849                StmtKind::LetPattern { .. } | StmtKind::ConstPattern { .. }
2850            ) {
2851                stmt.kind = StmtKind::Block(Vec::new());
2852            }
2853        }
2854    }
2855
2856    /// `i = 0, j = n` in a `for` initializer runs each expression in turn, so each
2857    /// becomes its own statement, as each declarator of a `let` head does.
2858    fn parse_for_init_expressions(&mut self) -> Option<Vec<StmtId>> {
2859        let mut init = vec![self.parse_for_clause_expr()?];
2860        while matches!(self.peek().kind, TokenKind::Comma) {
2861            self.advance();
2862            init.push(self.parse_for_clause_expr()?);
2863        }
2864        if !self.at_statement_end() {
2865            let last = parse_arena_result(self.ast.try_stmt(*init.last()?), &mut self.fatal)?;
2866            let what = if matches!(last.kind, StmtKind::Expr(_)) {
2867                "expression"
2868            } else {
2869                "assignment"
2870            };
2871            self.error_at_peek(format!("expected `;` after {what}"));
2872            return None;
2873        }
2874        self.finish_statement();
2875        Some(init)
2876    }
2877
2878    /// `i++, j--` in a `for` update runs each expression in turn, so it becomes a
2879    /// block of their statements; it declares nothing, so the block scopes nothing.
2880    fn parse_for_update_stmt(&mut self) -> Option<StmtId> {
2881        let first = self.parse_for_clause_expr()?;
2882        if !matches!(self.peek().kind, TokenKind::Comma) {
2883            return Some(first);
2884        }
2885        let mut stmts = vec![first];
2886        while matches!(self.peek().kind, TokenKind::Comma) {
2887            self.advance();
2888            stmts.push(self.parse_for_clause_expr()?);
2889        }
2890        let start = parse_arena_result(self.ast.try_stmt(first), &mut self.fatal)?
2891            .span
2892            .start;
2893        let span = self.span(start, self.prev_token_end());
2894        parse_arena_result(
2895            self.ast.try_push_stmt(Stmt {
2896                kind: StmtKind::Block(stmts),
2897                span,
2898            }),
2899            &mut self.fatal,
2900        )
2901    }
2902
2903    fn parse_for_clause_expr(&mut self) -> Option<StmtId> {
2904        let expr_id = self.parse_expression()?;
2905        let span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
2906        let kind = self.statement_kind_for(expr_id)?;
2907        parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
2908    }
2909
2910    fn parse_for_of_tail(&mut self, kw: Token) -> Option<StmtId> {
2911        let binding_tok = self.advance();
2912        let binding_kind = match binding_tok.kind {
2913            TokenKind::Let => crate::BindingKind::Let,
2914            TokenKind::Const => crate::BindingKind::Const,
2915            _ => return self.invariant_failure("scan_for_of_shape gated this"),
2916        };
2917        let (name, binding) = match self.peek().kind {
2918            TokenKind::LeftBracket => {
2919                let b = self.parse_binding()?;
2920                (None, Some(b))
2921            }
2922            TokenKind::LeftBrace => {
2923                let b = self.parse_binding()?;
2924                self.error_at(
2925                    b.span(),
2926                    "object destructuring is not supported in `for-of`; use array destructuring or unpack inside the loop body",
2927                );
2928                return None;
2929            }
2930            _ => {
2931                let name_tok = self.expect_binding_identifier(
2932                    "expected identifier after `let`/`const` in `for-of`",
2933                )?;
2934                (Some(self.ident_from_token(&name_tok)), None)
2935            }
2936        };
2937        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2938            self.advance();
2939            Some(self.parse_type_annotation()?)
2940        } else {
2941            None
2942        };
2943        if !self.peek_identifier_text_is("of") {
2944            self.error_at_peek("expected `of` in `for-of`");
2945            return None;
2946        }
2947        self.advance();
2948
2949        let iter = self.parse_expression()?;
2950        if !matches!(self.peek().kind, TokenKind::RightParen) {
2951            self.error_at_peek("expected `)` after `for-of` iterable");
2952            return None;
2953        }
2954        self.advance();
2955
2956        let body = self.parse_loop_body()?;
2957        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2958            .span
2959            .end;
2960        let kind = match (binding, name) {
2961            (Some(binding), _) => StmtKind::ForOfPattern {
2962                binding_kind,
2963                binding,
2964                ty,
2965                iter,
2966                body,
2967            },
2968            (None, Some(name)) => StmtKind::ForOf {
2969                binding_kind,
2970                name,
2971                ty,
2972                iter,
2973                body,
2974            },
2975            (None, None) => return self.invariant_failure("either binding or name must be Some"),
2976        };
2977        parse_arena_result(
2978            self.ast.try_push_stmt(Stmt {
2979                kind,
2980                span: self.span(kw.span.start, body_end),
2981            }),
2982            &mut self.fatal,
2983        )
2984    }
2985
2986    fn parse_do_while(&mut self) -> Option<StmtId> {
2987        let kw = self.advance();
2988        let body = self.parse_loop_body()?;
2989        self.eat_asi_semicolon_before(TokenKind::While);
2990        if !matches!(self.peek().kind, TokenKind::While) {
2991            self.error_at_peek("expected `while` after `do` body");
2992            return None;
2993        }
2994        self.advance();
2995        let condition = self.parse_paren_condition()?;
2996        // ECMAScript inserts the terminator after a do-while's `)` unconditionally, so the
2997        // `;` is optional here — ASI can't supply it, since that `)` closes a `while`
2998        // header everywhere else.
2999        let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
3000            self.advance().span.end
3001        } else {
3002            parse_arena_result(self.ast.try_expr(condition), &mut self.fatal)?
3003                .span
3004                .end
3005        };
3006        parse_arena_result(
3007            self.ast.try_push_stmt(Stmt {
3008                kind: StmtKind::DoWhile { body, condition },
3009                span: self.span(kw.span.start, end),
3010            }),
3011            &mut self.fatal,
3012        )
3013    }
3014
3015    // Multiple consecutive `case`/`default` labels share the next body.
3016    fn parse_switch(&mut self) -> Option<StmtId> {
3017        let kw = self.advance();
3018        if !matches!(self.peek().kind, TokenKind::LeftParen) {
3019            self.error_at_peek("expected `(` after `switch`");
3020            return None;
3021        }
3022        self.advance();
3023        let discriminant = self.parse_expression()?;
3024        if !matches!(self.peek().kind, TokenKind::RightParen) {
3025            self.error_at_peek("expected `)` after `switch` discriminant");
3026            return None;
3027        }
3028        self.advance();
3029
3030        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
3031            self.error_at_peek("expected `{` to start `switch` body");
3032            return None;
3033        }
3034        self.advance();
3035        self.block_depth += 1;
3036        let arms = self.parse_switch_arms();
3037        self.block_depth -= 1;
3038        let (cases, default) = arms?;
3039
3040        if !matches!(self.peek().kind, TokenKind::RightBrace) {
3041            self.error_at_peek("expected `}` to close `switch` body");
3042            return None;
3043        }
3044        let close = self.advance();
3045
3046        parse_arena_result(
3047            self.ast.try_push_stmt(Stmt {
3048                kind: StmtKind::Switch {
3049                    discriminant,
3050                    cases,
3051                    default,
3052                },
3053                span: self.span(kw.span.start, close.span.end),
3054            }),
3055            &mut self.fatal,
3056        )
3057    }
3058
3059    // Reads the arms up to the closing `}`, which is left for the caller.
3060    fn parse_switch_arms(&mut self) -> Option<(Vec<SwitchCase>, Option<SwitchDefault>)> {
3061        let mut cases: Vec<SwitchCase> = Vec::new();
3062        let mut default: Option<SwitchDefault> = None;
3063        let mut pending_values: Vec<ExprId> = Vec::new();
3064        let mut pending_label_start: Option<u32> = None;
3065        let mut pending_default: Option<Span> = None;
3066
3067        loop {
3068            match self.peek().kind {
3069                TokenKind::Eof => break,
3070                // ASI inserts `;` after `:` on a newline — treat it as a no-op here.
3071                TokenKind::Semicolon => {
3072                    self.advance();
3073                }
3074                TokenKind::RightBrace => {
3075                    // Case bodies are required (typechecker enforces break/return termination).
3076                    if pending_label_start.is_some() {
3077                        self.error_at_peek(
3078                            "expected case body before `}` — case labels must be followed by statements",
3079                        );
3080                        return None;
3081                    }
3082                    break;
3083                }
3084                TokenKind::Case => {
3085                    let case_kw = self.advance();
3086                    if pending_label_start.is_none() {
3087                        pending_label_start = Some(case_kw.span.start);
3088                    }
3089                    let value = self.parse_expression()?;
3090                    if !matches!(self.peek().kind, TokenKind::Colon) {
3091                        self.error_at_peek("expected `:` after `case` label");
3092                        return None;
3093                    }
3094                    self.advance();
3095                    pending_values.push(value);
3096                }
3097                TokenKind::Default => {
3098                    let kw_tok = self.advance();
3099                    if default.is_some() {
3100                        self.error_at(kw_tok.span, "duplicate `default` clause in `switch`");
3101                        return None;
3102                    }
3103                    if !matches!(self.peek().kind, TokenKind::Colon) {
3104                        self.error_at_peek("expected `:` after `default`");
3105                        return None;
3106                    }
3107                    self.advance();
3108                    if pending_label_start.is_none() {
3109                        pending_label_start = Some(kw_tok.span.start);
3110                    }
3111                    pending_default = Some(kw_tok.span);
3112                }
3113                _ => {
3114                    if pending_label_start.is_none() {
3115                        self.error_at_peek(
3116                            "expected `case` or `default` at the start of a `switch` body",
3117                        );
3118                        return None;
3119                    }
3120                    let Some(label_start) = pending_label_start else {
3121                        return self.invariant_failure("switch arm has no label");
3122                    };
3123                    let body = self.parse_switch_arm_body(label_start)?;
3124                    let arm_span =
3125                        parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
3126                    if !pending_values.is_empty() {
3127                        let first = parse_arena_result(
3128                            self.ast.try_expr(pending_values[0]),
3129                            &mut self.fatal,
3130                        )?
3131                        .span
3132                        .start;
3133                        let case_span = self.span(first, arm_span.end);
3134                        cases.push(SwitchCase {
3135                            values: std::mem::take(&mut pending_values),
3136                            body,
3137                            span: case_span,
3138                        });
3139                    }
3140                    if let Some(def_kw_span) = pending_default.take() {
3141                        default = Some(SwitchDefault {
3142                            body,
3143                            span: self.span(def_kw_span.start, arm_span.end),
3144                        });
3145                    }
3146                    pending_label_start = None;
3147                }
3148            }
3149        }
3150        Some((cases, default))
3151    }
3152
3153    // `body_start` is the label keyword's offset so diagnostics point at the label.
3154    fn parse_switch_arm_body(&mut self, body_start: u32) -> Option<StmtId> {
3155        let mut stmts: Vec<StmtId> = Vec::new();
3156        let mut end_offset = body_start;
3157        loop {
3158            if matches!(
3159                self.peek().kind,
3160                TokenKind::Case | TokenKind::Default | TokenKind::RightBrace | TokenKind::Eof
3161            ) {
3162                break;
3163            }
3164            if matches!(self.peek().kind, TokenKind::Semicolon) {
3165                end_offset = self.peek().span.end;
3166                self.advance();
3167                continue;
3168            }
3169            let pos_before = self.pos;
3170            if let Some(id) = self.parse_statement() {
3171                stmts.push(id);
3172                stmts.extend(self.take_trailing_declarators());
3173                let last = stmts.last().copied().unwrap_or(id);
3174                end_offset = parse_arena_result(self.ast.try_stmt(last), &mut self.fatal)?
3175                    .span
3176                    .end;
3177            } else {
3178                self.recover();
3179                if self.pos == pos_before && !self.is_at_eof() {
3180                    self.advance();
3181                }
3182                // Recovery consumed tokens but produced no statement; advance the body
3183                // end past them so the arm span can't end before its own case value.
3184                end_offset = end_offset.max(self.prev_token_end());
3185            }
3186        }
3187        let span = self.span(body_start, end_offset);
3188        parse_arena_result(
3189            self.ast.try_push_stmt(Stmt {
3190                kind: StmtKind::Block(stmts),
3191                span,
3192            }),
3193            &mut self.fatal,
3194        )
3195    }
3196
3197    fn parse_break(&mut self) -> Option<StmtId> {
3198        let kw = self.advance();
3199        if !matches!(self.peek().kind, TokenKind::Semicolon) {
3200            self.error_at_peek("expected `;` after `break`");
3201            return None;
3202        }
3203        let semi = self.advance();
3204        parse_arena_result(
3205            self.ast.try_push_stmt(Stmt {
3206                kind: StmtKind::Break,
3207                span: self.span(kw.span.start, semi.span.end),
3208            }),
3209            &mut self.fatal,
3210        )
3211    }
3212
3213    fn parse_continue(&mut self) -> Option<StmtId> {
3214        let kw = self.advance();
3215        if !matches!(self.peek().kind, TokenKind::Semicolon) {
3216            self.error_at_peek("expected `;` after `continue`");
3217            return None;
3218        }
3219        let semi = self.advance();
3220        parse_arena_result(
3221            self.ast.try_push_stmt(Stmt {
3222                kind: StmtKind::Continue,
3223                span: self.span(kw.span.start, semi.span.end),
3224            }),
3225            &mut self.fatal,
3226        )
3227    }
3228
3229    fn parse_return(&mut self) -> Option<StmtId> {
3230        let kw = self.advance();
3231
3232        let value = if matches!(self.peek().kind, TokenKind::Semicolon) {
3233            None
3234        } else {
3235            Some(self.parse_expression()?)
3236        };
3237
3238        if !self.at_statement_end() {
3239            self.error_at_peek("expected `;` after return");
3240            return None;
3241        }
3242        let end = self.finish_statement();
3243
3244        parse_arena_result(
3245            self.ast.try_push_stmt(Stmt {
3246                kind: StmtKind::Return(value),
3247                span: self.span(kw.span.start, end),
3248            }),
3249            &mut self.fatal,
3250        )
3251    }
3252
3253    fn parse_throw(&mut self) -> Option<StmtId> {
3254        let kw = self.advance();
3255
3256        if matches!(self.peek().kind, TokenKind::Semicolon) {
3257            self.error_at_peek_with_help(
3258                "expected expression after `throw`",
3259                vec!["throw new Error(\"message\");".to_string()],
3260            );
3261            return None;
3262        }
3263
3264        let value = self.parse_expression()?;
3265
3266        if !self.at_statement_end() {
3267            self.error_at_peek("expected `;` after `throw`");
3268            return None;
3269        }
3270        let end = self.finish_statement();
3271
3272        parse_arena_result(
3273            self.ast.try_push_stmt(Stmt {
3274                kind: StmtKind::Throw { value },
3275                span: self.span(kw.span.start, end),
3276            }),
3277            &mut self.fatal,
3278        )
3279    }
3280
3281    fn parse_try(&mut self) -> Option<StmtId> {
3282        let kw = self.advance();
3283
3284        let body = self.parse_block()?;
3285
3286        let mut catches: Vec<CatchClause> = Vec::new();
3287        let mut finally: Option<StmtId> = None;
3288        let mut tail_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3289            .span
3290            .end;
3291
3292        loop {
3293            match self.peek().kind {
3294                TokenKind::Catch => {
3295                    if finally.is_some() {
3296                        self.error_at_peek("`catch` clause must appear before `finally`");
3297                        return None;
3298                    }
3299                    let clause = self.parse_catch_clause()?;
3300                    tail_end = clause.span.end;
3301                    catches.push(clause);
3302                }
3303                TokenKind::Finally => {
3304                    if finally.is_some() {
3305                        self.error_at_peek("duplicate `finally` clause");
3306                        return None;
3307                    }
3308                    self.advance();
3309                    let block = self.parse_block()?;
3310                    tail_end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
3311                        .span
3312                        .end;
3313                    finally = Some(block);
3314                }
3315                _ => break,
3316            }
3317        }
3318
3319        if catches.is_empty() && finally.is_none() {
3320            self.error_at(
3321                self.span(kw.span.start, tail_end),
3322                "try requires at least one of `catch` or `finally`",
3323            );
3324            return None;
3325        }
3326
3327        parse_arena_result(
3328            self.ast.try_push_stmt(Stmt {
3329                kind: StmtKind::Try {
3330                    body,
3331                    catches,
3332                    finally,
3333                },
3334                span: self.span(kw.span.start, tail_end),
3335            }),
3336            &mut self.fatal,
3337        )
3338    }
3339
3340    fn parse_catch_clause(&mut self) -> Option<CatchClause> {
3341        let kw = self.advance();
3342
3343        let (binding, ty) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
3344            // This binding is inaccessible to source code, including nested catches.
3345            (
3346                Ident {
3347                    name: "#catch".into(),
3348                    span: kw.span,
3349                },
3350                None,
3351            )
3352        } else {
3353            self.parse_catch_binding()?
3354        };
3355
3356        let body = self.parse_block()?;
3357        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3358            .span
3359            .end;
3360
3361        Some(CatchClause {
3362            binding,
3363            ty,
3364            body,
3365            span: self.span(kw.span.start, body_end),
3366        })
3367    }
3368
3369    fn parse_catch_binding(&mut self) -> Option<(Ident, Option<TypeAnnotation>)> {
3370        if !matches!(self.peek().kind, TokenKind::LeftParen) {
3371            self.error_at_peek("expected `(` after `catch`");
3372            return None;
3373        }
3374        self.advance();
3375        let name_tok = self.expect_binding_identifier("expected catch binding name")?;
3376        let binding = self.ident_from_token(&name_tok);
3377        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3378            self.advance();
3379            if self.reject_any_catch_annotation() {
3380                // The error is reported; parsing continues as an unannotated catch.
3381                None
3382            } else {
3383                Some(self.parse_type_annotation()?)
3384            }
3385        } else {
3386            None
3387        };
3388        if !matches!(self.peek().kind, TokenKind::RightParen) {
3389            self.error_at_peek("expected `)` after catch binding");
3390            return None;
3391        }
3392        self.advance();
3393        Some((binding, ty))
3394    }
3395
3396    /// Reports `catch (e: any)` and consumes the `any`, returning whether it did.
3397    /// Only exactly `any)` is handled; compound types keep the general diagnostic.
3398    fn reject_any_catch_annotation(&mut self) -> bool {
3399        let is_bare_any = self.peek_identifier_text_is("any")
3400            && matches!(self.peek_at(1).kind, TokenKind::RightParen);
3401        if !is_bare_any {
3402            return false;
3403        }
3404        let any_tok = self.advance();
3405        // The general `any` help suggests `unknown`, which a catch binding refuses.
3406        self.error_at_with_help(
3407            any_tok.span,
3408            "`any` is not supported",
3409            vec![
3410                "write `catch (e)` to catch every thrown error, or name an error class, \
3411                 as in `catch (e: RangeError)`, to catch only that type"
3412                    .to_string(),
3413            ],
3414        );
3415        true
3416    }
3417
3418    fn parse_paren_condition(&mut self) -> Option<ExprId> {
3419        if !matches!(self.peek().kind, TokenKind::LeftParen) {
3420            self.error_at_peek("expected `(`");
3421            return None;
3422        }
3423        self.advance();
3424        let expr = self.parse_expression()?;
3425        if !matches!(self.peek().kind, TokenKind::RightParen) {
3426            self.error_at_peek("expected `)`");
3427            return None;
3428        }
3429        self.advance();
3430        Some(expr)
3431    }
3432
3433    fn parse_predicate_or_return_type(
3434        &mut self,
3435        type_pos: TypePos,
3436    ) -> Option<(
3437        Option<TypeAnnotation>,
3438        Option<crate::TypePredicateAnnotation>,
3439    )> {
3440        if matches!(self.peek().kind, TokenKind::Identifier) && self.peek_at_is_word(1, "is") {
3441            let param_tok = self.advance();
3442            let param = self.ident_from_token(&param_tok);
3443            self.advance();
3444            let asserted = self.parse_type_annotation_in(type_pos)?;
3445            let span = self.span(param.span.start, asserted.span.end);
3446            return Some((
3447                None,
3448                Some(crate::TypePredicateAnnotation {
3449                    param,
3450                    asserted,
3451                    span,
3452                }),
3453            ));
3454        }
3455        Some((Some(self.parse_type_annotation_in(type_pos)?), None))
3456    }
3457
3458    fn parse_type_annotation(&mut self) -> Option<TypeAnnotation> {
3459        self.parse_type_annotation_in(TypePos::Anywhere)
3460    }
3461
3462    fn parse_type_annotation_in(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3463        // A leading `|` (prettier's multiline format) is accepted and discarded.
3464        let leading_pipe = matches!(self.peek().kind, TokenKind::Pipe);
3465        if leading_pipe {
3466            self.advance();
3467        }
3468        let first = self.parse_type_array(type_pos)?;
3469        if !matches!(self.peek().kind, TokenKind::Pipe) {
3470            return Some(first);
3471        }
3472        let start = first.span.start;
3473        let mut end = first.span.end;
3474        let mut members = vec![first];
3475        while matches!(self.peek().kind, TokenKind::Pipe) {
3476            self.advance();
3477            let member = self.parse_type_array(type_pos)?;
3478            end = member.span.end;
3479            members.push(member);
3480        }
3481        Some(TypeAnnotation {
3482            kind: TypeAnnotationKind::Union(members),
3483            span: self.span(start, end),
3484        })
3485    }
3486
3487    fn parse_type_array(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3488        self.with_recursion_limit(|parser| parser.parse_type_array_inner(type_pos))
3489    }
3490
3491    fn parse_type_array_inner(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3492        // `keyof` is contextual, not reserved: TypeScript allows it as an ordinary
3493        // identifier, and so does Submilli. It is an operator only when another type
3494        // follows it, which an identifier in type position never does.
3495        if self.is_contextual_type_operator(self.pos, "keyof") {
3496            let kw = self.advance();
3497            let operand = self.parse_type_array(type_pos)?;
3498            let span = self.span(kw.span.start, operand.span.end);
3499            return Some(TypeAnnotation {
3500                kind: TypeAnnotationKind::KeyOf(Box::new(operand)),
3501                span,
3502            });
3503        }
3504        if self.is_contextual_type_operator(self.pos, "readonly") {
3505            return self.parse_readonly_type(type_pos);
3506        }
3507
3508        let mut ty = match self.peek().kind {
3509            // `typeof x` names the type of a *value*. Unlike `keyof` this is already a
3510            // reserved word (the `typeof x === "string"` guard uses it), so it needs no
3511            // contextual check — in type position it is only ever the operator. Built
3512            // here rather than returned early so the `[]` suffix loop below still runs,
3513            // making `typeof a[]` an array of it, as TypeScript parses it too.
3514            TokenKind::Typeof => {
3515                let kw = self.advance();
3516                let first = self.expect_identifier("expected a value name after `typeof`")?;
3517                let mut path = vec![self.type_name(first.span)?];
3518                let mut end = first.span.end;
3519                while matches!(self.peek().kind, TokenKind::Dot) {
3520                    self.advance();
3521                    let seg = self.expect_identifier_name("expected a property name after `.`")?;
3522                    end = seg.span.end;
3523                    path.push(self.type_name(seg.span)?);
3524                }
3525                TypeAnnotation {
3526                    kind: TypeAnnotationKind::TypeOf { path },
3527                    span: self.span(kw.span.start, end),
3528                }
3529            }
3530            TokenKind::Identifier | TokenKind::Void => {
3531                // `void` never starts a qualified path.
3532                let leading_is_identifier = matches!(self.peek().kind, TokenKind::Identifier);
3533                let tok = self.advance();
3534                let first_span = tok.span;
3535                let mut path_end = first_span.end;
3536                let first_name = self.type_name(first_span)?;
3537                let mut path = vec![first_name.clone()];
3538                if leading_is_identifier {
3539                    while matches!(self.peek().kind, TokenKind::Dot) {
3540                        self.advance();
3541                        if !matches!(self.peek().kind, TokenKind::Identifier) {
3542                            self.error_at_peek("expected identifier after `.` in type name");
3543                            return None;
3544                        }
3545                        let seg = self.advance();
3546                        path_end = seg.span.end;
3547                        path.push(self.type_name(seg.span)?);
3548                    }
3549                }
3550                // Type arguments, like the `[]` suffix, must start on the type's line.
3551                let (args, end) = if matches!(self.peek().kind, TokenKind::LessThan)
3552                    && !self.line_break_before_peek()
3553                {
3554                    self.parse_type_argument_list()?
3555                } else {
3556                    (Vec::new(), path_end)
3557                };
3558                let outer_span = self.span(first_span.start, end);
3559                let kind = if path.len() == 1 {
3560                    if first_name.name == "any" {
3561                        self.error_at_with_help(
3562                            first_span,
3563                            "`any` is not supported",
3564                            vec![
3565                                "annotate a concrete type instead — an object shape \
3566                                 like `{ id: string }`, a named `interface`, or \
3567                                 `unknown` (a safe dynamic type you narrow before use)"
3568                                    .to_string(),
3569                            ],
3570                        );
3571                    }
3572                    TypeAnnotationKind::Name {
3573                        name: first_name,
3574                        args,
3575                    }
3576                } else {
3577                    TypeAnnotationKind::Qualified { path, args }
3578                };
3579                TypeAnnotation {
3580                    kind,
3581                    span: outer_span,
3582                }
3583            }
3584            // `null<T>` is meaningless — no type args after `null`.
3585            TokenKind::NullLiteral => {
3586                let tok = self.advance();
3587                TypeAnnotation {
3588                    kind: TypeAnnotationKind::Name {
3589                        name: self.type_name(tok.span)?,
3590                        args: Vec::new(),
3591                    },
3592                    span: tok.span,
3593                }
3594            }
3595            TokenKind::StringLiteral(_) => {
3596                let tok = self.advance();
3597                let span = tok.span;
3598                let TokenKind::StringLiteral(s) = tok.kind else {
3599                    // advance returns the token just matched, before moving the cursor.
3600                    unreachable!("advance preserves the matched StringLiteral variant");
3601                };
3602                TypeAnnotation {
3603                    kind: TypeAnnotationKind::StringLiteral(s),
3604                    span,
3605                }
3606            }
3607            TokenKind::NumberLiteral(_) => {
3608                let tok = self.advance();
3609                let span = tok.span;
3610                let TokenKind::NumberLiteral(v) = tok.kind else {
3611                    // advance returns the token just matched, before moving the cursor.
3612                    unreachable!("advance preserves the matched NumberLiteral variant");
3613                };
3614                // Canonicalize `-0.0` → `0.0` so literal type `0` matches both signs.
3615                let canonical = if v == 0.0 { 0.0 } else { v };
3616                TypeAnnotation {
3617                    kind: TypeAnnotationKind::NumberLiteral(crate::types::LiteralF64(canonical)),
3618                    span,
3619                }
3620            }
3621            TokenKind::BigIntLiteral(_) => {
3622                let tok = self.advance();
3623                let TokenKind::BigIntLiteral(digits) = tok.kind else {
3624                    // advance returns the token just matched, before moving the cursor.
3625                    unreachable!("advance preserves the matched BigIntLiteral variant");
3626                };
3627                TypeAnnotation {
3628                    kind: TypeAnnotationKind::BigIntLiteral(digits),
3629                    span: tok.span,
3630                }
3631            }
3632            TokenKind::Minus
3633                if matches!(
3634                    self.peek_at(1).kind,
3635                    TokenKind::NumberLiteral(_) | TokenKind::BigIntLiteral(_)
3636                ) =>
3637            {
3638                let minus = self.advance().span;
3639                let tok = self.advance();
3640                let span = self.span(minus.start, tok.span.end);
3641                let kind =
3642                    match tok.kind {
3643                        TokenKind::NumberLiteral(v) => TypeAnnotationKind::NumberLiteral(
3644                            crate::types::LiteralF64(if v == 0.0 { 0.0 } else { -v }),
3645                        ),
3646                        TokenKind::BigIntLiteral(digits) => TypeAnnotationKind::BigIntLiteral(
3647                            crate::types::negate_bigint_digits(&digits),
3648                        ),
3649                        // The guard admits only the two numeric literal tokens.
3650                        _ => unreachable!("guarded to a numeric literal after `-`"),
3651                    };
3652                TypeAnnotation { kind, span }
3653            }
3654            TokenKind::BooleanLiteral(value) => {
3655                let span = self.advance().span;
3656                TypeAnnotation {
3657                    kind: TypeAnnotationKind::BooleanLiteral(value),
3658                    span,
3659                }
3660            }
3661            TokenKind::LeftBrace => self.parse_object_type_annotation()?,
3662            TokenKind::LeftParen if self.paren_at_opens_function_type(self.pos, type_pos) => {
3663                self.parse_function_type_annotation()?
3664            }
3665            TokenKind::LeftParen => self.parse_grouped_or_function_type()?,
3666            // Leading `[` is unambiguously a tuple (postfix `[]` can only follow a base type).
3667            TokenKind::LeftBracket => self.parse_tuple_type_annotation()?,
3668            _ => {
3669                self.error_at_peek("expected type");
3670                return None;
3671            }
3672        };
3673        // Array suffixes nest boxed annotations without recursing, so each one
3674        // spends the recursive grammar budget to keep the chain's drop bounded.
3675        let mut array_suffixes = 0usize;
3676        // As in TypeScript, a line break ends the type before `[`: in an object type,
3677        // `a: B` ⏎ `[k: string]: V` is a field followed by an index signature.
3678        while matches!(self.peek().kind, TokenKind::LeftBracket) && !self.line_break_before_peek() {
3679            array_suffixes += 1;
3680            if self.recursion_depth.saturating_add(array_suffixes) >= MAX_PARSE_DEPTH {
3681                self.stop_at_recursion_limit();
3682                return None;
3683            }
3684            let open = self.advance();
3685            if !matches!(self.peek().kind, TokenKind::RightBracket) {
3686                self.error_at(open.span, "expected `]` to close array type");
3687                return None;
3688            }
3689            let close = self.advance();
3690            let span = self.span(ty.span.start, close.span.end);
3691            ty = TypeAnnotation {
3692                kind: TypeAnnotationKind::Array(Box::new(ty)),
3693                span,
3694            };
3695        }
3696        Some(ty)
3697    }
3698
3699    /// `readonly T[]` or `readonly [A, B]`. The operand binds like `keyof`'s, so
3700    /// `readonly T[][]` is a readonly array of mutable arrays and
3701    /// `readonly T[] | null` a union with a readonly member.
3702    fn parse_readonly_type(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3703        let kw = self.advance();
3704        let opens_group = matches!(self.peek().kind, TokenKind::LeftParen);
3705        let operand = self.parse_type_array(type_pos)?;
3706        // `readonly (T[])` is rejected as TypeScript rejects it: the operand must be
3707        // written as an array or tuple type, not grouped into one.
3708        let grouped = opens_group
3709            && matches!(
3710                self.pos
3711                    .checked_sub(1)
3712                    .and_then(|index| self.tokens.get(index))
3713                    .map(|token| &token.kind),
3714                Some(TokenKind::RightParen)
3715            );
3716        if grouped
3717            || !matches!(
3718                operand.kind,
3719                TypeAnnotationKind::Array(_) | TypeAnnotationKind::Tuple(_)
3720            )
3721        {
3722            self.error_at_with_help(
3723                kw.span,
3724                "`readonly` only applies to array and tuple types",
3725                vec![
3726                    "write `readonly T[]` or `readonly [A, B]`; to protect an object's \
3727                     properties, mark each one `readonly` instead"
3728                        .to_string(),
3729                ],
3730            );
3731            return Some(operand);
3732        }
3733        let span = self.span(kw.span.start, operand.span.end);
3734        Some(TypeAnnotation {
3735            kind: TypeAnnotationKind::Readonly(Box::new(operand)),
3736            span,
3737        })
3738    }
3739
3740    /// In type position `(` is ambiguous: a function type's parameter list, or a
3741    /// grouping paren the postfix `[]` / `|` then composes with. Decided by what the
3742    /// parens *contain*, not by what follows them — a trailing `=>` can belong to an
3743    /// enclosing arrow function's body (`(): (string | null) => null`), so looking
3744    /// past the `)` would read that return type as a parameter list. The exception is
3745    /// `(T)` / `(T, U)`, spelled identically either way, which only a trailing `=>`
3746    /// can break.
3747    ///
3748    /// Empty parens count as a parameter list: `()` groups nothing, so the
3749    /// function-type parse's "expected `=>`" is the message that names the fix.
3750    fn paren_at_opens_function_type(&self, index: usize, type_pos: TypePos) -> bool {
3751        let after = |offset: usize| self.tokens.get(index + offset).map(|t| &t.kind);
3752        match (after(1), after(2)) {
3753            // Only a parameter list is empty or starts with a rest element.
3754            (Some(TokenKind::RightParen | TokenKind::DotDotDot), _) => true,
3755            // A named, annotated parameter — no type starts this way.
3756            (Some(TokenKind::Identifier), Some(TokenKind::Colon | TokenKind::Question)) => true,
3757            // `(T)` and `(T, U)` are spelled exactly like a grouped type. v1
3758            // rejects bare parameter names, and a trailing `=>` is the only sign
3759            // the writer meant one — routing those to the function-type parse
3760            // keeps its "params require named annotations" message reachable in
3761            // declaration position (`type F = (T) => R`). In an arrow's *return*
3762            // annotation the trailing `=>` is the body's, so `(): (T) => x` loses
3763            // that message and falls back to the expression parse; dropping the
3764            // redundant parens is the fix there.
3765            (Some(TokenKind::Identifier), Some(TokenKind::Comma | TokenKind::RightParen)) => {
3766                type_pos == TypePos::Anywhere
3767                    && self.index_after_matching_paren(index).is_some_and(|i| {
3768                        matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow))
3769                    })
3770            }
3771            // Anything else can only begin a type, so the parens group.
3772            _ => false,
3773        }
3774    }
3775
3776    /// Parse a `(` the contents rule called a group. If that fails and a `=>` follows
3777    /// the matching `)`, the author was writing a function type after all — a typo in
3778    /// its parameter list is what made it look like a group — so re-parse that way to
3779    /// recover the diagnostic naming the parameter rule instead of a stray-`)` error.
3780    fn parse_grouped_or_function_type(&mut self) -> Option<TypeAnnotation> {
3781        let open = self.pos;
3782        let diagnostics_before = self.diagnostics.len();
3783        if let Some(ty) = self.parse_grouped_type_annotation() {
3784            return Some(ty);
3785        }
3786        let arrow_follows = self
3787            .index_after_matching_paren(open)
3788            .is_some_and(|i| matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)));
3789        if !arrow_follows {
3790            return None;
3791        }
3792        self.pos = open;
3793        self.diagnostics.truncate(diagnostics_before);
3794        self.parse_function_type_annotation()
3795    }
3796
3797    /// A parenthesized type is its inner type — the parens only group, so no node
3798    /// records them. The span covers them so a diagnostic points at what was written.
3799    fn parse_grouped_type_annotation(&mut self) -> Option<TypeAnnotation> {
3800        let open = self.advance();
3801        let inner = self.parse_type_annotation()?;
3802        if !matches!(self.peek().kind, TokenKind::RightParen) {
3803            self.error_at_peek("expected `)` to close a parenthesized type");
3804            return None;
3805        }
3806        let close = self.advance();
3807        Some(TypeAnnotation {
3808            span: self.span(open.span.start, close.span.end),
3809            ..inner
3810        })
3811    }
3812
3813    // Param names are required — bare `(T, U) => R` is rejected.
3814    fn parse_function_type_annotation(&mut self) -> Option<TypeAnnotation> {
3815        let open = self.advance();
3816        let params = self.parse_function_type_params()?;
3817        if !matches!(self.peek().kind, TokenKind::Arrow) {
3818            self.error_at_peek("expected `=>` in function-type annotation");
3819            return None;
3820        }
3821        self.advance();
3822        let return_type = self.parse_type_annotation()?;
3823        let end = return_type.span.end;
3824        Some(TypeAnnotation {
3825            kind: TypeAnnotationKind::Function {
3826                params,
3827                return_type: Box::new(return_type),
3828            },
3829            span: self.span(open.span.start, end),
3830        })
3831    }
3832
3833    /// The `(…)` of a function type, its `(` already consumed and its `)` consumed here.
3834    /// Shared with a type literal's method members, which spell the same parameter list
3835    /// before a `:` rather than a `=>`.
3836    fn parse_function_type_params(&mut self) -> Option<Vec<TypeAnnotationField>> {
3837        let mut params: Vec<TypeAnnotationField> = Vec::new();
3838        if !matches!(self.peek().kind, TokenKind::RightParen) {
3839            loop {
3840                let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
3841                    self.advance();
3842                    true
3843                } else {
3844                    false
3845                };
3846                let name_tok = self.expect_binding_identifier(if rest {
3847                    "expected identifier after `...` in function-type annotation"
3848                } else {
3849                    "expected parameter name in function-type annotation"
3850                })?;
3851                let name = self.ident_from_token(&name_tok);
3852                let optional = self.parse_parameter_optional(rest, false)?;
3853                if !matches!(self.peek().kind, TokenKind::Colon) {
3854                    if rest {
3855                        self.error_at_peek_with_help(
3856                            "rest parameter requires a type annotation",
3857                            vec!["(...args: T[]) => R".to_string()],
3858                        );
3859                    } else {
3860                        self.error_at_peek(
3861                            "expected `:` after parameter name (function-type params \
3862                             require named annotations in v1)",
3863                        );
3864                    }
3865                    return None;
3866                }
3867                self.advance();
3868                let ty = self.parse_type_annotation()?;
3869                if rest && !is_rest_array_annotation(&ty) {
3870                    self.error_at(ty.span, "rest parameter type must be an array");
3871                    return None;
3872                }
3873                if matches!(self.peek().kind, TokenKind::Equals) {
3874                    if rest {
3875                        self.error_rest_parameter_default();
3876                    } else {
3877                        self.error_at_peek(
3878                            "default values in function-type annotations are not yet supported",
3879                        );
3880                    }
3881                    return None;
3882                }
3883                params.push(TypeAnnotationField {
3884                    name,
3885                    ty,
3886                    optional,
3887                    readonly: false,
3888                    rest,
3889                    method: false,
3890                });
3891                match self.peek().kind {
3892                    TokenKind::Comma => {
3893                        if rest {
3894                            let comma = self.advance();
3895                            self.error_at(comma.span, "rest parameter must be the last parameter");
3896                            return None;
3897                        }
3898                        self.advance();
3899                        if matches!(self.peek().kind, TokenKind::RightParen) {
3900                            break;
3901                        }
3902                    }
3903                    TokenKind::RightParen => break,
3904                    _ => {
3905                        self.error_at_peek("expected `,` or `)`");
3906                        return None;
3907                    }
3908                }
3909            }
3910            self.check_parameter_order(params.iter().map(ParamOrder::of_field));
3911        }
3912        if !matches!(self.peek().kind, TokenKind::RightParen) {
3913            self.error_at_peek("expected `)`");
3914            return None;
3915        }
3916        self.advance();
3917        Some(params)
3918    }
3919
3920    fn parse_tuple_type_annotation(&mut self) -> Option<TypeAnnotation> {
3921        let open = self.advance();
3922        if matches!(self.peek().kind, TokenKind::RightBracket) {
3923            let close = self.peek().span;
3924            self.error_at(
3925                self.span(open.span.start, close.end),
3926                "tuple types must have at least one element",
3927            );
3928            return None;
3929        }
3930        let mut elements: Vec<TypeAnnotation> = Vec::new();
3931        loop {
3932            if matches!(self.peek().kind, TokenKind::DotDotDot) {
3933                self.error_at_with_help(
3934                    self.peek().span,
3935                    "rest elements in tuple types are not supported",
3936                    vec!["use an array type `T[]` for a list of varying length".to_string()],
3937                );
3938                return None;
3939            }
3940            // Labels may be mixed with unlabeled elements, as TypeScript allows
3941            // since 5.2.
3942            let label = self.skip_tuple_element_label();
3943            let mut ty = self.parse_type_annotation()?;
3944            let suffix_optional = matches!(self.peek().kind, TokenKind::Question);
3945            if suffix_optional && label != TupleElementLabel::None {
3946                self.error_at_peek_with_help(
3947                    "put the optional marker after the tuple element label",
3948                    vec!["write `name?: T` instead of `name: T?`".to_string()],
3949                );
3950                return None;
3951            }
3952            let optional = label == TupleElementLabel::Optional || suffix_optional;
3953            if optional {
3954                let end = if suffix_optional {
3955                    self.advance().span.end
3956                } else {
3957                    ty.span.end
3958                };
3959                let span = self.span(ty.span.start, end);
3960                ty = TypeAnnotation {
3961                    kind: TypeAnnotationKind::Optional(Box::new(ty)),
3962                    span,
3963                };
3964            } else if elements
3965                .iter()
3966                .any(|element| matches!(element.kind, TypeAnnotationKind::Optional(_)))
3967            {
3968                self.error_at(
3969                    ty.span,
3970                    "a required tuple element cannot follow an optional element",
3971                );
3972                return None;
3973            }
3974            elements.push(ty);
3975            match self.peek().kind {
3976                TokenKind::Comma => {
3977                    self.advance();
3978                    if matches!(self.peek().kind, TokenKind::RightBracket) {
3979                        break;
3980                    }
3981                }
3982                TokenKind::RightBracket => break,
3983                _ => {
3984                    self.error_at_peek("expected `,` or `]`");
3985                    return None;
3986                }
3987            }
3988        }
3989        if !matches!(self.peek().kind, TokenKind::RightBracket) {
3990            self.error_at_peek("expected `]`");
3991            return None;
3992        }
3993        let close = self.advance();
3994        Some(TypeAnnotation {
3995            kind: TypeAnnotationKind::Tuple(elements),
3996            span: self.span(open.span.start, close.span.end),
3997        })
3998    }
3999
4000    /// Labels document positions; retain only whether the label was optional.
4001    fn skip_tuple_element_label(&mut self) -> TupleElementLabel {
4002        if !is_property_name(&self.peek().kind) {
4003            return TupleElementLabel::None;
4004        }
4005        match self.peek_at(1).kind {
4006            TokenKind::Colon => {
4007                self.advance();
4008                self.advance();
4009                TupleElementLabel::Required
4010            }
4011            TokenKind::Question if matches!(self.peek_at(2).kind, TokenKind::Colon) => {
4012                self.advance();
4013                self.advance();
4014                self.advance();
4015                TupleElementLabel::Optional
4016            }
4017            _ => TupleElementLabel::None,
4018        }
4019    }
4020
4021    /// `[]` or `[]: V` — an index signature with no parameter, which TypeScript
4022    /// reads and rejects.
4023    fn peek_is_parameterless_index_signature(&self) -> bool {
4024        matches!(self.peek().kind, TokenKind::LeftBracket)
4025            && matches!(self.peek_at(1).kind, TokenKind::RightBracket)
4026    }
4027
4028    /// Diagnoses and skips a parameterless index signature (see
4029    /// `peek_is_parameterless_index_signature`) so the members after it still parse.
4030    /// `member_start` is where the member begins, at a `readonly` modifier if any.
4031    fn skip_parameterless_index_signature(&mut self, member_start: u32) -> Option<()> {
4032        self.advance();
4033        let close = self.advance();
4034        self.error_at_with_help(
4035            self.span(member_start, close.span.end),
4036            "an index signature must declare exactly one parameter",
4037            vec!["write `[key: string]: V`".to_string()],
4038        );
4039        if matches!(self.peek().kind, TokenKind::Colon) {
4040            self.advance();
4041            self.parse_type_annotation()?;
4042        }
4043        Some(())
4044    }
4045
4046    fn parse_index_signature(&mut self, readonly: bool) -> Option<crate::IndexSignatureAnnotation> {
4047        let open = self.advance();
4048        let parameter = self.expect_property_ident("expected index parameter name")?;
4049        self.reject_eval_or_arguments(parameter.span);
4050        if !matches!(self.peek().kind, TokenKind::Colon) {
4051            self.error_at_peek("expected `:` after index parameter name");
4052            return None;
4053        }
4054        self.advance();
4055        if !self.peek_identifier_text_is("string") {
4056            self.error_at_peek("index signatures require string keys; use `[key: string]: V`");
4057            return None;
4058        }
4059        self.advance();
4060        if !matches!(self.peek().kind, TokenKind::RightBracket) {
4061            self.error_at_peek("expected `]` after string index key type");
4062            return None;
4063        }
4064        self.advance();
4065        if !matches!(self.peek().kind, TokenKind::Colon) {
4066            self.error_at_peek("expected `:` before index value type");
4067            return None;
4068        }
4069        self.advance();
4070        let value = self.parse_type_annotation()?;
4071        Some(crate::IndexSignatureAnnotation {
4072            span: self.span(open.span.start, value.span.end),
4073            value,
4074            readonly,
4075        })
4076    }
4077
4078    fn parse_object_type_annotation(&mut self) -> Option<TypeAnnotation> {
4079        let open = self.advance();
4080        let mut fields: Vec<TypeAnnotationField> = Vec::new();
4081        let mut index = None;
4082        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
4083            && !self.peek_starts_top_level_item()
4084        {
4085            let member_start = self.peek().span.start;
4086            let readonly = self.eat_readonly_property_modifier();
4087            if self.peek_is_parameterless_index_signature() {
4088                self.skip_parameterless_index_signature(member_start)?;
4089            } else if self.peek_is_construct_signature() {
4090                self.reject_construct_signature()?;
4091            } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
4092                let signature = self.parse_index_signature(readonly)?;
4093                if index.is_some() {
4094                    self.error_at_with_help(
4095                        signature.span,
4096                        "duplicate string index signature",
4097                        vec![],
4098                    );
4099                }
4100                index = Some(Box::new(signature));
4101            } else {
4102                let field = self.parse_object_type_field(readonly, member_start)?;
4103                if let Some(existing) = fields.iter().find(|f| f.name.name == field.name.name) {
4104                    self.diagnostics.push(Diagnostic {
4105                        severity: Severity::Error,
4106                        span: field.name.span,
4107                        message: format!("duplicate field `{}` in object type", field.name.name),
4108                        help: vec![],
4109                        notes: vec![(existing.name.span, "first defined here".into())],
4110                    });
4111                }
4112                fields.push(field);
4113            }
4114            self.finish_type_member(self.prev_token_end(), "expected `;`, `,`, or `}`")?;
4115        }
4116        if !matches!(self.peek().kind, TokenKind::RightBrace) {
4117            self.error_at_peek("expected `}`");
4118            return None;
4119        }
4120        let close = self.advance();
4121        Some(TypeAnnotation {
4122            kind: TypeAnnotationKind::Object { index, fields },
4123            span: self.span(open.span.start, close.span.end),
4124        })
4125    }
4126
4127    /// A declaration or an `import` statement: what a top-level item starts with.
4128    /// `import(…)` and `import.meta` are expressions, not statements.
4129    fn peek_starts_top_level_item(&self) -> bool {
4130        let import_statement = matches!(self.peek().kind, TokenKind::Import)
4131            && matches!(
4132                self.peek_at(1).kind,
4133                TokenKind::LeftBrace
4134                    | TokenKind::Star
4135                    | TokenKind::Identifier
4136                    | TokenKind::StringLiteral(_)
4137            );
4138        import_statement || self.peek_starts_declaration()
4139    }
4140
4141    /// Whether the next tokens start a declaration (`function f`, `class C`, `type T`,
4142    /// …, optionally after `export`, `declare`, `async` or `abstract`) or an export
4143    /// statement (`export {`, `export *`, `export default`), which no type or class
4144    /// member can: a keyword is a member name only before `:`, `?` or `(`. A member
4145    /// list that reaches one was left unclosed, so it stops there and leaves the
4146    /// declaration to be parsed.
4147    fn peek_starts_declaration(&self) -> bool {
4148        if matches!(self.peek().kind, TokenKind::Export)
4149            && matches!(
4150                self.peek_at(1).kind,
4151                TokenKind::Default | TokenKind::LeftBrace | TokenKind::Star
4152            )
4153        {
4154            return true;
4155        }
4156        let mut offset = 0;
4157        while self.peek_at_is_declaration_modifier(offset) {
4158            offset += 1;
4159        }
4160        let declaration_keyword = matches!(
4161            self.peek_at(offset).kind,
4162            TokenKind::Function
4163                | TokenKind::Class
4164                | TokenKind::Let
4165                | TokenKind::Const
4166                | TokenKind::Enum
4167                | TokenKind::Interface
4168        ) || self.peek_at_is_word(offset, "type");
4169        declaration_keyword && matches!(self.peek_at(offset + 1).kind, TokenKind::Identifier)
4170    }
4171
4172    /// `export`, `declare`, `async` or `abstract` before a declaration keyword.
4173    fn peek_at_is_declaration_modifier(&self, offset: usize) -> bool {
4174        matches!(self.peek_at(offset).kind, TokenKind::Export)
4175            || ["declare", "async", "abstract"]
4176                .iter()
4177                .any(|word| self.peek_at_is_word(offset, word))
4178    }
4179
4180    /// `new (params): T` or `new <T>(params): T` inside a type literal: a construct
4181    /// signature, which TypeScript reads where it would otherwise see a method named
4182    /// `new`. A quoted `"new"()`, an optional `new?()` and a field `new: T` are
4183    /// ordinary members, as in TypeScript.
4184    fn peek_is_construct_signature(&self) -> bool {
4185        matches!(self.peek().kind, TokenKind::New)
4186            && matches!(
4187                self.peek_at(1).kind,
4188                TokenKind::LeftParen | TokenKind::LessThan
4189            )
4190    }
4191
4192    /// Only interfaces declare construct signatures, so one in a type literal (see
4193    /// `peek_is_construct_signature`) is diagnosed, then skipped rather than misread
4194    /// as a method named `new`.
4195    fn reject_construct_signature(&mut self) -> Option<()> {
4196        let keyword = self.advance();
4197        self.error_at_with_help(
4198            keyword.span,
4199            "construct signatures are not supported in object types",
4200            vec![
4201                "declare an `interface` with the `new (…)` signature, or pass a factory \
4202                 function such as `() => T`"
4203                    .to_string(),
4204            ],
4205        );
4206        if matches!(self.peek().kind, TokenKind::LessThan) {
4207            self.parse_generic_param_list()?;
4208        }
4209        if !matches!(self.peek().kind, TokenKind::LeftParen) {
4210            self.error_at_peek("expected `(` after construct signature type parameters");
4211            return None;
4212        }
4213        self.parse_object_type_method_signature(keyword.span.start)?;
4214        Some(())
4215    }
4216
4217    /// `name: T`, `name?: T` or the method signature `name(params): T`.
4218    fn parse_object_type_field(
4219        &mut self,
4220        readonly: bool,
4221        member_start: u32,
4222    ) -> Option<TypeAnnotationField> {
4223        let name = self.expect_property_ident("expected field name in object type")?;
4224        // `name?: T` — omittable at construction; reads widen to `T | undefined`.
4225        let optional = matches!(self.peek().kind, TokenKind::Question);
4226        if optional {
4227            self.advance();
4228        }
4229        let method = matches!(self.peek().kind, TokenKind::LeftParen);
4230        let ty = if method {
4231            if readonly {
4232                self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
4233            }
4234            self.parse_object_type_method_signature(name.span.start)?
4235        } else {
4236            if !matches!(self.peek().kind, TokenKind::Colon) {
4237                self.error_at_peek("expected `:` after field name");
4238                return None;
4239            }
4240            self.advance();
4241            self.parse_type_annotation()?
4242        };
4243        Some(TypeAnnotationField {
4244            name,
4245            ty,
4246            optional,
4247            readonly,
4248            rest: false,
4249            method,
4250        })
4251    }
4252
4253    /// `m(params): T` inside a type literal, spelled as the function-typed field
4254    /// `m: (params) => T` — the two are interchangeable in a structural shape, and
4255    /// interfaces already accept both spellings for the same member. `start` is the
4256    /// member name's offset, so the annotation's span covers the name too.
4257    fn parse_object_type_method_signature(&mut self, start: u32) -> Option<TypeAnnotation> {
4258        self.advance();
4259        let params = self.parse_function_type_params()?;
4260        if !matches!(self.peek().kind, TokenKind::Colon) {
4261            self.error_at_peek("expected `:` and return type after method parameters");
4262            return None;
4263        }
4264        self.advance();
4265        let return_type = self.parse_type_annotation()?;
4266        let end = return_type.span.end;
4267        Some(TypeAnnotation {
4268            kind: TypeAnnotationKind::Function {
4269                params,
4270                return_type: Box::new(return_type),
4271            },
4272            span: self.span(start, end),
4273        })
4274    }
4275
4276    /// An identifier that names a binding or a type, which rejects the words strict
4277    /// mode reserves (see [`STRICT_MODE_RESERVED_WORDS`]).
4278    fn expect_identifier(&mut self, message: &str) -> Option<Token> {
4279        let tok = self.expect_identifier_name(message)?;
4280        self.reject_strict_mode_reserved_word(&tok);
4281        Some(tok)
4282    }
4283
4284    /// The name a variable, parameter, function or `catch` binds. Strict mode also
4285    /// forbids `eval` and `arguments` there, though not as a class, enum or type
4286    /// name (TypeScript's TS1100 covers the same positions).
4287    fn expect_binding_identifier(&mut self, message: &str) -> Option<Token> {
4288        let tok = self.expect_identifier(message)?;
4289        self.reject_eval_or_arguments(tok.span);
4290        Some(tok)
4291    }
4292
4293    /// An identifier in a position where strict mode's reserved words are allowed: an
4294    /// enum member, a property, or the name an import specifier refers to.
4295    fn expect_identifier_name(&mut self, message: &str) -> Option<Token> {
4296        if matches!(self.peek().kind, TokenKind::Identifier) {
4297            return Some(self.advance());
4298        }
4299        if is_reserved_identifier_word(&self.peek().kind) {
4300            let tok = self.advance();
4301            let keyword = &self.source[tok.span.start as usize..tok.span.end as usize];
4302            self.error_at_with_help(
4303                tok.span,
4304                format!("`{keyword}` is a reserved keyword and can't be used as a name"),
4305                vec![format!(
4306                    "rename it, for example: `{}`",
4307                    reserved_keyword_rename_example(keyword)
4308                )],
4309            );
4310            return None;
4311        }
4312        self.error_at_peek(message.to_string());
4313        None
4314    }
4315
4316    /// Reports a binding named by a word strict mode reserves. Parsing continues
4317    /// with the name: nothing else about it is wrong.
4318    fn reject_strict_mode_reserved_word(&mut self, tok: &Token) {
4319        let word = &self.source[tok.span.start as usize..tok.span.end as usize];
4320        if !STRICT_MODE_RESERVED_WORDS.contains(&word) {
4321            return;
4322        }
4323        self.error_at_with_help(
4324            tok.span,
4325            format!("`{word}` is a reserved word in strict mode and can't be used as a name"),
4326            vec![format!(
4327                "rename it, for example: `{}`",
4328                reserved_keyword_rename_example(word)
4329            )],
4330        );
4331    }
4332
4333    /// Reports a binding named `eval` or `arguments`. Parsing continues with the
4334    /// name: nothing else about it is wrong.
4335    fn reject_eval_or_arguments(&mut self, span: Span) {
4336        let word = &self.source[span.start as usize..span.end as usize];
4337        if !matches!(word, "eval" | "arguments") {
4338            return;
4339        }
4340        self.error_at_with_help(
4341            span,
4342            format!("`{word}` can't name a binding in strict mode"),
4343            vec![format!(
4344                "rename it, for example: `{}`",
4345                reserved_keyword_rename_example(word)
4346            )],
4347        );
4348    }
4349
4350    fn ident_from_token(&self, tok: &Token) -> Ident {
4351        Ident {
4352            name: self.source[tok.span.start as usize..tok.span.end as usize].to_string(),
4353            span: tok.span,
4354        }
4355    }
4356
4357    fn property_ident_from_token(&self, tok: &Token) -> Ident {
4358        if let TokenKind::StringLiteral(s) = &tok.kind {
4359            Ident {
4360                name: s.clone(),
4361                span: tok.span,
4362            }
4363        } else {
4364            self.ident_from_token(tok)
4365        }
4366    }
4367
4368    fn expect_property_name(&mut self, message: &str) -> Option<Token> {
4369        if !is_property_name(&self.peek().kind) {
4370            self.error_at_peek(message.to_string());
4371            return None;
4372        }
4373        Some(self.advance())
4374    }
4375
4376    fn expect_property_ident(&mut self, message: &str) -> Option<Ident> {
4377        if !is_property_name(&self.peek().kind)
4378            && !matches!(self.peek().kind, TokenKind::StringLiteral(_))
4379        {
4380            self.error_at_peek(message.to_string());
4381            return None;
4382        }
4383        let tok = self.advance();
4384        Some(self.property_ident_from_token(&tok))
4385    }
4386
4387    /// The span of a `readonly` modifier that [`Parser::eat_readonly_property_modifier`]
4388    /// consumed at `member_start`.
4389    fn readonly_modifier_span(&self, member_start: u32) -> Span {
4390        self.span(member_start, member_start + "readonly".len() as u32)
4391    }
4392
4393    /// Reports `readonly` on a method, accessor or method signature, as TypeScript
4394    /// does (TS1024). Parsing continues: the modifier changes nothing else.
4395    fn reject_readonly_modifier(&mut self, span: Span) {
4396        self.error_at_with_help(
4397            span,
4398            "`readonly` can only modify a property or index signature",
4399            vec!["remove `readonly`".to_string()],
4400        );
4401    }
4402
4403    fn eat_readonly_property_modifier(&mut self) -> bool {
4404        if self.peek_identifier_text_is("readonly")
4405            && !self.line_break_after_peek()
4406            && (self.peek_starts_property_after_readonly()
4407                || matches!(self.peek_at(1).kind, TokenKind::LeftBracket))
4408        {
4409            self.advance();
4410            return true;
4411        }
4412        false
4413    }
4414
4415    fn peek_starts_property_after_readonly(&self) -> bool {
4416        let name = &self.peek_at(1).kind;
4417        let after_name = &self.peek_at(2).kind;
4418        let after_optional = &self.peek_at(3).kind;
4419        // A member starts its type with `:`, or its parameter list with `(` — otherwise
4420        // `readonly` is the member's own name.
4421        let starts_member_type =
4422            |kind: &TokenKind| matches!(kind, TokenKind::Colon | TokenKind::LeftParen);
4423        (is_property_name(name) || matches!(name, TokenKind::StringLiteral(_)))
4424            && (starts_member_type(after_name)
4425                || matches!(after_name, TokenKind::Question) && starts_member_type(after_optional))
4426    }
4427
4428    /// `as` and `satisfies`, which lex as identifiers but continue an expression.
4429    fn peek_at_is_binary_word(&self, offset: usize) -> bool {
4430        self.peek_at_is_word(offset, "as") || self.peek_at_is_word(offset, "satisfies")
4431    }
4432
4433    fn peek_identifier_text_is(&self, expected: &str) -> bool {
4434        self.token_is_word(self.peek(), expected)
4435    }
4436
4437    fn peek_at_is_word(&self, offset: usize, expected: &str) -> bool {
4438        self.token_is_word(self.peek_at(offset), expected)
4439    }
4440
4441    fn token_is_word(&self, tok: &Token, expected: &str) -> bool {
4442        matches!(tok.kind, TokenKind::Identifier)
4443            && &self.source[tok.span.start as usize..tok.span.end as usize] == expected
4444    }
4445
4446    /// `delete` is lexed as an identifier: `map.delete(k)`, a `delete()` method and
4447    /// `submilli:http`'s `delete(url)` function all use the name. So it is the
4448    /// operator only when an operand that can't be an argument list follows it, and
4449    /// `delete (o.x)` stays a call. `as` and `satisfies` are lexed as identifiers too:
4450    /// `delete as unknown` casts the function value, while `delete as.x` deletes from a
4451    /// binding named `as`.
4452    fn at_delete_operator(&self) -> bool {
4453        if !self.peek_identifier_text_is("delete") {
4454            return false;
4455        }
4456        if !matches!(
4457            self.peek_at(1).kind,
4458            TokenKind::Identifier | TokenKind::This
4459        ) {
4460            return false;
4461        }
4462        let next_is_cast_keyword =
4463            self.peek_at_is_word(1, "as") || self.peek_at_is_word(1, "satisfies");
4464        !next_is_cast_keyword || cannot_start_type(&self.peek_at(2).kind)
4465    }
4466
4467    /// `type X …` commits to a type-alias declaration only when followed by an
4468    /// identifier, mirroring TypeScript; `type = 5`, `type;`, `type(x)` stay
4469    /// expression statements over a binding named `type`.
4470    fn at_type_alias_head(&self) -> bool {
4471        self.peek_identifier_text_is("type")
4472            && matches!(self.peek_at(1).kind, TokenKind::Identifier)
4473    }
4474
4475    /// Assignment is the lowest-precedence expression and right-associative:
4476    /// `a = b = c` assigns `c` to both, and yields it.
4477    fn parse_expression(&mut self) -> Option<ExprId> {
4478        self.with_recursion_limit(Self::parse_expression_inner)
4479    }
4480
4481    fn parse_expression_inner(&mut self) -> Option<ExprId> {
4482        let written = self.parse_conditional()?;
4483        let shift_assignment = self.peek_shift().filter(|shift| shift.assignment);
4484        if !is_assign_lookahead(&self.peek().kind) && shift_assignment.is_none() {
4485            return Some(written);
4486        }
4487        let target_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4488        let target = self.assignment_target(written)?;
4489        let op_tok = self.advance();
4490        let mut op_span = op_tok.span;
4491        let op = if let Some(ShiftToken { op, tokens, .. }) = shift_assignment {
4492            for _ in 1..tokens {
4493                op_span.end = self.advance().span.end;
4494            }
4495            Some(op)
4496        } else {
4497            compound_op_for_token(&op_tok.kind)
4498        };
4499        let value = self.parse_expression()?;
4500        let value_span = parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
4501        parse_arena_result(
4502            self.ast.try_push_expr(Expr {
4503                kind: ExprKind::Assign {
4504                    target,
4505                    op,
4506                    op_span,
4507                    value,
4508                },
4509                span: self.span(target_span.start, value_span.end),
4510            }),
4511            &mut self.fatal,
4512        )
4513    }
4514
4515    /// The binding, field, or element `written` names, through any parentheses:
4516    /// `(a) = 1` assigns `a`, as in JavaScript.
4517    fn assignment_target(&mut self, written: ExprId) -> Option<ExprId> {
4518        let mut target = written;
4519        while let ExprKind::Paren(inner) =
4520            parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind
4521        {
4522            target = inner;
4523        }
4524        if matches!(
4525            parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind,
4526            ExprKind::Identifier(_) | ExprKind::FieldAccess { .. } | ExprKind::IndexAccess { .. }
4527        ) {
4528            return Some(target);
4529        }
4530        let error_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4531        self.error_at_with_help(
4532            error_span,
4533            "invalid assignment target",
4534            vec![
4535                "assign to a variable, a field (`o.f = …`), or an element (`a[i] = …`)".to_string(),
4536            ],
4537        );
4538        None
4539    }
4540
4541    fn parse_conditional(&mut self) -> Option<ExprId> {
4542        self.reject_async_modifier();
4543        if self.is_arrow_start() {
4544            return self.parse_arrow();
4545        }
4546        let cond = self.parse_binary(0)?;
4547        // Ternary sits below every binary op; right-associative.
4548        if !matches!(self.peek().kind, TokenKind::Question) {
4549            return Some(cond);
4550        }
4551        self.advance();
4552        let then_ = self.parse_expression()?;
4553        if !matches!(self.peek().kind, TokenKind::Colon) {
4554            self.error_at_peek_with_help(
4555                "expected `:` to complete ternary expression",
4556                vec!["const x = cond ? then : else;".to_string()],
4557            );
4558            return None;
4559        }
4560        self.advance();
4561        let else_ = self.parse_expression()?;
4562        let cond_span = parse_arena_result(self.ast.try_expr(cond), &mut self.fatal)?.span;
4563        let else_span = parse_arena_result(self.ast.try_expr(else_), &mut self.fatal)?.span;
4564        parse_arena_result(
4565            self.ast.try_push_expr(Expr {
4566                kind: ExprKind::Ternary { cond, then_, else_ },
4567                span: self.span(cond_span.start, else_span.end),
4568            }),
4569            &mut self.fatal,
4570        )
4571    }
4572
4573    fn is_arrow_start(&self) -> bool {
4574        match self.peek().kind {
4575            TokenKind::Identifier => matches!(self.peek_at(1).kind, TokenKind::Arrow),
4576            TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos),
4577            _ => false,
4578        }
4579    }
4580
4581    /// Reject an `async` that modifies a function, arrow or method, and keep a
4582    /// binding named `async` as it is.
4583    fn reject_async_modifier(&mut self) {
4584        if !self.peek_identifier_text_is("async") {
4585            return;
4586        }
4587        let modifies = match self.peek_at(1).kind {
4588            TokenKind::Function => true,
4589            // `async (x) => …`, not a call of a function named `async`.
4590            TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos + 1),
4591            // `async x => …`, or a method `async run(…)` / `async run<T>(…)`.
4592            // `async in (o)` and `async as (T)` are operators on a binding named
4593            // `async`.
4594            ref name
4595                if is_property_name(name)
4596                    && !matches!(name, TokenKind::In)
4597                    && !self.peek_at_is_binary_word(1) =>
4598            {
4599                matches!(
4600                    self.peek_at(2).kind,
4601                    TokenKind::Arrow | TokenKind::LeftParen | TokenKind::LessThan
4602                )
4603            }
4604            _ => false,
4605        };
4606        if modifies {
4607            self.report_async_modifier();
4608        }
4609    }
4610
4611    /// Reject an `async` that starts a class or object-literal method. A member's
4612    /// `async` is never a value an operator applies to, so `async *run`,
4613    /// `async in()` and `async "s"()` are methods too. After a line break,
4614    /// `async` is a member of its own, as in TypeScript.
4615    fn reject_async_member_modifier(&mut self) {
4616        if !self.peek_identifier_text_is("async") || self.line_break_after_peek() {
4617            return;
4618        }
4619        let modifies = match self.peek_at(1).kind {
4620            TokenKind::Star
4621            | TokenKind::LeftBracket
4622            | TokenKind::StringLiteral(_)
4623            | TokenKind::NumberLiteral(_) => true,
4624            ref name if is_property_name(name) => {
4625                matches!(
4626                    self.peek_at(2).kind,
4627                    TokenKind::LeftParen | TokenKind::LessThan
4628                )
4629            }
4630            _ => false,
4631        };
4632        if modifies {
4633            self.report_async_modifier();
4634        }
4635    }
4636
4637    /// There are no promises here, so name the rewrite, then consume `async`
4638    /// and parse on as if it were absent: the body's `await`s are reported in
4639    /// the same run.
4640    fn report_async_modifier(&mut self) {
4641        let tok = self.advance();
4642        self.error_at_with_help(
4643            tok.span,
4644            "`async` functions are not supported",
4645            vec![
4646                "calls return their value directly: remove `async`, write the return type \
4647                 `T` instead of `Promise<T>`, and drop `await`"
4648                    .to_string(),
4649            ],
4650        );
4651    }
4652
4653    /// `await` followed on the same line by an operand that cannot continue a
4654    /// binding named `await`: an identifier, a keyword, a literal or a template. `await(x)`
4655    /// and `await[i]` stay a call and an index; the typechecker names the fix if
4656    /// `await` then does not resolve. Calls are synchronous, so report it and
4657    /// parse on: the operand stays, and one run names every `await` in the program.
4658    fn reject_await_operator(&mut self) -> bool {
4659        if !self.peek_identifier_text_is("await") {
4660            return false;
4661        }
4662        let operand_starts = matches!(
4663            self.peek_at(1).kind,
4664            TokenKind::Identifier
4665                | TokenKind::This
4666                | TokenKind::Super
4667                | TokenKind::New
4668                | TokenKind::Function
4669                | TokenKind::StringLiteral(_)
4670                | TokenKind::NumberLiteral(_)
4671                | TokenKind::NullLiteral
4672                | TokenKind::BooleanLiteral(_)
4673                // A tagged template is not supported, so a template after `await`
4674                // can only be its operand.
4675                | TokenKind::TemplateHead(_)
4676                | TokenKind::TemplateNoSubstitution(_)
4677        ) && !self.peek_at_is_binary_word(1);
4678        if !operand_starts || self.line_break_after_peek() {
4679            return false;
4680        }
4681        let tok = self.advance();
4682        self.error_at_with_help(
4683            tok.span,
4684            "`await` is not supported",
4685            vec!["calls return their value directly: remove `await`".to_string()],
4686        );
4687        true
4688    }
4689
4690    /// Index just past the `)` matching the `(` at `index`; `None` when the parens
4691    /// never balance. Pure lookahead — emits no diagnostics.
4692    fn index_after_matching_paren(&self, index: usize) -> Option<usize> {
4693        if !matches!(
4694            self.tokens.get(index).map(|t| &t.kind),
4695            Some(TokenKind::LeftParen)
4696        ) {
4697            return None;
4698        }
4699        self.scan_past_balanced(
4700            index,
4701            |k| matches!(k, TokenKind::LeftParen),
4702            |k| matches!(k, TokenKind::RightParen),
4703        )
4704    }
4705
4706    fn find_arrow_after_matching_paren(&self, open: usize) -> bool {
4707        let Some(i) = self.index_after_matching_paren(open) else {
4708            return false;
4709        };
4710        let after_type = if matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Colon)) {
4711            match self.scan_past_type_annotation(i + 1, TypePos::ArrowReturn) {
4712                Some(p) => p,
4713                None => return false,
4714            }
4715        } else {
4716            i
4717        };
4718        matches!(
4719            self.tokens.get(after_type).map(|t| &t.kind),
4720            Some(TokenKind::Arrow),
4721        )
4722    }
4723
4724    // Pure lookahead used only by the arrow-disambiguator. No diagnostics.
4725    // Must accept every form `parse_type_annotation` accepts: a form missing
4726    // here makes the disambiguator reject a valid arrow, so the expression
4727    // parser reports a misleading `expected expression` at the return type.
4728    // `arrow_return_type_scanner_matches_type_grammar` pins the two together.
4729    fn scan_past_type_annotation(&self, start: usize, type_pos: TypePos) -> Option<usize> {
4730        self.scan_past_type_annotation_at_depth(start, type_pos, 0)
4731    }
4732
4733    fn scan_past_type_annotation_at_depth(
4734        &self,
4735        start: usize,
4736        type_pos: TypePos,
4737        depth: usize,
4738    ) -> Option<usize> {
4739        if depth >= MAX_PARSE_DEPTH {
4740            return None;
4741        }
4742        if matches!(
4743            self.tokens.get(start).map(|t| &t.kind),
4744            Some(TokenKind::Identifier)
4745        ) && self
4746            .tokens
4747            .get(start + 1)
4748            .is_some_and(|t| self.token_is_word(t, "is"))
4749        {
4750            return self.scan_past_type_annotation_at_depth(start + 2, type_pos, depth + 1);
4751        }
4752        // Leading `|` (prettier's multiline union format).
4753        let start = if matches!(
4754            self.tokens.get(start).map(|t| &t.kind),
4755            Some(TokenKind::Pipe)
4756        ) {
4757            start + 1
4758        } else {
4759            start
4760        };
4761        let mut i = self.scan_past_single_type_member(start, type_pos, depth + 1)?;
4762        while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Pipe)) {
4763            i = self.scan_past_single_type_member(i + 1, type_pos, depth + 1)?;
4764        }
4765        Some(i)
4766    }
4767
4768    fn scan_past_single_type_member(
4769        &self,
4770        start: usize,
4771        type_pos: TypePos,
4772        depth: usize,
4773    ) -> Option<usize> {
4774        if depth >= MAX_PARSE_DEPTH {
4775            return None;
4776        }
4777        if self.is_contextual_type_operator(start, "keyof")
4778            || self.is_contextual_type_operator(start, "readonly")
4779        {
4780            return self.scan_past_single_type_member(start + 1, type_pos, depth + 1);
4781        }
4782        let mut i = start;
4783        match self.tokens.get(i)?.kind {
4784            TokenKind::Identifier | TokenKind::Void => {
4785                i += 1;
4786                while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Dot)) {
4787                    if !matches!(
4788                        self.tokens.get(i + 1).map(|t| &t.kind),
4789                        Some(TokenKind::Identifier)
4790                    ) {
4791                        return None;
4792                    }
4793                    i += 2;
4794                }
4795                if matches!(
4796                    self.tokens.get(i).map(|t| &t.kind),
4797                    Some(TokenKind::LessThan)
4798                ) {
4799                    i = self.scan_past_balanced(
4800                        i,
4801                        |k| matches!(k, TokenKind::LessThan),
4802                        |k| matches!(k, TokenKind::GreaterThan),
4803                    )?;
4804                }
4805            }
4806            TokenKind::NullLiteral
4807            | TokenKind::StringLiteral(_)
4808            | TokenKind::NumberLiteral(_)
4809            | TokenKind::BooleanLiteral(_) => {
4810                i += 1;
4811            }
4812            TokenKind::LeftBrace => {
4813                i = self.scan_past_balanced(
4814                    i,
4815                    |k| matches!(k, TokenKind::LeftBrace),
4816                    |k| matches!(k, TokenKind::RightBrace),
4817                )?;
4818            }
4819            TokenKind::LeftBracket => {
4820                i = self.scan_past_balanced(
4821                    i,
4822                    |k| matches!(k, TokenKind::LeftBracket),
4823                    |k| matches!(k, TokenKind::RightBracket),
4824                )?;
4825            }
4826            // Same `(`-disambiguation `parse_type_array` uses, and it has to agree:
4827            // a function type carries its own `=>`, so everything after it is the
4828            // return type and the scan ends with that. Grouping parens instead fall
4829            // through to the postfix `[]` loop below.
4830            TokenKind::LeftParen => {
4831                let is_function_type = self.paren_at_opens_function_type(i, type_pos);
4832                i = self.scan_past_balanced(
4833                    i,
4834                    |k| matches!(k, TokenKind::LeftParen),
4835                    |k| matches!(k, TokenKind::RightParen),
4836                )?;
4837                if is_function_type {
4838                    if !matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)) {
4839                        return None;
4840                    }
4841                    return self.scan_past_type_annotation_at_depth(
4842                        i + 1,
4843                        TypePos::Anywhere,
4844                        depth + 1,
4845                    );
4846                }
4847            }
4848            _ => return None,
4849        }
4850        while matches!(
4851            self.tokens.get(i).map(|t| &t.kind),
4852            Some(TokenKind::LeftBracket)
4853        ) {
4854            i += 1;
4855            if !matches!(
4856                self.tokens.get(i).map(|t| &t.kind),
4857                Some(TokenKind::RightBracket)
4858            ) {
4859                return None;
4860            }
4861            i += 1;
4862        }
4863        Some(i)
4864    }
4865
4866    // `start` must index the opening token; returns the index just past the
4867    // matching close, or None if the tokens run out first.
4868    fn scan_past_balanced(
4869        &self,
4870        start: usize,
4871        is_open: fn(&TokenKind) -> bool,
4872        is_close: fn(&TokenKind) -> bool,
4873    ) -> Option<usize> {
4874        let mut depth: i32 = 1;
4875        let mut i = start + 1;
4876        while i < self.tokens.len() && depth > 0 {
4877            let token = self.tokens.get(i)?;
4878            let kind = &token.kind;
4879            if is_open(kind) {
4880                depth += 1;
4881            } else if is_close(kind) {
4882                depth -= 1;
4883            } else if matches!(kind, TokenKind::Eof) {
4884                return None;
4885            }
4886            i += 1;
4887        }
4888        if depth == 0 { Some(i) } else { None }
4889    }
4890
4891    fn parse_arrow(&mut self) -> Option<ExprId> {
4892        let start_span = self.peek().span;
4893
4894        let params = match self.peek().kind {
4895            TokenKind::Identifier => {
4896                let name_tok = self.advance();
4897                self.reject_eval_or_arguments(name_tok.span);
4898                let name = self.ident_from_token(&name_tok);
4899                vec![ParamDecl {
4900                    name,
4901                    ty: None,
4902                    default: None,
4903                    optional: false,
4904                    pattern: None,
4905                    rest: false,
4906                    modifiers: None,
4907                }]
4908            }
4909            TokenKind::LeftParen => {
4910                self.advance();
4911                let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4912                    Vec::new()
4913                } else {
4914                    self.parse_arrow_param_list()?
4915                };
4916                if !matches!(self.peek().kind, TokenKind::RightParen) {
4917                    self.error_at_peek("expected `)`");
4918                    return None;
4919                }
4920                self.advance();
4921                params
4922            }
4923            _ => return self.invariant_failure("is_arrow_start guarded the entry"),
4924        };
4925
4926        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4927            self.advance();
4928            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4929        } else {
4930            (None, None)
4931        };
4932
4933        if !matches!(self.peek().kind, TokenKind::Arrow) {
4934            self.error_at_peek("expected `=>` in arrow function");
4935            return None;
4936        }
4937        // Like TypeScript, the arrow still parses, so later errors keep their spans.
4938        if self.line_break_before_peek() {
4939            let arrow_span = self.peek().span;
4940            self.error_at(arrow_span, "a line break is not allowed before `=>`");
4941        }
4942        self.advance();
4943
4944        let (body, end_pos) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
4945            let block = self.parse_block()?;
4946            let span = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?.span;
4947            (ArrowBody::Block(block), span.end)
4948        } else {
4949            let expr = self.parse_expression()?;
4950            let span = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?.span;
4951            (ArrowBody::Expr(expr), span.end)
4952        };
4953
4954        parse_arena_result(
4955            self.ast.try_push_expr(Expr {
4956                kind: ExprKind::Arrow {
4957                    params,
4958                    return_type,
4959                    type_predicate,
4960                    body,
4961                },
4962                span: self.span(start_span.start, end_pos),
4963            }),
4964            &mut self.fatal,
4965        )
4966    }
4967
4968    /// Function expressions share parameter/body parsing with arrows, but retain
4969    /// their own receiver and optional recursive-name binding.
4970    fn parse_function_expression(&mut self) -> Option<ExprId> {
4971        let kw = self.advance();
4972
4973        if matches!(self.peek().kind, TokenKind::LessThan) {
4974            self.error_at_peek("generic function expressions are not supported");
4975            return None;
4976        }
4977
4978        let self_name = if matches!(self.peek().kind, TokenKind::Identifier) {
4979            let name_tok = self.advance();
4980            self.reject_eval_or_arguments(name_tok.span);
4981            Some(self.ident_from_token(&name_tok))
4982        } else {
4983            None
4984        };
4985
4986        if !matches!(self.peek().kind, TokenKind::LeftParen) {
4987            self.error_at_peek("expected `(` after `function`");
4988            return None;
4989        }
4990        self.advance();
4991
4992        let this_type = if matches!(self.peek().kind, TokenKind::This) {
4993            self.advance();
4994            if !matches!(self.peek().kind, TokenKind::Colon) {
4995                self.error_at_peek("expected a type annotation after `this`");
4996                return None;
4997            }
4998            self.advance();
4999            let ty = self.parse_type_annotation()?;
5000            if matches!(self.peek().kind, TokenKind::Comma) {
5001                self.advance();
5002            } else if !matches!(self.peek().kind, TokenKind::RightParen) {
5003                self.error_at_peek("expected `,` or `)` after the `this` parameter");
5004                return None;
5005            }
5006            Some(ty)
5007        } else {
5008            None
5009        };
5010        let params = if matches!(self.peek().kind, TokenKind::RightParen) {
5011            Vec::new()
5012        } else {
5013            let saved_class = self.class_member_body_depth;
5014            let saved_function = self.function_expression_body_depth;
5015            self.class_member_body_depth = 0;
5016            self.function_expression_body_depth = 1;
5017            let params = self.parse_arrow_param_list();
5018            self.class_member_body_depth = saved_class;
5019            self.function_expression_body_depth = saved_function;
5020            params?
5021        };
5022        if !matches!(self.peek().kind, TokenKind::RightParen) {
5023            self.error_at_peek("expected `)`");
5024            return None;
5025        }
5026        self.advance();
5027
5028        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
5029            self.advance();
5030            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
5031        } else {
5032            (None, None)
5033        };
5034
5035        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
5036            self.error_at_peek("expected `{` to open the function body");
5037            return None;
5038        }
5039        let saved = self.class_member_body_depth;
5040        self.class_member_body_depth = 0;
5041        self.function_expression_body_depth += 1;
5042        let block = self.parse_block();
5043        self.function_expression_body_depth -= 1;
5044        self.class_member_body_depth = saved;
5045        let block = block?;
5046        let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
5047            .span
5048            .end;
5049
5050        let function = parse_arena_result(
5051            self.ast.try_push_expr(Expr {
5052                kind: ExprKind::Arrow {
5053                    params,
5054                    return_type,
5055                    type_predicate,
5056                    body: ArrowBody::Block(block),
5057                },
5058                span: self.span(kw.span.start, end),
5059            }),
5060            &mut self.fatal,
5061        )?;
5062        parse_arena_result(
5063            self.ast.try_push_expr(Expr {
5064                kind: ExprKind::FunctionExpression {
5065                    name: self_name,
5066                    this_type,
5067                    function,
5068                },
5069                span: self.span(kw.span.start, end),
5070            }),
5071            &mut self.fatal,
5072        )
5073    }
5074
5075    /// Shorthand methods still use arrow lowering. Reject their receiver syntax
5076    /// so an enclosing class or function receiver cannot leak into the body.
5077    fn parse_outer_this_boundary<T>(
5078        &mut self,
5079        parse: impl FnOnce(&mut Self) -> Option<T>,
5080    ) -> Option<T> {
5081        let saved = self.class_member_body_depth;
5082        self.class_member_body_depth = 0;
5083        let saved_function = self.function_expression_body_depth;
5084        self.function_expression_body_depth = 0;
5085        let parsed = parse(self);
5086        self.class_member_body_depth = saved;
5087        self.function_expression_body_depth = saved_function;
5088        parsed
5089    }
5090
5091    fn parse_arrow_param_list(&mut self) -> Option<Vec<ParamDecl>> {
5092        let mut params = Vec::new();
5093        loop {
5094            let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
5095                self.advance();
5096                true
5097            } else {
5098                false
5099            };
5100            // The lowering pass rewrites expression-body arrows to block form when any param
5101            // has a destructuring pattern.
5102            let (name, pattern) = match self.peek().kind {
5103                TokenKind::LeftBrace | TokenKind::LeftBracket => {
5104                    if rest {
5105                        self.error_at_peek("rest parameter cannot be destructured");
5106                        return None;
5107                    }
5108                    let binding = self.parse_binding()?;
5109                    let placeholder = Ident {
5110                        name: String::new(),
5111                        span: binding.span(),
5112                    };
5113                    (placeholder, Some(binding))
5114                }
5115                _ => {
5116                    let name_tok = self.expect_binding_identifier(if rest {
5117                        "expected identifier after `...`"
5118                    } else {
5119                        "expected parameter name"
5120                    })?;
5121                    let name = self.ident_from_token(&name_tok);
5122                    (name, None)
5123                }
5124            };
5125            let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
5126            let ty = if matches!(self.peek().kind, TokenKind::Colon) {
5127                self.advance();
5128                Some(self.parse_type_annotation()?)
5129            } else {
5130                // Rest without annotation is OK at parse time; the typechecker fills it
5131                // from a contextual function-type hint if available.
5132                None
5133            };
5134            let default = self.parse_parameter_default(rest, optional)?;
5135            params.push(ParamDecl {
5136                name,
5137                ty,
5138                default,
5139                optional,
5140                pattern,
5141                rest,
5142                modifiers: None,
5143            });
5144            match self.peek().kind {
5145                TokenKind::Comma => {
5146                    if rest {
5147                        let comma = self.advance();
5148                        self.error_at(comma.span, "rest parameter must be the last parameter");
5149                        return None;
5150                    }
5151                    self.advance();
5152                    if matches!(self.peek().kind, TokenKind::RightParen) {
5153                        break;
5154                    }
5155                }
5156                TokenKind::RightParen => break,
5157                _ => {
5158                    self.error_at_peek("expected `,` or `)`");
5159                    return None;
5160                }
5161            }
5162        }
5163        self.check_parameter_order(params.iter().map(ParamOrder::of_param));
5164        Some(params)
5165    }
5166
5167    fn parse_binary(&mut self, min_prec: u8) -> Option<ExprId> {
5168        self.with_recursion_limit(|parser| parser.parse_binary_inner(min_prec))
5169    }
5170
5171    /// Angle tokens stay separate for nested type arguments. Only adjacent
5172    /// source tokens form a shift; whitespace and comments cannot join them.
5173    fn peek_shift(&self) -> Option<ShiftToken> {
5174        let first = self.peek();
5175        let second = self.peek_at(1);
5176        if first.span.end != second.span.start {
5177            return None;
5178        }
5179        match (&first.kind, &second.kind) {
5180            (TokenKind::LessThan, TokenKind::LessThan) => {
5181                Some(ShiftToken::new(BinOp::Shl, 2, false))
5182            }
5183            (TokenKind::LessThan, TokenKind::LessEquals) => {
5184                Some(ShiftToken::new(BinOp::Shl, 2, true))
5185            }
5186            (TokenKind::GreaterThan, TokenKind::GreaterEquals) => {
5187                Some(ShiftToken::new(BinOp::Shr, 2, true))
5188            }
5189            (TokenKind::GreaterThan, TokenKind::GreaterThan) => {
5190                let third = self.peek_at(2);
5191                if second.span.end == third.span.start {
5192                    match third.kind {
5193                        TokenKind::GreaterThan => {
5194                            return Some(ShiftToken::new(BinOp::UnsignedShr, 3, false));
5195                        }
5196                        TokenKind::GreaterEquals => {
5197                            return Some(ShiftToken::new(BinOp::UnsignedShr, 3, true));
5198                        }
5199                        _ => {}
5200                    }
5201                }
5202                Some(ShiftToken::new(BinOp::Shr, 2, false))
5203            }
5204            _ => None,
5205        }
5206    }
5207
5208    fn peek_binary(&self) -> Option<BinaryToken> {
5209        if let Some(shift) = self.peek_shift() {
5210            return (!shift.assignment).then_some(BinaryToken {
5211                op: shift.op,
5212                precedence: SHIFT_PRECEDENCE,
5213                tokens: shift.tokens,
5214            });
5215        }
5216        peek_binop(&self.peek().kind).map(|(op, precedence)| BinaryToken {
5217            op,
5218            precedence,
5219            tokens: 1,
5220        })
5221    }
5222
5223    fn parse_binary_inner(&mut self, min_prec: u8) -> Option<ExprId> {
5224        let mut lhs = self.parse_cast()?;
5225        // Track the previous op to reject `a || b ?? c` / `a ?? b || c` mixes. Both
5226        // reach this loop: `??` parses its right operand above `&&` (see
5227        // `right_operand_min_prec`), so `||` / `&&` after it is left for this check.
5228        let mut last_op: Option<BinOp> = None;
5229        let mut reported_mixing = false;
5230        while let Some(BinaryToken {
5231            op,
5232            precedence: prec,
5233            tokens: count,
5234        }) = self.peek_binary()
5235        {
5236            if prec < min_prec {
5237                break;
5238            }
5239            if !reported_mixing
5240                && let Some(last) = last_op
5241                && mixed_logical(last, op)
5242            {
5243                reported_mixing = true;
5244                let op_span = self.peek().span;
5245                // Parsing continues as if the left side were grouped, so one error
5246                // inside an `if (...)` header doesn't cascade through the statement.
5247                // Later mixes in the same chain add nothing, so only the first is reported.
5248                self.error_at_with_help(
5249                    op_span,
5250                    "mixing `??` with `||` / `&&` requires parentheses",
5251                    vec![mixed_logical_help(last, op)],
5252                );
5253            }
5254            if op == BinOp::Pow
5255                && matches!(
5256                    parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.kind,
5257                    ExprKind::Unary { .. }
5258                        | ExprKind::Typeof { .. }
5259                        | ExprKind::Delete { .. }
5260                        | ExprKind::Void { .. }
5261                )
5262            {
5263                let error_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5264                self.error_at_with_help(
5265                    error_span,
5266                    "an unparenthesized unary expression cannot be the left operand of `**`",
5267                    vec!["choose the grouping explicitly: `(-x) ** 2` or `-(x ** 2)`".into()],
5268                );
5269                return None;
5270            }
5271            let loose = matches!(self.peek().kind, TokenKind::EqEq | TokenKind::BangEq);
5272            for _ in 0..count {
5273                self.advance();
5274            }
5275            let rhs = self.parse_binary(right_operand_min_prec(op, prec))?;
5276            let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5277            let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
5278            if loose {
5279                self.reject_loose_nullish_comparison(op, lhs, rhs)?;
5280            }
5281            lhs = parse_arena_result(
5282                self.ast.try_push_expr(Expr {
5283                    kind: ExprKind::Binary { op, lhs, rhs },
5284                    span: self.span(lhs_span.start, rhs_span.end),
5285                }),
5286                &mut self.fatal,
5287            )?;
5288            last_op = Some(op);
5289        }
5290        Some(lhs)
5291    }
5292
5293    // Higher than every binary op, lower than every unary prefix. Left-associative.
5294    fn parse_cast(&mut self) -> Option<ExprId> {
5295        let mut expr = self.parse_unary()?;
5296        while matches!(self.peek().kind, TokenKind::Instanceof)
5297            || self.peek_identifier_text_is("as")
5298        {
5299            let is_instanceof = matches!(self.peek().kind, TokenKind::Instanceof);
5300            self.advance();
5301            let ty = self.parse_type_annotation()?;
5302            let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5303                .span
5304                .start;
5305            let end = ty.span.end;
5306            let kind = if is_instanceof {
5307                ExprKind::InstanceOf { value: expr, ty }
5308            } else {
5309                self.last_as_type_end = Some(end);
5310                ExprKind::As { expr, ty }
5311            };
5312            expr = parse_arena_result(
5313                self.ast.try_push_expr(Expr {
5314                    kind,
5315                    span: self.span(start, end),
5316                }),
5317                &mut self.fatal,
5318            )?;
5319        }
5320        Some(expr)
5321    }
5322
5323    fn parse_unary(&mut self) -> Option<ExprId> {
5324        self.with_recursion_limit(Self::parse_unary_inner)
5325    }
5326
5327    fn parse_unary_inner(&mut self) -> Option<ExprId> {
5328        if self.reject_await_operator() {
5329            return self.parse_unary();
5330        }
5331        // `typeof` is parsed as a unary prefix so `typeof x === "T"` works without a new layer;
5332        // the typechecker rejects `Typeof` outside the recognized equality fold position.
5333        if matches!(self.peek().kind, TokenKind::Typeof) {
5334            return self.parse_prefix_expr(|operand| ExprKind::Typeof { operand });
5335        }
5336        if matches!(self.peek().kind, TokenKind::Void) {
5337            return self.parse_prefix_expr(|operand| ExprKind::Void { operand });
5338        }
5339        if self.at_delete_operator() {
5340            return self.parse_prefix_expr(|operand| ExprKind::Delete { operand });
5341        }
5342        if matches!(self.peek().kind, TokenKind::New) {
5343            let op_tok = self.advance();
5344            let mut callee = self.parse_atom()?;
5345            while matches!(self.peek().kind, TokenKind::Dot) {
5346                self.advance();
5347                if !matches!(self.peek().kind, TokenKind::Identifier) {
5348                    self.error_at_peek("expected identifier after `.` in constructor name");
5349                    return None;
5350                }
5351                let ident_tok = self.advance();
5352                let name = self.ident_from_token(&ident_tok);
5353                let receiver_span =
5354                    parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5355                let combined_span = self.span(receiver_span.start, ident_tok.span.end);
5356                callee = parse_arena_result(
5357                    self.ast.try_push_expr(Expr {
5358                        kind: ExprKind::FieldAccess {
5359                            receiver: callee,
5360                            name,
5361                        },
5362                        span: combined_span,
5363                    }),
5364                    &mut self.fatal,
5365                )?;
5366            }
5367            let type_args = if matches!(self.peek().kind, TokenKind::LessThan) {
5368                let saved_pos = self.pos;
5369                let saved_splits = self.split_type_tokens.len();
5370                let saved_diag_len = self.diagnostics.len();
5371                if let Some(args) = self.try_parse_type_args_only() {
5372                    Some(args)
5373                } else {
5374                    self.restore_type_tokens(saved_splits);
5375                    self.pos = saved_pos;
5376                    self.diagnostics.truncate(saved_diag_len);
5377                    None
5378                }
5379            } else {
5380                None
5381            };
5382            if !matches!(self.peek().kind, TokenKind::LeftParen) {
5383                self.error_at_peek("expected `(` after constructor name in `new` expression");
5384                return None;
5385            }
5386            self.advance();
5387            let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5388                Vec::new()
5389            } else {
5390                self.parse_call_args()?
5391            };
5392            if !matches!(self.peek().kind, TokenKind::RightParen) {
5393                self.error_at_peek("expected `)`");
5394                return None;
5395            }
5396            let close = self.advance();
5397            let new_expr = parse_arena_result(
5398                self.ast.try_push_expr(Expr {
5399                    kind: ExprKind::New {
5400                        callee,
5401                        type_args,
5402                        args,
5403                    },
5404                    span: self.span(op_tok.span.start, close.span.end),
5405                }),
5406                &mut self.fatal,
5407            )?;
5408            // `new Foo()` is a postfix primary: `new Map().set(...)`, `new Map().size`,
5409            // `new Foo()[i]` all continue the chain off the constructor result.
5410            return self.parse_postfix_from(new_expr);
5411        }
5412        let op = match self.peek().kind {
5413            TokenKind::Bang => UnOp::Not,
5414            TokenKind::Tilde => UnOp::BitNot,
5415            TokenKind::Minus => UnOp::Neg,
5416            TokenKind::Plus => UnOp::Pos,
5417            _ => return self.parse_postfix(),
5418        };
5419        self.parse_prefix_expr(|operand| ExprKind::Unary { op, operand })
5420    }
5421
5422    /// Consume a prefix operator and its operand, spanning both.
5423    fn parse_prefix_expr(&mut self, kind: impl FnOnce(ExprId) -> ExprKind) -> Option<ExprId> {
5424        let op_tok = self.advance();
5425        // ASI cannot distinguish the `void` type from the expression operator.
5426        // In expression position a newline after the keyword still awaits an operand.
5427        if matches!(op_tok.kind, TokenKind::Void) && self.at_inserted_semicolon() {
5428            self.advance();
5429        }
5430        let operand = self.parse_unary()?;
5431        let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5432        parse_arena_result(
5433            self.ast.try_push_expr(Expr {
5434                kind: kind(operand),
5435                span: self.span(op_tok.span.start, operand_span.end),
5436            }),
5437            &mut self.fatal,
5438        )
5439    }
5440
5441    fn parse_postfix(&mut self) -> Option<ExprId> {
5442        let expr = self.parse_atom()?;
5443        self.parse_postfix_from(expr)
5444    }
5445
5446    fn parse_postfix_from(&mut self, mut expr: ExprId) -> Option<ExprId> {
5447        // OptionalChain is emitted once at end-of-loop so a speculative-`<` rewind
5448        // never strands a half-built chain. Everything before the first `?.` accumulates
5449        // in `expr` normally; from the first `?.` onward ops go into `parts`.
5450        let mut chain_parts: Option<(ExprId, Vec<crate::ChainPart>)> = None;
5451        loop {
5452            match self.peek().kind {
5453                TokenKind::LeftParen => {
5454                    if let Some((_, parts)) = &mut chain_parts {
5455                        let part = self.parse_chain_call_tail(/*optional=*/ false)?;
5456                        parts.push(part);
5457                    } else {
5458                        expr = self.parse_call_tail(expr, None)?;
5459                    }
5460                }
5461                TokenKind::Dot => {
5462                    if let Some((_, parts)) = &mut chain_parts {
5463                        let part = self.parse_chain_field_tail(/*optional=*/ false)?;
5464                        parts.push(part);
5465                    } else {
5466                        expr = self.parse_field_access_tail(expr)?;
5467                    }
5468                }
5469                TokenKind::LeftBracket => {
5470                    if let Some((_, parts)) = &mut chain_parts {
5471                        let part = self.parse_chain_index_tail(/*optional=*/ false)?;
5472                        parts.push(part);
5473                    } else {
5474                        expr = self.parse_index_access_tail(expr)?;
5475                    }
5476                }
5477                TokenKind::QuestionDot => {
5478                    if chain_parts.is_none() {
5479                        chain_parts = Some((expr, Vec::new()));
5480                    }
5481                    self.advance();
5482                    let part = match self.peek().kind {
5483                        TokenKind::LeftParen => self.parse_chain_call_tail(true)?,
5484                        TokenKind::LeftBracket => self.parse_chain_index_tail(true)?,
5485                        _ if is_property_name(&self.peek().kind) => {
5486                            self.parse_chain_field_tail(true)?
5487                        }
5488                        _ => {
5489                            self.error_at_peek(
5490                                "expected field name, `(` for call, or `[` for index after `?.`",
5491                            );
5492                            return None;
5493                        }
5494                    };
5495                    if let Some((_, parts)) = &mut chain_parts {
5496                        parts.push(part);
5497                    }
5498                }
5499                // `<` after a callable expression might be a generic call `f<T>(args)`.
5500                // Speculatively parse; on failure restore state and let `parse_binary`
5501                // treat `<` as a comparison.
5502                TokenKind::LessThan => {
5503                    if chain_parts.is_some() {
5504                        // `a?.<T>()` not yet supported — terminate the chain.
5505                        break;
5506                    }
5507                    let saved_pos = self.pos;
5508                    let saved_splits = self.split_type_tokens.len();
5509                    let saved_diag_len = self.diagnostics.len();
5510                    if let Some(call_id) = self.try_parse_generic_call_tail(expr) {
5511                        expr = call_id;
5512                    } else {
5513                        self.restore_type_tokens(saved_splits);
5514                        self.pos = saved_pos;
5515                        self.diagnostics.truncate(saved_diag_len);
5516                        break;
5517                    }
5518                }
5519                TokenKind::Bang => {
5520                    if let Some((_, parts)) = &mut chain_parts {
5521                        let tok = self.advance();
5522                        parts.push(crate::ChainPart::NonNull { span: tok.span });
5523                        continue;
5524                    }
5525                    let tok = self.advance();
5526                    let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5527                        .span
5528                        .start;
5529                    expr = parse_arena_result(
5530                        self.ast.try_push_expr(Expr {
5531                            kind: ExprKind::PostfixUnary {
5532                                op: PostfixOp::NonNullAssert,
5533                                operand: expr,
5534                            },
5535                            span: self.span(start, tok.span.end),
5536                        }),
5537                        &mut self.fatal,
5538                    )?;
5539                }
5540                // Postfix `++`/`--` terminates the chain; `a?.b++` is rejected.
5541                TokenKind::PlusPlus | TokenKind::MinusMinus => {
5542                    if chain_parts.is_some() {
5543                        break;
5544                    }
5545                    let tok = self.advance();
5546                    let op = if matches!(tok.kind, TokenKind::PlusPlus) {
5547                        PostfixOp::Inc
5548                    } else {
5549                        PostfixOp::Dec
5550                    };
5551                    let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5552                        .span
5553                        .start;
5554                    expr = parse_arena_result(
5555                        self.ast.try_push_expr(Expr {
5556                            kind: ExprKind::PostfixUnary { op, operand: expr },
5557                            span: self.span(start, tok.span.end),
5558                        }),
5559                        &mut self.fatal,
5560                    )?;
5561                    break;
5562                }
5563                _ => break,
5564            }
5565        }
5566        if let Some((base, parts)) = chain_parts {
5567            let base_span = parse_arena_result(self.ast.try_expr(base), &mut self.fatal)?.span;
5568            let end = parts.last().map_or(base_span.end, |p| p.span().end);
5569            Some(parse_arena_result(
5570                self.ast.try_push_expr(Expr {
5571                    kind: ExprKind::OptionalChain { base, parts },
5572                    span: self.span(base_span.start, end),
5573                }),
5574                &mut self.fatal,
5575            )?)
5576        } else {
5577            Some(expr)
5578        }
5579    }
5580
5581    // `.` is consumed here for the non-optional case; `?.` was consumed by the caller.
5582    fn parse_chain_field_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5583        let start_pos = self.peek().span.start;
5584        if !optional {
5585            self.advance();
5586        }
5587        let name_tok = if is_property_name(&self.peek().kind) {
5588            self.advance()
5589        } else {
5590            let what = if optional { "?." } else { "." };
5591            self.error_at_peek(format!("expected field name after `{what}`"));
5592            return None;
5593        };
5594        let name = self.ident_from_token(&name_tok);
5595        let end = name.span.end;
5596        Some(crate::ChainPart::Field {
5597            name,
5598            optional,
5599            span: self.span(start_pos, end),
5600        })
5601    }
5602
5603    fn parse_chain_index_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5604        let open = self.advance();
5605        let idx = self.parse_expression()?;
5606        if !matches!(self.peek().kind, TokenKind::RightBracket) {
5607            self.error_at_peek("expected `]`");
5608            return None;
5609        }
5610        let close = self.advance();
5611        Some(crate::ChainPart::Index {
5612            idx,
5613            optional,
5614            span: self.span(open.span.start, close.span.end),
5615        })
5616    }
5617
5618    fn parse_chain_call_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5619        let open = self.advance();
5620        let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5621            Vec::new()
5622        } else {
5623            self.parse_call_args()?
5624        };
5625        if !matches!(self.peek().kind, TokenKind::RightParen) {
5626            self.error_at_peek("expected `)`");
5627            return None;
5628        }
5629        let close = self.advance();
5630        Some(crate::ChainPart::Call {
5631            args,
5632            type_args: None,
5633            optional,
5634            span: self.span(open.span.start, close.span.end),
5635        })
5636    }
5637
5638    fn parse_field_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5639        self.advance();
5640        // Keywords are valid field names — `Foo.new(args)` is the canonical constructor call.
5641        let name_tok = if is_property_name(&self.peek().kind) {
5642            self.advance()
5643        } else {
5644            self.error_at_peek("expected field name after `.`");
5645            return None;
5646        };
5647        let name = self.ident_from_token(&name_tok);
5648        let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5649        parse_arena_result(
5650            self.ast.try_push_expr(Expr {
5651                kind: ExprKind::FieldAccess {
5652                    receiver,
5653                    name: name.clone(),
5654                },
5655                span: self.span(receiver_span.start, name.span.end),
5656            }),
5657            &mut self.fatal,
5658        )
5659    }
5660
5661    fn parse_index_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5662        self.advance();
5663        let index = self.parse_expression()?;
5664        if !matches!(self.peek().kind, TokenKind::RightBracket) {
5665            self.error_at_peek("expected `]`");
5666            return None;
5667        }
5668        let close = self.advance();
5669        let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5670        parse_arena_result(
5671            self.ast.try_push_expr(Expr {
5672                kind: ExprKind::IndexAccess { receiver, index },
5673                span: self.span(receiver_span.start, close.span.end),
5674            }),
5675            &mut self.fatal,
5676        )
5677    }
5678
5679    fn parse_call_tail(
5680        &mut self,
5681        callee: ExprId,
5682        type_args: Option<Vec<TypeAnnotation>>,
5683    ) -> Option<ExprId> {
5684        self.advance();
5685        let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5686            Vec::new()
5687        } else {
5688            self.parse_call_args()?
5689        };
5690        if !matches!(self.peek().kind, TokenKind::RightParen) {
5691            self.error_at_peek("expected `)`");
5692            return None;
5693        }
5694        let close = self.advance();
5695
5696        let callee_span = parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5697        parse_arena_result(
5698            self.ast.try_push_expr(Expr {
5699                kind: ExprKind::Call {
5700                    callee,
5701                    type_args,
5702                    args,
5703                },
5704                span: self.span(callee_span.start, close.span.end),
5705            }),
5706            &mut self.fatal,
5707        )
5708    }
5709
5710    // Caller restores position + diagnostics on `None` so `<` is re-interpreted as comparison.
5711    fn try_parse_generic_call_tail(&mut self, callee: ExprId) -> Option<ExprId> {
5712        let type_args = self.try_parse_type_args_only()?;
5713        if !matches!(self.peek().kind, TokenKind::LeftParen) {
5714            return None;
5715        }
5716        self.parse_call_tail(callee, Some(type_args))
5717    }
5718
5719    fn try_parse_type_args_only(&mut self) -> Option<Vec<TypeAnnotation>> {
5720        if !matches!(self.peek().kind, TokenKind::LessThan) {
5721            return self.invariant_failure("type argument parser expected `<`");
5722        }
5723        self.advance();
5724
5725        if matches!(
5726            self.peek().kind,
5727            TokenKind::GreaterThan | TokenKind::GreaterEquals
5728        ) {
5729            return None;
5730        }
5731
5732        let mut type_args = Vec::new();
5733        loop {
5734            let ta = self.parse_type_annotation()?;
5735            type_args.push(ta);
5736            match self.peek().kind {
5737                TokenKind::Comma => {
5738                    self.advance();
5739                    if matches!(
5740                        self.peek().kind,
5741                        TokenKind::GreaterThan | TokenKind::GreaterEquals
5742                    ) {
5743                        self.consume_type_close()?;
5744                        break;
5745                    }
5746                }
5747                TokenKind::GreaterThan | TokenKind::GreaterEquals => {
5748                    self.consume_type_close()?;
5749                    break;
5750                }
5751                _ => return None,
5752            }
5753        }
5754        Some(type_args)
5755    }
5756
5757    fn parse_call_args(&mut self) -> Option<Vec<ExprId>> {
5758        let mut args = Vec::new();
5759        loop {
5760            let arg = self.parse_expression()?;
5761            args.push(arg);
5762            match self.peek().kind {
5763                TokenKind::Comma => {
5764                    self.advance();
5765                    if matches!(self.peek().kind, TokenKind::RightParen) {
5766                        return Some(args);
5767                    }
5768                }
5769                TokenKind::RightParen => return Some(args),
5770                _ => {
5771                    self.error_at_peek("expected `,` or `)`");
5772                    return None;
5773                }
5774            }
5775        }
5776    }
5777
5778    /// `<T>expr`, TypeScript's original cast syntax, lowered to the same
5779    /// [`ExprKind::As`] node `expr as T` produces — so it is checked and emitted
5780    /// identically, including the runtime `ref.test` our `as` performs.
5781    ///
5782    /// The operand is a unary expression, which is what binds `<T>-1` as a cast of
5783    /// `-1` rather than a cast of `1` that is then negated.
5784    fn parse_angle_cast(&mut self) -> Option<ExprId> {
5785        let open = self.advance();
5786        let ty = self.parse_type_annotation()?;
5787        if !matches!(self.peek().kind, TokenKind::GreaterThan) {
5788            self.error_at_peek("expected `>` to close a type assertion");
5789            return None;
5790        }
5791        self.advance();
5792        let expr = self.parse_unary()?;
5793        let end = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5794            .span
5795            .end;
5796        parse_arena_result(
5797            self.ast.try_push_expr(Expr {
5798                kind: ExprKind::As { expr, ty },
5799                span: self.span(open.span.start, end),
5800            }),
5801            &mut self.fatal,
5802        )
5803    }
5804
5805    fn parse_atom(&mut self) -> Option<ExprId> {
5806        match self.peek().kind {
5807            // `<T>expr` — the older spelling of `expr as T`, lowered to the same node.
5808            // Unambiguous only because this is an atom's first token: a `<` that means
5809            // comparison always has a left operand, and one that opens a generic
5810            // argument list always follows a callee, so neither reaches here.
5811            TokenKind::LessThan => return self.parse_angle_cast(),
5812            TokenKind::LeftParen => return self.parse_paren(),
5813            // `{` here is an object literal; statement-start `{` is dispatched to
5814            // `parse_block` before `parse_atom` runs, so the two stay disjoint.
5815            TokenKind::LeftBrace => return self.parse_object_literal(),
5816            TokenKind::LeftBracket => return self.parse_array_literal(),
5817            TokenKind::TemplateHead(_) => return self.parse_template_literal(),
5818            TokenKind::This => return self.parse_this_or_super(true),
5819            TokenKind::Super => return self.parse_this_or_super(false),
5820            TokenKind::Function => return self.parse_function_expression(),
5821            _ => {}
5822        }
5823        if !matches!(
5824            self.peek().kind,
5825            TokenKind::NumberLiteral(_)
5826                | TokenKind::BigIntLiteral(_)
5827                | TokenKind::StringLiteral(_)
5828                | TokenKind::TemplateNoSubstitution(_)
5829                | TokenKind::BooleanLiteral(_)
5830                | TokenKind::NullLiteral
5831                | TokenKind::Identifier
5832                | TokenKind::RegexLiteral { .. }
5833        ) {
5834            self.error_at_peek("expected expression");
5835            return None;
5836        }
5837        let tok = self.advance();
5838        let span = tok.span;
5839        let kind = match tok.kind {
5840            TokenKind::NumberLiteral(v) => ExprKind::Number(v),
5841            TokenKind::BigIntLiteral(s) => ExprKind::BigInt(s),
5842            TokenKind::StringLiteral(s) => ExprKind::String(s),
5843            TokenKind::TemplateNoSubstitution(s) => ExprKind::String(s),
5844            TokenKind::BooleanLiteral(b) => ExprKind::Boolean(b),
5845            TokenKind::NullLiteral => ExprKind::Null,
5846            TokenKind::Identifier => ExprKind::Identifier(Ident {
5847                name: self.source[span.start as usize..span.end as usize].to_string(),
5848                span,
5849            }),
5850            TokenKind::RegexLiteral { source, flags } => ExprKind::Regex { source, flags },
5851            // No cursor movement occurs between the accepted-kind check and advance.
5852            _ => unreachable!("advance preserves the accepted expression token"),
5853        };
5854        parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5855    }
5856
5857    /// Ordinary functions establish a `this` boundary and exclude an enclosing
5858    /// class's `super`; arrows inherit the current parsing context.
5859    fn parse_this_or_super(&mut self, is_this: bool) -> Option<ExprId> {
5860        let tok = self.advance();
5861        let span = tok.span;
5862        if !is_this && self.class_member_body_depth == 0 {
5863            self.error_at_with_help(
5864                span,
5865                "`super` is only valid inside a class method or constructor body",
5866                vec![
5867                    "call `super(...)` in a subclass constructor or `super.method(...)` \
5868                     in a subclass method"
5869                        .to_string(),
5870                ],
5871            );
5872        }
5873        let kind = if is_this
5874            && self.class_member_body_depth == 0
5875            && self.function_expression_body_depth == 0
5876        {
5877            ExprKind::ThisOutsideReceiver
5878        } else if is_this {
5879            ExprKind::This
5880        } else {
5881            ExprKind::Super
5882        };
5883        parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5884    }
5885
5886    // parse_atom dispatches immediately after matching TemplateHead.
5887    // The remaining template tokens are still parsed and diagnosed normally.
5888    fn parse_template_literal(&mut self) -> Option<ExprId> {
5889        let head_tok = self.advance();
5890        let TokenKind::TemplateHead(head) = head_tok.kind else {
5891            unreachable!("template dispatch requires TemplateHead");
5892        };
5893        let start = head_tok.span.start;
5894        let mut parts: Vec<String> = vec![head];
5895        let mut exprs: Vec<ExprId> = Vec::new();
5896        let mut substitution_spans: Vec<Span> = Vec::new();
5897        // The token before a substitution ends just past its `${`; the one after
5898        // starts at its `}`.
5899        let mut substitution_start = head_tok.span.end.saturating_sub(2);
5900        loop {
5901            let expr = self.parse_expression()?;
5902            exprs.push(expr);
5903            let closing = self.peek().span.start;
5904            let substitution_span = self.span(substitution_start, closing.saturating_add(1));
5905            match self.peek().kind.clone() {
5906                TokenKind::TemplateMiddle(s) => {
5907                    let tok = self.advance();
5908                    substitution_spans.push(substitution_span);
5909                    substitution_start = tok.span.end.saturating_sub(2);
5910                    parts.push(s);
5911                }
5912                TokenKind::TemplateTail(s) => {
5913                    let tok = self.advance();
5914                    substitution_spans.push(substitution_span);
5915                    parts.push(s);
5916                    return parse_arena_result(
5917                        self.ast.try_push_expr(Expr {
5918                            kind: ExprKind::TemplateLiteral {
5919                                parts,
5920                                exprs,
5921                                substitution_spans,
5922                            },
5923                            span: self.span(start, tok.span.end),
5924                        }),
5925                        &mut self.fatal,
5926                    );
5927                }
5928                _ => {
5929                    self.error_at_peek("expected `}` to close template interpolation");
5930                    return None;
5931                }
5932            }
5933        }
5934    }
5935
5936    fn parse_object_literal(&mut self) -> Option<ExprId> {
5937        let open = self.advance();
5938
5939        let mut members: Vec<ObjectLiteralMember> = Vec::new();
5940        if !matches!(self.peek().kind, TokenKind::RightBrace) {
5941            loop {
5942                if matches!(self.peek().kind, TokenKind::DotDotDot) {
5943                    let dots = self.advance();
5944                    let value = self.parse_expression()?;
5945                    let value_span =
5946                        parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5947                    members.push(ObjectLiteralMember::Spread {
5948                        value,
5949                        span: self.span(dots.span.start, value_span.end),
5950                    });
5951                } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
5952                    self.advance();
5953                    let key = self.parse_expression()?;
5954                    if !matches!(self.peek().kind, TokenKind::RightBracket) {
5955                        self.error_at_peek("expected `]` after computed property key");
5956                        return None;
5957                    }
5958                    self.advance();
5959                    if !matches!(self.peek().kind, TokenKind::Colon) {
5960                        self.error_at_peek("expected `:` after computed property key; computed methods are not supported");
5961                        return None;
5962                    }
5963                    self.advance();
5964                    let value = self.parse_expression()?;
5965                    members.push(ObjectLiteralMember::Computed { key, value });
5966                } else {
5967                    let field = self.parse_object_literal_field()?;
5968                    // Spread members are dynamic — only literal fields participate in
5969                    // the duplicate-key check.
5970                    if let Some(existing) = members.iter().find_map(|m| match m {
5971                        ObjectLiteralMember::Field(f) if f.name.name == field.name.name => Some(f),
5972                        _ => None,
5973                    }) {
5974                        self.diagnostics.push(Diagnostic {
5975                            severity: Severity::Error,
5976                            span: field.name.span,
5977                            message: format!(
5978                                "duplicate field `{}` in object literal",
5979                                field.name.name
5980                            ),
5981                            help: vec![],
5982                            notes: vec![(existing.name.span, "first defined here".into())],
5983                        });
5984                        // Keep parsing so the user sees all duplicates at once.
5985                    }
5986                    members.push(ObjectLiteralMember::Field(field));
5987                }
5988                if self.at_inserted_semicolon()
5989                    && matches!(self.peek_at(1).kind, TokenKind::RightBrace)
5990                {
5991                    self.advance();
5992                }
5993                match self.peek().kind {
5994                    TokenKind::Comma => {
5995                        self.advance();
5996                        if matches!(self.peek().kind, TokenKind::RightBrace) {
5997                            break;
5998                        }
5999                    }
6000                    TokenKind::RightBrace => break,
6001                    _ => {
6002                        self.error_at_peek("expected `,` or `}`");
6003                        return None;
6004                    }
6005                }
6006            }
6007        }
6008
6009        if !matches!(self.peek().kind, TokenKind::RightBrace) {
6010            self.error_at_peek("expected `}`");
6011            return None;
6012        }
6013        let close = self.advance();
6014
6015        parse_arena_result(
6016            self.ast.try_push_expr(Expr {
6017                kind: ExprKind::ObjectLiteral { members },
6018                span: self.span(open.span.start, close.span.end),
6019            }),
6020            &mut self.fatal,
6021        )
6022    }
6023
6024    fn parse_object_literal_field(&mut self) -> Option<ObjectLiteralField> {
6025        self.reject_async_member_modifier();
6026        let (name, shorthandable) = match self.peek().kind.clone() {
6027            TokenKind::Identifier => {
6028                let tok = self.advance();
6029                (self.ident_from_token(&tok), true)
6030            }
6031            kind if is_property_name(&kind) => {
6032                let tok = self.advance();
6033                (self.ident_from_token(&tok), false)
6034            }
6035            TokenKind::StringLiteral(_) => {
6036                let tok = self.advance();
6037                (self.property_ident_from_token(&tok), false)
6038            }
6039            _ => {
6040                self.error_at_peek("expected field name");
6041                return None;
6042            }
6043        };
6044
6045        // `{ m(x: T): R { … } }` is shorthand for `{ m: (x: T): R => { … } }`. Method
6046        // shorthand carries no `this` of its own here, for the same reason a function
6047        // expression does not, so the arrow is an exact lowering. Checked before the
6048        // `:` branch and independently of `shorthandable`, because a keyword or string
6049        // key can name a method too.
6050        if matches!(self.peek().kind, TokenKind::LeftParen) {
6051            let value = self.parse_method_shorthand_body(name.span)?;
6052            return Some(ObjectLiteralField { name, value });
6053        }
6054
6055        if !matches!(self.peek().kind, TokenKind::Colon) {
6056            // `{ x }` is shorthand for `{ x: x }`. Only identifier keys can use
6057            // it — a string key like `{ "x" }` has no binding to reference.
6058            if shorthandable {
6059                let value = parse_arena_result(
6060                    self.ast.try_push_expr(Expr {
6061                        kind: ExprKind::Identifier(name.clone()),
6062                        span: name.span,
6063                    }),
6064                    &mut self.fatal,
6065                )?;
6066                return Some(ObjectLiteralField { name, value });
6067            }
6068            self.error_at_peek("expected `:` after field name");
6069            return None;
6070        }
6071        self.advance();
6072
6073        let value = self.parse_expression()?;
6074        Some(ObjectLiteralField { name, value })
6075    }
6076
6077    /// The `(params): R { … }` tail of an object-literal method, lowered to an arrow.
6078    /// `name_span` is the key's span, so the arrow spans the whole member.
6079    fn parse_method_shorthand_body(&mut self, name_span: Span) -> Option<ExprId> {
6080        self.advance();
6081
6082        let params = if matches!(self.peek().kind, TokenKind::RightParen) {
6083            Vec::new()
6084        } else {
6085            self.parse_arrow_param_list()?
6086        };
6087        if !matches!(self.peek().kind, TokenKind::RightParen) {
6088            self.error_at_peek("expected `)`");
6089            return None;
6090        }
6091        self.advance();
6092
6093        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
6094            self.advance();
6095            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
6096        } else {
6097            (None, None)
6098        };
6099
6100        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
6101            self.error_at_peek("expected `{` to open the method body");
6102            return None;
6103        }
6104        let block = self.parse_outer_this_boundary(Self::parse_block)?;
6105        let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
6106            .span
6107            .end;
6108
6109        parse_arena_result(
6110            self.ast.try_push_expr(Expr {
6111                kind: ExprKind::Arrow {
6112                    params,
6113                    return_type,
6114                    type_predicate,
6115                    body: ArrowBody::Block(block),
6116                },
6117                span: self.span(name_span.start, end),
6118            }),
6119            &mut self.fatal,
6120        )
6121    }
6122
6123    fn parse_array_literal(&mut self) -> Option<ExprId> {
6124        let open = self.advance();
6125
6126        let mut elements: Vec<ArrayLiteralElement> = Vec::new();
6127        if !matches!(self.peek().kind, TokenKind::RightBracket) {
6128            loop {
6129                let element = if matches!(self.peek().kind, TokenKind::DotDotDot) {
6130                    let dots = self.advance();
6131                    let value = self.parse_expression()?;
6132                    let value_span =
6133                        parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
6134                    ArrayLiteralElement::Spread {
6135                        value,
6136                        span: self.span(dots.span.start, value_span.end),
6137                    }
6138                } else {
6139                    ArrayLiteralElement::Value(self.parse_expression()?)
6140                };
6141                elements.push(element);
6142                match self.peek().kind {
6143                    TokenKind::Comma => {
6144                        self.advance();
6145                        if matches!(self.peek().kind, TokenKind::RightBracket) {
6146                            break;
6147                        }
6148                    }
6149                    TokenKind::RightBracket => break,
6150                    _ => {
6151                        self.error_at_peek("expected `,` or `]`");
6152                        return None;
6153                    }
6154                }
6155            }
6156        }
6157
6158        if !matches!(self.peek().kind, TokenKind::RightBracket) {
6159            self.error_at_peek("expected `]`");
6160            return None;
6161        }
6162        let close = self.advance();
6163
6164        parse_arena_result(
6165            self.ast.try_push_expr(Expr {
6166                kind: ExprKind::ArrayLiteral { elements },
6167                span: self.span(open.span.start, close.span.end),
6168            }),
6169            &mut self.fatal,
6170        )
6171    }
6172
6173    fn parse_paren(&mut self) -> Option<ExprId> {
6174        let open = self.advance();
6175        let inner = self.parse_expression()?;
6176        if !matches!(self.peek().kind, TokenKind::RightParen) {
6177            self.error_at_peek("expected `)`");
6178            return None;
6179        }
6180        let close = self.advance();
6181        let span = self.span(open.span.start, close.span.end);
6182        parse_arena_result(
6183            self.ast.try_push_expr(Expr {
6184                kind: ExprKind::Paren(inner),
6185                span,
6186            }),
6187            &mut self.fatal,
6188        )
6189    }
6190
6191    fn with_recursion_limit<T>(&mut self, parse: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
6192        if self.fatal.is_some() {
6193            return None;
6194        }
6195        if self.recursion_limit_span.is_some() || self.recursion_depth >= MAX_PARSE_DEPTH {
6196            self.stop_at_recursion_limit();
6197            return None;
6198        }
6199        self.recursion_depth += 1;
6200        let result = parse(self);
6201        self.recursion_depth -= 1;
6202        result
6203    }
6204
6205    fn stop_at_recursion_limit(&mut self) {
6206        self.recursion_limit_span.get_or_insert(self.peek().span);
6207        // Stop recovery and enclosing block loops as well as recursive calls.
6208        self.pos = self.tokens.len().saturating_sub(1);
6209    }
6210
6211    fn recover(&mut self) {
6212        while !self.is_at_eof() {
6213            match self.peek().kind {
6214                TokenKind::Semicolon => {
6215                    self.advance();
6216                    return;
6217                }
6218                TokenKind::RightBrace
6219                | TokenKind::Let
6220                | TokenKind::Const
6221                | TokenKind::Function
6222                | TokenKind::Export
6223                | TokenKind::If
6224                | TokenKind::While
6225                | TokenKind::Return
6226                | TokenKind::Try
6227                | TokenKind::Throw => return,
6228                _ => {
6229                    self.advance();
6230                }
6231            }
6232        }
6233    }
6234
6235    fn peek(&self) -> &Token {
6236        self.tokens.get(self.pos).unwrap_or(&self.eof)
6237    }
6238
6239    fn peek_at(&self, offset: usize) -> &Token {
6240        self.pos
6241            .checked_add(offset)
6242            .and_then(|index| self.tokens.get(index))
6243            .unwrap_or(&self.eof)
6244    }
6245
6246    /// ASI inserts its `;` as a zero-width token; a written `;` always has width.
6247    fn at_inserted_semicolon(&self) -> bool {
6248        let token = self.peek();
6249        matches!(token.kind, TokenKind::Semicolon) && token.span.start == token.span.end
6250    }
6251
6252    fn advance(&mut self) -> Token {
6253        let tok = self.peek().clone();
6254        if self.pos < self.tokens.len().saturating_sub(1) {
6255            self.pos += 1;
6256        }
6257        tok
6258    }
6259
6260    fn is_at_eof(&self) -> bool {
6261        self.fatal.is_some() || matches!(self.peek().kind, TokenKind::Eof)
6262    }
6263
6264    /// Whether a line break separates the next token from the one before it.
6265    fn line_break_before_peek(&self) -> bool {
6266        self.source_has_line_break(self.prev_token_end(), self.peek().span.start)
6267    }
6268
6269    /// Whether a line break separates the next token from the one after it.
6270    fn line_break_after_peek(&self) -> bool {
6271        self.source_has_line_break(self.peek().span.end, self.peek_at(1).span.start)
6272    }
6273
6274    /// The ASI pass drops newline tokens, so line breaks between tokens are read
6275    /// from the source.
6276    fn source_has_line_break(&self, start: u32, end: u32) -> bool {
6277        self.source
6278            .get(start as usize..end as usize)
6279            .is_some_and(|gap| gap.contains(['\n', '\r']))
6280    }
6281
6282    /// Whether the statement being parsed ends here: at a `;`, or at a line break
6283    /// right after an `as T`. ASI keeps a `[` or `(` on the next line attached, but
6284    /// the type already stopped at the break (see `parse_type_array_inner`), and no
6285    /// expression continues past a type, so TypeScript ends the statement there.
6286    fn at_statement_end(&self) -> bool {
6287        matches!(self.peek().kind, TokenKind::Semicolon)
6288            || (self.last_as_type_end == Some(self.prev_token_end())
6289                && self.line_break_before_peek())
6290    }
6291
6292    /// Consumes the `;` that [`Parser::at_statement_end`] found, if there is one, and
6293    /// returns the statement's end offset.
6294    fn finish_statement(&mut self) -> u32 {
6295        if matches!(self.peek().kind, TokenKind::Semicolon) {
6296            return self.advance().span.end;
6297        }
6298        self.prev_token_end()
6299    }
6300
6301    /// End offset of the most recently consumed token (the body of the file's first
6302    /// token if nothing has been consumed yet).
6303    fn prev_token_end(&self) -> u32 {
6304        self.tokens
6305            .get(self.pos.saturating_sub(1))
6306            .unwrap_or(&self.eof)
6307            .span
6308            .end
6309    }
6310
6311    fn take_leading_doc(&mut self) -> Option<crate::DocComment> {
6312        let parsed = self
6313            .peek()
6314            .leading_doc
6315            .as_ref()
6316            .map(crate::parse_doc_comment)
6317            .transpose();
6318        match parsed {
6319            Ok(doc) => doc,
6320            Err(error) => {
6321                self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6322                None
6323            }
6324        }
6325    }
6326
6327    fn parse_doc(&mut self, raw: &crate::RawDoc) -> Option<crate::DocComment> {
6328        match crate::parse_doc_comment(raw) {
6329            Ok(doc) => Some(doc),
6330            Err(error) => {
6331                self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6332                None
6333            }
6334        }
6335    }
6336
6337    /// JavaScript forbids a `"use strict"` directive in a function whose parameters
6338    /// aren't all plain names: one with a default, a pattern, or `...`. As TypeScript
6339    /// does, each such parameter and the directive are reported.
6340    fn reject_use_strict_with_non_simple_params(&mut self) {
6341        let mut functions: Vec<(Vec<Span>, StmtId)> = Vec::new();
6342        let non_simple = |param: &ParamDecl| {
6343            (param.default.is_some() || param.pattern.is_some() || param.rest).then(|| {
6344                param
6345                    .pattern
6346                    .as_ref()
6347                    .map_or(param.name.span, Binding::span)
6348            })
6349        };
6350        for expr in self.ast.source_expressions() {
6351            if let ExprKind::Arrow {
6352                params,
6353                body: ArrowBody::Block(body),
6354                ..
6355            } = &expr.kind
6356            {
6357                functions.push((params.iter().filter_map(non_simple).collect(), *body));
6358            }
6359        }
6360        for stmt in self.ast.source_statements() {
6361            match &stmt.kind {
6362                StmtKind::Function { params, body, .. } => {
6363                    functions.push((params.iter().filter_map(non_simple).collect(), *body));
6364                }
6365                StmtKind::ClassDecl { members, .. } => {
6366                    for member in members {
6367                        match member {
6368                            ClassMember::Method { params, body, .. }
6369                            | ClassMember::Constructor { params, body, .. } => {
6370                                functions
6371                                    .push((params.iter().filter_map(non_simple).collect(), *body));
6372                            }
6373                            ClassMember::Accessor { param, body, .. } => {
6374                                let params = param.as_deref().and_then(non_simple);
6375                                functions.push((params.into_iter().collect(), *body));
6376                            }
6377                            _ => {}
6378                        }
6379                    }
6380                }
6381                _ => {}
6382            }
6383        }
6384        for (params, body) in functions {
6385            if params.is_empty() {
6386                continue;
6387            }
6388            let Some(directive) = self.use_strict_directive(body) else {
6389                continue;
6390            };
6391            for span in params {
6392                self.error_at(
6393                    span,
6394                    "this parameter is not allowed with a `\"use strict\"` directive",
6395                );
6396            }
6397            self.error_at_with_help(
6398                directive,
6399                "a `\"use strict\"` directive can't be used with a parameter list that has defaults, patterns, or a rest parameter",
6400                vec!["remove the directive; Submilli code is always strict".into()],
6401            );
6402        }
6403    }
6404
6405    /// The span of a `"use strict"` directive in the prologue of a function body:
6406    /// the string-literal statements it starts with. As in TypeScript, the string's
6407    /// value decides, so an escape spelling it counts too; a template doesn't.
6408    fn use_strict_directive(&self, body: StmtId) -> Option<Span> {
6409        let StmtKind::Block(stmts) = &self.ast.try_stmt(body).ok()?.kind else {
6410            return None;
6411        };
6412        for stmt in stmts {
6413            let stmt = self.ast.try_stmt(*stmt).ok()?;
6414            let StmtKind::Expr(expr) = stmt.kind else {
6415                return None;
6416            };
6417            let expr = self.ast.try_expr(expr).ok()?;
6418            let ExprKind::String(value) = &expr.kind else {
6419                return None;
6420            };
6421            if self.source.as_bytes().get(expr.span.start as usize) == Some(&b'`') {
6422                return None;
6423            }
6424            if value == "use strict" {
6425                return Some(stmt.span);
6426            }
6427        }
6428        None
6429    }
6430
6431    fn error_at(&mut self, span: Span, message: impl Into<String>) {
6432        self.diagnostics.push(Diagnostic {
6433            severity: Severity::Error,
6434            span,
6435            message: message.into(),
6436            help: vec![],
6437            notes: vec![],
6438        });
6439    }
6440
6441    /// `==` is `===` here, so `x == null` would silently miss `undefined` — the
6442    /// one case where TypeScript's loose equality differs in a way code relies on.
6443    fn reject_loose_nullish_comparison(
6444        &mut self,
6445        op: BinOp,
6446        lhs: ExprId,
6447        rhs: ExprId,
6448    ) -> Option<()> {
6449        let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
6450        let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
6451        let (nullish, operand, nullish_first) =
6452            match (self.nullish_operand(lhs)?, self.nullish_operand(rhs)?) {
6453                (Some(left), Some(right)) => {
6454                    self.reject_nullish_against_nullish(op, lhs_span, rhs_span, [left, right]);
6455                    return Some(());
6456                }
6457                (None, Some(nullish)) => (nullish, lhs, false),
6458                (Some(nullish), None) => (nullish, rhs, true),
6459                (None, None) => return Some(()),
6460            };
6461        let comparison = LooseComparison {
6462            nullish,
6463            nullish_first,
6464            negated: op == BinOp::NotEq,
6465        };
6466        let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
6467        let x = self.single_line_source(operand_span).unwrap_or("x");
6468        let strict = self.strict_suggestion(&comparison, x);
6469        let either = self.either_suggestion(&comparison, operand, x)?;
6470        let literal = comparison.nullish.literal.text();
6471        // Quote the comparison as written, so `null == z` stays in that order.
6472        let written = self
6473            .single_line_source(self.span(lhs_span.start, rhs_span.end))
6474            .map_or_else(
6475                || {
6476                    let written_op = if comparison.negated { "!=" } else { "==" };
6477                    comparison.ordered(x, written_op, literal)
6478                },
6479                str::to_string,
6480            );
6481        self.error_at_with_help(
6482            self.span(lhs_span.start, rhs_span.end),
6483            format!(
6484                "`{written}` does not also match `{}`: `==` compares like `===`",
6485                comparison.nullish.literal.other().text()
6486            ),
6487            vec![
6488                either,
6489                format!("to test for `{literal}` alone, write `{strict}`"),
6490            ],
6491        );
6492        Some(())
6493    }
6494
6495    /// Both sides are always nullish. TypeScript's `==` treats `null` and
6496    /// `undefined` as equal, so the comparison has one value, which the help
6497    /// names; a `void` operand with effects still runs, as a statement.
6498    fn reject_nullish_against_nullish(
6499        &mut self,
6500        op: BinOp,
6501        lhs: Span,
6502        rhs: Span,
6503        sides: [NullishOperand; 2],
6504    ) {
6505        let span = self.span(lhs.start, rhs.end);
6506        // A comparison written over several lines can't be quoted on one, so
6507        // the message names it without the source.
6508        let subject = self.single_line_source(span).map_or_else(
6509            || "this comparison".to_string(),
6510            |written| format!("`{written}`"),
6511        );
6512        let value = if op == BinOp::NotEq { "false" } else { "true" };
6513        let effects: Vec<String> = sides
6514            .iter()
6515            .filter_map(|side| side.effect)
6516            .map(|effect| {
6517                self.single_line_source(effect.operand).map_or_else(
6518                    || "the `void` operand's expression".to_string(),
6519                    |statement| format!("`{statement};`"),
6520                )
6521            })
6522            .collect();
6523        let help = if effects.is_empty() {
6524            format!("in TypeScript this is always `{value}`; write `{value}`")
6525        } else {
6526            format!(
6527                "in TypeScript this is always `{value}`; run {} {}, then use `{value}`",
6528                effects.join(" and "),
6529                if effects.len() > 1 {
6530                    "each as its own statement"
6531                } else {
6532                    "as its own statement"
6533                }
6534            )
6535        };
6536        self.error_at_with_help(
6537            span,
6538            format!("{subject} compares two nullish values: `==` compares like `===`"),
6539            vec![help],
6540        );
6541    }
6542
6543    /// The strict comparison against the written nullish value. A `void`
6544    /// operand with effects stays as written, so the operand runs once.
6545    fn strict_suggestion(&self, comparison: &LooseComparison, x: &str) -> String {
6546        let eq = comparison.strict_op();
6547        match comparison.nullish.effect {
6548            Some(effect) => {
6549                let void = self.single_line_source(effect.void).unwrap_or("void expr");
6550                comparison.ordered(x, eq, void)
6551            }
6552            None => format!("{x} {eq} {}", comparison.nullish.literal.text()),
6553        }
6554    }
6555
6556    /// "To test for either" names the operand twice, so one with effects is
6557    /// bound to a `const` first, and a `void` operand's expression runs as a
6558    /// statement of its own.
6559    fn either_suggestion(
6560        &mut self,
6561        comparison: &LooseComparison,
6562        operand: ExprId,
6563        x: &str,
6564    ) -> Option<String> {
6565        let mut steps = Vec::new();
6566        let tested = if self.repeats_without_effect(operand)? {
6567            x
6568        } else {
6569            steps.push(format!("bind `{x}` to a `const` (`const value = {x};`)"));
6570            "value"
6571        };
6572        if let Some(effect) = comparison.nullish.effect {
6573            let statement = self.single_line_source(effect.operand).unwrap_or("expr");
6574            let step = format!("run `{statement};` as its own statement");
6575            if comparison.nullish_first {
6576                steps.insert(0, step);
6577            } else {
6578                steps.push(step);
6579            }
6580        }
6581        let eq = comparison.strict_op();
6582        let joiner = if comparison.negated { "&&" } else { "||" };
6583        let both = format!("{tested} {eq} null {joiner} {tested} {eq} undefined");
6584        Some(if steps.is_empty() {
6585            format!("to test for either, write `{both}`")
6586        } else {
6587            format!(
6588                "to test for either, first {}, then write `{both}`",
6589                steps.join(", then ")
6590            )
6591        })
6592    }
6593
6594    /// Whether naming `id` twice evaluates it twice with no effect: a literal,
6595    /// or a reference path.
6596    fn repeats_without_effect(&mut self, id: ExprId) -> Option<bool> {
6597        let unwrapped = self.unparenthesized(id)?;
6598        let expr = parse_arena_result(self.ast.try_expr(unwrapped), &mut self.fatal)?;
6599        if is_literal(&expr.kind) {
6600            return Some(true);
6601        }
6602        if self
6603            .nullish_operand(unwrapped)?
6604            .is_some_and(|nullish| nullish.effect.is_none())
6605        {
6606            return Some(true);
6607        }
6608        self.is_reference_path(id)
6609    }
6610
6611    /// `null`, `undefined` or `void e`, looking through parentheses.
6612    fn nullish_operand(&mut self, id: ExprId) -> Option<Option<NullishOperand>> {
6613        let id = self.unparenthesized(id)?;
6614        let expr = parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?;
6615        let span = expr.span;
6616        Some(match &expr.kind {
6617            ExprKind::Null => Some(NullishOperand {
6618                literal: Nullish::Null,
6619                effect: None,
6620            }),
6621            ExprKind::Identifier(ident) if ident.name == "undefined" => Some(NullishOperand {
6622                literal: Nullish::Undefined,
6623                effect: None,
6624            }),
6625            ExprKind::Void { operand } => {
6626                let operand_id = self.unparenthesized(*operand)?;
6627                let operand =
6628                    parse_arena_result(self.ast.try_expr(operand_id), &mut self.fatal)?.clone();
6629                // A literal or a reference path reads with no effect to keep.
6630                let effect_free =
6631                    is_literal(&operand.kind) || self.is_reference_path(operand_id)?;
6632                let effect = (!effect_free).then_some(VoidEffect {
6633                    void: span,
6634                    operand: operand.span,
6635                });
6636                Some(NullishOperand {
6637                    literal: Nullish::Undefined,
6638                    effect,
6639                })
6640            }
6641            _ => None,
6642        })
6643    }
6644
6645    /// An identifier, `this`, or a `.field` chain on one: naming it twice evaluates
6646    /// it twice with no effect.
6647    fn is_reference_path(&mut self, id: ExprId) -> Option<bool> {
6648        let mut id = id;
6649        loop {
6650            match &parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6651                ExprKind::Identifier(_) | ExprKind::This => return Some(true),
6652                ExprKind::FieldAccess { receiver, .. } => id = *receiver,
6653                ExprKind::Paren(inner) => id = *inner,
6654                _ => return Some(false),
6655            }
6656        }
6657    }
6658
6659    fn unparenthesized(&mut self, id: ExprId) -> Option<ExprId> {
6660        let mut id = id;
6661        loop {
6662            match parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6663                ExprKind::Paren(inner) => id = inner,
6664                _ => return Some(id),
6665            }
6666        }
6667    }
6668
6669    fn single_line_source(&self, span: Span) -> Option<&'a str> {
6670        self.source
6671            .get(span.start as usize..span.end as usize)
6672            .filter(|text| !text.contains('\n'))
6673    }
6674
6675    fn error_at_with_help(&mut self, span: Span, message: impl Into<String>, help: Vec<String>) {
6676        self.diagnostics.push(Diagnostic {
6677            severity: Severity::Error,
6678            span,
6679            message: message.into(),
6680            help,
6681            notes: vec![],
6682        });
6683    }
6684
6685    fn invariant_failure<T>(&mut self, message: &str) -> Option<T> {
6686        self.fatal.get_or_insert_with(|| CompilerFailure::Internal {
6687            stage: CompilerStage::Parse,
6688            span: None,
6689            message: message.into(),
6690        });
6691        self.pos = self.tokens.len();
6692        None
6693    }
6694
6695    fn error_at_peek(&mut self, message: impl Into<String>) {
6696        let span = self.peek().span;
6697        self.error_at(span, message);
6698    }
6699
6700    fn error_at_peek_with_help(&mut self, message: impl Into<String>, help: Vec<String>) {
6701        let span = self.peek().span;
6702        self.error_at_with_help(span, message, help);
6703    }
6704
6705    fn error_count(&self) -> usize {
6706        self.diagnostics
6707            .iter()
6708            .filter(|d| d.severity == Severity::Error)
6709            .count()
6710    }
6711}
6712
6713pub(crate) fn is_property_name(kind: &TokenKind) -> bool {
6714    matches!(
6715        kind,
6716        TokenKind::Identifier
6717            | TokenKind::BooleanLiteral(_)
6718            | TokenKind::NullLiteral
6719            | TokenKind::Let
6720            | TokenKind::Const
6721            | TokenKind::Function
6722            | TokenKind::If
6723            | TokenKind::Else
6724            | TokenKind::While
6725            | TokenKind::Do
6726            | TokenKind::For
6727            | TokenKind::Break
6728            | TokenKind::Continue
6729            | TokenKind::Return
6730            | TokenKind::Switch
6731            | TokenKind::Case
6732            | TokenKind::Default
6733            | TokenKind::Void
6734            | TokenKind::Interface
6735            | TokenKind::Enum
6736            | TokenKind::In
6737            | TokenKind::Typeof
6738            | TokenKind::Import
6739            | TokenKind::Export
6740            | TokenKind::New
6741            | TokenKind::Try
6742            | TokenKind::Catch
6743            | TokenKind::Finally
6744            | TokenKind::Throw
6745            | TokenKind::Class
6746            | TokenKind::Extends
6747            | TokenKind::Implements
6748            | TokenKind::Super
6749            | TokenKind::This
6750    )
6751}
6752
6753fn is_reserved_identifier_word(kind: &TokenKind) -> bool {
6754    matches!(
6755        kind,
6756        TokenKind::BooleanLiteral(_)
6757            | TokenKind::NullLiteral
6758            | TokenKind::Let
6759            | TokenKind::Const
6760            | TokenKind::Function
6761            | TokenKind::If
6762            | TokenKind::Else
6763            | TokenKind::While
6764            | TokenKind::Do
6765            | TokenKind::For
6766            | TokenKind::Break
6767            | TokenKind::Continue
6768            | TokenKind::Return
6769            | TokenKind::Switch
6770            | TokenKind::Case
6771            | TokenKind::Default
6772            | TokenKind::Export
6773            | TokenKind::Void
6774            | TokenKind::Interface
6775            | TokenKind::Enum
6776            | TokenKind::In
6777            | TokenKind::Typeof
6778            | TokenKind::Import
6779            | TokenKind::New
6780            | TokenKind::Try
6781            | TokenKind::Catch
6782            | TokenKind::Finally
6783            | TokenKind::Throw
6784            | TokenKind::Class
6785            | TokenKind::Extends
6786            | TokenKind::Implements
6787            | TokenKind::Super
6788            | TokenKind::This
6789    )
6790}
6791
6792/// Which statement a `{ … }` specifier list belongs to. An import binds local
6793/// names, which strict mode restricts; an export's are module export names,
6794/// which it doesn't.
6795#[derive(Clone, Copy, PartialEq, Eq)]
6796enum SpecifierList {
6797    Import,
6798    Export,
6799}
6800
6801impl SpecifierList {
6802    fn noun(self) -> &'static str {
6803        match self {
6804            SpecifierList::Import => "import",
6805            SpecifierList::Export => "export",
6806        }
6807    }
6808}
6809
6810/// Words strict mode reserves that Submilli otherwise lexes as identifiers: they
6811/// can't name a binding or a type, but stay valid as property names and modifiers.
6812/// (`implements`, `interface` and `let` are keywords already.)
6813const STRICT_MODE_RESERVED_WORDS: [&str; 6] = [
6814    "package",
6815    "private",
6816    "protected",
6817    "public",
6818    "static",
6819    "yield",
6820];
6821
6822fn reserved_keyword_rename_example(keyword: &str) -> &'static str {
6823    match keyword {
6824        "default" => "defaultValue",
6825        _ => "valueName",
6826    }
6827}
6828
6829fn mixed_logical(prev: BinOp, next: BinOp) -> bool {
6830    let prev_logical = matches!(prev, BinOp::Or | BinOp::And);
6831    let next_logical = matches!(next, BinOp::Or | BinOp::And);
6832    let prev_nullish = matches!(prev, BinOp::NullishCoalesce);
6833    let next_nullish = matches!(next, BinOp::NullishCoalesce);
6834    (prev_logical && next_nullish) || (prev_nullish && next_logical)
6835}
6836
6837/// Spells out both groupings in the order the operators were written. Called
6838/// only for pairs `mixed_logical` accepts, so the non-`??` operator is `||` or `&&`.
6839fn mixed_logical_help(prev: BinOp, next: BinOp) -> String {
6840    let symbol = |op: BinOp| if op == BinOp::And { "&&" } else { "||" };
6841    let (first, second) = if prev == BinOp::NullishCoalesce {
6842        ("??", symbol(next))
6843    } else {
6844        (symbol(prev), "??")
6845    };
6846    format!(
6847        "add parentheses to choose the grouping: `(a {first} b) {second} c` or `a {first} (b {second} c)`"
6848    )
6849}
6850
6851const LOGICAL_AND_PREC: u8 = 2;
6852
6853/// One side of a loose comparison that is always nullish.
6854#[derive(Clone, Copy)]
6855struct NullishOperand {
6856    literal: Nullish,
6857    /// Set for `void e` when `e` is not a literal and may have effects.
6858    effect: Option<VoidEffect>,
6859}
6860
6861#[derive(Clone, Copy, PartialEq, Eq)]
6862enum Nullish {
6863    Null,
6864    Undefined,
6865}
6866
6867impl Nullish {
6868    fn text(self) -> &'static str {
6869        match self {
6870            Nullish::Null => "null",
6871            Nullish::Undefined => "undefined",
6872        }
6873    }
6874
6875    /// The nullish value a loose comparison against `self` fails to match.
6876    fn other(self) -> Self {
6877        match self {
6878            Nullish::Null => Nullish::Undefined,
6879            Nullish::Undefined => Nullish::Null,
6880        }
6881    }
6882}
6883
6884/// A rejected `x == null`-style comparison, as written.
6885struct LooseComparison {
6886    nullish: NullishOperand,
6887    /// Whether the nullish side is written first, as in `null == x`.
6888    nullish_first: bool,
6889    /// `!=` rather than `==`.
6890    negated: bool,
6891}
6892
6893impl LooseComparison {
6894    fn strict_op(&self) -> &'static str {
6895        if self.negated { "!==" } else { "===" }
6896    }
6897
6898    /// `x op nullish`, in the order the comparison was written.
6899    fn ordered(&self, x: &str, op: &str, nullish: &str) -> String {
6900        if self.nullish_first {
6901            format!("{nullish} {op} {x}")
6902        } else {
6903            format!("{x} {op} {nullish}")
6904        }
6905    }
6906}
6907
6908#[derive(Clone, Copy)]
6909struct VoidEffect {
6910    /// The whole `void e`.
6911    void: Span,
6912    /// The `e`, without enclosing parentheses.
6913    operand: Span,
6914}
6915
6916fn is_literal(kind: &ExprKind) -> bool {
6917    matches!(
6918        kind,
6919        ExprKind::Number(_)
6920            | ExprKind::BigInt(_)
6921            | ExprKind::String(_)
6922            | ExprKind::Boolean(_)
6923            | ExprKind::Null
6924    )
6925}
6926
6927/// What `Parser::check_parameter_order` needs from either kind of parameter list.
6928struct ParamOrder<'p> {
6929    optional: bool,
6930    /// Neither optional, defaulted, nor rest.
6931    required: bool,
6932    /// `None` for a destructuring pattern.
6933    name: Option<&'p str>,
6934    span: Span,
6935}
6936
6937impl<'p> ParamOrder<'p> {
6938    fn of_param(param: &'p ParamDecl) -> Self {
6939        let (name, span) = match &param.pattern {
6940            Some(binding) => (None, binding.span()),
6941            None => (Some(param.name.name.as_str()), param.name.span),
6942        };
6943        Self {
6944            optional: param.optional,
6945            required: !param.is_omittable() && !param.rest,
6946            name,
6947            span,
6948        }
6949    }
6950
6951    fn of_field(field: &'p TypeAnnotationField) -> Self {
6952        Self {
6953            optional: field.optional,
6954            required: !field.optional && !field.rest,
6955            name: Some(field.name.name.as_str()),
6956            span: field.name.span,
6957        }
6958    }
6959}
6960
6961/// A binary operator at the cursor, which may span several tokens.
6962struct BinaryToken {
6963    op: BinOp,
6964    precedence: u8,
6965    tokens: usize,
6966}
6967
6968/// `<<`, `>>`, `>>>` or their compound assignments, read from adjacent angle tokens.
6969struct ShiftToken {
6970    op: BinOp,
6971    tokens: usize,
6972    /// The operator ends in `=`: `<<=`, `>>=`, `>>>=`.
6973    assignment: bool,
6974}
6975
6976impl ShiftToken {
6977    fn new(op: BinOp, tokens: usize, assignment: bool) -> Self {
6978        Self {
6979            op,
6980            tokens,
6981            assignment,
6982        }
6983    }
6984}
6985
6986/// Between relational (7) and additive (9) in the `peek_binop` table.
6987const SHIFT_PRECEDENCE: u8 = 8;
6988
6989#[derive(Clone, Copy, PartialEq)]
6990enum TupleElementLabel {
6991    None,
6992    /// `name: T`
6993    Required,
6994    /// `name?: T`
6995    Optional,
6996}
6997
6998fn peek_binop(kind: &TokenKind) -> Option<(BinOp, u8)> {
6999    Some(match kind {
7000        TokenKind::QuestionQuestion => (BinOp::NullishCoalesce, 0),
7001        TokenKind::PipePipe => (BinOp::Or, 1),
7002        TokenKind::AmpAmp => (BinOp::And, LOGICAL_AND_PREC),
7003        TokenKind::Pipe => (BinOp::BitOr, 3),
7004        TokenKind::Caret => (BinOp::BitXor, 4),
7005        TokenKind::Amp => (BinOp::BitAnd, 5),
7006        TokenKind::EqEqEq | TokenKind::EqEq => (BinOp::Eq, 6),
7007        TokenKind::BangEqEq | TokenKind::BangEq => (BinOp::NotEq, 6),
7008        TokenKind::LessThan => (BinOp::Lt, 7),
7009        TokenKind::GreaterThan => (BinOp::Gt, 7),
7010        TokenKind::LessEquals => (BinOp::Le, 7),
7011        TokenKind::GreaterEquals => (BinOp::Ge, 7),
7012        TokenKind::In => (BinOp::In, 7),
7013        // Shifts take `SHIFT_PRECEDENCE` (8); they span several angle tokens, so
7014        // `Parser::peek_shift` recognizes them rather than this table.
7015        TokenKind::Plus => (BinOp::Add, 9),
7016        TokenKind::Minus => (BinOp::Sub, 9),
7017        TokenKind::Star => (BinOp::Mul, 10),
7018        TokenKind::Slash => (BinOp::Div, 10),
7019        TokenKind::Percent => (BinOp::Rem, 10),
7020        // `**` recurses with `prec` not `prec + 1` — that's what makes it right-associative.
7021        TokenKind::StarStar => (BinOp::Pow, 11),
7022        _ => return None,
7023    })
7024}
7025
7026/// After `as` or `satisfies`, a token that can only extend or end an operand, never
7027/// begin a type. `[` counts, as TypeScript reads `delete as [0]` as an index, so a
7028/// tuple cast of a binding named `delete` needs parentheses.
7029fn cannot_start_type(kind: &TokenKind) -> bool {
7030    matches!(
7031        kind,
7032        TokenKind::Dot
7033            | TokenKind::QuestionDot
7034            | TokenKind::LeftBracket
7035            | TokenKind::Bang
7036            | TokenKind::Semicolon
7037            | TokenKind::RightParen
7038            | TokenKind::RightBrace
7039            | TokenKind::Comma
7040            | TokenKind::Eof
7041    )
7042}
7043
7044/// The lowest precedence the right operand of `op` may contain. `**` is
7045/// right-associative, so it recurses at its own level. An operand of `??` may
7046/// not be an unparenthesized `||` / `&&` expression, so it stops below them too.
7047fn right_operand_min_prec(op: BinOp, prec: u8) -> u8 {
7048    match op {
7049        BinOp::Pow => prec,
7050        BinOp::NullishCoalesce => LOGICAL_AND_PREC + 1,
7051        _ => prec + 1,
7052    }
7053}
7054
7055fn compound_op_for_token(kind: &TokenKind) -> Option<BinOp> {
7056    Some(match kind {
7057        TokenKind::PlusEquals => BinOp::Add,
7058        TokenKind::MinusEquals => BinOp::Sub,
7059        TokenKind::StarEquals => BinOp::Mul,
7060        TokenKind::SlashEquals => BinOp::Div,
7061        TokenKind::PercentEquals => BinOp::Rem,
7062        TokenKind::StarStarEquals => BinOp::Pow,
7063        TokenKind::AmpEquals => BinOp::BitAnd,
7064        TokenKind::PipeEquals => BinOp::BitOr,
7065        TokenKind::CaretEquals => BinOp::BitXor,
7066        _ => return None,
7067    })
7068}
7069
7070fn is_assign_lookahead(kind: &TokenKind) -> bool {
7071    matches!(kind, TokenKind::Equals) || compound_op_for_token(kind).is_some()
7072}
7073
7074/// Parser productions use `None` for recovery; arena errors also latch a fatal
7075/// cause so speculative parsing cannot turn them into ordinary syntax errors.
7076fn parse_arena_result<T>(
7077    result: Result<T, crate::arena::ArenaError>,
7078    fatal: &mut Option<CompilerFailure>,
7079) -> Option<T> {
7080    match result {
7081        Ok(value) => Some(value),
7082        Err(error) => {
7083            fatal.get_or_insert_with(|| error.into_compiler_failure(CompilerStage::Parse));
7084            None
7085        }
7086    }
7087}
7088
7089/// Whether a rest parameter's annotation names an array: `T[]`, or `readonly T[]`.
7090fn is_rest_array_annotation(ty: &crate::TypeAnnotation) -> bool {
7091    use crate::ast::TypeAnnotationKind;
7092    match &ty.kind {
7093        TypeAnnotationKind::Array(_) => true,
7094        TypeAnnotationKind::Readonly(inner) => matches!(inner.kind, TypeAnnotationKind::Array(_)),
7095        _ => false,
7096    }
7097}
7098
7099#[cfg(test)]
7100mod tests {
7101    use super::{MAX_ERRORS, MAX_PARSE_DEPTH, Parser, parse};
7102    use crate::source::Sources;
7103    use crate::{Asi, Ast, ExprKind, FileId, ImportKind, StmtKind, Token, TokenKind, diagnostics};
7104
7105    const F: FileId = FileId(0);
7106
7107    #[test]
7108    fn malformed_direct_token_streams_return_typed_failures() {
7109        use crate::compiler_error::CompilerFailure;
7110        for tokens in [
7111            Vec::new(),
7112            vec![Token::new(
7113                TokenKind::Identifier,
7114                crate::Span::new(F, 0, 2).unwrap(),
7115            )],
7116            vec![
7117                Token::new(
7118                    TokenKind::Identifier,
7119                    crate::Span {
7120                        file: F,
7121                        start: 1,
7122                        end: 2,
7123                    },
7124                ),
7125                Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
7126            ],
7127            vec![
7128                Token::new(TokenKind::Eof, crate::Span::at(F)),
7129                Token::new(TokenKind::Eof, crate::Span::at(F)),
7130            ],
7131            vec![
7132                Token::new(
7133                    TokenKind::Identifier,
7134                    crate::Span {
7135                        file: F,
7136                        start: 2,
7137                        end: 0,
7138                    },
7139                ),
7140                Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
7141            ],
7142            vec![Token::new(TokenKind::Eof, crate::Span::at(FileId(9)))],
7143        ] {
7144            let error = super::parse_checked("é", tokens, F).expect_err("invalid token metadata");
7145            assert!(matches!(
7146                error.fatal,
7147                Some(CompilerFailure::Internal { .. })
7148            ));
7149        }
7150    }
7151
7152    #[test]
7153    fn malformed_documentation_metadata_returns_a_fatal_error() {
7154        let source = "/** hello */ function main(): void {}";
7155        let valid = tokens_of(source);
7156        for doc in [
7157            crate::RawDoc {
7158                text: "/** hi */".into(),
7159                span: crate::Span {
7160                    file: F,
7161                    start: u32::MAX,
7162                    end: u32::MAX,
7163                },
7164            },
7165            crate::RawDoc {
7166                text: "/** different */".into(),
7167                span: crate::Span::new(F, 0, 12).unwrap(),
7168            },
7169            crate::RawDoc {
7170                text: "/** hello */".into(),
7171                span: crate::Span::new(FileId(9), 0, 12).unwrap(),
7172            },
7173        ] {
7174            let mut tokens = valid.clone();
7175            tokens[0].leading_doc = Some(doc);
7176            let error = super::parse_checked(source, tokens, F).expect_err("invalid doc metadata");
7177            assert!(error.to_string().contains("documentation metadata"));
7178        }
7179        assert!(super::parse_checked(source, valid, F).is_ok());
7180    }
7181
7182    #[test]
7183    fn invalid_parser_dispatch_survives_diagnostic_rollback() {
7184        let mut parser = parser_from_source("let x = 1;");
7185        assert!(parser.try_parse_type_args_only().is_none());
7186        parser.diagnostics.clear();
7187        assert!(parser.fatal.is_some());
7188        assert!(parser.parse_expression().is_none());
7189    }
7190
7191    #[test]
7192    fn invalid_arena_read_stops_parser_and_survives_diagnostic_rollback() {
7193        use crate::{
7194            ExprId,
7195            compiler_error::{CompilerFailure, CompilerStage},
7196        };
7197        let mut parser = parser_from_source("let x = 1;");
7198        assert!(parser.statement_kind_for(ExprId(u32::MAX)).is_none());
7199        assert!(matches!(
7200            parser.fatal,
7201            Some(CompilerFailure::Internal {
7202                stage: CompilerStage::Parse,
7203                span: None,
7204                ..
7205            })
7206        ));
7207        parser.diagnostics.clear();
7208        let position = parser.pos;
7209        assert!(parser.parse_statement().is_none());
7210        parser.recover();
7211        assert_eq!(parser.pos, position);
7212        assert!(parser.ast.expr_ids().unwrap().next().is_none());
7213    }
7214
7215    fn tokens_of(source: &str) -> Vec<Token> {
7216        let mut asi = Asi::new(source, crate::FileId(0));
7217        let mut tokens = Vec::new();
7218        loop {
7219            let tok = asi.next_token();
7220            let is_eof = matches!(tok.kind, TokenKind::Eof);
7221            tokens.push(tok);
7222            if is_eof {
7223                break;
7224            }
7225        }
7226        let diags = asi.into_diagnostics();
7227        assert!(diags.is_empty(), "unexpected lexer diagnostics: {diags:?}");
7228        tokens
7229    }
7230
7231    fn parse_str(source: &str) -> (Ast, Vec<crate::Diagnostic>) {
7232        parse(source, tokens_of(source), crate::FileId(0))
7233    }
7234
7235    fn parser_from_source(source: &str) -> Parser<'_> {
7236        Parser {
7237            source,
7238            tokens: tokens_of(source),
7239            split_type_tokens: Vec::new(),
7240            file: F,
7241            pos: 0,
7242            ast: Ast::new(),
7243            diagnostics: Vec::new(),
7244            block_depth: 0,
7245            class_member_body_depth: 0,
7246            function_expression_body_depth: 0,
7247            recursion_depth: 0,
7248            recursion_limit_span: None,
7249            resume_at_next_member: false,
7250            last_as_type_end: None,
7251            trailing_declarators: Vec::new(),
7252            eof: Token::new(
7253                TokenKind::Eof,
7254                crate::Span::new(F, source.len() as u32, source.len() as u32).unwrap(),
7255            ),
7256            fatal: None,
7257        }
7258    }
7259
7260    #[test]
7261    fn recursion_budget_accepts_boundary_and_restores_depth() {
7262        let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH - 1));
7263        let mut parser = parser_from_source(&source);
7264        assert!(parser.parse_unary().is_some());
7265        assert_eq!(parser.recursion_depth, 0);
7266        assert!(parser.recursion_limit_span.is_none());
7267
7268        let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH));
7269        let mut parser = parser_from_source(&source);
7270        assert!(parser.parse_unary().is_none());
7271        assert_eq!(parser.recursion_depth, 0);
7272        assert!(parser.recursion_limit_span.is_some());
7273    }
7274
7275    #[test]
7276    fn parse_empty_input() {
7277        let (ast, diags) = parse_str("");
7278        assert!(diags.is_empty());
7279        assert!(ast.top_level.is_empty());
7280    }
7281
7282    #[test]
7283    fn any_type_is_rejected() {
7284        // `any` is not supported — the parser rejects it in every type position
7285        // (annotation, cast target, array/generic element) and points to the fix.
7286        for src in [
7287            "function main(): void { const x: any = 1; }",
7288            "function main(): void { const x = 1 as any; }",
7289            "function main(): void { const x: any[] = []; }",
7290        ] {
7291            let (_ast, diags) = parse_str(src);
7292            assert!(
7293                diags
7294                    .iter()
7295                    .any(|d| d.message.contains("`any` is not supported")),
7296                "expected an `any` rejection for {src:?}, got: {diags:?}",
7297            );
7298            assert!(
7299                diags
7300                    .iter()
7301                    .any(|d| d.help.iter().any(|h| h.contains("`unknown`"))),
7302                "expected the fix-shape help for {src:?}, got: {diags:?}",
7303            );
7304        }
7305    }
7306
7307    #[test]
7308    fn any_catch_binding_help_suggests_catch_forms() {
7309        // `unknown` is not a valid catch binding type, so the general `any`
7310        // help would send the reader to a second error.
7311        let (_ast, diags) = parse_str("function main(): void { try { } catch (e: any) { } }");
7312        let any_diags: Vec<_> = diags
7313            .iter()
7314            .filter(|d| d.message.contains("`any` is not supported"))
7315            .collect();
7316        assert_eq!(any_diags.len(), 1, "got: {diags:?}");
7317        assert!(
7318            any_diags[0].help.iter().any(|h| h.contains("`catch (e)`")),
7319            "got: {diags:?}"
7320        );
7321        assert!(
7322            !any_diags[0].help.iter().any(|h| h.contains("`unknown`")),
7323            "got: {diags:?}"
7324        );
7325    }
7326
7327    #[test]
7328    fn arrow_return_type_scanner_matches_type_grammar() {
7329        // Every form the annotation grammar accepts must also be accepted by
7330        // the arrow-disambiguator's lookahead (scan_past_type_annotation):
7331        // a miss there turns a valid arrow into `expected expression`.
7332        // When adding a type form to parse_type_array, add a row here.
7333        for ty in [
7334            "number",
7335            "void",
7336            "null",
7337            "ns.Foo",
7338            "Map<string, number>",
7339            "ns.Foo<string>[]",
7340            "number[]",
7341            "[number, number]",
7342            "[number, [string, boolean]]",
7343            "[number, string][]",
7344            "{ a: number }",
7345            "{ a: number }[]",
7346            "\"on\" | \"off\"",
7347            "1 | 2",
7348            "Point | null",
7349            "| number | string",
7350            "(n: number) => number",
7351            "(n: number) => [number, number]",
7352            "readonly number[]",
7353            "readonly number[][]",
7354            "readonly [number, string]",
7355            "readonly number[] | null",
7356            "[first: number, second: string]",
7357            "[new: number, string]",
7358            "keyof { a: number }",
7359        ] {
7360            let annotation = format!("function f(): void {{ const a: {ty} = x; }}");
7361            let (_ast, diags) = parse_str(&annotation);
7362            assert!(
7363                diags.is_empty(),
7364                "annotation grammar rejected {ty:?}: {diags:?}",
7365            );
7366
7367            let arrow = format!("function f(): void {{ const g = (): {ty} => x; }}");
7368            let (_ast, diags) = parse_str(&arrow);
7369            assert!(
7370                diags.is_empty(),
7371                "arrow lookahead rejected return type {ty:?}: {diags:?}",
7372            );
7373        }
7374
7375        // Type-predicate returns ride the same scanner.
7376        let (_ast, diags) =
7377            parse_str("function f(): void { const g = (v: unknown): v is string => x; }");
7378        assert!(
7379            diags.is_empty(),
7380            "arrow lookahead rejected predicate return: {diags:?}",
7381        );
7382    }
7383
7384    #[test]
7385    fn bare_semicolons_are_silent_empty_statements() {
7386        let (ast, diags) = parse_str(";");
7387        assert!(diags.is_empty());
7388        assert!(ast.top_level.is_empty());
7389    }
7390
7391    #[test]
7392    fn parse_single_non_expression_token_errors() {
7393        let (ast, diags) = parse_str("}");
7394        assert_eq!(diags.len(), 1);
7395        assert_eq!(diags[0].message, "expected expression");
7396        assert!(ast.top_level.is_empty());
7397    }
7398
7399    #[test]
7400    fn recovery_across_many_bad_starts() {
7401        let (ast, diags) = parse_str("} } } x;");
7402        assert_eq!(diags.len(), 3);
7403        for d in &diags {
7404            assert_eq!(d.message, "expected expression");
7405        }
7406        assert_eq!(ast.top_level.len(), 1);
7407    }
7408
7409    #[test]
7410    fn recovery_consumes_semicolon() {
7411        let mut p = parser_from_source("foo ;");
7412        p.error_at_peek("test");
7413        p.recover();
7414        assert!(matches!(
7415            p.peek().kind,
7416            TokenKind::Semicolon | TokenKind::Eof
7417        ));
7418        assert!(matches!(p.peek().kind, TokenKind::Eof));
7419    }
7420
7421    #[test]
7422    fn recovery_stops_at_right_brace() {
7423        let mut p = parser_from_source("foo }");
7424        p.error_at_peek("test");
7425        p.recover();
7426        assert!(matches!(p.peek().kind, TokenKind::RightBrace));
7427    }
7428
7429    #[test]
7430    fn recovery_stops_at_let_keyword() {
7431        let mut p = parser_from_source("foo let x");
7432        p.error_at_peek("test");
7433        p.recover();
7434        assert!(matches!(p.peek().kind, TokenKind::Let));
7435    }
7436
7437    #[test]
7438    fn recovery_stops_at_function_keyword() {
7439        let mut p = parser_from_source("foo function bar");
7440        p.error_at_peek("test");
7441        p.recover();
7442        assert!(matches!(p.peek().kind, TokenKind::Function));
7443    }
7444
7445    #[test]
7446    fn error_cap_at_20() {
7447        let source = "} ".repeat(25);
7448        let (_, diags) = parse_str(&source);
7449        assert_eq!(diags.len(), MAX_ERRORS);
7450    }
7451
7452    #[test]
7453    fn driver_makes_progress_past_unmatched_brace() {
7454        let (ast, diags) = parse_str("} x;");
7455        assert_eq!(diags.len(), 1);
7456        assert_eq!(diags[0].message, "expected expression");
7457        assert_eq!(ast.top_level.len(), 1);
7458    }
7459
7460    #[test]
7461    fn snapshot_multi_error_render() {
7462        let source = "foo bar;\nbaz qux;\nhello world;";
7463        let (_, diags) = parse_str(source);
7464        let (sources, _) = Sources::single("script.subm", source).unwrap();
7465        let rendered: String = diags
7466            .iter()
7467            .map(|d| diagnostics::render(d, &sources))
7468            .collect::<Vec<_>>()
7469            .join("\n");
7470        insta::assert_snapshot!(rendered);
7471    }
7472
7473    fn single_stmt(ast: &Ast) -> &crate::Stmt {
7474        assert_eq!(ast.top_level.len(), 1);
7475        ast.try_stmt(ast.top_level[0]).unwrap()
7476    }
7477
7478    fn expr_of_single_stmt(ast: &Ast) -> &crate::Expr {
7479        let stmt = single_stmt(ast);
7480        match stmt.kind {
7481            crate::StmtKind::Expr(id) => ast.try_expr(id).unwrap(),
7482            _ => panic!("expected expression statement, got {:?}", stmt.kind),
7483        }
7484    }
7485
7486    #[test]
7487    fn parse_number_literal() {
7488        let (ast, diags) = parse_str("42;");
7489        assert!(diags.is_empty());
7490        let expr = expr_of_single_stmt(&ast);
7491        assert_eq!(expr.kind, crate::ExprKind::Number(42.0));
7492        assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
7493    }
7494
7495    #[test]
7496    fn parse_string_literal() {
7497        let (ast, diags) = parse_str(r#""s";"#);
7498        assert!(diags.is_empty());
7499        let expr = expr_of_single_stmt(&ast);
7500        assert_eq!(expr.kind, crate::ExprKind::String("s".to_string()));
7501        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7502    }
7503
7504    #[test]
7505    fn parse_boolean_true() {
7506        let (ast, diags) = parse_str("true;");
7507        assert!(diags.is_empty());
7508        let expr = expr_of_single_stmt(&ast);
7509        assert_eq!(expr.kind, crate::ExprKind::Boolean(true));
7510        assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7511    }
7512
7513    #[test]
7514    fn parse_boolean_false() {
7515        let (ast, diags) = parse_str("false;");
7516        assert!(diags.is_empty());
7517        let expr = expr_of_single_stmt(&ast);
7518        assert_eq!(expr.kind, crate::ExprKind::Boolean(false));
7519        assert_eq!(expr.span, crate::Span::new(F, 0, 5).unwrap());
7520    }
7521
7522    #[test]
7523    fn parse_null_literal() {
7524        let (ast, diags) = parse_str("null;");
7525        assert!(diags.is_empty());
7526        let expr = expr_of_single_stmt(&ast);
7527        assert_eq!(expr.kind, crate::ExprKind::Null);
7528        assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7529    }
7530
7531    #[test]
7532    fn parse_identifier() {
7533        let (ast, diags) = parse_str("x;");
7534        assert!(diags.is_empty());
7535        let expr = expr_of_single_stmt(&ast);
7536        assert!(matches!(expr.kind, crate::ExprKind::Identifier(_)));
7537        assert_eq!(expr.span, crate::Span::new(F, 0, 1).unwrap());
7538    }
7539
7540    #[test]
7541    fn reserved_keywords_remain_invalid_declaration_names() {
7542        let (_ast, diags) = parse_str("function default(): void {}");
7543        assert_eq!(diags.len(), 1, "{diags:#?}");
7544        assert_eq!(
7545            diags[0].message,
7546            "`default` is a reserved keyword and can't be used as a name"
7547        );
7548        assert!(
7549            diags[0]
7550                .help
7551                .iter()
7552                .any(|h| h == "rename it, for example: `defaultValue`"),
7553            "expected rename help, got: {diags:?}"
7554        );
7555    }
7556
7557    #[test]
7558    fn eval_and_arguments_cannot_name_bindings() {
7559        let bindings = [
7560            ("let arguments = 1;", "arguments"),
7561            ("const [eval] = [1];", "eval"),
7562            ("const { a: eval } = { a: 1 };", "eval"),
7563            ("const { eval } = { eval: 1 };", "eval"),
7564            ("const [...arguments] = [1];", "arguments"),
7565            ("function eval(): void {}", "eval"),
7566            ("function f(arguments: number): void {}", "arguments"),
7567            ("const f = arguments => arguments;", "arguments"),
7568            ("const f = (...eval: number[]) => 1;", "eval"),
7569            ("const f = function eval(): void {};", "eval"),
7570            ("for (const eval of [1]) {}", "eval"),
7571            ("try {} catch (arguments) {}", "arguments"),
7572            ("type F = (eval: number) => void;", "eval"),
7573            ("type G = (...arguments: number[]) => void;", "arguments"),
7574            ("type H = { [eval: string]: number };", "eval"),
7575        ];
7576        for (source, word) in bindings {
7577            let (_ast, diags) = parse_str(source);
7578            assert!(
7579                diags
7580                    .iter()
7581                    .any(|d| d.message == format!("`{word}` can't name a binding in strict mode")),
7582                "{source}: {diags:#?}"
7583            );
7584        }
7585    }
7586
7587    #[test]
7588    fn eval_and_arguments_still_name_types_and_properties() {
7589        for source in [
7590            "class eval {}",
7591            "interface arguments { eval: number }",
7592            "type eval = number;",
7593            "enum arguments { eval }",
7594            "const o = { eval: 1, arguments: 2 };",
7595            "import { eval } from \"./m\";",
7596        ] {
7597            let (_ast, diags) = parse_str(source);
7598            assert!(diags.is_empty(), "{source}: {diags:#?}");
7599        }
7600    }
7601
7602    #[test]
7603    fn reserved_keywords_remain_invalid_parameter_names() {
7604        let (_ast, diags) = parse_str("function f(default: string): void {}");
7605        assert_eq!(diags.len(), 1, "{diags:#?}");
7606        assert_eq!(
7607            diags[0].message,
7608            "`default` is a reserved keyword and can't be used as a name"
7609        );
7610        assert!(
7611            diags[0]
7612                .help
7613                .iter()
7614                .any(|h| h == "rename it, for example: `defaultValue`"),
7615            "expected rename help, got: {diags:?}"
7616        );
7617    }
7618
7619    #[test]
7620    fn contextual_keywords_are_valid_names() {
7621        for src in [
7622            "const type = \"x\";",
7623            "const from = 1;",
7624            "let of = 2;",
7625            "const as = 3;",
7626            "const is = 4;",
7627            "function f(from: string): void {}",
7628        ] {
7629            let (_ast, diags) = parse_str(src);
7630            assert!(
7631                diags.is_empty(),
7632                "unexpected diagnostics for {src:?}: {diags:#?}"
7633            );
7634        }
7635    }
7636
7637    #[test]
7638    fn type_alias_dispatch_table() {
7639        let (ast, diags) = parse_str("type X = number;");
7640        assert!(diags.is_empty(), "{diags:#?}");
7641        assert!(matches!(
7642            ast.try_stmt(ast.top_level[0]).unwrap().kind,
7643            crate::StmtKind::TypeAliasDecl { .. }
7644        ));
7645
7646        // A binding named `type` keeps expression/assignment statements working.
7647        for src in ["type = 5;", "type;", "type(1);", "type < 3;", "type.foo;"] {
7648            let (ast, diags) = parse_str(src);
7649            assert!(
7650                diags.is_empty(),
7651                "unexpected diagnostics for {src:?}: {diags:#?}"
7652            );
7653            assert!(
7654                !matches!(
7655                    ast.try_stmt(ast.top_level[0]).unwrap().kind,
7656                    crate::StmtKind::TypeAliasDecl { .. }
7657                ),
7658                "{src:?} must not parse as a type alias"
7659            );
7660        }
7661
7662        let (_ast, diags) = parse_str("type X;");
7663        assert!(
7664            diags
7665                .iter()
7666                .any(|d| d.message == "expected `=` after type alias name"),
7667            "bare `type X` should commit to the alias diagnostic, got: {diags:#?}"
7668        );
7669    }
7670
7671    #[test]
7672    fn export_type_alias_dispatch() {
7673        let (ast, diags) = parse_str("export type X = number;");
7674        assert!(diags.is_empty(), "{diags:#?}");
7675        assert_eq!(ast.exported_decls.len(), 1);
7676        assert!(matches!(
7677            ast.try_stmt(ast.exported_decls[0].stmt).unwrap().kind,
7678            crate::StmtKind::TypeAliasDecl { .. }
7679        ));
7680
7681        let (_ast, diags) = parse_str("export type = 5;");
7682        assert!(
7683            diags
7684                .iter()
7685                .any(|d| d.message == "expected a declaration after `export`"),
7686            "`export type = 5` must not become an exported assignment, got: {diags:#?}"
7687        );
7688    }
7689
7690    #[test]
7691    fn parse_paren_simple() {
7692        let (ast, diags) = parse_str("(x);");
7693        assert!(diags.is_empty());
7694        let expr = expr_of_single_stmt(&ast);
7695        let crate::ExprKind::Paren(inner_id) = expr.kind else {
7696            panic!("expected Paren, got {:?}", expr.kind);
7697        };
7698        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7699        let inner = ast.try_expr(inner_id).unwrap();
7700        assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7701        assert_eq!(inner.span, crate::Span::new(F, 1, 2).unwrap());
7702    }
7703
7704    #[test]
7705    fn parse_paren_nested() {
7706        let (ast, diags) = parse_str("((x));");
7707        assert!(diags.is_empty());
7708        let outer = expr_of_single_stmt(&ast);
7709        let crate::ExprKind::Paren(mid_id) = outer.kind else {
7710            panic!("expected outer Paren");
7711        };
7712        assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7713        let mid = ast.try_expr(mid_id).unwrap();
7714        let crate::ExprKind::Paren(inner_id) = mid.kind else {
7715            panic!("expected inner Paren");
7716        };
7717        assert_eq!(mid.span, crate::Span::new(F, 1, 4).unwrap());
7718        let inner = ast.try_expr(inner_id).unwrap();
7719        assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7720        assert_eq!(inner.span, crate::Span::new(F, 2, 3).unwrap());
7721    }
7722
7723    #[test]
7724    fn parse_sequence_of_atoms() {
7725        let (ast, diags) = parse_str(r#"42; "hi"; true; null; x; (x);"#);
7726        assert!(diags.is_empty());
7727        assert_eq!(ast.top_level.len(), 6);
7728        insta::assert_debug_snapshot!(ast);
7729    }
7730
7731    #[test]
7732    fn missing_semicolon_after_expression() {
7733        let (ast, diags) = parse_str("42 43");
7734        assert_eq!(diags.len(), 1);
7735        assert_eq!(diags[0].message, "expected `;` after expression");
7736        assert!(ast.top_level.is_empty());
7737    }
7738
7739    #[test]
7740    fn empty_paren_diagnoses() {
7741        let (ast, diags) = parse_str("();");
7742        assert_eq!(diags.len(), 1);
7743        assert_eq!(diags[0].message, "expected expression");
7744        assert!(ast.top_level.is_empty());
7745    }
7746
7747    #[test]
7748    fn unterminated_paren_diagnoses() {
7749        let (ast, diags) = parse_str("(x;");
7750        assert_eq!(diags.len(), 1);
7751        assert_eq!(diags[0].message, "expected `)`");
7752        assert!(ast.top_level.is_empty());
7753    }
7754
7755    fn binary(e: &crate::Expr) -> (crate::BinOp, crate::ExprId, crate::ExprId) {
7756        match e.kind {
7757            crate::ExprKind::Binary { op, lhs, rhs } => (op, lhs, rhs),
7758            _ => panic!("expected Binary, got {:?}", e.kind),
7759        }
7760    }
7761
7762    #[test]
7763    fn parse_binary_add() {
7764        let (ast, diags) = parse_str("a + b;");
7765        assert!(diags.is_empty());
7766        let outer = expr_of_single_stmt(&ast);
7767        let (op, lhs, rhs) = binary(outer);
7768        assert_eq!(op, crate::BinOp::Add);
7769        assert!(matches!(
7770            ast.try_expr(lhs).unwrap().kind,
7771            crate::ExprKind::Identifier(_)
7772        ));
7773        assert!(matches!(
7774            ast.try_expr(rhs).unwrap().kind,
7775            crate::ExprKind::Identifier(_)
7776        ));
7777        assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7778    }
7779
7780    #[test]
7781    fn bitwise_precedence_and_shift_adjacency() {
7782        let (ast, diags) = parse_str("a | b ^ c & d == e >> f + g;");
7783        assert!(diags.is_empty(), "{diags:?}");
7784        let (op, _, mut rhs) = binary(expr_of_single_stmt(&ast));
7785        assert_eq!(op, crate::BinOp::BitOr);
7786        for expected in [
7787            crate::BinOp::BitXor,
7788            crate::BinOp::BitAnd,
7789            crate::BinOp::Eq,
7790            crate::BinOp::Shr,
7791            crate::BinOp::Add,
7792        ] {
7793            let (op, _, next) = binary(ast.try_expr(rhs).unwrap());
7794            assert_eq!(op, expected);
7795            rhs = next;
7796        }
7797        for source in ["a > > b;", "a >/*gap*/> b;", "a < < b;"] {
7798            let (_, diags) = parse_str(source);
7799            assert!(!diags.is_empty(), "{source}");
7800        }
7801    }
7802
7803    #[test]
7804    fn bitwise_shift_assignment_span() {
7805        let (ast, diags) = parse_str("a >>>= b;");
7806        assert!(diags.is_empty(), "{diags:?}");
7807        let crate::StmtKind::CompoundAssign { op, op_span, .. } = single_stmt(&ast).kind else {
7808            panic!("expected assignment");
7809        };
7810        assert_eq!(op, crate::BinOp::UnsignedShr);
7811        assert_eq!(op_span, crate::Span::new(F, 2, 6).unwrap());
7812    }
7813
7814    #[test]
7815    fn precedence_mul_binds_tighter_than_add() {
7816        let (ast, diags) = parse_str("a + b * c;");
7817        assert!(diags.is_empty());
7818        let outer = expr_of_single_stmt(&ast);
7819        let (op, lhs, rhs) = binary(outer);
7820        assert_eq!(op, crate::BinOp::Add);
7821        assert!(matches!(
7822            ast.try_expr(lhs).unwrap().kind,
7823            crate::ExprKind::Identifier(_)
7824        ));
7825        let (rop, rlhs, rrhs) = binary(ast.try_expr(rhs).unwrap());
7826        assert_eq!(rop, crate::BinOp::Mul);
7827        assert!(matches!(
7828            ast.try_expr(rlhs).unwrap().kind,
7829            crate::ExprKind::Identifier(_)
7830        ));
7831        assert!(matches!(
7832            ast.try_expr(rrhs).unwrap().kind,
7833            crate::ExprKind::Identifier(_)
7834        ));
7835    }
7836
7837    #[test]
7838    fn precedence_mul_left_of_add() {
7839        let (ast, diags) = parse_str("a * b + c;");
7840        assert!(diags.is_empty());
7841        let outer = expr_of_single_stmt(&ast);
7842        let (op, lhs, rhs) = binary(outer);
7843        assert_eq!(op, crate::BinOp::Add);
7844        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7845        assert_eq!(lop, crate::BinOp::Mul);
7846        assert!(matches!(
7847            ast.try_expr(rhs).unwrap().kind,
7848            crate::ExprKind::Identifier(_)
7849        ));
7850    }
7851
7852    #[test]
7853    fn left_associative_same_precedence() {
7854        let (ast, diags) = parse_str("a - b - c;");
7855        assert!(diags.is_empty());
7856        let outer = expr_of_single_stmt(&ast);
7857        let (op, lhs, rhs) = binary(outer);
7858        assert_eq!(op, crate::BinOp::Sub);
7859        let (lop, llhs, lrhs) = binary(ast.try_expr(lhs).unwrap());
7860        assert_eq!(lop, crate::BinOp::Sub);
7861        assert!(matches!(
7862            ast.try_expr(llhs).unwrap().kind,
7863            crate::ExprKind::Identifier(_)
7864        ));
7865        assert!(matches!(
7866            ast.try_expr(lrhs).unwrap().kind,
7867            crate::ExprKind::Identifier(_)
7868        ));
7869        assert!(matches!(
7870            ast.try_expr(rhs).unwrap().kind,
7871            crate::ExprKind::Identifier(_)
7872        ));
7873    }
7874
7875    #[test]
7876    fn equality_inside_logical() {
7877        let (ast, diags) = parse_str("a === b && c === d;");
7878        assert!(diags.is_empty());
7879        let outer = expr_of_single_stmt(&ast);
7880        let (op, lhs, rhs) = binary(outer);
7881        assert_eq!(op, crate::BinOp::And);
7882        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7883        assert_eq!(lop, crate::BinOp::Eq);
7884        let (rop, ..) = binary(ast.try_expr(rhs).unwrap());
7885        assert_eq!(rop, crate::BinOp::Eq);
7886    }
7887
7888    #[test]
7889    fn double_equal_is_same_variant_as_triple() {
7890        let (ast, _) = parse_str("a == b;");
7891        let outer = expr_of_single_stmt(&ast);
7892        let (op, ..) = binary(outer);
7893        assert_eq!(op, crate::BinOp::Eq);
7894    }
7895
7896    #[test]
7897    fn bang_eq_is_not_eq() {
7898        let (ast, _) = parse_str("a != b;");
7899        let outer = expr_of_single_stmt(&ast);
7900        let (op, ..) = binary(outer);
7901        assert_eq!(op, crate::BinOp::NotEq);
7902    }
7903
7904    #[test]
7905    fn comparison_has_higher_prec_than_logical() {
7906        let (ast, _) = parse_str("a < b && c;");
7907        let outer = expr_of_single_stmt(&ast);
7908        let (op, lhs, _) = binary(outer);
7909        assert_eq!(op, crate::BinOp::And);
7910        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7911        assert_eq!(lop, crate::BinOp::Lt);
7912    }
7913
7914    #[test]
7915    fn full_precedence_chain_snapshot() {
7916        let (ast, diags) = parse_str("a || b && c === d < e + f * g;");
7917        assert!(diags.is_empty());
7918        insta::assert_debug_snapshot!(ast);
7919    }
7920
7921    #[test]
7922    fn paren_overrides_precedence() {
7923        let (ast, _) = parse_str("(a + b) * c;");
7924        let outer = expr_of_single_stmt(&ast);
7925        let (op, lhs, rhs) = binary(outer);
7926        assert_eq!(op, crate::BinOp::Mul);
7927        let crate::ExprKind::Paren(inner_id) = ast.try_expr(lhs).unwrap().kind else {
7928            panic!("expected Paren on lhs");
7929        };
7930        let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7931        assert_eq!(inner_op, crate::BinOp::Add);
7932        assert!(matches!(
7933            ast.try_expr(rhs).unwrap().kind,
7934            crate::ExprKind::Identifier(_)
7935        ));
7936    }
7937
7938    #[test]
7939    fn binary_missing_rhs_diagnoses() {
7940        let (ast, diags) = parse_str("a +;");
7941        assert_eq!(diags.len(), 1);
7942        assert_eq!(diags[0].message, "expected expression");
7943        assert!(ast.top_level.is_empty());
7944    }
7945
7946    fn unary(e: &crate::Expr) -> (crate::UnOp, crate::ExprId) {
7947        match e.kind {
7948            crate::ExprKind::Unary { op, operand } => (op, operand),
7949            _ => panic!("expected Unary, got {:?}", e.kind),
7950        }
7951    }
7952
7953    #[test]
7954    fn parse_unary_not() {
7955        let (ast, _) = parse_str("!x;");
7956        let outer = expr_of_single_stmt(&ast);
7957        let (op, operand) = unary(outer);
7958        assert_eq!(op, crate::UnOp::Not);
7959        assert!(matches!(
7960            ast.try_expr(operand).unwrap().kind,
7961            crate::ExprKind::Identifier(_)
7962        ));
7963        assert_eq!(outer.span, crate::Span::new(F, 0, 2).unwrap());
7964    }
7965
7966    #[test]
7967    fn parse_unary_neg() {
7968        let (ast, _) = parse_str("-x;");
7969        let (op, _) = unary(expr_of_single_stmt(&ast));
7970        assert_eq!(op, crate::UnOp::Neg);
7971    }
7972
7973    #[test]
7974    fn parse_unary_pos() {
7975        let (ast, _) = parse_str("+x;");
7976        let (op, _) = unary(expr_of_single_stmt(&ast));
7977        assert_eq!(op, crate::UnOp::Pos);
7978    }
7979
7980    #[test]
7981    fn repeated_unary_not() {
7982        let (ast, _) = parse_str("!!x;");
7983        let outer = expr_of_single_stmt(&ast);
7984        let (op, operand) = unary(outer);
7985        assert_eq!(op, crate::UnOp::Not);
7986        let (inner_op, inner_operand) = unary(ast.try_expr(operand).unwrap());
7987        assert_eq!(inner_op, crate::UnOp::Not);
7988        assert!(matches!(
7989            ast.try_expr(inner_operand).unwrap().kind,
7990            crate::ExprKind::Identifier(_)
7991        ));
7992    }
7993
7994    #[test]
7995    fn unary_binds_tighter_than_binary() {
7996        let (ast, _) = parse_str("-x + 1;");
7997        let outer = expr_of_single_stmt(&ast);
7998        let (op, lhs, rhs) = binary(outer);
7999        assert_eq!(op, crate::BinOp::Add);
8000        let (unop, _) = unary(ast.try_expr(lhs).unwrap());
8001        assert_eq!(unop, crate::UnOp::Neg);
8002        assert_eq!(
8003            ast.try_expr(rhs).unwrap().kind,
8004            crate::ExprKind::Number(1.0)
8005        );
8006    }
8007
8008    #[test]
8009    fn unary_over_paren() {
8010        let (ast, _) = parse_str("-(x + 1);");
8011        let outer = expr_of_single_stmt(&ast);
8012        let (op, operand) = unary(outer);
8013        assert_eq!(op, crate::UnOp::Neg);
8014        let crate::ExprKind::Paren(inner_id) = ast.try_expr(operand).unwrap().kind else {
8015            panic!("expected Paren");
8016        };
8017        let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
8018        assert_eq!(inner_op, crate::BinOp::Add);
8019    }
8020
8021    #[test]
8022    fn parse_is_as_expression_rejected() {
8023        let (_, diags) = parse_str("x is number;");
8024        assert!(!diags.is_empty(), "expected parse error for `x is T`");
8025    }
8026
8027    fn as_cast(e: &crate::Expr) -> (crate::ExprId, &crate::TypeAnnotation) {
8028        match &e.kind {
8029            crate::ExprKind::As { expr, ty } => (*expr, ty),
8030            other => panic!("expected ExprKind::As, got {other:?}"),
8031        }
8032    }
8033
8034    #[test]
8035    fn parse_instanceof_class() {
8036        let (ast, diags) = parse_str("x instanceof Foo;");
8037        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8038        let outer = expr_of_single_stmt(&ast);
8039        let crate::ExprKind::InstanceOf { value, ty } = &outer.kind else {
8040            panic!("expected ExprKind::InstanceOf, got {:?}", outer.kind);
8041        };
8042        assert!(matches!(
8043            ast.try_expr(*value).unwrap().kind,
8044            crate::ExprKind::Identifier(_)
8045        ));
8046        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8047    }
8048
8049    #[test]
8050    fn parse_instanceof_binds_tighter_than_logical_and() {
8051        // `a instanceof B && c` parses as `(a instanceof B) && c`.
8052        let (ast, diags) = parse_str("a instanceof B && c;");
8053        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8054        let outer = expr_of_single_stmt(&ast);
8055        let (op, lhs, rhs) = binary(outer);
8056        assert_eq!(op, crate::BinOp::And);
8057        assert!(matches!(
8058            ast.try_expr(lhs).unwrap().kind,
8059            crate::ExprKind::InstanceOf { .. }
8060        ));
8061        assert!(matches!(
8062            ast.try_expr(rhs).unwrap().kind,
8063            crate::ExprKind::Identifier(_)
8064        ));
8065    }
8066
8067    /// `<T>x` produces the same node as `x as T`, so everything downstream of the
8068    /// parser sees one construct rather than two.
8069    #[test]
8070    fn parse_angle_cast_lowers_to_the_as_node() {
8071        let (ast, diags) = parse_str("<number>x;");
8072        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8073        let outer = expr_of_single_stmt(&ast);
8074        let (inner, ty) = as_cast(outer);
8075        assert!(matches!(
8076            ast.try_expr(inner).unwrap().kind,
8077            crate::ExprKind::Identifier(_)
8078        ));
8079        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8080    }
8081
8082    /// A `<` that means comparison always has a left operand, so it never reaches
8083    /// the cast branch. This is the regression the cast syntax could most easily
8084    /// have caused.
8085    #[test]
8086    fn a_less_than_comparison_is_not_read_as_a_cast() {
8087        let (ast, diags) = parse_str("a < b;");
8088        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8089        let e = expr_of_single_stmt(&ast);
8090        assert!(matches!(e.kind, crate::ExprKind::Binary { .. }));
8091    }
8092
8093    #[test]
8094    fn parse_typeof_in_type_position() {
8095        let (_ast, diags) = parse_str("let a: typeof b = 1;");
8096        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8097    }
8098
8099    #[test]
8100    fn parse_typeof_with_a_dotted_path() {
8101        let (_ast, diags) = parse_str("let a: typeof b.c.d = 1;");
8102        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8103    }
8104
8105    #[test]
8106    fn parse_as_primitive() {
8107        let (ast, diags) = parse_str("x as number;");
8108        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8109        let outer = expr_of_single_stmt(&ast);
8110        let (inner, ty) = as_cast(outer);
8111        assert!(matches!(
8112            ast.try_expr(inner).unwrap().kind,
8113            crate::ExprKind::Identifier(_)
8114        ));
8115        match &ty.kind {
8116            crate::TypeAnnotationKind::Name { args, .. } => {
8117                assert!(args.is_empty());
8118            }
8119            other => panic!("expected Name target, got {other:?}"),
8120        }
8121        assert_eq!(outer.span, crate::Span::new(F, 0, 11).unwrap());
8122    }
8123
8124    #[test]
8125    fn parse_as_object_literal_type() {
8126        let (ast, diags) = parse_str("x as { a: number };");
8127        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8128        let outer = expr_of_single_stmt(&ast);
8129        let (_, ty) = as_cast(outer);
8130        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Object { .. }));
8131    }
8132
8133    #[test]
8134    fn parse_as_array_short() {
8135        let (ast, diags) = parse_str("x as string[];");
8136        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8137        let outer = expr_of_single_stmt(&ast);
8138        let (_, ty) = as_cast(outer);
8139        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
8140    }
8141
8142    #[test]
8143    fn parse_as_array_generic() {
8144        let (ast, diags) = parse_str("x as T[];");
8145        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8146        let outer = expr_of_single_stmt(&ast);
8147        let (_, ty) = as_cast(outer);
8148        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
8149    }
8150
8151    #[test]
8152    fn parse_as_generic_instantiation() {
8153        let (ast, diags) = parse_str("x as Foo<Bar>;");
8154        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8155        let outer = expr_of_single_stmt(&ast);
8156        let (_, ty) = as_cast(outer);
8157        match &ty.kind {
8158            crate::TypeAnnotationKind::Name { args, .. } => {
8159                assert_eq!(args.len(), 1, "Foo<Bar> has one type arg");
8160            }
8161            other => panic!("expected Name with type args, got {other:?}"),
8162        }
8163    }
8164
8165    #[test]
8166    fn parse_as_precedence_higher_than_additive() {
8167        let (ast, diags) = parse_str("x as number + 1;");
8168        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8169        let outer = expr_of_single_stmt(&ast);
8170        let (op, lhs, rhs) = binary(outer);
8171        assert_eq!(op, crate::BinOp::Add);
8172        assert!(matches!(
8173            ast.try_expr(lhs).unwrap().kind,
8174            crate::ExprKind::As { .. }
8175        ));
8176        assert!(matches!(
8177            ast.try_expr(rhs).unwrap().kind,
8178            crate::ExprKind::Number(_)
8179        ));
8180    }
8181
8182    #[test]
8183    fn parse_as_precedence_lower_than_unary() {
8184        let (ast, diags) = parse_str("!x as boolean;");
8185        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8186        let outer = expr_of_single_stmt(&ast);
8187        let (inner, _) = as_cast(outer);
8188        assert!(matches!(
8189            ast.try_expr(inner).unwrap().kind,
8190            crate::ExprKind::Unary {
8191                op: crate::UnOp::Not,
8192                ..
8193            }
8194        ));
8195    }
8196
8197    #[test]
8198    fn parse_as_left_associative_chain() {
8199        let (ast, diags) = parse_str("x as A as B;");
8200        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8201        let outer = expr_of_single_stmt(&ast);
8202        let (inner, _outer_ty) = as_cast(outer);
8203        let (innermost, _inner_ty) = as_cast(ast.try_expr(inner).unwrap());
8204        assert!(matches!(
8205            ast.try_expr(innermost).unwrap().kind,
8206            crate::ExprKind::Identifier(_)
8207        ));
8208    }
8209
8210    #[test]
8211    fn parse_as_inside_call_arg() {
8212        let (ast, diags) = parse_str("f(x as T);");
8213        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8214        let outer = expr_of_single_stmt(&ast);
8215        match &outer.kind {
8216            crate::ExprKind::Call { args, .. } => {
8217                assert_eq!(args.len(), 1);
8218                assert!(matches!(
8219                    ast.try_expr(args[0]).unwrap().kind,
8220                    crate::ExprKind::As { .. }
8221                ));
8222            }
8223            other => panic!("expected Call, got {other:?}"),
8224        }
8225    }
8226
8227    #[test]
8228    fn parse_as_after_field_access() {
8229        let (ast, diags) = parse_str("obj.field as T;");
8230        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8231        let outer = expr_of_single_stmt(&ast);
8232        let (inner, _) = as_cast(outer);
8233        assert!(matches!(
8234            ast.try_expr(inner).unwrap().kind,
8235            crate::ExprKind::FieldAccess { .. }
8236        ));
8237    }
8238
8239    #[test]
8240    fn parse_type_guard_return_annotation_on_function() {
8241        let src = "function isCircle(s: Shape): s is Circle { return true; }";
8242        let (ast, diags) = parse_str(src);
8243        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8244        let stmt = single_stmt(&ast);
8245        match stmt.kind {
8246            crate::StmtKind::Function {
8247                ref return_type,
8248                ref type_predicate,
8249                ..
8250            } => {
8251                assert!(
8252                    return_type.is_none(),
8253                    "predicate form leaves return_type empty",
8254                );
8255                let tp = type_predicate.as_ref().expect("predicate parsed");
8256                assert_eq!(tp.param.name, "s");
8257                assert!(matches!(
8258                    tp.asserted.kind,
8259                    crate::TypeAnnotationKind::Name { .. }
8260                ));
8261                assert_eq!(tp.span, crate::Span::new(F, 29, 40).unwrap());
8262            }
8263            _ => panic!("expected Function"),
8264        }
8265    }
8266
8267    #[test]
8268    fn parse_type_guard_return_annotation_on_arrow() {
8269        let src = "const f = (s: Shape): s is Circle => true;";
8270        let (ast, diags) = parse_str(src);
8271        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8272        let value = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
8273            crate::StmtKind::Const { value, .. } => *value,
8274            _ => panic!("expected const"),
8275        };
8276        match &ast.try_expr(value).unwrap().kind {
8277            crate::ExprKind::Arrow {
8278                return_type,
8279                type_predicate,
8280                ..
8281            } => {
8282                assert!(return_type.is_none());
8283                let tp = type_predicate.as_ref().expect("predicate parsed");
8284                assert_eq!(tp.param.name, "s");
8285            }
8286            other => panic!("expected Arrow, got {other:?}"),
8287        }
8288    }
8289
8290    #[test]
8291    fn parse_plain_return_type_unaffected_by_predicate_parser() {
8292        let src = "function f(s: Shape): boolean { return true; }";
8293        let (ast, diags) = parse_str(src);
8294        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8295        let stmt = single_stmt(&ast);
8296        match stmt.kind {
8297            crate::StmtKind::Function {
8298                ref return_type,
8299                ref type_predicate,
8300                ..
8301            } => {
8302                assert!(return_type.is_some(), "plain form has return_type");
8303                assert!(type_predicate.is_none(), "plain form has no type_predicate",);
8304            }
8305            _ => panic!("expected Function"),
8306        }
8307    }
8308
8309    #[test]
8310    fn parse_type_guard_rejects_non_return_position() {
8311        let (_, diags) = parse_str("let x: y is Foo = null;");
8312        assert!(
8313            !diags.is_empty(),
8314            "expected parse error for `is` outside return-type position",
8315        );
8316    }
8317
8318    #[test]
8319    fn parse_typeof_prefix() {
8320        let (ast, diags) = parse_str("typeof x;");
8321        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8322        let outer = expr_of_single_stmt(&ast);
8323        let crate::ExprKind::Typeof { operand } = outer.kind else {
8324            panic!("expected Typeof, got {:?}", outer.kind);
8325        };
8326        assert!(matches!(
8327            ast.try_expr(operand).unwrap().kind,
8328            crate::ExprKind::Identifier(_)
8329        ));
8330        assert_eq!(outer.span, crate::Span::new(F, 0, 8).unwrap());
8331    }
8332
8333    #[test]
8334    fn parse_typeof_in_equality() {
8335        let (ast, diags) = parse_str(r#"typeof x === "number";"#);
8336        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8337        let outer = expr_of_single_stmt(&ast);
8338        let (op, lhs, rhs) = binary(outer);
8339        assert_eq!(op, crate::BinOp::Eq);
8340        assert!(matches!(
8341            ast.try_expr(lhs).unwrap().kind,
8342            crate::ExprKind::Typeof { .. }
8343        ));
8344        assert!(matches!(
8345            ast.try_expr(rhs).unwrap().kind,
8346            crate::ExprKind::String(ref s) if s == "number"
8347        ));
8348    }
8349
8350    #[test]
8351    fn parse_let_without_type() {
8352        let (ast, diags) = parse_str("let x = 1;");
8353        assert!(diags.is_empty());
8354        let stmt = single_stmt(&ast);
8355        assert_eq!(stmt.span, crate::Span::new(F, 0, 10).unwrap());
8356        match stmt.kind {
8357            crate::StmtKind::Let {
8358                ref name,
8359                ref ty,
8360                value,
8361                ..
8362            } => {
8363                assert_eq!(name.name, "x");
8364                assert_eq!(name.span, crate::Span::new(F, 4, 5).unwrap());
8365                assert!(ty.is_none());
8366                assert_eq!(
8367                    ast.try_expr(value).unwrap().kind,
8368                    crate::ExprKind::Number(1.0)
8369                );
8370            }
8371            _ => panic!("expected Let"),
8372        }
8373    }
8374
8375    #[test]
8376    fn parse_const_with_type_and_string() {
8377        let (ast, diags) = parse_str(r#"const y: string = "hi";"#);
8378        assert!(diags.is_empty());
8379        let stmt = single_stmt(&ast);
8380        match stmt.kind {
8381            crate::StmtKind::Const {
8382                ref name,
8383                ref ty,
8384                value,
8385                ..
8386            } => {
8387                assert_eq!(name.name, "y");
8388                assert_eq!(name.span, crate::Span::new(F, 6, 7).unwrap());
8389                let ty = ty.as_ref().expect("expected type annotation");
8390                assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8391                assert_eq!(ty.span, crate::Span::new(F, 9, 15).unwrap());
8392                assert_eq!(
8393                    ast.try_expr(value).unwrap().kind,
8394                    crate::ExprKind::String("hi".to_string())
8395                );
8396            }
8397            _ => panic!("expected Const"),
8398        }
8399    }
8400
8401    #[test]
8402    fn parse_let_with_type_and_binary_initializer() {
8403        let (ast, diags) = parse_str("let z: number = 1 + 2;");
8404        assert!(diags.is_empty());
8405        let stmt = single_stmt(&ast);
8406        match stmt.kind {
8407            crate::StmtKind::Let { ref ty, value, .. } => {
8408                assert!(ty.is_some());
8409                let (op, ..) = binary(ast.try_expr(value).unwrap());
8410                assert_eq!(op, crate::BinOp::Add);
8411            }
8412            _ => panic!("expected Let"),
8413        }
8414    }
8415
8416    #[test]
8417    fn void_type_annotation_is_accepted_syntactically() {
8418        let (ast, diags) = parse_str("let v: void = null;");
8419        assert!(diags.is_empty());
8420        let stmt = single_stmt(&ast);
8421        match stmt.kind {
8422            crate::StmtKind::Let { ref ty, .. } => {
8423                let ty = ty.as_ref().unwrap();
8424                assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8425                assert_eq!(ty.span, crate::Span::new(F, 7, 11).unwrap());
8426            }
8427            _ => panic!("expected Let"),
8428        }
8429    }
8430
8431    #[test]
8432    fn const_missing_initializer_diagnoses() {
8433        let (ast, diags) = parse_str("const x;");
8434        assert_eq!(diags.len(), 1);
8435        assert_eq!(
8436            diags[0].message,
8437            "`const` declaration requires an initializer"
8438        );
8439        assert!(ast.top_level.is_empty());
8440    }
8441
8442    #[test]
8443    fn parse_const_object_shorthand_pattern() {
8444        let (ast, diags) = parse_str("const { a, b } = obj;");
8445        assert!(diags.is_empty(), "unexpected: {diags:?}");
8446        let stmt = single_stmt(&ast);
8447        match stmt.kind {
8448            crate::StmtKind::ConstPattern {
8449                binding:
8450                    crate::Binding::Object {
8451                        ref fields,
8452                        ref rest,
8453                        ..
8454                    },
8455                ..
8456            } => {
8457                assert_eq!(fields.len(), 2);
8458                assert_eq!(fields[0].source.name, "a");
8459                assert_eq!(fields[0].local.name, "a");
8460                assert_eq!(fields[1].source.name, "b");
8461                assert_eq!(fields[1].local.name, "b");
8462                assert!(rest.is_none());
8463            }
8464            _ => panic!("expected ConstPattern Object, got {:?}", stmt.kind),
8465        }
8466    }
8467
8468    #[test]
8469    fn parse_const_object_pattern_keyword_source_key() {
8470        let (ast, diags) = parse_str("const { type: statusType, default: fallback } = obj;");
8471        assert!(diags.is_empty(), "unexpected: {diags:?}");
8472        let stmt = single_stmt(&ast);
8473        let crate::StmtKind::ConstPattern {
8474            binding: crate::Binding::Object { ref fields, .. },
8475            ..
8476        } = stmt.kind
8477        else {
8478            panic!("expected object const pattern");
8479        };
8480
8481        let names: Vec<(&str, &str)> = fields
8482            .iter()
8483            .map(|f| (f.source.name.as_str(), f.local.name.as_str()))
8484            .collect();
8485        assert_eq!(names, vec![("type", "statusType"), ("default", "fallback")]);
8486    }
8487
8488    #[test]
8489    fn parse_const_object_pattern_keyword_shorthand_rejected() {
8490        let (_ast, diags) = parse_str("const { default } = obj;");
8491        assert!(
8492            diags
8493                .iter()
8494                .any(|d| d.message == "expected `:` after keyword field name in object pattern"),
8495            "expected keyword-shorthand diagnostic, got: {diags:?}"
8496        );
8497    }
8498
8499    #[test]
8500    fn parse_const_object_renamed_pattern() {
8501        let (ast, diags) = parse_str("const { a: x, b: y } = obj;");
8502        assert!(diags.is_empty(), "unexpected: {diags:?}");
8503        match single_stmt(&ast).kind {
8504            crate::StmtKind::ConstPattern {
8505                binding: crate::Binding::Object { ref fields, .. },
8506                ..
8507            } => {
8508                assert_eq!(fields[0].source.name, "a");
8509                assert_eq!(fields[0].local.name, "x");
8510                assert_eq!(fields[1].source.name, "b");
8511                assert_eq!(fields[1].local.name, "y");
8512            }
8513            ref other => panic!("expected ConstPattern Object, got {other:?}"),
8514        }
8515    }
8516
8517    #[test]
8518    fn parse_const_object_with_rest() {
8519        let (ast, diags) = parse_str("const { a, ...rest } = obj;");
8520        assert!(diags.is_empty(), "unexpected: {diags:?}");
8521        match single_stmt(&ast).kind {
8522            crate::StmtKind::ConstPattern {
8523                binding:
8524                    crate::Binding::Object {
8525                        ref fields,
8526                        ref rest,
8527                        ..
8528                    },
8529                ..
8530            } => {
8531                assert_eq!(fields.len(), 1);
8532                assert_eq!(fields[0].source.name, "a");
8533                let r = rest.as_ref().expect("expected rest");
8534                assert_eq!(r.name, "rest");
8535            }
8536            ref other => panic!("expected ConstPattern Object, got {other:?}"),
8537        }
8538    }
8539
8540    #[test]
8541    fn parse_const_array_pattern() {
8542        let (ast, diags) = parse_str("const [x, y] = arr;");
8543        assert!(diags.is_empty(), "unexpected: {diags:?}");
8544        match single_stmt(&ast).kind {
8545            crate::StmtKind::ConstPattern {
8546                binding:
8547                    crate::Binding::Array {
8548                        ref elems,
8549                        ref rest,
8550                        ..
8551                    },
8552                ..
8553            } => {
8554                assert_eq!(elems.len(), 2);
8555                assert_eq!(elems[0].as_ref().unwrap().name, "x");
8556                assert_eq!(elems[1].as_ref().unwrap().name, "y");
8557                assert!(rest.is_none());
8558            }
8559            ref other => panic!("expected ConstPattern Array, got {other:?}"),
8560        }
8561    }
8562
8563    #[test]
8564    fn parse_const_array_with_hole_and_rest() {
8565        let (ast, diags) = parse_str("const [, x, ...rest] = arr;");
8566        assert!(diags.is_empty(), "unexpected: {diags:?}");
8567        match single_stmt(&ast).kind {
8568            crate::StmtKind::ConstPattern {
8569                binding:
8570                    crate::Binding::Array {
8571                        ref elems,
8572                        ref rest,
8573                        ..
8574                    },
8575                ..
8576            } => {
8577                assert_eq!(elems.len(), 2);
8578                assert!(elems[0].is_none(), "first slot should be a hole");
8579                assert_eq!(elems[1].as_ref().unwrap().name, "x");
8580                assert_eq!(rest.as_ref().unwrap().name, "rest");
8581            }
8582            ref other => panic!("expected ConstPattern Array, got {other:?}"),
8583        }
8584    }
8585
8586    #[test]
8587    fn parse_let_object_pattern() {
8588        let (ast, diags) = parse_str("let { a, b } = obj;");
8589        assert!(diags.is_empty(), "unexpected: {diags:?}");
8590        assert!(matches!(
8591            single_stmt(&ast).kind,
8592            crate::StmtKind::LetPattern {
8593                binding: crate::Binding::Object { .. },
8594                ..
8595            }
8596        ));
8597    }
8598
8599    #[test]
8600    fn parse_function_param_object_pattern() {
8601        let src = "function f({ a, b }: T): void { return; }";
8602        let (ast, diags) = parse_str(src);
8603        assert!(diags.is_empty(), "unexpected: {diags:?}");
8604        match single_stmt(&ast).kind {
8605            crate::StmtKind::Function { ref params, .. } => {
8606                assert_eq!(params.len(), 1);
8607                let p = &params[0];
8608                assert!(p.pattern.is_some(), "expected pattern param");
8609                assert_eq!(p.name.name, "", "pattern placeholder uses empty name");
8610                match p.pattern.as_ref().unwrap() {
8611                    crate::Binding::Object { fields, .. } => {
8612                        assert_eq!(fields.len(), 2);
8613                        assert_eq!(fields[0].source.name, "a");
8614                    }
8615                    other => panic!("expected Object binding, got {other:?}"),
8616                }
8617            }
8618            ref other => panic!("expected Function, got {other:?}"),
8619        }
8620    }
8621
8622    #[test]
8623    fn parse_arrow_param_array_pattern() {
8624        let src = "const g = ([x, y]: number[]): number => x + y;";
8625        let (_ast, diags) = parse_str(src);
8626        assert!(diags.is_empty(), "unexpected: {diags:?}");
8627    }
8628
8629    #[test]
8630    fn parse_destructure_nested_rejected() {
8631        let (_ast, diags) = parse_str("const { a: { b } } = obj;");
8632        assert!(
8633            diags
8634                .iter()
8635                .any(|d| d.message.contains("nested destructuring")),
8636            "expected nested-destructuring diagnostic, got: {diags:?}",
8637        );
8638    }
8639
8640    #[test]
8641    fn parse_destructure_array_nested_rejected() {
8642        let (_ast, diags) = parse_str("const [[a]] = arr;");
8643        assert!(
8644            diags
8645                .iter()
8646                .any(|d| d.message.contains("nested destructuring")),
8647            "expected nested-destructuring diagnostic, got: {diags:?}",
8648        );
8649    }
8650
8651    #[test]
8652    fn parse_destructure_pattern_defaults() {
8653        let (ast, diags) = parse_str("const { a = 1, b: c = 2 } = obj;");
8654        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8655        let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
8656            panic!("expected pattern")
8657        };
8658        let crate::Binding::Object { fields, .. } = binding else {
8659            panic!("expected object")
8660        };
8661        assert_eq!(fields.len(), 2);
8662        assert!(fields.iter().all(|field| field.default.is_some()));
8663        assert_eq!(fields[1].local.name, "c");
8664    }
8665
8666    #[test]
8667    fn parse_destructure_param_default() {
8668        let (ast, diags) = parse_str("function f({ a = 1 }: T = obj): void { return; }");
8669        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8670        let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
8671            panic!("expected function")
8672        };
8673        assert!(params[0].default.is_some());
8674        assert!(params[0].pattern.is_some());
8675    }
8676
8677    #[test]
8678    fn parse_destructure_rest_not_last_rejected() {
8679        let (_ast, diags) = parse_str("const { ...rest, a } = obj;");
8680        assert!(
8681            diags
8682                .iter()
8683                .any(|d| d.message.contains("rest element must be the last element")),
8684            "expected rest-not-last diagnostic, got: {diags:?}",
8685        );
8686    }
8687
8688    #[test]
8689    fn parse_destructure_empty_object_rejected() {
8690        let (_ast, diags) = parse_str("const { } = obj;");
8691        assert!(
8692            diags
8693                .iter()
8694                .any(|d| d.message.contains("empty object destructuring pattern")),
8695            "expected empty-pattern diagnostic, got: {diags:?}",
8696        );
8697    }
8698
8699    #[test]
8700    fn parse_ternary_simple() {
8701        let (ast, diags) = parse_str("const x: number = a ? 1 : 2;");
8702        assert!(diags.is_empty(), "unexpected: {diags:?}");
8703        let stmt = single_stmt(&ast);
8704        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8705            panic!("expected Const, got {:?}", stmt.kind);
8706        };
8707        assert!(matches!(
8708            ast.try_expr(value).unwrap().kind,
8709            crate::ExprKind::Ternary { .. }
8710        ));
8711    }
8712
8713    #[test]
8714    fn parse_ternary_nested_right_assoc() {
8715        let (ast, diags) = parse_str("const x: number = a ? 1 : b ? 2 : 3;");
8716        assert!(diags.is_empty(), "unexpected: {diags:?}");
8717        let stmt = single_stmt(&ast);
8718        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8719            panic!("expected Const");
8720        };
8721        let crate::ExprKind::Ternary { else_, .. } = ast.try_expr(value).unwrap().kind else {
8722            panic!("expected outer Ternary");
8723        };
8724        assert!(matches!(
8725            ast.try_expr(else_).unwrap().kind,
8726            crate::ExprKind::Ternary { .. }
8727        ));
8728    }
8729
8730    #[test]
8731    fn parse_ternary_missing_colon_diagnoses() {
8732        let (_, diags) = parse_str("const x: number = a ? 1;");
8733        assert!(
8734            diags
8735                .iter()
8736                .any(|d| d.message.contains("expected `:` to complete ternary")),
8737            "expected colon diagnostic, got: {diags:?}",
8738        );
8739    }
8740
8741    #[test]
8742    fn parse_nullish_coalesce_binop() {
8743        let (ast, diags) = parse_str("const x: number = a ?? 1;");
8744        assert!(diags.is_empty(), "unexpected: {diags:?}");
8745        let stmt = single_stmt(&ast);
8746        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8747            panic!("expected Const");
8748        };
8749        match ast.try_expr(value).unwrap().kind {
8750            crate::ExprKind::Binary { op, .. } => {
8751                assert_eq!(op, crate::BinOp::NullishCoalesce);
8752            }
8753            ref other => panic!("expected Binary NullishCoalesce, got {other:?}"),
8754        }
8755    }
8756
8757    #[test]
8758    fn parse_nullish_left_associative() {
8759        let (ast, diags) = parse_str("const x: number = a ?? b ?? c;");
8760        assert!(diags.is_empty(), "unexpected: {diags:?}");
8761        let stmt = single_stmt(&ast);
8762        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8763            panic!("expected Const");
8764        };
8765        let crate::ExprKind::Binary { lhs, .. } = ast.try_expr(value).unwrap().kind else {
8766            panic!("expected outer Binary");
8767        };
8768        match ast.try_expr(lhs).unwrap().kind {
8769            crate::ExprKind::Binary { op, .. } => {
8770                assert_eq!(op, crate::BinOp::NullishCoalesce);
8771            }
8772            ref other => panic!("expected inner Binary, got {other:?}"),
8773        }
8774    }
8775
8776    #[test]
8777    fn parse_pow_is_right_associative() {
8778        let (ast, diags) = parse_str("const x: number = 2 ** 3 ** 2;");
8779        assert!(diags.is_empty(), "unexpected: {diags:?}");
8780        let stmt = single_stmt(&ast);
8781        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8782            panic!("expected Const");
8783        };
8784        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8785            panic!("expected outer Binary");
8786        };
8787        assert_eq!(op, crate::BinOp::Pow);
8788        match ast.try_expr(rhs).unwrap().kind {
8789            crate::ExprKind::Binary { op: inner_op, .. } => {
8790                assert_eq!(inner_op, crate::BinOp::Pow);
8791            }
8792            ref other => panic!("expected inner Pow Binary on RHS, got {other:?}"),
8793        }
8794    }
8795
8796    #[test]
8797    fn parse_pow_higher_than_multiply() {
8798        let (ast, diags) = parse_str("const x: number = 2 * 3 ** 2;");
8799        assert!(diags.is_empty(), "unexpected: {diags:?}");
8800        let stmt = single_stmt(&ast);
8801        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8802            panic!("expected Const");
8803        };
8804        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8805            panic!("expected outer Binary");
8806        };
8807        assert_eq!(op, crate::BinOp::Mul);
8808        match ast.try_expr(rhs).unwrap().kind {
8809            crate::ExprKind::Binary { op: inner_op, .. } => {
8810                assert_eq!(inner_op, crate::BinOp::Pow);
8811            }
8812            ref other => panic!("expected Pow on the RHS of *, got {other:?}"),
8813        }
8814    }
8815
8816    #[test]
8817    fn parse_compound_assign_ident() {
8818        let (ast, diags) = parse_str("function main(): void { let x: number = 0; x += 5; }");
8819        assert!(diags.is_empty(), "unexpected: {diags:?}");
8820        let func = ast.top_level.iter().find_map(|sid| {
8821            if let crate::StmtKind::Function { body, .. } = &ast.try_stmt(*sid).unwrap().kind {
8822                Some(*body)
8823            } else {
8824                None
8825            }
8826        });
8827        let crate::StmtKind::Block(stmts) = &ast.try_stmt(func.expect("main")).unwrap().kind else {
8828            panic!("expected function body to be a block");
8829        };
8830        let target = stmts
8831            .iter()
8832            .find_map(|sid| match &ast.try_stmt(*sid).unwrap().kind {
8833                crate::StmtKind::CompoundAssign { target, op, .. } => {
8834                    Some((target.name.clone(), *op))
8835                }
8836                _ => None,
8837            });
8838        let (name, op) = target.expect("expected a CompoundAssign in the body");
8839        assert_eq!(name, "x");
8840        assert_eq!(op, crate::BinOp::Add);
8841    }
8842
8843    #[test]
8844    fn parse_nullish_mixed_with_or_rejected() {
8845        let (_, diags) = parse_str("const x: number = a || b ?? c;");
8846        assert!(
8847            diags
8848                .iter()
8849                .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8850            "expected mixing diagnostic, got: {diags:?}",
8851        );
8852    }
8853
8854    #[test]
8855    fn parse_nullish_mixed_with_and_rejected() {
8856        let (_, diags) = parse_str("const x: number = a && b ?? c;");
8857        assert!(
8858            diags
8859                .iter()
8860                .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8861            "expected mixing diagnostic, got: {diags:?}",
8862        );
8863    }
8864
8865    #[test]
8866    fn parse_nullish_followed_by_logical_rejected() {
8867        for (source, help) in [
8868            (
8869                "const x: number = a ?? b || c;",
8870                "`(a ?? b) || c` or `a ?? (b || c)`",
8871            ),
8872            (
8873                "const x: number = a ?? b && c;",
8874                "`(a ?? b) && c` or `a ?? (b && c)`",
8875            ),
8876            (
8877                "const x: number = a ?? b ?? c || d;",
8878                "`(a ?? b) || c` or `a ?? (b || c)`",
8879            ),
8880            // One error per chain, however many mixes it has.
8881            (
8882                "const x: number = a || b ?? c || d;",
8883                "`(a || b) ?? c` or `a || (b ?? c)`",
8884            ),
8885        ] {
8886            let (_, diags) = parse_str(source);
8887            assert_eq!(diags.len(), 1, "diags for {source:?}: {diags:?}");
8888            assert!(
8889                diags[0].message.contains("mixing `??` with `||` / `&&`"),
8890                "message for {source:?}: {diags:?}",
8891            );
8892            assert!(
8893                diags[0].help.iter().any(|h| h.contains(help)),
8894                "help for {source:?}: {diags:?}",
8895            );
8896        }
8897    }
8898
8899    #[test]
8900    fn parse_nullish_operand_keeps_tighter_operators() {
8901        // `a ?? (b == c)`: only `||` and `&&` are excluded from a `??` operand.
8902        let (ast, diags) = parse_str("const x: boolean = a ?? b == c;");
8903        assert!(diags.is_empty(), "unexpected: {diags:?}");
8904        let stmt = single_stmt(&ast);
8905        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8906            panic!("expected Const");
8907        };
8908        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8909            panic!("expected outer Binary");
8910        };
8911        assert_eq!(op, crate::BinOp::NullishCoalesce);
8912        assert!(matches!(
8913            ast.try_expr(rhs).unwrap().kind,
8914            crate::ExprKind::Binary {
8915                op: crate::BinOp::Eq,
8916                ..
8917            }
8918        ));
8919    }
8920
8921    #[test]
8922    fn parse_nullish_with_parens_ok() {
8923        let (_, diags) = parse_str("const x: number = (a || b) ?? c;");
8924        assert!(diags.is_empty(), "unexpected: {diags:?}");
8925    }
8926
8927    #[test]
8928    fn parse_optional_field_chain() {
8929        let (ast, diags) = parse_str("const x: number = a?.b;");
8930        assert!(diags.is_empty(), "unexpected: {diags:?}");
8931        let stmt = single_stmt(&ast);
8932        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8933            panic!("expected Const");
8934        };
8935        match ast.try_expr(value).unwrap().kind {
8936            crate::ExprKind::OptionalChain { ref parts, .. } => {
8937                assert_eq!(parts.len(), 1);
8938                match &parts[0] {
8939                    crate::ChainPart::Field { name, optional, .. } => {
8940                        assert_eq!(name.name, "b");
8941                        assert!(*optional);
8942                    }
8943                    other => panic!("expected Field, got {other:?}"),
8944                }
8945            }
8946            ref other => panic!("expected OptionalChain, got {other:?}"),
8947        }
8948    }
8949
8950    #[test]
8951    fn parse_optional_chain_continues_after_first_dot() {
8952        let (ast, diags) = parse_str("const x: number = a?.b.c;");
8953        assert!(diags.is_empty(), "unexpected: {diags:?}");
8954        let stmt = single_stmt(&ast);
8955        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8956            panic!("expected Const");
8957        };
8958        match ast.try_expr(value).unwrap().kind {
8959            crate::ExprKind::OptionalChain { ref parts, .. } => {
8960                assert_eq!(parts.len(), 2);
8961                if let crate::ChainPart::Field { optional, .. } = &parts[0] {
8962                    assert!(*optional);
8963                }
8964                if let crate::ChainPart::Field { optional, .. } = &parts[1] {
8965                    assert!(!*optional);
8966                }
8967            }
8968            ref other => panic!("expected OptionalChain, got {other:?}"),
8969        }
8970    }
8971
8972    #[test]
8973    fn parse_optional_chain_multiple_short_circuits() {
8974        let (ast, diags) = parse_str("const x: number = a?.b?.c;");
8975        assert!(diags.is_empty(), "unexpected: {diags:?}");
8976        let stmt = single_stmt(&ast);
8977        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8978            panic!("expected Const");
8979        };
8980        let crate::ExprKind::OptionalChain { parts, .. } = &ast.try_expr(value).unwrap().kind
8981        else {
8982            panic!("expected OptionalChain");
8983        };
8984        assert_eq!(parts.len(), 2);
8985        for p in parts {
8986            if let crate::ChainPart::Field { optional, .. } = p {
8987                assert!(*optional, "both parts should be optional");
8988            }
8989        }
8990    }
8991
8992    #[test]
8993    fn parse_optional_call_and_index() {
8994        let (_, diags) = parse_str("const x: number = f?.();");
8995        assert!(diags.is_empty(), "unexpected: {diags:?}");
8996        let (_, diags) = parse_str("const y: number = arr?.[0];");
8997        assert!(diags.is_empty(), "unexpected: {diags:?}");
8998    }
8999
9000    #[test]
9001    fn parse_optional_chain_terminates_at_questionmark_for_ternary() {
9002        let (ast, diags) = parse_str("const x: number = a?.b ? c : d;");
9003        assert!(diags.is_empty(), "unexpected: {diags:?}");
9004        let stmt = single_stmt(&ast);
9005        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9006            panic!("expected Const");
9007        };
9008        let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
9009            panic!(
9010                "expected top-level Ternary, got {:?}",
9011                ast.try_expr(value).unwrap().kind
9012            );
9013        };
9014        assert!(matches!(
9015            ast.try_expr(cond).unwrap().kind,
9016            crate::ExprKind::OptionalChain { .. }
9017        ));
9018    }
9019
9020    #[test]
9021    fn parse_nullish_below_ternary() {
9022        let (ast, diags) = parse_str("const x: number = a ?? b ? c : d;");
9023        assert!(diags.is_empty(), "unexpected: {diags:?}");
9024        let stmt = single_stmt(&ast);
9025        let crate::StmtKind::Const { value, .. } = stmt.kind else {
9026            panic!("expected Const");
9027        };
9028        let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
9029            panic!("expected top-level Ternary");
9030        };
9031        match ast.try_expr(cond).unwrap().kind {
9032            crate::ExprKind::Binary { op, .. } => {
9033                assert_eq!(op, crate::BinOp::NullishCoalesce);
9034            }
9035            ref other => panic!("expected Binary ??, got {other:?}"),
9036        }
9037    }
9038
9039    #[test]
9040    fn let_missing_initializer_diagnoses() {
9041        let (ast, diags) = parse_str("let x;");
9042        assert_eq!(diags.len(), 1);
9043        assert_eq!(
9044            diags[0].message,
9045            "`let` declaration requires an initializer"
9046        );
9047        assert!(ast.top_level.is_empty());
9048    }
9049
9050    #[test]
9051    fn missing_identifier_after_let_diagnoses() {
9052        let (ast, diags) = parse_str("let = 1;");
9053        assert_eq!(diags.len(), 1);
9054        assert_eq!(diags[0].message, "expected identifier after `let`/`const`");
9055        assert!(ast.top_level.is_empty());
9056    }
9057
9058    #[test]
9059    fn missing_type_after_colon_diagnoses() {
9060        let (ast, diags) = parse_str("let x: = 1;");
9061        assert_eq!(diags.len(), 1);
9062        assert_eq!(diags[0].message, "expected type");
9063        assert!(ast.top_level.is_empty());
9064    }
9065
9066    #[test]
9067    fn missing_semicolon_after_declaration_diagnoses() {
9068        let (ast, diags) = parse_str("let x = 1 y;");
9069        assert_eq!(diags.len(), 1);
9070        assert_eq!(diags[0].message, "expected `;` after declaration");
9071        assert!(ast.top_level.is_empty());
9072    }
9073
9074    #[test]
9075    fn expression_statements_still_parse() {
9076        let (ast, diags) = parse_str("42;");
9077        assert!(diags.is_empty());
9078        assert_eq!(ast.top_level.len(), 1);
9079    }
9080
9081    #[test]
9082    fn snapshot_mixed_declarations() {
9083        let (ast, diags) =
9084            parse_str(r#"let x = 1; const y: string = "hi"; let z: number = 1 + 2;"#);
9085        assert!(diags.is_empty());
9086        assert_eq!(ast.top_level.len(), 3);
9087        insta::assert_debug_snapshot!(ast);
9088    }
9089
9090    #[test]
9091    fn parse_function_typical() {
9092        let (ast, diags) = parse_str("function f(a: number, b: string): boolean { }");
9093        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9094        let stmt = single_stmt(&ast);
9095        match stmt.kind {
9096            crate::StmtKind::Function {
9097                ref name,
9098                ref generics,
9099                ref params,
9100                ref return_type,
9101                body,
9102                ..
9103            } => {
9104                assert_eq!(name.name, "f");
9105                assert_eq!(name.span, crate::Span::new(F, 9, 10).unwrap());
9106                assert!(generics.is_empty());
9107                assert_eq!(params.len(), 2);
9108                assert_eq!(params[0].name.name, "a");
9109                assert_eq!(params[0].name.span, crate::Span::new(F, 11, 12).unwrap());
9110                let p0_ty = params[0]
9111                    .ty
9112                    .as_ref()
9113                    .expect("function-decl param requires annotation");
9114                assert!(matches!(p0_ty.kind, crate::TypeAnnotationKind::Name { .. }));
9115                assert_eq!(p0_ty.span, crate::Span::new(F, 14, 20).unwrap()); // `number`
9116                assert_eq!(params[1].name.name, "b");
9117                assert_eq!(params[1].name.span, crate::Span::new(F, 22, 23).unwrap());
9118                let p1_ty = params[1]
9119                    .ty
9120                    .as_ref()
9121                    .expect("function-decl param requires annotation");
9122                assert_eq!(p1_ty.span, crate::Span::new(F, 25, 31).unwrap()); // `string`
9123                let return_type = return_type.as_ref().expect("plain return type");
9124                assert!(matches!(
9125                    return_type.kind,
9126                    crate::TypeAnnotationKind::Name { .. }
9127                ));
9128                assert_eq!(return_type.span, crate::Span::new(F, 34, 41).unwrap()); // `boolean`
9129                let block = ast.try_stmt(body).unwrap();
9130                assert!(matches!(block.kind, crate::StmtKind::Block(ref v) if v.is_empty()));
9131            }
9132            _ => panic!("expected Function"),
9133        }
9134    }
9135
9136    #[test]
9137    fn parse_function_with_doc_comment() {
9138        let (ast, diags) = parse_str(
9139            "/**\n * Sum two numbers.\n * @param a First.\n * @param b Second.\n */\nfunction add(a: number, b: number): number { return a + b; }",
9140        );
9141        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9142        let stmt = single_stmt(&ast);
9143        match &stmt.kind {
9144            crate::StmtKind::Function { doc, .. } => {
9145                let d = doc.as_ref().expect("doc attached");
9146                assert_eq!(d.summary, "Sum two numbers.");
9147                assert_eq!(d.params.len(), 2);
9148                assert_eq!(d.params[0].name, "a");
9149                assert_eq!(d.params[1].name, "b");
9150            }
9151            _ => panic!("expected Function"),
9152        }
9153    }
9154
9155    #[test]
9156    fn parse_interface_member_docs() {
9157        let (ast, diags) = parse_str(
9158            "interface Foo {\n  /** First method. */ first(): void;\n  /** A field. */ field: number;\n}",
9159        );
9160        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9161        let stmt = single_stmt(&ast);
9162        match &stmt.kind {
9163            crate::StmtKind::InterfaceDecl { members, .. } => {
9164                assert_eq!(members.len(), 2);
9165                match &members[0] {
9166                    crate::InterfaceMember::Method { doc, .. } => {
9167                        assert_eq!(doc.as_ref().unwrap().summary, "First method.");
9168                    }
9169                    _ => panic!("expected Method first"),
9170                }
9171                match &members[1] {
9172                    crate::InterfaceMember::Property { doc, .. } => {
9173                        assert_eq!(doc.as_ref().unwrap().summary, "A field.");
9174                    }
9175                    _ => panic!("expected Property second"),
9176                }
9177            }
9178            _ => panic!("expected InterfaceDecl"),
9179        }
9180    }
9181
9182    #[test]
9183    fn parse_numeric_enum_all_implicit() {
9184        let (ast, diags) = parse_str("enum Direction { Up, Down, Left, Right }");
9185        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9186        let stmt = single_stmt(&ast);
9187        match &stmt.kind {
9188            crate::StmtKind::EnumDecl { name, members, doc } => {
9189                assert_eq!(name.name, "Direction");
9190                assert!(doc.is_none());
9191                assert_eq!(members.len(), 4);
9192                for (m, expected) in members.iter().zip(["Up", "Down", "Left", "Right"]) {
9193                    assert_eq!(m.name.name, expected);
9194                    assert!(m.value.is_none(), "expected no initializer on {expected}");
9195                }
9196            }
9197            _ => panic!("expected EnumDecl"),
9198        }
9199    }
9200
9201    #[test]
9202    fn parse_string_enum_all_explicit() {
9203        let (ast, diags) =
9204            parse_str("enum Status { Active = \"active\", Inactive = \"inactive\" }");
9205        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9206        let stmt = single_stmt(&ast);
9207        match &stmt.kind {
9208            crate::StmtKind::EnumDecl { name, members, .. } => {
9209                assert_eq!(name.name, "Status");
9210                assert_eq!(members.len(), 2);
9211                match &members[0].value {
9212                    Some(crate::EnumInitializer::String { value, .. }) => {
9213                        assert_eq!(value, "active");
9214                    }
9215                    other => panic!("expected String init, got {other:?}"),
9216                }
9217                match &members[1].value {
9218                    Some(crate::EnumInitializer::String { value, .. }) => {
9219                        assert_eq!(value, "inactive");
9220                    }
9221                    other => panic!("expected String init, got {other:?}"),
9222                }
9223            }
9224            _ => panic!("expected EnumDecl"),
9225        }
9226    }
9227
9228    #[test]
9229    fn parse_numeric_enum_with_negative_init() {
9230        let (ast, diags) = parse_str("enum D { Up = 1, Down = -1 }");
9231        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9232        let stmt = single_stmt(&ast);
9233        match &stmt.kind {
9234            crate::StmtKind::EnumDecl { members, .. } => {
9235                assert_eq!(members.len(), 2);
9236                match &members[0].value {
9237                    Some(crate::EnumInitializer::Number { value, .. }) => {
9238                        assert_eq!(*value, 1.0);
9239                    }
9240                    other => panic!("expected Number init, got {other:?}"),
9241                }
9242                match &members[1].value {
9243                    Some(crate::EnumInitializer::Number { value, .. }) => {
9244                        assert_eq!(*value, -1.0);
9245                    }
9246                    other => panic!("expected Number init, got {other:?}"),
9247                }
9248            }
9249            _ => panic!("expected EnumDecl"),
9250        }
9251    }
9252
9253    #[test]
9254    fn parse_enum_mixed_kinds_is_parse_error() {
9255        let (_ast, diags) = parse_str("enum Mix { A = 1, B = \"two\" }");
9256        assert!(
9257            diags
9258                .iter()
9259                .any(|d| d.message.contains("mixed numeric and string")),
9260            "expected mixed-kind diagnostic, got: {diags:?}"
9261        );
9262    }
9263
9264    #[test]
9265    fn parse_enum_member_docs() {
9266        let (ast, diags) = parse_str(
9267            "enum D {\n  /** The first one. */ A = 1,\n  /** The second one. */ B = 2,\n}",
9268        );
9269        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9270        let stmt = single_stmt(&ast);
9271        match &stmt.kind {
9272            crate::StmtKind::EnumDecl { members, .. } => {
9273                assert_eq!(members.len(), 2);
9274                assert_eq!(
9275                    members[0].doc.as_ref().expect("first doc").summary,
9276                    "The first one."
9277                );
9278                assert_eq!(
9279                    members[1].doc.as_ref().expect("second doc").summary,
9280                    "The second one."
9281                );
9282            }
9283            _ => panic!("expected EnumDecl"),
9284        }
9285    }
9286
9287    #[test]
9288    fn parse_enum_trailing_comma_ok() {
9289        let (ast, diags) = parse_str("enum D { A, B, }");
9290        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9291        let stmt = single_stmt(&ast);
9292        match &stmt.kind {
9293            crate::StmtKind::EnumDecl { members, .. } => assert_eq!(members.len(), 2),
9294            _ => panic!("expected EnumDecl"),
9295        }
9296    }
9297
9298    #[test]
9299    fn parse_function_empty_params() {
9300        let (ast, diags) = parse_str("function noop(): void { }");
9301        assert!(diags.is_empty());
9302        let stmt = single_stmt(&ast);
9303        match stmt.kind {
9304            crate::StmtKind::Function { ref params, .. } => assert!(params.is_empty()),
9305            _ => panic!("expected Function"),
9306        }
9307    }
9308
9309    #[test]
9310    fn parse_generic_function_single_param() {
9311        let (ast, diags) = parse_str("function identity<T>(x: T): T { return x; }");
9312        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9313        let stmt = single_stmt(&ast);
9314        match stmt.kind {
9315            crate::StmtKind::Function {
9316                ref name,
9317                ref generics,
9318                ref params,
9319                ..
9320            } => {
9321                assert_eq!(name.name, "identity");
9322                assert_eq!(generics.len(), 1);
9323                assert_eq!(generics[0].name, "T");
9324                assert_eq!(params.len(), 1);
9325                assert_eq!(params[0].name.name, "x");
9326            }
9327            _ => panic!("expected Function"),
9328        }
9329    }
9330
9331    #[test]
9332    fn parse_generic_function_multiple_params() {
9333        let (ast, diags) = parse_str("function pair<T, U>(a: T, b: U): T { return a; }");
9334        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9335        let stmt = single_stmt(&ast);
9336        match stmt.kind {
9337            crate::StmtKind::Function { ref generics, .. } => {
9338                assert_eq!(generics.len(), 2);
9339                assert_eq!(generics[0].name, "T");
9340                assert_eq!(generics[1].name, "U");
9341            }
9342            _ => panic!("expected Function"),
9343        }
9344    }
9345
9346    #[test]
9347    fn parse_non_generic_function_has_empty_generics() {
9348        let (ast, diags) = parse_str("function noop(): void { }");
9349        assert!(diags.is_empty());
9350        let stmt = single_stmt(&ast);
9351        match stmt.kind {
9352            crate::StmtKind::Function { ref generics, .. } => assert!(generics.is_empty()),
9353            _ => panic!("expected Function"),
9354        }
9355    }
9356
9357    #[test]
9358    fn parse_empty_generic_list_diagnoses() {
9359        let (_ast, diags) = parse_str("function f<>(): void { }");
9360        assert!(
9361            diags
9362                .iter()
9363                .any(|d| d.message.contains("empty generic parameter list")),
9364            "expected empty-generic-list diagnostic, got: {diags:?}",
9365        );
9366    }
9367
9368    #[test]
9369    fn parse_trailing_comma_in_generic_list_ok() {
9370        let (ast, diags) = parse_str("function f<T,>(): void { }");
9371        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9372        match single_stmt(&ast).kind {
9373            crate::StmtKind::Function { ref generics, .. } => assert_eq!(generics.len(), 1),
9374            _ => panic!("expected Function"),
9375        }
9376    }
9377
9378    #[test]
9379    fn parse_generic_call_single_type_arg() {
9380        let (ast, diags) = parse_str("identity<number>(42);");
9381        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9382        let stmt = single_stmt(&ast);
9383        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9384            panic!("expected Expr stmt");
9385        };
9386        match &ast.try_expr(call_id).unwrap().kind {
9387            crate::ExprKind::Call {
9388                type_args, args, ..
9389            } => {
9390                let ta = type_args.as_ref().expect("type_args set for generic call");
9391                assert_eq!(ta.len(), 1);
9392                assert_eq!(args.len(), 1);
9393            }
9394            other => panic!("expected Call, got {other:?}"),
9395        }
9396    }
9397
9398    #[test]
9399    fn parse_generic_call_trailing_comma_ok() {
9400        let (ast, diags) = parse_str("identity<number,>(42);");
9401        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9402        let stmt = single_stmt(&ast);
9403        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9404            panic!("expected Expr stmt");
9405        };
9406        match &ast.try_expr(call_id).unwrap().kind {
9407            crate::ExprKind::Call {
9408                type_args, args, ..
9409            } => {
9410                assert_eq!(type_args.as_ref().expect("type_args set").len(), 1);
9411                assert_eq!(args.len(), 1);
9412            }
9413            other => panic!("expected Call, got {other:?}"),
9414        }
9415    }
9416
9417    #[test]
9418    fn parse_generic_call_multi_type_args() {
9419        let (ast, diags) = parse_str("pair<number, string>(1, \"x\");");
9420        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9421        let stmt = single_stmt(&ast);
9422        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9423            panic!("expected Expr stmt");
9424        };
9425        match &ast.try_expr(call_id).unwrap().kind {
9426            crate::ExprKind::Call { type_args, .. } => {
9427                let ta = type_args.as_ref().expect("type_args set for generic call");
9428                assert_eq!(ta.len(), 2);
9429            }
9430            other => panic!("expected Call, got {other:?}"),
9431        }
9432    }
9433
9434    #[test]
9435    fn parse_comparison_chain_unaffected_by_generic_lookahead() {
9436        let (ast, diags) = parse_str("let z = a < b > c;");
9437        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9438        assert!(
9439            !ast_contains_call_with_type_args(&ast),
9440            "should not have parsed any generic call",
9441        );
9442    }
9443
9444    #[test]
9445    fn parse_lt_with_paren_rhs_is_comparison_not_generic() {
9446        let (ast, diags) = parse_str("let z = a < (b);");
9447        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9448        assert!(!ast_contains_call_with_type_args(&ast));
9449    }
9450
9451    #[test]
9452    fn parse_generic_call_no_args() {
9453        let (ast, diags) = parse_str("none<number>();");
9454        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9455        let stmt = single_stmt(&ast);
9456        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9457            panic!("expected Expr stmt");
9458        };
9459        match &ast.try_expr(call_id).unwrap().kind {
9460            crate::ExprKind::Call {
9461                type_args, args, ..
9462            } => {
9463                assert_eq!(type_args.as_ref().unwrap().len(), 1);
9464                assert!(args.is_empty());
9465            }
9466            other => panic!("expected Call, got {other:?}"),
9467        }
9468    }
9469
9470    fn ast_contains_call_with_type_args(ast: &crate::Ast) -> bool {
9471        for id in 0..ast.top_level.len() {
9472            let stmt = ast.try_stmt(ast.top_level[id]).unwrap();
9473            if walk_stmt_for_typed_call(ast, stmt) {
9474                return true;
9475            }
9476        }
9477        false
9478    }
9479
9480    fn walk_stmt_for_typed_call(ast: &crate::Ast, stmt: &crate::Stmt) -> bool {
9481        match &stmt.kind {
9482            crate::StmtKind::Let { value, .. } | crate::StmtKind::Const { value, .. } => {
9483                walk_expr_for_typed_call(ast, *value)
9484            }
9485            crate::StmtKind::Expr(e) => walk_expr_for_typed_call(ast, *e),
9486            crate::StmtKind::Block(stmts) => stmts
9487                .iter()
9488                .any(|sid| walk_stmt_for_typed_call(ast, ast.try_stmt(*sid).unwrap())),
9489            _ => false,
9490        }
9491    }
9492
9493    fn walk_expr_for_typed_call(ast: &crate::Ast, id: crate::ExprId) -> bool {
9494        match &ast.try_expr(id).unwrap().kind {
9495            crate::ExprKind::Call {
9496                type_args: Some(_), ..
9497            } => true,
9498            crate::ExprKind::Call { callee, args, .. } => {
9499                walk_expr_for_typed_call(ast, *callee)
9500                    || args.iter().any(|a| walk_expr_for_typed_call(ast, *a))
9501            }
9502            crate::ExprKind::Binary { lhs, rhs, .. } => {
9503                walk_expr_for_typed_call(ast, *lhs) || walk_expr_for_typed_call(ast, *rhs)
9504            }
9505            crate::ExprKind::Unary { operand, .. } => walk_expr_for_typed_call(ast, *operand),
9506            crate::ExprKind::Paren(e) => walk_expr_for_typed_call(ast, *e),
9507            crate::ExprKind::FieldAccess { receiver, .. } => {
9508                walk_expr_for_typed_call(ast, *receiver)
9509            }
9510            crate::ExprKind::IndexAccess { receiver, index } => {
9511                walk_expr_for_typed_call(ast, *receiver) || walk_expr_for_typed_call(ast, *index)
9512            }
9513            _ => false,
9514        }
9515    }
9516
9517    #[test]
9518    fn parse_function_body_with_statements() {
9519        let (ast, diags) = parse_str("function g(x: number): number { let y = x; y; }");
9520        assert!(diags.is_empty());
9521        let stmt = single_stmt(&ast);
9522        match stmt.kind {
9523            crate::StmtKind::Function { body, .. } => {
9524                let block = ast.try_stmt(body).unwrap();
9525                match block.kind {
9526                    crate::StmtKind::Block(ref v) => assert_eq!(v.len(), 2),
9527                    _ => panic!("expected Block"),
9528                }
9529            }
9530            _ => panic!("expected Function"),
9531        }
9532    }
9533
9534    #[test]
9535    fn function_missing_return_type_diagnoses() {
9536        let (ast, diags) = parse_str("function f() { }");
9537        assert!(!diags.is_empty());
9538        assert_eq!(diags[0].message, "expected `:` and return type");
9539        assert!(ast.top_level.is_empty());
9540    }
9541
9542    #[test]
9543    fn method_missing_return_type_still_reads_the_body() {
9544        let (_ast, diags) = parse_str(
9545            "class Box {\n  async run() {\n    const x = await f();\n    return x;\n  }\n  ok(): void {}\n}",
9546        );
9547        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9548        assert_eq!(
9549            messages,
9550            [
9551                "`async` functions are not supported",
9552                "expected `:` and return type",
9553                "`await` is not supported"
9554            ],
9555            "{diags:?}"
9556        );
9557        assert_eq!(
9558            diags[1].help,
9559            ["write the return type: `run(…): T { … }`, with `void` when it returns nothing"]
9560        );
9561    }
9562
9563    #[test]
9564    fn an_unclosed_method_body_leaves_the_next_declaration_alone() {
9565        let (ast, diags) =
9566            parse_str("class Box {\n  a() { return 1;\n}\nfunction main(): number { return 0; }");
9567        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9568        assert_eq!(
9569            messages,
9570            [
9571                "expected `:` and return type",
9572                "expected `}` to close class"
9573            ],
9574            "{diags:?}"
9575        );
9576        assert_eq!(ast.top_level.len(), 1, "`main` is still declared");
9577    }
9578
9579    #[test]
9580    fn every_declaration_after_an_unclosed_method_body_is_kept() {
9581        for next in [
9582            "class B { ok(): number { return 2; } }",
9583            "interface I { a: number; }",
9584            "enum E { A }",
9585            "type X = number;",
9586            "import * as fs from \"submilli:fs\";",
9587            "async function g(): number { return 1; }",
9588        ] {
9589            let source = format!(
9590                "class A {{\n  bad() {{\n    return 1;\n}}\n{next}\nfunction main(): void {{}}"
9591            );
9592            let (ast, diags) = parse_str(&source);
9593            let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9594            let expected_async = next.starts_with("async");
9595            let mut expected = vec![
9596                "expected `:` and return type",
9597                "expected `}` to close class",
9598            ];
9599            if expected_async {
9600                expected.push("`async` functions are not supported");
9601            }
9602            assert_eq!(messages, expected, "{next}: {diags:?}");
9603            assert_eq!(
9604                ast.top_level.len(),
9605                2,
9606                "{next} and `main` are kept: {diags:?}"
9607            );
9608        }
9609    }
9610
9611    #[test]
9612    fn a_failed_statement_leaves_the_next_declaration_to_be_parsed() {
9613        for (source, kept) in [
9614            ("let x = 1 +\nimport { exists } from \"submilli:fs\";", 1),
9615            ("const y =\nclass C {}\nfunction main(): void {}", 2),
9616            (
9617                "interface I {\n  a: number;\nimport * as fs from \"submilli:fs\";",
9618                1,
9619            ),
9620        ] {
9621            let (ast, diags) = parse_str(source);
9622            assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9623            assert_eq!(ast.top_level.len(), kept, "{source}: {diags:?}");
9624        }
9625    }
9626
9627    #[test]
9628    fn a_dynamic_import_after_a_failed_statement_reports_once() {
9629        for source in [
9630            "const m = import(\"submilli:fs\");\nclass C {}",
9631            "const d = import.meta.dirname;\nclass C {}",
9632        ] {
9633            let (_ast, diags) = parse_str(source);
9634            assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9635        }
9636    }
9637
9638    #[test]
9639    fn members_named_like_declarations_after_a_bad_method_stay_members() {
9640        let (_ast, diags) = parse_str(
9641            "class A {\n  bad() {}\n  type: number = 1;\n  function(): void {}\n  declare(): void {}\n  async(): void {}\n}",
9642        );
9643        assert_eq!(diags.len(), 1, "{diags:?}");
9644    }
9645
9646    #[test]
9647    fn every_member_after_a_bad_method_is_still_read() {
9648        let (_ast, diags) = parse_str(
9649            "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}",
9650        );
9651        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9652        assert_eq!(messages, ["expected `:` and return type"; 3], "{diags:?}");
9653    }
9654
9655    #[test]
9656    fn a_member_failing_after_a_type_literal_reports_once() {
9657        let (_ast, diags) = parse_str("class A {\n  x: { a: number } 5\n}");
9658        assert_eq!(diags.len(), 1, "{diags:?}");
9659    }
9660
9661    #[test]
9662    fn multiple_declarators_become_one_statement_each() {
9663        let (ast, diags) = parse_str("let a = 0, b: string = \"s\", [c] = [1];");
9664        assert!(diags.is_empty(), "{diags:?}");
9665        let kinds: Vec<&crate::StmtKind> = ast
9666            .top_level
9667            .iter()
9668            .map(|id| &ast.try_stmt(*id).unwrap().kind)
9669            .collect();
9670        assert!(
9671            matches!(
9672                kinds.as_slice(),
9673                [
9674                    crate::StmtKind::Let { name: a, ty: None, .. },
9675                    crate::StmtKind::Let { name: b, ty: Some(_), .. },
9676                    crate::StmtKind::LetPattern { .. },
9677                ] if a.name == "a" && b.name == "b"
9678            ),
9679            "{kinds:?}"
9680        );
9681    }
9682
9683    #[test]
9684    fn declarators_keep_their_own_spans() {
9685        let source = "const a = 1, b = 2;";
9686        let (ast, diags) = parse_str(source);
9687        assert!(diags.is_empty(), "{diags:?}");
9688        let spans: Vec<&str> = ast
9689            .top_level
9690            .iter()
9691            .map(|id| {
9692                let span = ast.try_stmt(*id).unwrap().span;
9693                &source[span.start as usize..span.end as usize]
9694            })
9695            .collect();
9696        assert_eq!(spans, ["const a = 1", "b = 2;"]);
9697    }
9698
9699    #[test]
9700    fn a_declarator_missing_its_initializer_is_reported_at_it() {
9701        let (_ast, diags) = parse_str("const a = 1, b;");
9702        assert_eq!(diags.len(), 1, "{diags:?}");
9703        assert_eq!(
9704            diags[0].message,
9705            "`const` declaration requires an initializer"
9706        );
9707        assert_eq!(diags[0].span.start, 14, "{diags:?}");
9708        let (_ast, diags) = parse_str("let a = 1, ;");
9709        assert_eq!(
9710            diags[0].message, "expected identifier after `let`/`const`",
9711            "{diags:?}"
9712        );
9713    }
9714
9715    #[test]
9716    fn a_typed_declarator_without_a_value_may_precede_another() {
9717        let (ast, diags) = parse_str("let a: number | undefined, b = 1;");
9718        assert!(diags.is_empty(), "{diags:?}");
9719        assert_eq!(ast.top_level.len(), 2);
9720    }
9721
9722    #[test]
9723    fn declarators_stay_in_their_block() {
9724        let (ast, diags) = parse_str("function f(): void { let a = 1, b = 2; }\nlet c = 3;");
9725        assert!(diags.is_empty(), "{diags:?}");
9726        assert_eq!(
9727            ast.top_level.len(),
9728            2,
9729            "the block's declarators don't leak out"
9730        );
9731    }
9732
9733    #[test]
9734    fn every_exported_declarator_is_exported() {
9735        let (ast, diags) = parse_str("export const a = 1, b = 2;");
9736        assert!(diags.is_empty(), "{diags:?}");
9737        assert_eq!(ast.top_level.len(), 2);
9738        let exported: Vec<crate::StmtId> =
9739            ast.exported_decls.iter().map(|decl| decl.stmt).collect();
9740        assert_eq!(exported, ast.top_level);
9741    }
9742
9743    #[test]
9744    fn a_for_head_holds_every_declarator() {
9745        let (ast, diags) = parse_str("for (let i = 0, j = 10; i < j; i++, j--) {}");
9746        assert!(diags.is_empty(), "{diags:?}");
9747        let crate::StmtKind::For { init, update, .. } = &single_stmt(&ast).kind else {
9748            panic!("expected For");
9749        };
9750        assert_eq!(init.len(), 2);
9751        let (ast2, diags) = parse_str("for (i = 0, j = 10; i < j; i++) {}");
9752        assert!(diags.is_empty(), "{diags:?}");
9753        let crate::StmtKind::For { init: assigns, .. } = &single_stmt(&ast2).kind else {
9754            panic!("expected For");
9755        };
9756        assert_eq!(assigns.len(), 2);
9757        let update = ast.try_stmt(update.unwrap()).unwrap();
9758        assert!(
9759            matches!(&update.kind, crate::StmtKind::Block(stmts) if stmts.len() == 2),
9760            "{update:?}"
9761        );
9762    }
9763
9764    #[test]
9765    fn missing_return_type_still_reads_the_body() {
9766        let (_ast, diags) = parse_str(
9767            "async function main() {\n  const x = await f();\n  if (x) { return 1; }\n  return 2;\n}\nfunction g(): void {}",
9768        );
9769        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9770        assert_eq!(
9771            messages,
9772            [
9773                "`async` functions are not supported",
9774                "expected `:` and return type",
9775                "`await` is not supported"
9776            ],
9777            "{diags:?}"
9778        );
9779        assert_eq!(
9780            diags[1].help,
9781            [
9782                "write the return type: `function main(…): T { … }`, with `void` when it returns nothing"
9783            ]
9784        );
9785    }
9786
9787    #[test]
9788    fn function_missing_param_type_diagnoses() {
9789        let (ast, diags) = parse_str("function f(a): void { }");
9790        assert!(!diags.is_empty());
9791        assert_eq!(diags[0].message, "parameter requires a type annotation");
9792        assert!(ast.top_level.is_empty());
9793    }
9794
9795    #[test]
9796    fn function_missing_name_diagnoses() {
9797        let (ast, diags) = parse_str("function (x: number): void { }");
9798        assert!(!diags.is_empty());
9799        assert_eq!(diags[0].message, "expected function name");
9800        assert!(ast.top_level.is_empty());
9801    }
9802
9803    #[test]
9804    fn function_trailing_param_comma_ok() {
9805        let (ast, diags) = parse_str("function f(a: number,): void { }");
9806        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9807        match single_stmt(&ast).kind {
9808            crate::StmtKind::Function { ref params, .. } => assert_eq!(params.len(), 1),
9809            _ => panic!("expected Function"),
9810        }
9811    }
9812
9813    #[test]
9814    fn function_missing_open_brace_diagnoses() {
9815        let (ast, diags) = parse_str("function f(): void");
9816        assert_eq!(diags.len(), 1);
9817        assert_eq!(diags[0].message, "expected `{`");
9818        assert!(ast.top_level.is_empty());
9819    }
9820
9821    #[test]
9822    fn snapshot_function_declaration() {
9823        let (ast, diags) =
9824            parse_str("function add(a: number, b: number): number { let sum = a + b; sum; }");
9825        assert!(diags.is_empty());
9826        insta::assert_debug_snapshot!(ast);
9827    }
9828
9829    #[test]
9830    fn rest_param_basic_parses() {
9831        let (_, diags) = parse_str("function sum(...nums: number[]): number { return 0; }");
9832        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9833    }
9834
9835    #[test]
9836    fn rest_param_with_fixed_prefix_parses() {
9837        let (_, diags) =
9838            parse_str("function tag(label: string, ...vals: number[]): string { return label; }");
9839        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9840    }
9841
9842    #[test]
9843    fn rest_param_in_arrow_parses() {
9844        let (_, diags) = parse_str("const f = (...n: number[]) => n.length;");
9845        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9846    }
9847
9848    #[test]
9849    fn rest_param_in_function_type_annotation_parses() {
9850        let (_, diags) =
9851            parse_str("let f: (a: number, ...rest: string[]) => void = (a, ...r) => {};");
9852        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9853    }
9854
9855    #[test]
9856    fn rest_param_not_last_rejected() {
9857        let (_, diags) = parse_str("function f(...xs: number[], y: number): void {}");
9858        assert!(
9859            diags.iter().any(|d| d
9860                .message
9861                .contains("rest parameter must be the last parameter")),
9862            "expected 'must be last' diagnostic, got {diags:?}",
9863        );
9864    }
9865
9866    #[test]
9867    fn rest_param_with_default_rejected() {
9868        let (_, diags) = parse_str("function f(...xs: number[] = []): void {}");
9869        assert!(
9870            diags.iter().any(|d| d
9871                .message
9872                .contains("rest parameter cannot have a default value")),
9873            "expected default-rejection diagnostic, got {diags:?}",
9874        );
9875    }
9876
9877    #[test]
9878    fn rest_param_without_annotation_rejected() {
9879        let (_, diags) = parse_str("function f(...xs): void {}");
9880        assert!(
9881            diags.iter().any(|d| d
9882                .message
9883                .contains("rest parameter requires a type annotation")),
9884            "expected missing-annotation diagnostic, got {diags:?}",
9885        );
9886    }
9887
9888    #[test]
9889    fn rest_param_destructured_rejected() {
9890        let (_, diags) = parse_str("function f(...{a, b}): void {}");
9891        assert!(
9892            diags
9893                .iter()
9894                .any(|d| d.message.contains("rest parameter cannot be destructured")),
9895            "expected destructured-rejection diagnostic, got {diags:?}",
9896        );
9897    }
9898
9899    #[test]
9900    fn rest_param_in_function_type_with_non_array_rejected() {
9901        let (_, diags) = parse_str("let f: (...xs: number) => void = () => {};");
9902        assert!(
9903            diags
9904                .iter()
9905                .any(|d| d.message.contains("rest parameter type must be an array")),
9906            "expected non-array-type diagnostic, got {diags:?}",
9907        );
9908    }
9909
9910    fn call(e: &crate::Expr) -> (crate::ExprId, &[crate::ExprId]) {
9911        match e.kind {
9912            crate::ExprKind::Call {
9913                callee, ref args, ..
9914            } => (callee, args.as_slice()),
9915            _ => panic!("expected Call, got {:?}", e.kind),
9916        }
9917    }
9918
9919    #[test]
9920    fn parse_call_no_args() {
9921        let (ast, diags) = parse_str("f();");
9922        assert!(diags.is_empty());
9923        let outer = expr_of_single_stmt(&ast);
9924        let (callee, args) = call(outer);
9925        assert!(matches!(
9926            ast.try_expr(callee).unwrap().kind,
9927            crate::ExprKind::Identifier(_)
9928        ));
9929        assert!(args.is_empty());
9930        assert_eq!(outer.span, crate::Span::new(F, 0, 3).unwrap());
9931    }
9932
9933    #[test]
9934    fn parse_call_one_arg() {
9935        let (ast, diags) = parse_str("f(1);");
9936        assert!(diags.is_empty());
9937        let outer = expr_of_single_stmt(&ast);
9938        let (callee, args) = call(outer);
9939        assert!(matches!(
9940            ast.try_expr(callee).unwrap().kind,
9941            crate::ExprKind::Identifier(_)
9942        ));
9943        assert_eq!(args.len(), 1);
9944        assert_eq!(
9945            ast.try_expr(args[0]).unwrap().kind,
9946            crate::ExprKind::Number(1.0)
9947        );
9948    }
9949
9950    #[test]
9951    fn parse_call_three_args() {
9952        let (ast, diags) = parse_str("f(1, 2, 3);");
9953        assert!(diags.is_empty());
9954        let outer = expr_of_single_stmt(&ast);
9955        let (_callee, args) = call(outer);
9956        assert_eq!(args.len(), 3);
9957        assert_eq!(
9958            ast.try_expr(args[0]).unwrap().kind,
9959            crate::ExprKind::Number(1.0)
9960        );
9961        assert_eq!(
9962            ast.try_expr(args[1]).unwrap().kind,
9963            crate::ExprKind::Number(2.0)
9964        );
9965        assert_eq!(
9966            ast.try_expr(args[2]).unwrap().kind,
9967            crate::ExprKind::Number(3.0)
9968        );
9969    }
9970
9971    #[test]
9972    fn parse_call_nested() {
9973        let (ast, diags) = parse_str("f(g(x));");
9974        assert!(diags.is_empty());
9975        let outer = expr_of_single_stmt(&ast);
9976        let (_callee, args) = call(outer);
9977        assert_eq!(args.len(), 1);
9978        let (_inner_callee, inner_args) = call(ast.try_expr(args[0]).unwrap());
9979        assert_eq!(inner_args.len(), 1);
9980        assert!(matches!(
9981            ast.try_expr(inner_args[0]).unwrap().kind,
9982            crate::ExprKind::Identifier(_)
9983        ));
9984    }
9985
9986    #[test]
9987    fn parse_curried_call() {
9988        let (ast, diags) = parse_str("f()(x);");
9989        assert!(diags.is_empty());
9990        let outer = expr_of_single_stmt(&ast);
9991        let (callee, args) = call(outer);
9992        assert_eq!(args.len(), 1);
9993        let (_inner_callee, inner_args) = call(ast.try_expr(callee).unwrap());
9994        assert!(inner_args.is_empty());
9995    }
9996
9997    #[test]
9998    fn unary_binds_looser_than_call() {
9999        let (ast, diags) = parse_str("-f();");
10000        assert!(diags.is_empty());
10001        let outer = expr_of_single_stmt(&ast);
10002        let (op, operand) = unary(outer);
10003        assert_eq!(op, crate::UnOp::Neg);
10004        let (_callee, args) = call(ast.try_expr(operand).unwrap());
10005        assert!(args.is_empty());
10006    }
10007
10008    #[test]
10009    fn call_inside_binary() {
10010        let (ast, diags) = parse_str("f() + 1;");
10011        assert!(diags.is_empty());
10012        let outer = expr_of_single_stmt(&ast);
10013        let (op, lhs, rhs) = binary(outer);
10014        assert_eq!(op, crate::BinOp::Add);
10015        let (_callee, args) = call(ast.try_expr(lhs).unwrap());
10016        assert!(args.is_empty());
10017        assert_eq!(
10018            ast.try_expr(rhs).unwrap().kind,
10019            crate::ExprKind::Number(1.0)
10020        );
10021    }
10022
10023    #[test]
10024    fn call_with_complex_args() {
10025        let (ast, diags) = parse_str("f(1 + 2, g(x));");
10026        assert!(diags.is_empty());
10027        let outer = expr_of_single_stmt(&ast);
10028        let (_callee, args) = call(outer);
10029        assert_eq!(args.len(), 2);
10030        let (op, ..) = binary(ast.try_expr(args[0]).unwrap());
10031        assert_eq!(op, crate::BinOp::Add);
10032        let (_, inner_args) = call(ast.try_expr(args[1]).unwrap());
10033        assert_eq!(inner_args.len(), 1);
10034    }
10035
10036    #[test]
10037    fn call_trailing_comma_ok() {
10038        let (ast, diags) = parse_str("f(1, 2,);");
10039        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10040        let (_callee, args) = call(expr_of_single_stmt(&ast));
10041        assert_eq!(args.len(), 2);
10042    }
10043
10044    #[test]
10045    fn unclosed_call_diagnoses() {
10046        let (ast, diags) = parse_str("f(1, 2");
10047        assert!(!diags.is_empty());
10048        assert!(diags[0].message == "expected `,` or `)`" || diags[0].message == "expected `)`");
10049        assert!(ast.top_level.is_empty());
10050    }
10051
10052    #[test]
10053    fn call_expression_statement() {
10054        let (ast, diags) = parse_str(r#"greet("world");"#);
10055        assert!(diags.is_empty());
10056        let outer = expr_of_single_stmt(&ast);
10057        let (callee, args) = call(outer);
10058        assert!(matches!(
10059            ast.try_expr(callee).unwrap().kind,
10060            crate::ExprKind::Identifier(_)
10061        ));
10062        assert_eq!(args.len(), 1);
10063        assert_eq!(
10064            ast.try_expr(args[0]).unwrap().kind,
10065            crate::ExprKind::String("world".to_string())
10066        );
10067    }
10068
10069    #[test]
10070    fn snapshot_multi_call_program() {
10071        let (ast, diags) = parse_str("f(); g(1); h(f(2), 3);");
10072        assert!(diags.is_empty());
10073        assert_eq!(ast.top_level.len(), 3);
10074        insta::assert_debug_snapshot!(ast);
10075    }
10076
10077    #[test]
10078    fn parse_if_simple() {
10079        let (ast, diags) = parse_str("if (a) { }");
10080        assert!(diags.is_empty());
10081        let stmt = single_stmt(&ast);
10082        match stmt.kind {
10083            crate::StmtKind::If {
10084                condition,
10085                then_block,
10086                else_block,
10087            } => {
10088                assert!(matches!(
10089                    ast.try_expr(condition).unwrap().kind,
10090                    crate::ExprKind::Identifier(_)
10091                ));
10092                assert!(matches!(
10093                    ast.try_stmt(then_block).unwrap().kind,
10094                    crate::StmtKind::Block(ref v) if v.is_empty()
10095                ));
10096                assert!(else_block.is_none());
10097            }
10098            _ => panic!("expected If"),
10099        }
10100    }
10101
10102    #[test]
10103    fn parse_if_else() {
10104        let (ast, diags) = parse_str("if (a) { } else { }");
10105        assert!(diags.is_empty());
10106        let stmt = single_stmt(&ast);
10107        match stmt.kind {
10108            crate::StmtKind::If { else_block, .. } => {
10109                let else_id = else_block.expect("expected else block");
10110                assert!(matches!(
10111                    ast.try_stmt(else_id).unwrap().kind,
10112                    crate::StmtKind::Block(ref v) if v.is_empty()
10113                ));
10114            }
10115            _ => panic!("expected If"),
10116        }
10117    }
10118
10119    #[test]
10120    fn parse_else_if_chain() {
10121        let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10122        assert!(diags.is_empty());
10123        let stmt = single_stmt(&ast);
10124        let crate::StmtKind::If {
10125            else_block: Some(inner_id),
10126            ..
10127        } = stmt.kind
10128        else {
10129            panic!("expected outer If with else_block");
10130        };
10131        let inner = ast.try_stmt(inner_id).unwrap();
10132        let crate::StmtKind::If {
10133            else_block: Some(tail_id),
10134            ..
10135        } = inner.kind
10136        else {
10137            panic!("expected inner If for `else if`");
10138        };
10139        assert!(matches!(
10140            ast.try_stmt(tail_id).unwrap().kind,
10141            crate::StmtKind::Block(_)
10142        ));
10143    }
10144
10145    #[test]
10146    fn parse_if_with_body_statements() {
10147        let (ast, diags) = parse_str("if (a) { let x = 1; }");
10148        assert!(diags.is_empty());
10149        let stmt = single_stmt(&ast);
10150        match stmt.kind {
10151            crate::StmtKind::If { then_block, .. } => {
10152                let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind
10153                else {
10154                    panic!("expected Block")
10155                };
10156                assert_eq!(body.len(), 1);
10157            }
10158            _ => panic!("expected If"),
10159        }
10160    }
10161
10162    #[test]
10163    fn parse_if_braceless_body() {
10164        let (ast, diags) = parse_str("if (a) return b;");
10165        assert!(diags.is_empty(), "{diags:?}");
10166        let stmt = single_stmt(&ast);
10167        let crate::StmtKind::If {
10168            then_block,
10169            else_block,
10170            ..
10171        } = stmt.kind
10172        else {
10173            panic!("expected If");
10174        };
10175        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10176            panic!("braceless body should wrap in a Block");
10177        };
10178        assert_eq!(body.len(), 1);
10179        assert!(matches!(
10180            ast.try_stmt(body[0]).unwrap().kind,
10181            crate::StmtKind::Return(_)
10182        ));
10183        assert!(else_block.is_none());
10184    }
10185
10186    #[test]
10187    fn parse_braceless_dangling_else_binds_nearest_if() {
10188        // `else` attaches to the inner `if`, not the outer one.
10189        let (ast, diags) = parse_str("if (a) if (b) x(); else y();");
10190        assert!(diags.is_empty(), "{diags:?}");
10191        let crate::StmtKind::If {
10192            then_block,
10193            else_block: outer_else,
10194            ..
10195        } = single_stmt(&ast).kind
10196        else {
10197            panic!("expected outer If");
10198        };
10199        assert!(outer_else.is_none(), "else must bind to the inner if");
10200        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10201            panic!("expected wrapped Block");
10202        };
10203        let crate::StmtKind::If {
10204            else_block: Some(_),
10205            ..
10206        } = ast.try_stmt(body[0]).unwrap().kind
10207        else {
10208            panic!("inner If should own the else");
10209        };
10210    }
10211
10212    #[test]
10213    fn parse_while_braceless_body() {
10214        let (ast, diags) = parse_str("while (a) x();");
10215        assert!(diags.is_empty(), "{diags:?}");
10216        let crate::StmtKind::While { body, .. } = single_stmt(&ast).kind else {
10217            panic!("expected While");
10218        };
10219        assert!(matches!(
10220            ast.try_stmt(body).unwrap().kind,
10221            crate::StmtKind::Block(ref v) if v.len() == 1
10222        ));
10223    }
10224
10225    #[test]
10226    fn parse_loop_with_empty_body() {
10227        for src in [
10228            "while (a());",
10229            "for (const x of xs);",
10230            "for (let i = 0; i < 3; i++);",
10231            "do ; while (a());",
10232        ] {
10233            let (ast, diags) = parse_str(src);
10234            assert!(diags.is_empty(), "{src}: {diags:?}");
10235            let body = match single_stmt(&ast).kind {
10236                crate::StmtKind::While { body, .. }
10237                | crate::StmtKind::DoWhile { body, .. }
10238                | crate::StmtKind::For { body, .. }
10239                | crate::StmtKind::ForOf { body, .. } => body,
10240                ref other => panic!("{src}: expected a loop, got {other:?}"),
10241            };
10242            assert!(
10243                matches!(ast.try_stmt(body).unwrap().kind, crate::StmtKind::Block(ref v) if v.is_empty()),
10244                "{src}"
10245            );
10246        }
10247    }
10248
10249    #[test]
10250    fn parse_if_with_empty_body_is_an_error() {
10251        let (_ast, diags) = parse_str("if (a);");
10252        assert_eq!(diags[0].message, "`if` has an empty body");
10253    }
10254
10255    #[test]
10256    fn parse_empty_branch_body_reports_once() {
10257        let (_ast, diags) = parse_str("if (a) ; else b();");
10258        assert_eq!(diags.len(), 1, "{diags:?}");
10259        assert_eq!(diags[0].message, "`if` has an empty body");
10260        let (_ast, diags) = parse_str("if (a) b(); else ;");
10261        assert_eq!(diags.len(), 1, "{diags:?}");
10262        assert_eq!(diags[0].message, "`else` has an empty body");
10263        // One diagnostic per stray `;`, and the `else if` chain still parses.
10264        let (_ast, diags) = parse_str("if (a) ; else if (b) ; else ;");
10265        let messages: Vec<_> = diags.iter().map(|d| d.message.as_str()).collect();
10266        assert_eq!(
10267            messages,
10268            [
10269                "`if` has an empty body",
10270                "`if` has an empty body",
10271                "`else` has an empty body"
10272            ]
10273        );
10274    }
10275
10276    #[test]
10277    fn parse_for_of_braceless_body() {
10278        let (ast, diags) = parse_str("for (const x of xs) f(x);");
10279        assert!(diags.is_empty(), "{diags:?}");
10280        let crate::StmtKind::ForOf { body, .. } = single_stmt(&ast).kind else {
10281            panic!("expected ForOf");
10282        };
10283        assert!(matches!(
10284            ast.try_stmt(body).unwrap().kind,
10285            crate::StmtKind::Block(ref v) if v.len() == 1
10286        ));
10287    }
10288
10289    #[test]
10290    fn parse_nested_if() {
10291        let (ast, diags) = parse_str("if (a) { if (b) { } }");
10292        assert!(diags.is_empty());
10293        let outer = single_stmt(&ast);
10294        let crate::StmtKind::If { then_block, .. } = outer.kind else {
10295            panic!("expected outer If");
10296        };
10297        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10298            panic!("expected Block")
10299        };
10300        assert_eq!(body.len(), 1);
10301        assert!(matches!(
10302            ast.try_stmt(body[0]).unwrap().kind,
10303            crate::StmtKind::If { .. }
10304        ));
10305    }
10306
10307    #[test]
10308    fn if_missing_open_paren_diagnoses() {
10309        let (ast, diags) = parse_str("if a { }");
10310        assert!(!diags.is_empty());
10311        assert_eq!(diags[0].message, "expected `(`");
10312        assert!(ast.top_level.is_empty());
10313    }
10314
10315    #[test]
10316    fn if_missing_close_paren_diagnoses() {
10317        let (ast, diags) = parse_str("if (a { }");
10318        assert!(!diags.is_empty());
10319        assert_eq!(diags[0].message, "expected `)`");
10320        assert!(ast.top_level.is_empty());
10321    }
10322
10323    #[test]
10324    fn if_braceless_body_parses() {
10325        let (ast, diags) = parse_str("if (a) x;");
10326        assert!(diags.is_empty(), "{diags:?}");
10327        let crate::StmtKind::If { then_block, .. } = single_stmt(&ast).kind else {
10328            panic!("expected If");
10329        };
10330        assert!(matches!(
10331            ast.try_stmt(then_block).unwrap().kind,
10332            crate::StmtKind::Block(ref v) if v.len() == 1
10333        ));
10334    }
10335
10336    #[test]
10337    fn snapshot_else_if_chain() {
10338        let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10339        assert!(diags.is_empty());
10340        insta::assert_debug_snapshot!(ast);
10341    }
10342
10343    #[test]
10344    fn parse_while_simple() {
10345        let (ast, diags) = parse_str("while (a) { }");
10346        assert!(diags.is_empty());
10347        let stmt = single_stmt(&ast);
10348        match stmt.kind {
10349            crate::StmtKind::While { condition, body } => {
10350                assert!(matches!(
10351                    ast.try_expr(condition).unwrap().kind,
10352                    crate::ExprKind::Identifier(_)
10353                ));
10354                assert!(matches!(
10355                    ast.try_stmt(body).unwrap().kind,
10356                    crate::StmtKind::Block(ref v) if v.is_empty()
10357                ));
10358            }
10359            _ => panic!("expected While"),
10360        }
10361    }
10362
10363    #[test]
10364    fn parse_while_with_body() {
10365        let (ast, diags) = parse_str("while (a) { x; y; }");
10366        assert!(diags.is_empty());
10367        let stmt = single_stmt(&ast);
10368        let crate::StmtKind::While { body, .. } = stmt.kind else {
10369            panic!("expected While");
10370        };
10371        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10372            panic!("expected Block")
10373        };
10374        assert_eq!(stmts.len(), 2);
10375    }
10376
10377    #[test]
10378    fn while_missing_paren_diagnoses() {
10379        let (ast, diags) = parse_str("while a { }");
10380        assert!(!diags.is_empty());
10381        assert_eq!(diags[0].message, "expected `(`");
10382        assert!(ast.top_level.is_empty());
10383    }
10384
10385    #[test]
10386    fn parse_for_all_slots() {
10387        let (ast, diags) = parse_str("for (let i = 0; i < 10; i = i + 1) { x; }");
10388        assert!(diags.is_empty(), "{diags:?}");
10389        let stmt = single_stmt(&ast);
10390        let crate::StmtKind::For {
10391            init,
10392            condition,
10393            update,
10394            body,
10395        } = &stmt.kind
10396        else {
10397            panic!("expected For");
10398        };
10399        assert!(!init.is_empty(), "init missing");
10400        assert!(condition.is_some(), "condition missing");
10401        assert!(update.is_some(), "update missing");
10402        assert!(matches!(
10403            ast.try_stmt(*body).unwrap().kind,
10404            crate::StmtKind::Block(_)
10405        ));
10406    }
10407
10408    #[test]
10409    fn parse_for_empty_slots() {
10410        let (ast, diags) = parse_str("for (;;) { break; }");
10411        assert!(diags.is_empty(), "{diags:?}");
10412        let stmt = single_stmt(&ast);
10413        let crate::StmtKind::For {
10414            init,
10415            condition,
10416            update,
10417            ..
10418        } = &stmt.kind
10419        else {
10420            panic!("expected For");
10421        };
10422        assert!(init.is_empty() && condition.is_none() && update.is_none());
10423    }
10424
10425    #[test]
10426    fn parse_for_of_const_binding() {
10427        let (ast, diags) = parse_str("for (const x of arr) { y; }");
10428        assert!(diags.is_empty(), "{diags:?}");
10429        let stmt = single_stmt(&ast);
10430        let crate::StmtKind::ForOf {
10431            binding_kind,
10432            ref name,
10433            ref ty,
10434            ..
10435        } = stmt.kind
10436        else {
10437            panic!("expected ForOf");
10438        };
10439        assert!(matches!(binding_kind, crate::BindingKind::Const));
10440        assert_eq!(name.name, "x");
10441        assert!(ty.is_none());
10442    }
10443
10444    #[test]
10445    fn parse_for_of_let_with_annotation() {
10446        let (ast, diags) = parse_str("for (let x: number of arr) { y; }");
10447        assert!(diags.is_empty(), "{diags:?}");
10448        let stmt = single_stmt(&ast);
10449        let crate::StmtKind::ForOf {
10450            binding_kind,
10451            ref ty,
10452            ..
10453        } = stmt.kind
10454        else {
10455            panic!("expected ForOf");
10456        };
10457        assert!(matches!(binding_kind, crate::BindingKind::Let));
10458        assert!(ty.is_some());
10459    }
10460
10461    #[test]
10462    fn parse_do_while_simple() {
10463        let (ast, diags) = parse_str("do { x; } while (a);");
10464        assert!(diags.is_empty(), "{diags:?}");
10465        let stmt = single_stmt(&ast);
10466        assert!(matches!(stmt.kind, crate::StmtKind::DoWhile { .. }));
10467    }
10468
10469    #[test]
10470    fn parse_break_and_continue() {
10471        let (ast, diags) = parse_str("while (a) { break; continue; }");
10472        assert!(diags.is_empty(), "{diags:?}");
10473        let stmt = single_stmt(&ast);
10474        let crate::StmtKind::While { body, .. } = stmt.kind else {
10475            panic!("expected While");
10476        };
10477        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10478            panic!("expected Block");
10479        };
10480        assert!(matches!(
10481            ast.try_stmt(stmts[0]).unwrap().kind,
10482            crate::StmtKind::Break
10483        ));
10484        assert!(matches!(
10485            ast.try_stmt(stmts[1]).unwrap().kind,
10486            crate::StmtKind::Continue
10487        ));
10488    }
10489
10490    #[test]
10491    fn parse_bare_return_in_function() {
10492        let (ast, diags) = parse_str("function f(): void { return; }");
10493        assert!(diags.is_empty());
10494        let f = single_stmt(&ast);
10495        let crate::StmtKind::Function { body, .. } = f.kind else {
10496            panic!("expected Function");
10497        };
10498        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10499            panic!("expected Block")
10500        };
10501        assert_eq!(stmts.len(), 1);
10502        match ast.try_stmt(stmts[0]).unwrap().kind {
10503            crate::StmtKind::Return(None) => {}
10504            _ => panic!("expected Return(None)"),
10505        }
10506    }
10507
10508    #[test]
10509    fn parse_return_with_value() {
10510        let (ast, diags) = parse_str("function f(): number { return 1; }");
10511        assert!(diags.is_empty());
10512        let f = single_stmt(&ast);
10513        let crate::StmtKind::Function { body, .. } = f.kind else {
10514            panic!("expected Function");
10515        };
10516        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10517            panic!("expected Block")
10518        };
10519        match ast.try_stmt(stmts[0]).unwrap().kind {
10520            crate::StmtKind::Return(Some(value)) => {
10521                assert_eq!(
10522                    ast.try_expr(value).unwrap().kind,
10523                    crate::ExprKind::Number(1.0)
10524                );
10525            }
10526            _ => panic!("expected Return(Some)"),
10527        }
10528    }
10529
10530    #[test]
10531    fn parse_return_with_expression() {
10532        let (ast, diags) = parse_str("function f(): number { return 1 + 2; }");
10533        assert!(diags.is_empty());
10534        let f = single_stmt(&ast);
10535        let crate::StmtKind::Function { body, .. } = f.kind else {
10536            panic!("expected Function");
10537        };
10538        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10539            panic!("expected Block")
10540        };
10541        match ast.try_stmt(stmts[0]).unwrap().kind {
10542            crate::StmtKind::Return(Some(value)) => {
10543                let (op, ..) = binary(ast.try_expr(value).unwrap());
10544                assert_eq!(op, crate::BinOp::Add);
10545            }
10546            _ => panic!("expected Return(Some(Binary))"),
10547        }
10548    }
10549
10550    #[test]
10551    fn return_missing_semicolon_diagnoses() {
10552        let (_, diags) = parse_str("function f(): number { return x x; }");
10553        assert!(!diags.is_empty());
10554        assert_eq!(diags[0].message, "expected `;` after return");
10555    }
10556
10557    #[test]
10558    fn parse_bare_block() {
10559        let (ast, diags) = parse_str("{ x; y; }");
10560        assert!(diags.is_empty());
10561        let stmt = single_stmt(&ast);
10562        let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10563            panic!("expected Block");
10564        };
10565        assert_eq!(stmts.len(), 2);
10566    }
10567
10568    #[test]
10569    fn parse_empty_bare_block() {
10570        let (ast, diags) = parse_str("{ }");
10571        assert!(diags.is_empty());
10572        let stmt = single_stmt(&ast);
10573        let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10574            panic!("expected Block");
10575        };
10576        assert!(stmts.is_empty());
10577    }
10578
10579    #[test]
10580    fn parse_nested_bare_blocks() {
10581        let (ast, diags) = parse_str("{ { } }");
10582        assert!(diags.is_empty());
10583        let outer = single_stmt(&ast);
10584        let crate::StmtKind::Block(ref stmts) = outer.kind else {
10585            panic!("expected outer Block");
10586        };
10587        assert_eq!(stmts.len(), 1);
10588        assert!(matches!(
10589            ast.try_stmt(stmts[0]).unwrap().kind,
10590            crate::StmtKind::Block(ref v) if v.is_empty()
10591        ));
10592    }
10593
10594    #[test]
10595    fn snapshot_function_with_if_and_return() {
10596        let source = "function isPositive(n: number): boolean {\n  if (n > 0) {\n    return true;\n  } else {\n    return false;\n  }\n}";
10597        let (ast, diags) = parse_str(source);
10598        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10599        insta::assert_debug_snapshot!(ast);
10600    }
10601
10602    fn object_literal(e: &crate::Expr) -> &[crate::ObjectLiteralMember] {
10603        match e.kind {
10604            crate::ExprKind::ObjectLiteral { ref members } => members.as_slice(),
10605            _ => panic!("expected ObjectLiteral, got {:?}", e.kind),
10606        }
10607    }
10608
10609    fn object_literal_field(m: &crate::ObjectLiteralMember) -> &crate::ObjectLiteralField {
10610        match m {
10611            crate::ObjectLiteralMember::Field(f) => f,
10612            crate::ObjectLiteralMember::Spread { .. }
10613            | crate::ObjectLiteralMember::Computed { .. } => {
10614                panic!("expected Field member, got Spread")
10615            }
10616        }
10617    }
10618
10619    fn array_literal(e: &crate::Expr) -> &[crate::ArrayLiteralElement] {
10620        match e.kind {
10621            crate::ExprKind::ArrayLiteral { ref elements } => elements.as_slice(),
10622            _ => panic!("expected ArrayLiteral, got {:?}", e.kind),
10623        }
10624    }
10625
10626    fn array_literal_value(el: &crate::ArrayLiteralElement) -> crate::ExprId {
10627        match el {
10628            crate::ArrayLiteralElement::Value(id) => *id,
10629            crate::ArrayLiteralElement::Spread { .. } => {
10630                panic!("expected Value element, got Spread")
10631            }
10632        }
10633    }
10634
10635    #[test]
10636    fn parse_empty_object_literal() {
10637        let (ast, diags) = parse_str("let x = {};");
10638        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10639        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10640            panic!("expected Let");
10641        };
10642        assert!(object_literal(ast.try_expr(value).unwrap()).is_empty());
10643    }
10644
10645    #[test]
10646    fn parse_object_literal_simple() {
10647        let (ast, diags) = parse_str("let p = { x: 1, y: 2 };");
10648        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10649        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10650            panic!("expected Let");
10651        };
10652        let members = object_literal(ast.try_expr(value).unwrap());
10653        assert_eq!(members.len(), 2);
10654        let f0 = object_literal_field(&members[0]);
10655        let f1 = object_literal_field(&members[1]);
10656        assert_eq!(f0.name.name, "x");
10657        assert_eq!(f1.name.name, "y");
10658        assert_eq!(
10659            ast.try_expr(f0.value).unwrap().kind,
10660            crate::ExprKind::Number(1.0)
10661        );
10662        assert_eq!(
10663            ast.try_expr(f1.value).unwrap().kind,
10664            crate::ExprKind::Number(2.0)
10665        );
10666    }
10667
10668    #[test]
10669    fn parse_object_literal_keyword_keys() {
10670        let (ast, diags) = parse_str("let p = { type: 1, default: 2, null: 3, true: 4 };");
10671        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10672        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10673            panic!("expected Let");
10674        };
10675        let names: Vec<&str> = object_literal(ast.try_expr(value).unwrap())
10676            .iter()
10677            .map(object_literal_field)
10678            .map(|f| f.name.name.as_str())
10679            .collect();
10680        assert_eq!(names, vec!["type", "default", "null", "true"]);
10681    }
10682
10683    #[test]
10684    fn parse_object_literal_string_key() {
10685        let (ast, diags) = parse_str(r#"let p = { "hello": 1 };"#);
10686        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10687        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10688            panic!("expected Let");
10689        };
10690        let members = object_literal(ast.try_expr(value).unwrap());
10691        assert_eq!(members.len(), 1);
10692        let f0 = object_literal_field(&members[0]);
10693        assert_eq!(f0.name.name, "hello");
10694    }
10695
10696    #[test]
10697    fn parse_object_literal_string_key_decodes_escape() {
10698        let (ast, diags) = parse_str(r#"let p = { "content\u002dtype": 1 };"#);
10699        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10700        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10701            panic!("expected Let");
10702        };
10703        let members = object_literal(ast.try_expr(value).unwrap());
10704        assert_eq!(members.len(), 1);
10705        let f0 = object_literal_field(&members[0]);
10706        assert_eq!(f0.name.name, "content-type");
10707    }
10708
10709    #[test]
10710    fn parse_object_literal_shorthand() {
10711        let (ast, diags) = parse_str("let p = { x, y: 2 };");
10712        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10713        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10714            panic!("expected Let");
10715        };
10716        let members = object_literal(ast.try_expr(value).unwrap());
10717        assert_eq!(members.len(), 2);
10718        let f0 = object_literal_field(&members[0]);
10719        assert_eq!(f0.name.name, "x");
10720        // `{ x }` desugars to `{ x: x }` — value is an identifier reference.
10721        assert_eq!(
10722            ast.try_expr(f0.value).unwrap().kind,
10723            crate::ExprKind::Identifier(crate::Ident {
10724                name: "x".into(),
10725                span: f0.name.span,
10726            })
10727        );
10728        let f1 = object_literal_field(&members[1]);
10729        assert_eq!(f1.name.name, "y");
10730        assert_eq!(
10731            ast.try_expr(f1.value).unwrap().kind,
10732            crate::ExprKind::Number(2.0)
10733        );
10734    }
10735
10736    #[test]
10737    fn parse_object_literal_string_key_shorthand_rejected() {
10738        let (_ast, diags) = parse_str(r#"let p = { "x" };"#);
10739        assert!(
10740            diags
10741                .iter()
10742                .any(|d| d.message.contains("expected `:` after field name")),
10743            "string-key shorthand should be rejected: {diags:?}"
10744        );
10745    }
10746
10747    #[test]
10748    fn parse_object_literal_trailing_comma() {
10749        let (ast, diags) = parse_str("let p = { x: 1, y: 2, };");
10750        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10751        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10752            panic!("expected Let");
10753        };
10754        assert_eq!(object_literal(ast.try_expr(value).unwrap()).len(), 2);
10755    }
10756
10757    #[test]
10758    fn parse_object_literal_duplicate_key_diagnoses() {
10759        let (_, diags) = parse_str("let p = { x: 1, x: 2 };");
10760        assert_eq!(diags.len(), 1);
10761        assert_eq!(diags[0].message, "duplicate field `x` in object literal");
10762    }
10763
10764    #[test]
10765    fn parse_empty_array_literal() {
10766        let (ast, diags) = parse_str("let xs = [];");
10767        assert!(diags.is_empty());
10768        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10769            panic!("expected Let");
10770        };
10771        assert!(array_literal(ast.try_expr(value).unwrap()).is_empty());
10772    }
10773
10774    #[test]
10775    fn parse_array_literal_simple() {
10776        let (ast, diags) = parse_str("let xs = [1, 2, 3];");
10777        assert!(diags.is_empty());
10778        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10779            panic!("expected Let");
10780        };
10781        let elements = array_literal(ast.try_expr(value).unwrap());
10782        assert_eq!(elements.len(), 3);
10783        assert_eq!(
10784            ast.try_expr(array_literal_value(&elements[0]))
10785                .unwrap()
10786                .kind,
10787            crate::ExprKind::Number(1.0)
10788        );
10789        assert_eq!(
10790            ast.try_expr(array_literal_value(&elements[2]))
10791                .unwrap()
10792                .kind,
10793            crate::ExprKind::Number(3.0)
10794        );
10795    }
10796
10797    #[test]
10798    fn parse_array_literal_trailing_comma() {
10799        let (ast, diags) = parse_str("let xs = [1, 2,];");
10800        assert!(diags.is_empty());
10801        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10802            panic!("expected Let");
10803        };
10804        assert_eq!(array_literal(ast.try_expr(value).unwrap()).len(), 2);
10805    }
10806
10807    #[test]
10808    fn parse_nested_array_in_object() {
10809        let (ast, diags) = parse_str("let p = { items: [1, 2] };");
10810        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10811        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10812            panic!("expected Let");
10813        };
10814        let members = object_literal(ast.try_expr(value).unwrap());
10815        let f0 = object_literal_field(&members[0]);
10816        assert_eq!(f0.name.name, "items");
10817        let inner = array_literal(ast.try_expr(f0.value).unwrap());
10818        assert_eq!(inner.len(), 2);
10819    }
10820
10821    #[test]
10822    fn parse_nested_object_in_array() {
10823        let (ast, diags) = parse_str("let xs = [{ x: 1 }, { x: 2 }];");
10824        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10825        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10826            panic!("expected Let");
10827        };
10828        let elements = array_literal(ast.try_expr(value).unwrap());
10829        assert_eq!(elements.len(), 2);
10830        assert_eq!(
10831            object_literal(ast.try_expr(array_literal_value(&elements[0])).unwrap()).len(),
10832            1
10833        );
10834        assert_eq!(
10835            object_literal(ast.try_expr(array_literal_value(&elements[1])).unwrap()).len(),
10836            1
10837        );
10838    }
10839
10840    #[test]
10841    fn brace_at_statement_start_is_block_not_object() {
10842        let (_, diags) = parse_str("{ x: 1 }");
10843        assert!(
10844            !diags.is_empty(),
10845            "expected a diagnostic for `x:` inside block"
10846        );
10847    }
10848
10849    fn field_access(e: &crate::Expr) -> (crate::ExprId, &str) {
10850        match e.kind {
10851            crate::ExprKind::FieldAccess { receiver, ref name } => (receiver, name.name.as_str()),
10852            _ => panic!("expected FieldAccess, got {:?}", e.kind),
10853        }
10854    }
10855
10856    fn index_access(e: &crate::Expr) -> (crate::ExprId, crate::ExprId) {
10857        match e.kind {
10858            crate::ExprKind::IndexAccess { receiver, index } => (receiver, index),
10859            _ => panic!("expected IndexAccess, got {:?}", e.kind),
10860        }
10861    }
10862
10863    #[test]
10864    fn parse_field_access_simple() {
10865        let (ast, diags) = parse_str("a.b;");
10866        assert!(diags.is_empty());
10867        let outer = expr_of_single_stmt(&ast);
10868        let (receiver, name) = field_access(outer);
10869        assert!(matches!(
10870            ast.try_expr(receiver).unwrap().kind,
10871            crate::ExprKind::Identifier(_)
10872        ));
10873        assert_eq!(name, "b");
10874    }
10875
10876    #[test]
10877    fn parse_field_access_keyword_name() {
10878        let (ast, diags) = parse_str("status.type.default.null;");
10879        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10880        let outer = expr_of_single_stmt(&ast);
10881        let (receiver, name) = field_access(outer);
10882        assert_eq!(name, "null");
10883        let (receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10884        assert_eq!(name, "default");
10885        let (_receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10886        assert_eq!(name, "type");
10887    }
10888
10889    #[test]
10890    fn parse_field_access_chain_left_assoc() {
10891        let (ast, diags) = parse_str("a.b.c;");
10892        assert!(diags.is_empty());
10893        let outer = expr_of_single_stmt(&ast);
10894        let (mid_id, c) = field_access(outer);
10895        assert_eq!(c, "c");
10896        let (a_id, b) = field_access(ast.try_expr(mid_id).unwrap());
10897        assert_eq!(b, "b");
10898        assert!(matches!(
10899            ast.try_expr(a_id).unwrap().kind,
10900            crate::ExprKind::Identifier(_)
10901        ));
10902    }
10903
10904    #[test]
10905    fn parse_index_access_simple() {
10906        let (ast, diags) = parse_str("a[0];");
10907        assert!(diags.is_empty());
10908        let outer = expr_of_single_stmt(&ast);
10909        let (receiver, idx) = index_access(outer);
10910        assert!(matches!(
10911            ast.try_expr(receiver).unwrap().kind,
10912            crate::ExprKind::Identifier(_)
10913        ));
10914        assert_eq!(
10915            ast.try_expr(idx).unwrap().kind,
10916            crate::ExprKind::Number(0.0)
10917        );
10918    }
10919
10920    #[test]
10921    fn parse_index_access_chain() {
10922        let (ast, diags) = parse_str("a[0][1];");
10923        assert!(diags.is_empty());
10924        let outer = expr_of_single_stmt(&ast);
10925        let (mid_id, one) = index_access(outer);
10926        assert_eq!(
10927            ast.try_expr(one).unwrap().kind,
10928            crate::ExprKind::Number(1.0)
10929        );
10930        let (a_id, zero) = index_access(ast.try_expr(mid_id).unwrap());
10931        assert_eq!(
10932            ast.try_expr(zero).unwrap().kind,
10933            crate::ExprKind::Number(0.0)
10934        );
10935        assert!(matches!(
10936            ast.try_expr(a_id).unwrap().kind,
10937            crate::ExprKind::Identifier(_)
10938        ));
10939    }
10940
10941    #[test]
10942    fn parse_mixed_access_and_call_chain() {
10943        let (ast, diags) = parse_str("a.b[c].d();");
10944        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10945        let outer = expr_of_single_stmt(&ast);
10946        let (callee, args) = call(outer);
10947        assert!(args.is_empty());
10948        let (idx_id, d_name) = field_access(ast.try_expr(callee).unwrap());
10949        assert_eq!(d_name, "d");
10950        let (b_id, c_idx) = index_access(ast.try_expr(idx_id).unwrap());
10951        assert!(matches!(
10952            ast.try_expr(c_idx).unwrap().kind,
10953            crate::ExprKind::Identifier(_)
10954        ));
10955        let (a_id, b_name) = field_access(ast.try_expr(b_id).unwrap());
10956        assert_eq!(b_name, "b");
10957        assert!(matches!(
10958            ast.try_expr(a_id).unwrap().kind,
10959            crate::ExprKind::Identifier(_)
10960        ));
10961    }
10962
10963    #[test]
10964    fn parse_call_then_field() {
10965        let (ast, diags) = parse_str("f().x;");
10966        assert!(diags.is_empty());
10967        let outer = expr_of_single_stmt(&ast);
10968        let (call_id, x_name) = field_access(outer);
10969        assert_eq!(x_name, "x");
10970        let (_callee, args) = call(ast.try_expr(call_id).unwrap());
10971        assert!(args.is_empty());
10972    }
10973
10974    #[test]
10975    fn parse_index_with_complex_expression() {
10976        let (ast, diags) = parse_str("a[i + 1];");
10977        assert!(diags.is_empty());
10978        let outer = expr_of_single_stmt(&ast);
10979        let (_, idx) = index_access(outer);
10980        let (op, ..) = binary(ast.try_expr(idx).unwrap());
10981        assert_eq!(op, crate::BinOp::Add);
10982    }
10983
10984    #[test]
10985    fn field_access_missing_name_diagnoses() {
10986        let (_, diags) = parse_str("a.;");
10987        assert!(!diags.is_empty());
10988        assert_eq!(diags[0].message, "expected field name after `.`");
10989    }
10990
10991    #[test]
10992    fn parse_postfix_increment() {
10993        let (ast, diags) = parse_str("x++;");
10994        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10995        let expr = expr_of_single_stmt(&ast);
10996        let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10997            panic!("expected PostfixUnary, got {:?}", expr.kind);
10998        };
10999        assert_eq!(op, crate::PostfixOp::Inc);
11000        assert!(matches!(
11001            ast.try_expr(operand).unwrap().kind,
11002            crate::ExprKind::Identifier(_)
11003        ));
11004        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
11005    }
11006
11007    #[test]
11008    fn parse_postfix_decrement() {
11009        let (ast, diags) = parse_str("x--;");
11010        assert!(diags.is_empty());
11011        let expr = expr_of_single_stmt(&ast);
11012        let crate::ExprKind::PostfixUnary { op, .. } = expr.kind else {
11013            panic!("expected PostfixUnary");
11014        };
11015        assert_eq!(op, crate::PostfixOp::Dec);
11016    }
11017
11018    #[test]
11019    fn parse_postfix_non_null_assertion() {
11020        let (ast, diags) = parse_str("x!;");
11021        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11022        let expr = expr_of_single_stmt(&ast);
11023        let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
11024            panic!("expected PostfixUnary, got {:?}", expr.kind);
11025        };
11026        assert_eq!(op, crate::PostfixOp::NonNullAssert);
11027        assert!(matches!(
11028            ast.try_expr(operand).unwrap().kind,
11029            crate::ExprKind::Identifier(_)
11030        ));
11031        assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
11032    }
11033
11034    #[test]
11035    fn parse_non_null_assertion_continues_chain() {
11036        let (ast, diags) = parse_str("x!.y;");
11037        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11038        let expr = expr_of_single_stmt(&ast);
11039        let crate::ExprKind::FieldAccess { receiver, name } = &expr.kind else {
11040            panic!("expected FieldAccess, got {:?}", expr.kind);
11041        };
11042        assert_eq!(name.name, "y");
11043        assert!(matches!(
11044            ast.try_expr(*receiver).unwrap().kind,
11045            crate::ExprKind::PostfixUnary {
11046                op: crate::PostfixOp::NonNullAssert,
11047                ..
11048            }
11049        ));
11050    }
11051
11052    #[test]
11053    fn parse_postfix_on_field_access() {
11054        let (ast, diags) = parse_str("o.f++;");
11055        assert!(diags.is_empty());
11056        let expr = expr_of_single_stmt(&ast);
11057        let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
11058            panic!("expected PostfixUnary");
11059        };
11060        assert!(matches!(
11061            ast.try_expr(operand).unwrap().kind,
11062            crate::ExprKind::FieldAccess { .. }
11063        ));
11064    }
11065
11066    #[test]
11067    fn parse_postfix_on_index_access() {
11068        let (ast, diags) = parse_str("xs[0]++;");
11069        assert!(diags.is_empty());
11070        let expr = expr_of_single_stmt(&ast);
11071        let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
11072            panic!("expected PostfixUnary");
11073        };
11074        assert!(matches!(
11075            ast.try_expr(operand).unwrap().kind,
11076            crate::ExprKind::IndexAccess { .. }
11077        ));
11078    }
11079
11080    #[test]
11081    fn parse_postfix_terminates_chain() {
11082        let (_, diags) = parse_str("x++.y;");
11083        assert!(!diags.is_empty());
11084        assert_eq!(diags[0].message, "expected `;` after expression");
11085    }
11086
11087    #[test]
11088    fn parse_postfix_in_binary_continues() {
11089        let (ast, diags) = parse_str("let y = x++ + 1;");
11090        assert!(diags.is_empty(), "unexpected: {diags:?}");
11091        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11092            panic!("expected Let");
11093        };
11094        let (op, lhs, _) = binary(ast.try_expr(value).unwrap());
11095        assert_eq!(op, crate::BinOp::Add);
11096        assert!(matches!(
11097            ast.try_expr(lhs).unwrap().kind,
11098            crate::ExprKind::PostfixUnary { .. }
11099        ));
11100    }
11101
11102    #[test]
11103    fn parse_index_assignment() {
11104        let (ast, diags) = parse_str("xs[0] = 1;");
11105        assert!(diags.is_empty(), "unexpected: {diags:?}");
11106        let stmt = single_stmt(&ast);
11107        let crate::StmtKind::AssignIndex {
11108            receiver,
11109            index,
11110            value,
11111        } = stmt.kind
11112        else {
11113            panic!("expected AssignIndex, got {:?}", stmt.kind);
11114        };
11115        assert!(matches!(
11116            ast.try_expr(receiver).unwrap().kind,
11117            crate::ExprKind::Identifier(_)
11118        ));
11119        assert_eq!(
11120            ast.try_expr(index).unwrap().kind,
11121            crate::ExprKind::Number(0.0)
11122        );
11123        assert_eq!(
11124            ast.try_expr(value).unwrap().kind,
11125            crate::ExprKind::Number(1.0)
11126        );
11127    }
11128
11129    #[test]
11130    fn parse_for_update_postfix() {
11131        let (ast, diags) = parse_str("function main(): void { for (let i = 0; i < 3; i++) {} }");
11132        assert!(diags.is_empty(), "unexpected: {diags:?}");
11133        assert_eq!(ast.top_level.len(), 1);
11134    }
11135
11136    #[test]
11137    fn parse_for_update_index_assignment() {
11138        let (ast, diags) =
11139            parse_str("function main(): void { for (let i = 0; i < 3; xs[i] = i) {} }");
11140        assert!(diags.is_empty(), "unexpected: {diags:?}");
11141        assert_eq!(ast.top_level.len(), 1);
11142    }
11143
11144    #[test]
11145    fn index_access_missing_close_bracket_diagnoses() {
11146        let (_, diags) = parse_str("a[1;");
11147        assert!(!diags.is_empty());
11148        assert_eq!(diags[0].message, "expected `]`");
11149    }
11150
11151    #[test]
11152    fn field_access_inside_object_literal_value_does_not_steal_dot() {
11153        let (ast, diags) = parse_str("let x = { a: 1 }.foo;");
11154        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11155        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11156            panic!("expected Let");
11157        };
11158        let (recv_id, name) = field_access(ast.try_expr(value).unwrap());
11159        assert_eq!(name, "foo");
11160        assert_eq!(object_literal(ast.try_expr(recv_id).unwrap()).len(), 1);
11161    }
11162
11163    fn type_of_let(stmt: &crate::Stmt) -> &crate::TypeAnnotation {
11164        match stmt.kind {
11165            crate::StmtKind::Let { ref ty, .. } => ty.as_ref().expect("expected type annotation"),
11166            _ => panic!("expected Let"),
11167        }
11168    }
11169
11170    #[test]
11171    fn parse_array_type_annotation() {
11172        let (ast, diags) = parse_str("let xs: number[] = null;");
11173        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11174        let ty = type_of_let(single_stmt(&ast));
11175        match ty.kind {
11176            crate::TypeAnnotationKind::Array(ref inner) => {
11177                assert!(matches!(inner.kind, crate::TypeAnnotationKind::Name { .. }));
11178            }
11179            _ => panic!("expected Array, got {:?}", ty.kind),
11180        }
11181    }
11182
11183    #[test]
11184    fn parse_nested_array_type_annotation() {
11185        let (ast, diags) = parse_str("let xs: number[][] = null;");
11186        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11187        let ty = type_of_let(single_stmt(&ast));
11188        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11189            panic!("expected outer Array");
11190        };
11191        let crate::TypeAnnotationKind::Array(ref inner2) = inner.kind else {
11192            panic!("expected inner Array");
11193        };
11194        assert!(matches!(
11195            inner2.kind,
11196            crate::TypeAnnotationKind::Name { .. }
11197        ));
11198    }
11199
11200    #[test]
11201    fn parse_object_type_annotation_simple() {
11202        let (ast, diags) = parse_str("let p: { x: number; y: number } = null;");
11203        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11204        let ty = type_of_let(single_stmt(&ast));
11205        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11206            panic!("expected Object, got {:?}", ty.kind);
11207        };
11208        assert_eq!(fields.len(), 2);
11209        assert_eq!(fields[0].name.name, "x");
11210        assert_eq!(fields[1].name.name, "y");
11211    }
11212
11213    #[test]
11214    fn readonly_is_rejected_on_methods_accessors_and_method_signatures() {
11215        let source = "class K {\n\
11216             readonly m(): number { return 1; }\n\
11217             public readonly n<T>(): number { return 1; }\n\
11218             readonly get g(): number { return 1; }\n\
11219             readonly p: number = 1;\n\
11220             }\n\
11221             type T = { readonly r(a: number): boolean; readonly f: () => void };\n\
11222             interface I { readonly s(): void; readonly q: number; }\n";
11223        let (_, diags) = parse_str(source);
11224        let lines: Vec<usize> = diags
11225            .iter()
11226            .map(|d| {
11227                assert_eq!(
11228                    d.message,
11229                    "`readonly` can only modify a property or index signature"
11230                );
11231                source[..d.span.start as usize].matches('\n').count() + 1
11232            })
11233            .collect();
11234        assert_eq!(lines, vec![2, 3, 4, 7, 8]);
11235    }
11236
11237    #[test]
11238    fn parse_object_type_method_members() {
11239        // `m(): T` is the same member as `m: () => T`, so it parses to a function-typed
11240        // field — including through the `?` modifier.
11241        let (ast, diags) =
11242            parse_str("let p: { m(): number; opt?(): string; r(a: number): boolean } = null;");
11243        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11244        let ty = type_of_let(single_stmt(&ast));
11245        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11246            panic!("expected Object, got {:?}", ty.kind);
11247        };
11248        assert_eq!(fields.len(), 3);
11249        for field in fields {
11250            assert!(
11251                matches!(field.ty.kind, crate::TypeAnnotationKind::Function { .. }),
11252                "member `{}` should be function-typed, got {:?}",
11253                field.name.name,
11254                field.ty.kind,
11255            );
11256        }
11257        assert!(fields[1].optional, "`opt?()` is an optional member");
11258        let crate::TypeAnnotationKind::Function { ref params, .. } = fields[2].ty.kind else {
11259            unreachable!("checked above");
11260        };
11261        assert_eq!(params.len(), 1);
11262        assert_eq!(params[0].name.name, "a");
11263    }
11264
11265    #[test]
11266    fn parse_object_type_annotation_with_commas() {
11267        let (ast, diags) = parse_str("let p: { x: number, y: number } = null;");
11268        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11269        let ty = type_of_let(single_stmt(&ast));
11270        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11271            panic!("expected Object");
11272        };
11273        assert_eq!(fields.len(), 2);
11274    }
11275
11276    #[test]
11277    fn parse_object_type_annotation_empty() {
11278        let (ast, diags) = parse_str("let p: {} = null;");
11279        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11280        let ty = type_of_let(single_stmt(&ast));
11281        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11282            panic!("expected Object");
11283        };
11284        assert!(fields.is_empty());
11285    }
11286
11287    #[test]
11288    fn parse_object_type_with_optional_field() {
11289        let (ast, diags) = parse_str("let p: { id: number; nick?: string } = null;");
11290        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11291        let ty = type_of_let(single_stmt(&ast));
11292        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11293            panic!("expected Object");
11294        };
11295        assert_eq!(fields.len(), 2);
11296        assert_eq!(fields[0].name.name, "id");
11297        assert!(!fields[0].optional);
11298        assert_eq!(fields[1].name.name, "nick");
11299        assert!(fields[1].optional);
11300    }
11301
11302    #[test]
11303    fn parse_object_type_with_readonly_fields() {
11304        let (ast, diags) = parse_str(
11305            "let issue: { readonly id: string; readonly title?: string; body: string } = null;",
11306        );
11307        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11308        let ty = type_of_let(single_stmt(&ast));
11309        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11310            panic!("expected Object");
11311        };
11312        let fields: Vec<(&str, bool, bool)> = fields
11313            .iter()
11314            .map(|field| (field.name.name.as_str(), field.optional, field.readonly))
11315            .collect();
11316        assert_eq!(
11317            fields,
11318            vec![
11319                ("id", false, true),
11320                ("title", true, true),
11321                ("body", false, false),
11322            ]
11323        );
11324    }
11325
11326    #[test]
11327    fn parse_object_type_mixed_optional_and_required() {
11328        let (ast, diags) = parse_str("let p: { a?: number; b: string; c?: boolean } = null;");
11329        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11330        let ty = type_of_let(single_stmt(&ast));
11331        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11332            panic!("expected Object");
11333        };
11334        let opt: Vec<(&str, bool)> = fields
11335            .iter()
11336            .map(|f| (f.name.name.as_str(), f.optional))
11337            .collect();
11338        assert_eq!(opt, vec![("a", true), ("b", false), ("c", true)]);
11339    }
11340
11341    #[test]
11342    fn parse_object_type_keyword_fields() {
11343        let (ast, diags) =
11344            parse_str("let p: { type: string; default?: number; null: boolean } = null;");
11345        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11346        let ty = type_of_let(single_stmt(&ast));
11347        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11348            panic!("expected Object");
11349        };
11350        let opt: Vec<(&str, bool)> = fields
11351            .iter()
11352            .map(|f| (f.name.name.as_str(), f.optional))
11353            .collect();
11354        assert_eq!(
11355            opt,
11356            vec![("type", false), ("default", true), ("null", false)]
11357        );
11358    }
11359
11360    #[test]
11361    fn parse_object_type_quoted_fields() {
11362        let (ast, diags) =
11363            parse_str(r#"let p: { "content-type": string; "x\u002drequest-id"?: string } = null;"#);
11364        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11365        let ty = type_of_let(single_stmt(&ast));
11366        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11367            panic!("expected Object");
11368        };
11369        let opt: Vec<(&str, bool)> = fields
11370            .iter()
11371            .map(|f| (f.name.name.as_str(), f.optional))
11372            .collect();
11373        assert_eq!(opt, vec![("content-type", false), ("x-request-id", true)]);
11374    }
11375
11376    #[test]
11377    fn parse_interface_with_optional_property() {
11378        let (ast, diags) = parse_str("interface User { id: number; deletedAt?: string; }");
11379        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11380        let stmt = single_stmt(&ast);
11381        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11382            panic!("expected interface decl");
11383        };
11384        let opt: Vec<(&str, bool)> = members
11385            .iter()
11386            .map(|m| match m {
11387                crate::InterfaceMember::Property { name, optional, .. } => {
11388                    (name.name.as_str(), *optional)
11389                }
11390                _ => panic!("expected property"),
11391            })
11392            .collect();
11393        assert_eq!(opt, vec![("id", false), ("deletedAt", true)]);
11394    }
11395
11396    #[test]
11397    fn parse_interface_with_readonly_properties() {
11398        let (ast, diags) = parse_str(
11399            "interface Issue { readonly id: string; readonly title?: string; body: string; }",
11400        );
11401        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11402        let stmt = single_stmt(&ast);
11403        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11404            panic!("expected interface decl");
11405        };
11406        let opt: Vec<(&str, bool, bool)> = members
11407            .iter()
11408            .map(|member| match member {
11409                crate::InterfaceMember::Property {
11410                    name,
11411                    optional,
11412                    readonly,
11413                    ..
11414                } => (name.name.as_str(), *optional, *readonly),
11415                _ => panic!("expected property"),
11416            })
11417            .collect();
11418        assert_eq!(
11419            opt,
11420            vec![
11421                ("id", false, true),
11422                ("title", true, true),
11423                ("body", false, false),
11424            ]
11425        );
11426    }
11427
11428    #[test]
11429    fn parse_readonly_property_name_without_modifier() {
11430        let (ast, diags) = parse_str(
11431            "interface Meta { readonly: boolean; }\nlet meta: { readonly: boolean } = null;",
11432        );
11433        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11434        let crate::StmtKind::InterfaceDecl { ref members, .. } =
11435            ast.try_stmt(ast.top_level[0]).unwrap().kind
11436        else {
11437            panic!("expected interface decl");
11438        };
11439        let crate::InterfaceMember::Property {
11440            ref name, readonly, ..
11441        } = members[0]
11442        else {
11443            panic!("expected property");
11444        };
11445        assert_eq!(name.name, "readonly");
11446        assert!(
11447            !readonly,
11448            "`readonly` here is the property name, not a modifier"
11449        );
11450
11451        let ty = type_of_let(ast.try_stmt(ast.top_level[1]).unwrap());
11452        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11453            panic!("expected Object");
11454        };
11455        assert_eq!(fields[0].name.name, "readonly");
11456        assert!(!fields[0].readonly);
11457    }
11458
11459    #[test]
11460    fn parse_interface_keyword_members() {
11461        let (ast, diags) =
11462            parse_str("interface IssueStatus { type: string; default(): string; null: boolean; }");
11463        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11464        let stmt = single_stmt(&ast);
11465        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11466            panic!("expected interface decl");
11467        };
11468        let names: Vec<&str> = members
11469            .iter()
11470            .map(|m| match m {
11471                crate::InterfaceMember::Method { name, .. }
11472                | crate::InterfaceMember::Property { name, .. } => name.name.as_str(),
11473                crate::InterfaceMember::IndexSignature(_) => panic!("unexpected index signature"),
11474            })
11475            .collect();
11476        assert_eq!(names, vec!["type", "default", "null"]);
11477    }
11478
11479    #[test]
11480    fn parse_interface_quoted_property_members() {
11481        let (ast, diags) = parse_str(
11482            r#"interface Headers { "content-type": string; "x\u002drequest-id"?: string; }"#,
11483        );
11484        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11485        let stmt = single_stmt(&ast);
11486        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11487            panic!("expected interface decl");
11488        };
11489        let names: Vec<(&str, bool)> = members
11490            .iter()
11491            .map(|m| match m {
11492                crate::InterfaceMember::Property { name, optional, .. } => {
11493                    (name.name.as_str(), *optional)
11494                }
11495                _ => panic!("expected property"),
11496            })
11497            .collect();
11498        assert_eq!(names, vec![("content-type", false), ("x-request-id", true)]);
11499    }
11500
11501    #[test]
11502    fn parse_param_optional_marker() {
11503        let (ast, diags) = parse_str("function f(x?: number): void {}");
11504        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11505        let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
11506            panic!("expected function")
11507        };
11508        assert!(params[0].optional);
11509        assert!(params[0].default.is_none());
11510    }
11511
11512    #[test]
11513    fn parse_interface_optional_method() {
11514        let (ast, diags) = parse_str("interface F { foo?(x?: number): void; }");
11515        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11516        let crate::StmtKind::InterfaceDecl { ref members, .. } = single_stmt(&ast).kind else {
11517            panic!("expected interface")
11518        };
11519        let crate::InterfaceMember::Property { optional, ty, .. } = &members[0] else {
11520            panic!("expected callable property")
11521        };
11522        assert!(*optional);
11523        let crate::TypeAnnotationKind::Function { params, .. } = &ty.kind else {
11524            panic!("expected function type")
11525        };
11526        assert!(params[0].optional);
11527    }
11528
11529    #[test]
11530    fn parse_interface_with_call_signature() {
11531        let (ast, diags) = parse_str("interface F { (s: string): number; }");
11532        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11533        let stmt = single_stmt(&ast);
11534        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11535            panic!("expected interface decl");
11536        };
11537        assert_eq!(members.len(), 1);
11538        match &members[0] {
11539            crate::InterfaceMember::Method { name, params, .. } => {
11540                assert_eq!(name.name, "@call");
11541                assert_eq!(params.len(), 1);
11542                assert_eq!(params[0].name.name, "s");
11543            }
11544            other => panic!("expected method (call signature), got {other:?}"),
11545        }
11546    }
11547
11548    #[test]
11549    fn parse_interface_mixed_call_signature_and_method() {
11550        let (ast, diags) = parse_str("interface F { greet(): string; (x: number): number; }");
11551        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11552        let stmt = single_stmt(&ast);
11553        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11554            panic!("expected interface decl");
11555        };
11556        let names: Vec<&str> = members
11557            .iter()
11558            .map(|m| match m {
11559                crate::InterfaceMember::Method { name, .. } => name.name.as_str(),
11560                _ => "<property>",
11561            })
11562            .collect();
11563        assert_eq!(names, vec!["greet", "@call"]);
11564    }
11565
11566    #[test]
11567    fn parse_interface_rejects_generic_call_signature() {
11568        let (_ast, diags) = parse_str("interface F { <T>(x: T): T; }");
11569        assert!(
11570            diags
11571                .iter()
11572                .any(|d| d.message.contains("generic call signatures")),
11573            "expected generic-call-signature diagnostic, got {diags:?}"
11574        );
11575    }
11576
11577    #[test]
11578    fn parse_object_type_with_array_field() {
11579        let (ast, diags) = parse_str("let p: { xs: number[] } = null;");
11580        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11581        let ty = type_of_let(single_stmt(&ast));
11582        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11583            panic!("expected Object");
11584        };
11585        assert_eq!(fields[0].name.name, "xs");
11586        assert!(matches!(
11587            fields[0].ty.kind,
11588            crate::TypeAnnotationKind::Array(_)
11589        ));
11590    }
11591
11592    #[test]
11593    fn parse_array_of_objects() {
11594        let (ast, diags) = parse_str("let xs: { x: number }[] = null;");
11595        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11596        let ty = type_of_let(single_stmt(&ast));
11597        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11598            panic!("expected Array");
11599        };
11600        assert!(matches!(
11601            inner.kind,
11602            crate::TypeAnnotationKind::Object { .. }
11603        ));
11604    }
11605
11606    #[test]
11607    fn parse_tuple_type_annotation() {
11608        let (ast, diags) = parse_str("let p: [string, number] = null;");
11609        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11610        let ty = type_of_let(single_stmt(&ast));
11611        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11612            panic!("expected Tuple, got {:?}", ty.kind);
11613        };
11614        assert_eq!(elems.len(), 2);
11615        assert!(matches!(
11616            elems[0].kind,
11617            crate::TypeAnnotationKind::Name { .. }
11618        ));
11619        assert!(matches!(
11620            elems[1].kind,
11621            crate::TypeAnnotationKind::Name { .. }
11622        ));
11623    }
11624
11625    #[test]
11626    fn parse_singleton_tuple_type() {
11627        let (ast, diags) = parse_str("let p: [number] = null;");
11628        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11629        let ty = type_of_let(single_stmt(&ast));
11630        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11631            panic!("expected Tuple");
11632        };
11633        assert_eq!(elems.len(), 1);
11634    }
11635
11636    #[test]
11637    fn parse_nested_tuple_type() {
11638        let (ast, diags) = parse_str("let p: [[number, string], boolean] = null;");
11639        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11640        let ty = type_of_let(single_stmt(&ast));
11641        let crate::TypeAnnotationKind::Tuple(ref outer) = ty.kind else {
11642            panic!("expected outer Tuple");
11643        };
11644        assert_eq!(outer.len(), 2);
11645        let crate::TypeAnnotationKind::Tuple(ref inner) = outer[0].kind else {
11646            panic!("expected inner Tuple");
11647        };
11648        assert_eq!(inner.len(), 2);
11649    }
11650
11651    #[test]
11652    fn parse_array_of_tuple_type() {
11653        let (ast, diags) = parse_str("let xs: [string, number][] = null;");
11654        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11655        let ty = type_of_let(single_stmt(&ast));
11656        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11657            panic!("expected outer Array, got {:?}", ty.kind);
11658        };
11659        assert!(matches!(inner.kind, crate::TypeAnnotationKind::Tuple(_)));
11660    }
11661
11662    #[test]
11663    fn parse_trailing_comma_tuple_type() {
11664        let (ast, diags) = parse_str("let p: [number, string,] = null;");
11665        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11666        let ty = type_of_let(single_stmt(&ast));
11667        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11668            panic!("expected Tuple");
11669        };
11670        assert_eq!(elems.len(), 2);
11671    }
11672
11673    #[test]
11674    fn parse_empty_tuple_type_rejected() {
11675        let (_ast, diags) = parse_str("let p: [] = null;");
11676        assert!(
11677            diags.iter().any(|d| d
11678                .message
11679                .contains("tuple types must have at least one element")),
11680            "expected empty-tuple diagnostic, got: {diags:?}"
11681        );
11682    }
11683
11684    #[test]
11685    fn parse_labeled_tuple_drops_labels() {
11686        let (ast, diags) = parse_str("let p: [first: number, string] = null;");
11687        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11688        let ty = type_of_let(single_stmt(&ast));
11689        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11690            panic!("expected Tuple, got {:?}", ty.kind);
11691        };
11692        assert_eq!(elems.len(), 2);
11693        assert!(
11694            elems
11695                .iter()
11696                .all(|e| matches!(e.kind, crate::TypeAnnotationKind::Name { .. }))
11697        );
11698    }
11699
11700    #[test]
11701    fn parse_labeled_optional_tuple_element() {
11702        let (ast, diags) = parse_str("let p: [first: number, second?: string] = null;");
11703        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11704        let ty = type_of_let(single_stmt(&ast));
11705        let crate::TypeAnnotationKind::Tuple(elements) = &ty.kind else {
11706            panic!("expected tuple")
11707        };
11708        assert!(matches!(
11709            elements[1].kind,
11710            crate::TypeAnnotationKind::Optional(_)
11711        ));
11712    }
11713
11714    #[test]
11715    fn parse_readonly_binds_to_the_postfix_type() {
11716        let (ast, diags) = parse_str("let p: readonly number[][] | null = null;");
11717        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11718        let ty = type_of_let(single_stmt(&ast));
11719        let crate::TypeAnnotationKind::Union(ref members) = ty.kind else {
11720            panic!("expected Union, got {:?}", ty.kind);
11721        };
11722        let crate::TypeAnnotationKind::Readonly(ref operand) = members[0].kind else {
11723            panic!("expected Readonly, got {:?}", members[0].kind);
11724        };
11725        let crate::TypeAnnotationKind::Array(ref inner) = operand.kind else {
11726            panic!("expected Array, got {:?}", operand.kind);
11727        };
11728        assert!(matches!(inner.kind, crate::TypeAnnotationKind::Array(_)));
11729    }
11730
11731    #[test]
11732    fn parse_readonly_as_a_type_name_is_not_the_operator() {
11733        let (ast, diags) = parse_str("let p: readonly = null;");
11734        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11735        let ty = type_of_let(single_stmt(&ast));
11736        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11737    }
11738
11739    #[test]
11740    fn parse_generic_one_arg() {
11741        let (ast, diags) = parse_str("let x: Foo<number> = null;");
11742        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11743        let ty = type_of_let(single_stmt(&ast));
11744        let crate::TypeAnnotationKind::Name { ref name, ref args } = ty.kind else {
11745            panic!("expected Name, got {:?}", ty.kind);
11746        };
11747        assert_eq!(name.span, crate::Span::new(F, 7, 10).unwrap());
11748        assert_eq!(ty.span, crate::Span::new(F, 7, 18).unwrap());
11749        assert_eq!(args.len(), 1);
11750        assert!(matches!(
11751            args[0].kind,
11752            crate::TypeAnnotationKind::Name { .. }
11753        ));
11754    }
11755
11756    #[test]
11757    fn parse_generic_two_args() {
11758        let (ast, diags) = parse_str("let m: Map<string, number> = null;");
11759        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11760        let ty = type_of_let(single_stmt(&ast));
11761        let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11762            panic!("expected Name");
11763        };
11764        assert_eq!(args.len(), 2);
11765    }
11766
11767    #[test]
11768    fn parse_generic_nested() {
11769        let (ast, diags) = parse_str("let b: Box<Box<T>> = null;");
11770        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11771        let ty = type_of_let(single_stmt(&ast));
11772        let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11773            panic!("expected outer Name");
11774        };
11775        assert_eq!(args.len(), 1);
11776        let crate::TypeAnnotationKind::Name {
11777            args: ref inner, ..
11778        } = args[0].kind
11779        else {
11780            panic!("expected inner Name");
11781        };
11782        assert_eq!(inner.len(), 1);
11783    }
11784
11785    #[test]
11786    fn parse_generic_array_postfix() {
11787        let (ast, diags) = parse_str("let xs: Foo<number>[] = null;");
11788        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11789        let ty = type_of_let(single_stmt(&ast));
11790        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11791            panic!("expected Array");
11792        };
11793        assert!(
11794            matches!(inner.kind, crate::TypeAnnotationKind::Name { ref args, .. } if args.len() == 1)
11795        );
11796    }
11797
11798    #[test]
11799    fn parse_generic_empty_diagnoses() {
11800        let (_ast, diags) = parse_str("let x: Foo<> = null;");
11801        assert!(
11802            diags
11803                .iter()
11804                .any(|d| d.message.contains("empty generic argument list")),
11805            "expected empty-generic-args diagnostic, got: {diags:?}"
11806        );
11807    }
11808
11809    #[test]
11810    fn parse_generic_trailing_comma_ok() {
11811        let (_ast, diags) = parse_str("let x: Foo<number,> = null;");
11812        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11813    }
11814
11815    #[test]
11816    fn parse_generic_missing_close_diagnoses() {
11817        let (_ast, diags) = parse_str("let x: Foo<number = null;");
11818        assert!(
11819            diags
11820                .iter()
11821                .any(|d| d.message.contains("expected `,` or `>`")),
11822            "expected unterminated-generic diagnostic, got: {diags:?}"
11823        );
11824    }
11825
11826    #[test]
11827    fn parse_qualified_two_segments() {
11828        let (ast, diags) = parse_str("let x: Foo.Bar = null;");
11829        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11830        let ty = type_of_let(single_stmt(&ast));
11831        let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11832            panic!("expected Qualified, got {:?}", ty.kind);
11833        };
11834        assert_eq!(path.len(), 2);
11835        // `Foo` at 7..10, `Bar` at 11..14.
11836        assert_eq!(path[0].span, crate::Span::new(F, 7, 10).unwrap());
11837        assert_eq!(path[1].span, crate::Span::new(F, 11, 14).unwrap());
11838        assert!(args.is_empty());
11839        assert_eq!(ty.span, crate::Span::new(F, 7, 14).unwrap());
11840    }
11841
11842    #[test]
11843    fn parse_qualified_three_segments() {
11844        let (ast, diags) = parse_str("let x: Foo.Bar.Baz = null;");
11845        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11846        let ty = type_of_let(single_stmt(&ast));
11847        let crate::TypeAnnotationKind::Qualified { ref path, .. } = ty.kind else {
11848            panic!("expected Qualified, got {:?}", ty.kind);
11849        };
11850        assert_eq!(path.len(), 3);
11851    }
11852
11853    #[test]
11854    fn parse_qualified_with_generic_args() {
11855        let (ast, diags) = parse_str("let x: Foo.Box<number> = null;");
11856        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11857        let ty = type_of_let(single_stmt(&ast));
11858        let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11859            panic!("expected Qualified, got {:?}", ty.kind);
11860        };
11861        assert_eq!(path.len(), 2);
11862        assert_eq!(args.len(), 1);
11863        assert!(matches!(
11864            args[0].kind,
11865            crate::TypeAnnotationKind::Name { .. }
11866        ));
11867    }
11868
11869    #[test]
11870    fn parse_qualified_with_array_postfix() {
11871        let (ast, diags) = parse_str("let xs: Foo.Bar[] = null;");
11872        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11873        let ty = type_of_let(single_stmt(&ast));
11874        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11875            panic!("expected Array, got {:?}", ty.kind);
11876        };
11877        assert!(matches!(
11878            inner.kind,
11879            crate::TypeAnnotationKind::Qualified { .. }
11880        ));
11881    }
11882
11883    #[test]
11884    fn parse_qualified_trailing_dot_diagnoses() {
11885        let (_ast, diags) = parse_str("let x: Foo. = null;");
11886        assert!(
11887            diags.iter().any(|d| d
11888                .message
11889                .contains("expected identifier after `.` in type name")),
11890            "expected dotted-name diagnostic, got: {diags:?}"
11891        );
11892    }
11893
11894    #[test]
11895    fn parse_qualified_void_root_rejected_dot() {
11896        let (ast, diags) = parse_str("function f(): void.Bar { }");
11897        assert!(!diags.is_empty(), "expected a diagnostic for `void.Bar`");
11898        let _ = ast;
11899    }
11900
11901    fn union_members(ty: &crate::TypeAnnotation) -> &[crate::TypeAnnotation] {
11902        match &ty.kind {
11903            crate::TypeAnnotationKind::Union(members) => members,
11904            other => panic!("expected Union, got {other:?}"),
11905        }
11906    }
11907
11908    #[test]
11909    fn parse_union_two_members() {
11910        let (ast, diags) = parse_str("let x: number | string = null;");
11911        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11912        let ty = type_of_let(single_stmt(&ast));
11913        let members = union_members(ty);
11914        assert_eq!(members.len(), 2);
11915        assert!(matches!(
11916            members[0].kind,
11917            crate::TypeAnnotationKind::Name { .. }
11918        ));
11919        assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); // `number`
11920        assert!(matches!(
11921            members[1].kind,
11922            crate::TypeAnnotationKind::Name { .. }
11923        ));
11924        assert_eq!(members[1].span, crate::Span::new(F, 16, 22).unwrap()); // `string`
11925        assert_eq!(ty.span, crate::Span::new(F, 7, 22).unwrap());
11926    }
11927
11928    #[test]
11929    fn parse_union_three_members() {
11930        let (ast, diags) = parse_str("let x: A | B | C = null;");
11931        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11932        let ty = type_of_let(single_stmt(&ast));
11933        let members = union_members(ty);
11934        assert_eq!(members.len(), 3);
11935        for m in members {
11936            assert!(matches!(m.kind, crate::TypeAnnotationKind::Name { .. }));
11937        }
11938    }
11939
11940    #[test]
11941    fn parse_union_lower_than_array() {
11942        let (ast, diags) = parse_str("let x: number[] | string = null;");
11943        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11944        let ty = type_of_let(single_stmt(&ast));
11945        let members = union_members(ty);
11946        assert_eq!(members.len(), 2);
11947        assert!(
11948            matches!(members[0].kind, crate::TypeAnnotationKind::Array(_)),
11949            "expected first member to be Array, got {:?}",
11950            members[0].kind
11951        );
11952        assert!(matches!(
11953            members[1].kind,
11954            crate::TypeAnnotationKind::Name { .. }
11955        ));
11956    }
11957
11958    #[test]
11959    fn parse_union_with_null_member() {
11960        let (ast, diags) = parse_str("let x: number | null = null;");
11961        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11962        let ty = type_of_let(single_stmt(&ast));
11963        let members = union_members(ty);
11964        assert_eq!(members.len(), 2);
11965        assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); // `number`
11966        assert!(matches!(
11967            members[1].kind,
11968            crate::TypeAnnotationKind::Name { .. }
11969        ));
11970        assert_eq!(members[1].span, crate::Span::new(F, 16, 20).unwrap()); // `null`
11971    }
11972
11973    #[test]
11974    fn parse_union_in_function_return_type() {
11975        let (ast, diags) = parse_str("function f(): number | string { }");
11976        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11977        let stmt = single_stmt(&ast);
11978        match stmt.kind {
11979            crate::StmtKind::Function {
11980                ref return_type, ..
11981            } => {
11982                let return_type = return_type.as_ref().expect("plain return type");
11983                let members = union_members(return_type);
11984                assert_eq!(members.len(), 2);
11985            }
11986            _ => panic!("expected Function"),
11987        }
11988    }
11989
11990    #[test]
11991    fn parse_union_in_function_param_type() {
11992        let (ast, diags) = parse_str("function f(x: number | string): void { }");
11993        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11994        let stmt = single_stmt(&ast);
11995        match stmt.kind {
11996            crate::StmtKind::Function { ref params, .. } => {
11997                let p_ty = params[0].ty.as_ref().expect("param type");
11998                let members = union_members(p_ty);
11999                assert_eq!(members.len(), 2);
12000            }
12001            _ => panic!("expected Function"),
12002        }
12003    }
12004
12005    #[test]
12006    fn parse_union_in_object_field_type() {
12007        let (ast, diags) = parse_str("let p: { x: number | string } = null;");
12008        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12009        let ty = type_of_let(single_stmt(&ast));
12010        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
12011            panic!("expected Object, got {:?}", ty.kind);
12012        };
12013        assert_eq!(fields.len(), 1);
12014        let members = union_members(&fields[0].ty);
12015        assert_eq!(members.len(), 2);
12016    }
12017
12018    #[test]
12019    fn parse_union_leading_pipe_is_forgiven() {
12020        let (ast, diags) = parse_str("let x: | number | string = null;");
12021        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12022        let ty = type_of_let(single_stmt(&ast));
12023        let members = union_members(ty);
12024        assert_eq!(members.len(), 2);
12025    }
12026
12027    #[test]
12028    fn parse_union_leading_pipe_single_member_unwraps() {
12029        let (ast, diags) = parse_str("let x: | number = null;");
12030        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12031        let ty = type_of_let(single_stmt(&ast));
12032        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
12033    }
12034
12035    #[test]
12036    fn parse_union_trailing_pipe_diagnoses() {
12037        let (_, diags) = parse_str("let x: number | = null;");
12038        assert!(!diags.is_empty());
12039        assert_eq!(diags[0].message, "expected type");
12040    }
12041
12042    #[test]
12043    fn snapshot_union_type_mix() {
12044        let (ast, diags) = parse_str("let xs: number[] | string | null = null;");
12045        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12046        insta::assert_debug_snapshot!(ast);
12047    }
12048
12049    #[test]
12050    fn parse_object_type_duplicate_field_diagnoses() {
12051        let (_, diags) = parse_str("let p: { readonly x: number; x: string } = null;");
12052        assert!(!diags.is_empty());
12053        assert!(
12054            diags
12055                .iter()
12056                .any(|d| d.message == "duplicate field `x` in object type"),
12057            "expected duplicate-field diagnostic, got: {diags:?}"
12058        );
12059    }
12060
12061    #[test]
12062    fn snapshot_object_and_array_literals() {
12063        let (ast, diags) = parse_str(r#"let mix = [{ a: 1, b: "hi" }, {}];"#);
12064        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12065        insta::assert_debug_snapshot!(ast);
12066    }
12067
12068    #[test]
12069    fn snapshot_function_with_while() {
12070        let source = "function loop(n: number): void {\n  while (n > 0) {\n    log(n);\n  }\n}";
12071        let (ast, diags) = parse_str(source);
12072        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12073        insta::assert_debug_snapshot!(ast);
12074    }
12075
12076    fn parse_arrow_const(
12077        source: &str,
12078    ) -> (
12079        Ast,
12080        Vec<crate::ParamDecl>,
12081        Option<crate::TypeAnnotation>,
12082        crate::ArrowBody,
12083    ) {
12084        let (ast, diags) = parse_str(source);
12085        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12086        assert_eq!(ast.top_level.len(), 1);
12087        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12088        let value = match &stmt.kind {
12089            StmtKind::Const { value, .. } => *value,
12090            _ => panic!("expected const declaration"),
12091        };
12092        let value = match ast.try_expr(value).unwrap().kind {
12093            ExprKind::FunctionExpression { function, .. } => function,
12094            _ => value,
12095        };
12096        let expr = ast.try_expr(value).unwrap().clone();
12097        let (params, return_type, body) = match expr.kind {
12098            ExprKind::Arrow {
12099                params,
12100                return_type,
12101                body,
12102                ..
12103            } => (params, return_type, body),
12104            other => panic!("expected Arrow, got {other:?}"),
12105        };
12106        (ast, params, return_type, body)
12107    }
12108
12109    #[test]
12110    fn parses_anonymous_function_expression_as_arrow() {
12111        let (ast, params, return_type, body) =
12112            parse_arrow_const("const f = function (x: number): number { return x + 1; };");
12113        assert_eq!(params.len(), 1);
12114        assert_eq!(params[0].name.name, "x");
12115        assert!(return_type.is_some());
12116        assert!(matches!(body, crate::ArrowBody::Block(_)));
12117        let _ = ast;
12118    }
12119
12120    #[test]
12121    fn parses_function_expression_with_no_params() {
12122        let (_ast, params, _return_type, body) =
12123            parse_arrow_const("const f = function (): void {};");
12124        assert!(params.is_empty());
12125        assert!(matches!(body, crate::ArrowBody::Block(_)));
12126    }
12127
12128    /// The named wrapper preserves its body signature.
12129    #[test]
12130    fn parses_named_function_expression_signature() {
12131        let (_ast, params, _return_type, _body) =
12132            parse_arrow_const("const f = function named(x: number): number { return x + 1; };");
12133        assert_eq!(params.len(), 1);
12134    }
12135
12136    /// Self references are resolved in the function expression scope.
12137    #[test]
12138    fn accepts_a_named_function_expression_that_calls_itself() {
12139        let (_ast, diags) =
12140            parse_str("const f = function bar(x: number): number { return bar(x); };");
12141        assert!(diags.is_empty(), "{diags:?}");
12142    }
12143
12144    /// A member name after `.` is never a reference to the function itself.
12145    #[test]
12146    fn accepts_a_named_function_expression_using_the_name_as_a_property() {
12147        let (_ast, diags) = parse_str("const f = function bar(o: O): number { return o.bar; };");
12148        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12149    }
12150
12151    /// Nor is a key before `:`.
12152    #[test]
12153    fn accepts_a_named_function_expression_using_the_name_as_an_object_key() {
12154        let (_ast, diags) = parse_str(
12155            "const f = function bar(x: number): number { const o = { bar: 1 }; return o.bar + x; };",
12156        );
12157        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12158    }
12159
12160    /// A string that happens to spell the name is not a reference.
12161    #[test]
12162    fn accepts_a_named_function_expression_with_its_name_in_a_string() {
12163        let (_ast, diags) =
12164            parse_str("const f = function bar(x: number): string { return \"bar\"; };");
12165        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12166    }
12167
12168    /// A self reference in a template substitution remains a normal expression.
12169    #[test]
12170    fn accepts_a_self_call_inside_a_template_substitution() {
12171        let (_ast, diags) =
12172            parse_str("const f = function bar(x: number): string { return `${bar(0)}`; };");
12173        assert!(diags.is_empty(), "{diags:?}");
12174    }
12175
12176    #[test]
12177    fn rejects_a_generic_function_expression() {
12178        let (_ast, diags) = parse_str("const f = function <T>(x: T): T { return x; };");
12179        assert!(
12180            diags
12181                .iter()
12182                .any(|d| d.message.contains("generic function expressions")),
12183            "generic function expression should be rejected: {diags:?}"
12184        );
12185    }
12186
12187    #[test]
12188    fn accepts_this_inside_a_function_expression_in_a_class_method() {
12189        let (_ast, diags) = parse_str(
12190            "class C { x: number = 1; m(): number { \
12191             const f = function (this: { x: number }): number { return this.x; }; return f(); } }",
12192        );
12193        assert!(diags.is_empty(), "{diags:?}");
12194    }
12195
12196    #[test]
12197    fn retains_this_boundary_inside_method_shorthand() {
12198        let (ast, diags) = parse_str(
12199            "class C { x: number = 1; m(): number { \
12200             const o = { x: 2, g(): number { return this.x; } }; return o.g(); } }",
12201        );
12202        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12203        assert!((0..ast.exprs_len()).any(|index| matches!(
12204            ast.try_expr(crate::ExprId(index as u32)).unwrap().kind,
12205            ExprKind::ThisOutsideReceiver
12206        )));
12207    }
12208
12209    #[test]
12210    fn this_still_parses_in_an_ordinary_class_method() {
12211        let (_ast, diags) = parse_str("class C { x: number = 1; m(): number { return this.x; } }");
12212        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12213    }
12214
12215    /// `function` is a valid property name; the expression form must not shadow that.
12216    #[test]
12217    fn function_keyword_still_parses_as_an_object_key() {
12218        let (_ast, diags) = parse_str("let o = { function: 1 };");
12219        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12220    }
12221
12222    fn object_literal_member_values(source: &str) -> (Ast, Vec<crate::ExprId>) {
12223        let (ast, diags) = parse_str(source);
12224        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12225        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12226        let value = match &stmt.kind {
12227            StmtKind::Const { value, .. } => *value,
12228            other => panic!("expected a const declaration, got {other:?}"),
12229        };
12230        let members = match &ast.try_expr(value).unwrap().kind {
12231            ExprKind::ObjectLiteral { members } => members
12232                .iter()
12233                .map(crate::ObjectLiteralMember::value)
12234                .collect::<Vec<_>>(),
12235            other => panic!("expected ObjectLiteral, got {other:?}"),
12236        };
12237        (ast, members)
12238    }
12239
12240    #[test]
12241    fn parses_object_literal_method_shorthand_as_arrow() {
12242        let (ast, members) =
12243            object_literal_member_values("const o = { m(x: number): number { return x; } };");
12244        assert_eq!(members.len(), 1);
12245        match &ast.try_expr(members[0]).unwrap().kind {
12246            ExprKind::Arrow { params, body, .. } => {
12247                assert_eq!(params.len(), 1);
12248                assert_eq!(params[0].name.name, "x");
12249                assert!(matches!(body, crate::ArrowBody::Block(_)));
12250            }
12251            other => panic!("expected Arrow, got {other:?}"),
12252        }
12253    }
12254
12255    /// A keyword is a valid method name, just as it is a valid property name.
12256    #[test]
12257    fn parses_method_shorthand_with_a_keyword_name() {
12258        let (ast, members) =
12259            object_literal_member_values("const o = { if(x: number): number { return x; } };");
12260        assert!(matches!(
12261            ast.try_expr(members[0]).unwrap().kind,
12262            ExprKind::Arrow { .. }
12263        ));
12264    }
12265
12266    #[test]
12267    fn parses_method_shorthand_alongside_plain_and_shorthand_properties() {
12268        let (ast, members) =
12269            object_literal_member_values("const o = { a: 1, m(): void {}, b: 2 };");
12270        assert_eq!(members.len(), 3);
12271        assert!(matches!(
12272            ast.try_expr(members[1]).unwrap().kind,
12273            ExprKind::Arrow { .. }
12274        ));
12275    }
12276
12277    #[test]
12278    fn parses_bare_ident_arrow_expression_body() {
12279        let (_ast, params, return_type, body) = parse_arrow_const("const f = x => x + 1;");
12280        assert_eq!(params.len(), 1);
12281        assert_eq!(params[0].name.name, "x");
12282        assert!(params[0].ty.is_none());
12283        assert!(return_type.is_none());
12284        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12285    }
12286
12287    #[test]
12288    fn parses_zero_param_arrow() {
12289        let (_ast, params, return_type, body) = parse_arrow_const("const f = () => 0;");
12290        assert!(params.is_empty());
12291        assert!(return_type.is_none());
12292        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12293    }
12294
12295    #[test]
12296    fn parses_paren_unannotated_arrow() {
12297        let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x) => x;");
12298        assert_eq!(params.len(), 1);
12299        assert_eq!(params[0].name.name, "x");
12300        assert!(params[0].ty.is_none());
12301    }
12302
12303    #[test]
12304    fn parses_typed_param_arrow() {
12305        let (_ast, params, return_type, body) =
12306            parse_arrow_const("const f = (x: number) => x * 2;");
12307        assert_eq!(params.len(), 1);
12308        assert!(params[0].ty.is_some());
12309        assert!(return_type.is_none());
12310        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12311    }
12312
12313    #[test]
12314    fn parses_typed_param_and_return_arrow() {
12315        let (_ast, params, return_type, body) =
12316            parse_arrow_const("const f = (x: number): number => x * 2;");
12317        assert_eq!(params.len(), 1);
12318        assert!(params[0].ty.is_some());
12319        assert!(return_type.is_some());
12320        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12321    }
12322
12323    #[test]
12324    fn parses_multi_param_unannotated_arrow() {
12325        let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x, y) => x + y;");
12326        assert_eq!(params.len(), 2);
12327        assert_eq!(params[0].name.name, "x");
12328        assert_eq!(params[1].name.name, "y");
12329        assert!(params[0].ty.is_none());
12330        assert!(params[1].ty.is_none());
12331    }
12332
12333    #[test]
12334    fn parses_multi_param_typed_arrow() {
12335        let (_ast, params, _return_type, _body) =
12336            parse_arrow_const("const f = (x: number, y: number) => x + y;");
12337        assert_eq!(params.len(), 2);
12338        assert!(params[0].ty.is_some());
12339        assert!(params[1].ty.is_some());
12340    }
12341
12342    #[test]
12343    fn parses_block_body_arrow() {
12344        let (_ast, params, _return_type, body) =
12345            parse_arrow_const("const f = (x: number) => { return x; };");
12346        assert_eq!(params.len(), 1);
12347        assert!(matches!(body, crate::ArrowBody::Block(_)));
12348    }
12349
12350    #[test]
12351    fn rejects_typed_bare_ident_arrow() {
12352        let (_ast, diags) = parse_str("const f = x: number => x;");
12353        assert!(
12354            !diags.is_empty(),
12355            "expected diagnostics for `x: number => …`",
12356        );
12357    }
12358
12359    #[test]
12360    fn arrow_inside_call_argument_parses() {
12361        let (ast, diags) = parse_str("f((x: number) => x + 1, 2);");
12362        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12363        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12364        let call = match &stmt.kind {
12365            StmtKind::Expr(eid) => ast.try_expr(*eid).unwrap(),
12366            _ => panic!("expected expression statement"),
12367        };
12368        let args = match &call.kind {
12369            ExprKind::Call { args, .. } => args.clone(),
12370            other => panic!("expected Call, got {other:?}"),
12371        };
12372        assert_eq!(args.len(), 2);
12373        assert!(matches!(
12374            ast.try_expr(args[0]).unwrap().kind,
12375            ExprKind::Arrow { .. }
12376        ));
12377    }
12378
12379    #[test]
12380    fn function_decl_param_still_requires_annotation() {
12381        let (_ast, diags) = parse_str("function f(x): number { return x; }");
12382        assert!(
12383            diags.iter().any(|d| d.message.contains("type annotation")),
12384            "expected `parameter requires a type annotation` diagnostic, got {diags:?}",
12385        );
12386    }
12387
12388    #[test]
12389    fn paren_expression_still_parses_when_no_arrow() {
12390        let (ast, diags) = parse_str("const a = (1 + 2);");
12391        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12392        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12393        let value = match &stmt.kind {
12394            StmtKind::Const { value, .. } => *value,
12395            _ => panic!("expected const declaration"),
12396        };
12397        assert!(matches!(
12398            ast.try_expr(value).unwrap().kind,
12399            ExprKind::Paren(_)
12400        ));
12401    }
12402
12403    #[test]
12404    fn parse_named_import_single() {
12405        let (ast, diags) = parse_str("import { v4 } from \"submilli:uuid\";");
12406        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12407        assert_eq!(ast.top_level.len(), 1);
12408        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12409        let (module, kind) = match &stmt.kind {
12410            StmtKind::Import { module, kind, .. } => (module, kind),
12411            other => panic!("expected Import, got {other:?}"),
12412        };
12413        assert_eq!(module, "submilli:uuid");
12414        let specs = match kind {
12415            ImportKind::Named(specs) => specs,
12416            other => panic!("expected Named, got {other:?}"),
12417        };
12418        assert_eq!(specs.len(), 1);
12419        assert_eq!(specs[0].imported_name.name, "v4");
12420        assert_eq!(specs[0].local_name.name, "v4");
12421    }
12422
12423    #[test]
12424    fn parse_named_import_multi_and_alias() {
12425        let (ast, diags) =
12426            parse_str("import { v4, v7 as makeId, validate } from \"submilli:uuid\";");
12427        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12428        let specs = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12429            StmtKind::Import {
12430                kind: ImportKind::Named(s),
12431                ..
12432            } => s.clone(),
12433            other => panic!("expected named import, got {other:?}"),
12434        };
12435        assert_eq!(specs.len(), 3);
12436        assert_eq!(specs[0].imported_name.name, "v4");
12437        assert_eq!(specs[0].local_name.name, "v4");
12438        assert_eq!(specs[1].imported_name.name, "v7");
12439        assert_eq!(specs[1].local_name.name, "makeId");
12440        assert_eq!(specs[2].imported_name.name, "validate");
12441        assert_eq!(specs[2].local_name.name, "validate");
12442    }
12443
12444    #[test]
12445    fn parse_namespace_import() {
12446        let (ast, diags) = parse_str("import uuid from \"submilli:uuid\";");
12447        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12448        let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12449            StmtKind::Import { kind, .. } => kind.clone(),
12450            other => panic!("expected import, got {other:?}"),
12451        };
12452        let local = match kind {
12453            ImportKind::Namespace { local_name } => local_name,
12454            other => panic!("expected Namespace, got {other:?}"),
12455        };
12456        assert_eq!(local.name, "uuid");
12457    }
12458
12459    #[test]
12460    fn parse_wildcard_namespace_import_synonym() {
12461        let (ast, diags) = parse_str("import * as uuid from \"submilli:uuid\";");
12462        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12463        let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12464            StmtKind::Import { kind, .. } => kind.clone(),
12465            other => panic!("expected import, got {other:?}"),
12466        };
12467        let local = match kind {
12468            ImportKind::Namespace { local_name } => local_name,
12469            other => panic!("expected Namespace, got {other:?}"),
12470        };
12471        assert_eq!(local.name, "uuid");
12472    }
12473
12474    #[test]
12475    fn parse_wildcard_namespace_import_without_as_rejected() {
12476        let (_ast, diags) = parse_str("import * from \"submilli:uuid\";");
12477        assert!(
12478            diags
12479                .iter()
12480                .any(|d| d.message.contains("expected `as <name>`")),
12481            "expected missing-as diagnostic, got: {diags:?}",
12482        );
12483    }
12484
12485    #[test]
12486    fn parse_combined_default_and_named_rejected() {
12487        let (_ast, diags) = parse_str("import uuid, { v4 } from \"submilli:uuid\";");
12488        assert!(
12489            diags
12490                .iter()
12491                .any(|d| d.message.contains("combining default and named")),
12492            "expected combined-import diagnostic, got: {diags:?}",
12493        );
12494    }
12495
12496    #[test]
12497    fn parse_side_effect_import_rejected() {
12498        let (_ast, diags) = parse_str("import \"submilli:uuid\";");
12499        assert!(
12500            diags
12501                .iter()
12502                .any(|d| d.message.contains("side-effect-only imports")),
12503            "expected side-effect-import diagnostic, got: {diags:?}",
12504        );
12505    }
12506
12507    #[test]
12508    fn parse_empty_specifier_list_rejected() {
12509        let (_ast, diags) = parse_str("import {} from \"submilli:uuid\";");
12510        assert!(
12511            diags
12512                .iter()
12513                .any(|d| d.message.contains("empty import specifier list")),
12514            "expected empty-specifier diagnostic, got: {diags:?}",
12515        );
12516    }
12517
12518    #[test]
12519    fn parse_import_inside_function_body_rejected() {
12520        let (_ast, diags) =
12521            parse_str("function main(): void { import { v4 } from \"submilli:uuid\"; }");
12522        assert!(
12523            diags
12524                .iter()
12525                .any(|d| d.message.contains("must appear at the top of the file")),
12526            "expected top-level diagnostic, got: {diags:?}",
12527        );
12528    }
12529
12530    #[test]
12531    fn parse_missing_from_rejected() {
12532        let (_ast, diags) = parse_str("import { v4 } \"submilli:uuid\";");
12533        assert!(
12534            diags.iter().any(|d| d.message.contains("expected `from`")),
12535            "expected missing-from diagnostic, got: {diags:?}",
12536        );
12537    }
12538
12539    #[test]
12540    fn parse_missing_module_specifier_rejected() {
12541        let (_ast, diags) = parse_str("import { v4 } from;");
12542        assert!(
12543            diags
12544                .iter()
12545                .any(|d| d.message.contains("expected module specifier")),
12546            "expected missing-module diagnostic, got: {diags:?}",
12547        );
12548    }
12549
12550    #[test]
12551    fn parse_export_function_marks_exported() {
12552        // `export function main() {}` parses to a single function declaration in
12553        // `top_level`, plus an `exported_decls` entry pointing at it.
12554        let (ast, diags) = parse_str("export function main(): void {}");
12555        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12556        let stmt = single_stmt(&ast);
12557        assert!(
12558            matches!(stmt.kind, crate::StmtKind::Function { .. }),
12559            "expected Function decl, got {:?}",
12560            stmt.kind,
12561        );
12562        assert_eq!(ast.exported_decls.len(), 1, "one exported decl");
12563        assert_eq!(ast.exported_decls[0].stmt, ast.top_level[0]);
12564    }
12565
12566    #[test]
12567    fn parse_export_const_type_interface_enum_are_marked() {
12568        for src in [
12569            "export const X: number = 1;",
12570            "export type Id = string;",
12571            "export interface P { x: number; }",
12572            "export enum E { A, B }",
12573        ] {
12574            let (ast, diags) = parse_str(src);
12575            assert!(diags.is_empty(), "unexpected diags for {src:?}: {diags:?}");
12576            assert_eq!(ast.top_level.len(), 1, "one decl for {src:?}");
12577            assert_eq!(ast.exported_decls.len(), 1, "one exported decl for {src:?}");
12578        }
12579    }
12580
12581    #[test]
12582    fn parse_export_default_is_rejected_with_focused_message() {
12583        let (_ast, diags) = parse_str("export default function main(): void {}");
12584        assert!(
12585            diags
12586                .iter()
12587                .any(|d| d.message.contains("`export default` is not supported")),
12588            "expected focused export-default diagnostic, got: {diags:?}",
12589        );
12590        assert!(
12591            !diags.iter().any(|d| d.message.contains("expected `;`")),
12592            "the spurious missing-semicolon error must be gone: {diags:?}",
12593        );
12594    }
12595
12596    #[test]
12597    fn parse_export_list_parses_as_export_from() {
12598        // Form 2 (`export { x };`, no `from`) now parses cleanly — the
12599        // single-file gating happens in the typechecker, not the parser.
12600        let (ast, diags) = parse_str("function main(): void {}\nexport { main };");
12601        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12602        assert_eq!(ast.top_level.len(), 2, "function + export-from");
12603        let export = ast.try_stmt(ast.top_level[1]).unwrap();
12604        let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12605            panic!("expected ExportFrom, got {:?}", export.kind);
12606        };
12607        assert!(source.is_none(), "bare `export {{ x }}` has no source");
12608        assert_eq!(specs.len(), 1);
12609        assert_eq!(specs[0].imported_name.name, "main");
12610        assert_eq!(specs[0].local_name.name, "main");
12611    }
12612
12613    #[test]
12614    fn parse_export_from_with_source_and_alias_parses() {
12615        let (ast, diags) = parse_str("export { foo, bar as baz } from \"./util\";");
12616        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12617        let export = ast.try_stmt(ast.top_level[0]).unwrap();
12618        let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12619            panic!("expected ExportFrom, got {:?}", export.kind);
12620        };
12621        let Some((module, _)) = source else {
12622            panic!("expected a `from` source");
12623        };
12624        assert_eq!(module, "./util");
12625        assert_eq!(specs.len(), 2);
12626        assert_eq!(specs[1].imported_name.name, "bar");
12627        assert_eq!(specs[1].local_name.name, "baz");
12628    }
12629
12630    #[test]
12631    fn parse_export_inside_function_body_rejected() {
12632        let (_ast, diags) = parse_str("function main(): void { export const X: number = 1; }");
12633        assert!(
12634            diags
12635                .iter()
12636                .any(|d| d.message.contains("must appear at the top of the file")),
12637            "expected top-level diagnostic, got: {diags:?}",
12638        );
12639    }
12640
12641    #[test]
12642    fn parse_throw_with_expression() {
12643        let (ast, diags) = parse_str(r#"throw new Error("oops");"#);
12644        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12645        let stmt = single_stmt(&ast);
12646        let crate::StmtKind::Throw { value } = stmt.kind else {
12647            panic!("expected Throw, got {:?}", stmt.kind);
12648        };
12649        assert!(matches!(
12650            ast.try_expr(value).unwrap().kind,
12651            crate::ExprKind::New { .. }
12652        ));
12653    }
12654
12655    #[test]
12656    fn parse_new_with_dotted_callee() {
12657        let (ast, diags) = parse_str("new Temporal.Duration({});");
12658        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12659        let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12660            panic!("expected expression statement");
12661        };
12662        let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12663            panic!("expected New");
12664        };
12665        let crate::ExprKind::FieldAccess { name, .. } = &ast.try_expr(*callee).unwrap().kind else {
12666            panic!("expected FieldAccess callee");
12667        };
12668        assert_eq!(name.name, "Duration");
12669    }
12670
12671    #[test]
12672    fn parse_new_with_three_level_dotted_callee() {
12673        let (ast, diags) = parse_str("new Foo.Bar.Baz();");
12674        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12675        let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12676            panic!("expected expression statement");
12677        };
12678        let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12679            panic!("expected New");
12680        };
12681        let crate::ExprKind::FieldAccess { receiver, name } = &ast.try_expr(*callee).unwrap().kind
12682        else {
12683            panic!("expected outer FieldAccess");
12684        };
12685        assert_eq!(name.name, "Baz");
12686        let crate::ExprKind::FieldAccess { name: mid_name, .. } =
12687            &ast.try_expr(*receiver).unwrap().kind
12688        else {
12689            panic!("expected inner FieldAccess");
12690        };
12691        assert_eq!(mid_name.name, "Bar");
12692    }
12693
12694    #[test]
12695    fn parse_new_with_trailing_dot_diagnoses() {
12696        let (_ast, diags) = parse_str("new Foo.;");
12697        assert!(
12698            diags.iter().any(|d| d
12699                .message
12700                .contains("expected identifier after `.` in constructor name")),
12701            "expected trailing-dot diagnostic, got: {diags:?}",
12702        );
12703    }
12704
12705    #[test]
12706    fn throw_without_expression_diagnoses() {
12707        let (_ast, diags) = parse_str("throw;");
12708        assert!(
12709            diags
12710                .iter()
12711                .any(|d| d.message == "expected expression after `throw`"),
12712            "expected bare-throw diagnostic, got: {diags:?}",
12713        );
12714    }
12715
12716    #[test]
12717    fn parse_try_catch() {
12718        let (ast, diags) = parse_str("try { } catch (e: Error) { }");
12719        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12720        let stmt = single_stmt(&ast);
12721        let crate::StmtKind::Try {
12722            body,
12723            ref catches,
12724            ref finally,
12725        } = stmt.kind
12726        else {
12727            panic!("expected Try, got {:?}", stmt.kind);
12728        };
12729        assert!(matches!(
12730            ast.try_stmt(body).unwrap().kind,
12731            crate::StmtKind::Block(ref v) if v.is_empty()
12732        ));
12733        let [clause] = catches.as_slice() else {
12734            panic!("expected one catch clause, got {catches:?}");
12735        };
12736        assert_eq!(clause.binding.name, "e");
12737        assert!(clause.ty.is_some());
12738        assert!(finally.is_none());
12739    }
12740
12741    #[test]
12742    fn parse_try_finally() {
12743        let (ast, diags) = parse_str("try { } finally { }");
12744        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12745        let stmt = single_stmt(&ast);
12746        let crate::StmtKind::Try {
12747            ref catches,
12748            ref finally,
12749            ..
12750        } = stmt.kind
12751        else {
12752            panic!("expected Try");
12753        };
12754        assert!(catches.is_empty());
12755        let finally_id = finally.expect("finally block");
12756        assert!(matches!(
12757            ast.try_stmt(finally_id).unwrap().kind,
12758            crate::StmtKind::Block(_)
12759        ));
12760    }
12761
12762    #[test]
12763    fn parse_try_catch_finally() {
12764        let (ast, diags) = parse_str("try { } catch (e: Error) { } finally { }");
12765        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12766        let stmt = single_stmt(&ast);
12767        let crate::StmtKind::Try {
12768            ref catches,
12769            ref finally,
12770            ..
12771        } = stmt.kind
12772        else {
12773            panic!("expected Try");
12774        };
12775        assert_eq!(catches.len(), 1);
12776        assert!(finally.is_some());
12777    }
12778
12779    #[test]
12780    fn bare_try_diagnoses() {
12781        let (_ast, diags) = parse_str("try { }");
12782        assert!(
12783            diags
12784                .iter()
12785                .any(|d| d.message == "try requires at least one of `catch` or `finally`"),
12786            "expected bare-try diagnostic, got: {diags:?}",
12787        );
12788    }
12789
12790    #[test]
12791    fn parse_untyped_catch() {
12792        let (ast, diags) = parse_str("try { } catch (e) { }");
12793        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12794        let stmt = single_stmt(&ast);
12795        let crate::StmtKind::Try {
12796            ref catches,
12797            ref finally,
12798            ..
12799        } = stmt.kind
12800        else {
12801            panic!("expected Try");
12802        };
12803        let [clause] = catches.as_slice() else {
12804            panic!("expected one catch clause, got {catches:?}");
12805        };
12806        assert_eq!(clause.binding.name, "e");
12807        assert!(clause.ty.is_none());
12808        assert!(finally.is_none());
12809    }
12810
12811    #[test]
12812    fn parse_multiple_catch_clauses() {
12813        let (ast, diags) = parse_str("try { } catch (e: HttpError) { } catch (f) { }");
12814        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12815        let stmt = single_stmt(&ast);
12816        let crate::StmtKind::Try {
12817            ref catches,
12818            ref finally,
12819            ..
12820        } = stmt.kind
12821        else {
12822            panic!("expected Try");
12823        };
12824        let [first, second] = catches.as_slice() else {
12825            panic!("expected two catch clauses, got {catches:?}");
12826        };
12827        assert_eq!(first.binding.name, "e");
12828        assert!(first.ty.is_some());
12829        assert_eq!(second.binding.name, "f");
12830        assert!(second.ty.is_none());
12831        assert!(finally.is_none());
12832    }
12833
12834    #[test]
12835    fn catch_after_finally_diagnoses() {
12836        let (_ast, diags) = parse_str("try { } finally { } catch (e: Error) { }");
12837        assert!(
12838            !diags.is_empty(),
12839            "expected diagnostic for catch-after-finally"
12840        );
12841    }
12842
12843    #[test]
12844    fn catch_missing_open_paren_diagnoses() {
12845        let (_ast, diags) = parse_str("try { } catch e: Error) { }");
12846        assert!(
12847            diags
12848                .iter()
12849                .any(|d| d.message == "expected `(` after `catch`"),
12850            "expected open-paren diagnostic, got: {diags:?}",
12851        );
12852    }
12853
12854    fn class_members(ast: &Ast) -> &[crate::ClassMember] {
12855        match &single_stmt(ast).kind {
12856            StmtKind::ClassDecl { members, .. } => members,
12857            other => panic!("expected ClassDecl, got {other:?}"),
12858        }
12859    }
12860
12861    #[test]
12862    fn parse_animal_class() {
12863        let src = r#"
12864class Animal {
12865  name: string;
12866  private sound: string;
12867  readonly species: string;
12868
12869  constructor(name: string, sound: string, species: string) {
12870    this.name = name;
12871    this.sound = sound;
12872    this.species = species;
12873  }
12874
12875  speak(): string {
12876    return this.name + " says " + this.sound;
12877  }
12878}
12879"#;
12880        let (ast, diags) = parse_str(src);
12881        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12882        let StmtKind::ClassDecl {
12883            name,
12884            generics,
12885            extends,
12886            implements,
12887            members,
12888            ..
12889        } = &single_stmt(&ast).kind
12890        else {
12891            panic!("expected ClassDecl");
12892        };
12893        assert_eq!(name.name, "Animal");
12894        assert!(generics.is_empty());
12895        assert!(extends.is_none());
12896        assert!(implements.is_empty());
12897        assert_eq!(members.len(), 5);
12898
12899        let crate::ClassMember::Field {
12900            name, modifiers, ..
12901        } = &members[0]
12902        else {
12903            panic!("member 0 should be a field");
12904        };
12905        assert_eq!(name.name, "name");
12906        assert_eq!(modifiers.visibility, crate::Visibility::Public);
12907        assert!(modifiers.readonly.is_none());
12908
12909        let crate::ClassMember::Field { modifiers, .. } = &members[1] else {
12910            panic!("member 1 should be a field");
12911        };
12912        assert_eq!(modifiers.visibility, crate::Visibility::Private);
12913
12914        let crate::ClassMember::Field { modifiers, .. } = &members[2] else {
12915            panic!("member 2 should be a field");
12916        };
12917        assert!(modifiers.readonly.is_some());
12918        assert_eq!(modifiers.visibility, crate::Visibility::Public);
12919
12920        assert!(matches!(
12921            &members[3],
12922            crate::ClassMember::Constructor { .. }
12923        ));
12924
12925        let crate::ClassMember::Method { name, .. } = &members[4] else {
12926            panic!("member 4 should be a method");
12927        };
12928        assert_eq!(name.name, "speak");
12929    }
12930
12931    #[test]
12932    fn parse_dog_class_extends_super_and_void_method() {
12933        let src = r#"
12934class Dog extends Animal {
12935  private tricks: string[];
12936
12937  constructor(name: string) {
12938    super(name, "woof", "canis familiaris");
12939    this.tricks = [];
12940  }
12941
12942  learn(trick: string): void {
12943    this.tricks.push(trick);
12944  }
12945}
12946"#;
12947        let (ast, diags) = parse_str(src);
12948        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12949        let StmtKind::ClassDecl {
12950            extends, members, ..
12951        } = &single_stmt(&ast).kind
12952        else {
12953            panic!("expected ClassDecl");
12954        };
12955        assert!(extends.is_some(), "Dog should extend Animal");
12956        assert_eq!(members.len(), 3);
12957        assert!(matches!(&members[0], crate::ClassMember::Field { .. }));
12958        assert!(matches!(
12959            &members[1],
12960            crate::ClassMember::Constructor { .. }
12961        ));
12962        assert!(matches!(&members[2], crate::ClassMember::Method { .. }));
12963    }
12964
12965    #[test]
12966    fn parse_class_implements_list() {
12967        let (ast, diags) = parse_str("class C implements I, J { run(): void {} }");
12968        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12969        let StmtKind::ClassDecl { implements, .. } = &single_stmt(&ast).kind else {
12970            panic!("expected ClassDecl");
12971        };
12972        assert_eq!(implements.len(), 2);
12973    }
12974
12975    #[test]
12976    fn parse_class_extends_and_implements() {
12977        let (ast, diags) = parse_str("class C extends B implements I { run(): void {} }");
12978        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12979        let StmtKind::ClassDecl {
12980            extends,
12981            implements,
12982            ..
12983        } = &single_stmt(&ast).kind
12984        else {
12985            panic!("expected ClassDecl");
12986        };
12987        assert!(extends.is_some());
12988        assert_eq!(implements.len(), 1);
12989    }
12990
12991    #[test]
12992    fn parse_class_generics_optional_field_and_initializer() {
12993        let (ast, diags) = parse_str("class Box<T> { value?: T; items: T[] = []; }");
12994        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12995        let StmtKind::ClassDecl {
12996            generics, members, ..
12997        } = &single_stmt(&ast).kind
12998        else {
12999            panic!("expected ClassDecl");
13000        };
13001        assert_eq!(generics.len(), 1);
13002        assert_eq!(generics[0].name, "T");
13003
13004        let crate::ClassMember::Field {
13005            optional,
13006            initializer,
13007            ..
13008        } = &members[0]
13009        else {
13010            panic!("member 0 should be a field");
13011        };
13012        assert!(*optional);
13013        assert!(initializer.is_none());
13014
13015        let crate::ClassMember::Field { initializer, .. } = &members[1] else {
13016            panic!("member 1 should be a field");
13017        };
13018        assert!(initializer.is_some());
13019    }
13020
13021    #[test]
13022    fn export_class_is_marked() {
13023        let (ast, diags) = parse_str("export class Foo {}");
13024        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13025        assert_eq!(ast.top_level.len(), 1);
13026        assert_eq!(ast.exported_decls.len(), 1);
13027        assert!(matches!(
13028            &single_stmt(&ast).kind,
13029            StmtKind::ClassDecl { .. }
13030        ));
13031    }
13032
13033    #[test]
13034    fn parameterless_index_signature_is_reported_from_its_member_start() {
13035        for (source, expected) in [
13036            ("interface P { readonly []: number }", "readonly []"),
13037            ("let p: { readonly []: number } = {};", "readonly []"),
13038            ("let q: { []: number } = {};", "[]"),
13039        ] {
13040            let (_ast, diags) = parse_str(source);
13041            let diag = diags
13042                .iter()
13043                .find(|d| d.message.contains("must declare exactly one parameter"))
13044                .unwrap_or_else(|| panic!("no diagnostic for {source}"));
13045            let reported = &source[diag.span.start as usize..diag.span.end as usize];
13046            assert_eq!(reported, expected, "{source}");
13047        }
13048    }
13049
13050    #[test]
13051    fn unterminated_object_type_expects_closing_brace() {
13052        let (_ast, diags) = parse_str("let p: { a: number\n");
13053        assert!(
13054            diags.iter().any(|d| d.message == "expected `}`"),
13055            "{diags:?}"
13056        );
13057    }
13058
13059    #[test]
13060    fn conflicting_visibility_modifiers_diagnose() {
13061        let (_ast, diags) = parse_str("class C { public private x: number; }");
13062        assert!(
13063            diags
13064                .iter()
13065                .any(|d| d.message.contains("at most one visibility modifier")),
13066            "got: {diags:?}",
13067        );
13068    }
13069
13070    #[test]
13071    fn duplicate_readonly_modifier_diagnoses() {
13072        let (_ast, diags) = parse_str("class C { readonly readonly x: number; }");
13073        assert!(
13074            diags
13075                .iter()
13076                .any(|d| d.message.contains("duplicate `readonly`")),
13077            "got: {diags:?}",
13078        );
13079    }
13080
13081    #[test]
13082    fn field_named_like_a_modifier_is_allowed() {
13083        let (ast, diags) = parse_str("class C { private: number; }");
13084        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13085        let crate::ClassMember::Field {
13086            name, modifiers, ..
13087        } = &class_members(&ast)[0]
13088        else {
13089            panic!("expected a field");
13090        };
13091        assert_eq!(name.name, "private");
13092        assert_eq!(modifiers.visibility, crate::Visibility::Public);
13093    }
13094
13095    #[test]
13096    fn method_named_get_is_allowed() {
13097        let (ast, diags) = parse_str("class C { get(): number { return 1; } }");
13098        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13099        let crate::ClassMember::Method { name, .. } = &class_members(&ast)[0] else {
13100            panic!("expected a method");
13101        };
13102        assert_eq!(name.name, "get");
13103    }
13104
13105    #[test]
13106    fn static_method_parses() {
13107        let (ast, diags) = parse_str("class C { static f(): number { return 1; } }");
13108        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13109        let crate::ClassMember::Method {
13110            name, modifiers, ..
13111        } = &class_members(&ast)[0]
13112        else {
13113            panic!("expected a method");
13114        };
13115        assert_eq!(name.name, "f");
13116        assert!(modifiers.static_span.is_some());
13117    }
13118
13119    #[test]
13120    fn static_readonly_field_parses() {
13121        let (ast, diags) = parse_str("class C { static readonly x: number = 1; }");
13122        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13123        let crate::ClassMember::Field {
13124            name,
13125            modifiers,
13126            initializer,
13127            ..
13128        } = &class_members(&ast)[0]
13129        else {
13130            panic!("expected a field");
13131        };
13132        assert_eq!(name.name, "x");
13133        assert!(modifiers.static_span.is_some());
13134        assert!(modifiers.readonly.is_some());
13135        assert!(initializer.is_some());
13136    }
13137
13138    #[test]
13139    fn private_static_method_parses() {
13140        let (ast, diags) = parse_str("class C { private static f(): void {} }");
13141        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13142        let crate::ClassMember::Method { modifiers, .. } = &class_members(&ast)[0] else {
13143            panic!("expected a method");
13144        };
13145        assert_eq!(modifiers.visibility, crate::Visibility::Private);
13146        assert!(modifiers.static_span.is_some());
13147    }
13148
13149    #[test]
13150    fn method_named_static_is_allowed() {
13151        let (ast, diags) = parse_str("class C { static(): number { return 1; } }");
13152        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13153        let crate::ClassMember::Method {
13154            name, modifiers, ..
13155        } = &class_members(&ast)[0]
13156        else {
13157            panic!("expected a method");
13158        };
13159        assert_eq!(name.name, "static");
13160        assert!(modifiers.static_span.is_none());
13161    }
13162
13163    #[test]
13164    fn visibility_after_static_diagnoses() {
13165        let (_ast, diags) = parse_str("class C { static private f(): void {} }");
13166        assert!(
13167            diags
13168                .iter()
13169                .any(|d| d.message.contains("`private` must come before `static`")),
13170            "got: {diags:?}",
13171        );
13172    }
13173
13174    #[test]
13175    fn duplicate_static_modifier_diagnoses() {
13176        let (_ast, diags) = parse_str("class C { static static f(): void {} }");
13177        assert!(
13178            diags
13179                .iter()
13180                .any(|d| d.message.contains("duplicate `static` modifier")),
13181            "got: {diags:?}",
13182        );
13183    }
13184
13185    #[test]
13186    fn static_accessor_is_rejected() {
13187        let (_ast, diags) = parse_str("class C { static get x(): number { return 1; } }");
13188        assert!(
13189            diags
13190                .iter()
13191                .any(|d| d.message.contains("static accessors are not supported")),
13192            "got: {diags:?}",
13193        );
13194    }
13195
13196    #[test]
13197    fn static_constructor_is_rejected() {
13198        let (_ast, diags) = parse_str("class C { static constructor() {} }");
13199        assert!(
13200            diags
13201                .iter()
13202                .any(|d| d.message.contains("a constructor cannot be `static`")),
13203            "got: {diags:?}",
13204        );
13205    }
13206
13207    #[test]
13208    fn optional_static_field_is_rejected() {
13209        let (_ast, diags) = parse_str("class C { static x?: number; }");
13210        assert!(
13211            diags
13212                .iter()
13213                .any(|d| d.message.contains("a static field cannot be optional")),
13214            "got: {diags:?}",
13215        );
13216    }
13217
13218    #[test]
13219    fn async_function_names_the_synchronous_rewrite() {
13220        let (_ast, diags) = parse_str(
13221            "async function main(): Promise<string> {\n  const x = await f();\n  return x;\n}",
13222        );
13223        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13224        assert_eq!(
13225            messages,
13226            [
13227                "`async` functions are not supported",
13228                "`await` is not supported"
13229            ],
13230            "{diags:?}"
13231        );
13232        assert_eq!(
13233            diags[0].help,
13234            [
13235                "calls return their value directly: remove `async`, write the return type `T` \
13236              instead of `Promise<T>`, and drop `await`"
13237            ]
13238        );
13239        assert_eq!(
13240            diags[1].help,
13241            ["calls return their value directly: remove `await`"]
13242        );
13243    }
13244
13245    #[test]
13246    fn async_is_rejected_on_every_function_form() {
13247        for source in [
13248            "export async function f(): void {}",
13249            "const f = async (): void => {};",
13250            "const f = async x => x;",
13251            "const f = async function (): void {};",
13252            "class C { async run(): void {} }",
13253            "class C { static async run(): void {} }",
13254        ] {
13255            let (_ast, diags) = parse_str(source);
13256            assert_eq!(
13257                diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13258                ["`async` functions are not supported"],
13259                "{source}: {diags:?}"
13260            );
13261        }
13262    }
13263
13264    #[test]
13265    fn await_before_a_template_is_reported() {
13266        for source in ["const a = await `hi`;", "const a = await `a${1}b`;"] {
13267            let (_ast, diags) = parse_str(source);
13268            let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13269            assert_eq!(
13270                messages,
13271                ["`await` is not supported"],
13272                "{source}: {diags:?}"
13273            );
13274        }
13275    }
13276
13277    #[test]
13278    fn async_and_await_stay_ordinary_names() {
13279        for source in [
13280            "const async = 1; const y = async + 1;",
13281            "function async(n: number): number { return n; } async(1);",
13282            "const await = 2; const z = await * 2;",
13283            "const await = (n: number): number => n; const a = await(1);",
13284            "const await = [1, 2]; const b = await[0];",
13285            "const async = { k: 1 }; const c = \"k\" in async; const d = async in ({ o: 1 });",
13286            "const async: unknown = 1; const n = async as (number);",
13287            "const await: unknown = 1; const m = await as number;",
13288            "const await = 1; const t = `${await}`;",
13289            "class C { async(): void {} async: number = 1; }",
13290        ] {
13291            let (_ast, diags) = parse_str(source);
13292            assert!(diags.is_empty(), "{source}: {diags:?}");
13293        }
13294    }
13295
13296    #[test]
13297    fn an_async_object_literal_method_names_the_rewrite_once() {
13298        assert_eq!(
13299            crate::typechecker::rules::test_util::compile_messages(
13300                "function main(): string {\n  const o = { async run(): string { return \"a\"; } };\n  return o.run();\n}\n"
13301            ),
13302            ["`async` functions are not supported"]
13303        );
13304        let (_ast, diags) = parse_str("const o = { async run(): string { return \"a\"; } };");
13305        assert_eq!(
13306            diags[0].help,
13307            [
13308                "calls return their value directly: remove `async`, write the return type `T` \
13309              instead of `Promise<T>`, and drop `await`"
13310            ]
13311        );
13312    }
13313
13314    #[test]
13315    fn await_in_an_async_object_literal_method_is_reported_too() {
13316        let (_ast, diags) =
13317            parse_str("const o = { async run(): string { const x = await f(); return x; } };");
13318        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13319        assert_eq!(
13320            messages,
13321            [
13322                "`async` functions are not supported",
13323                "`await` is not supported"
13324            ],
13325            "{diags:?}"
13326        );
13327    }
13328
13329    #[test]
13330    fn every_async_member_form_reports_async() {
13331        for source in [
13332            "const o = { async *run(): void {} };",
13333            "class C { async *run(): void {} }",
13334            "const o = { async [k](): void {} };",
13335            "const o = { async in(): void {} };",
13336            "class C { async as(): void {} }",
13337        ] {
13338            let (_ast, diags) = parse_str(source);
13339            assert_eq!(
13340                diags[0].message, "`async` functions are not supported",
13341                "{source}: {diags:?}"
13342            );
13343        }
13344    }
13345
13346    #[test]
13347    fn async_string_key_methods_report_only_async() {
13348        for source in [
13349            "const o = { async \"s\"(): string { return \"a\"; } };",
13350            "class C { async \"s\"(): string { return \"a\"; } }",
13351        ] {
13352            let (_ast, diags) = parse_str(source);
13353            assert_eq!(
13354                diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13355                ["`async` functions are not supported"],
13356                "{source}: {diags:?}"
13357            );
13358        }
13359    }
13360
13361    #[test]
13362    fn async_before_a_line_break_is_a_member_of_its_own() {
13363        for source in [
13364            "class C {\n  async\n  run(): void {}\n}",
13365            "const o = { async\n run(): void {} };",
13366        ] {
13367            let (_ast, diags) = parse_str(source);
13368            assert!(
13369                diags
13370                    .iter()
13371                    .all(|d| d.message != "`async` functions are not supported"),
13372                "{source}: {diags:?}"
13373            );
13374        }
13375    }
13376
13377    #[test]
13378    fn async_stays_an_object_literal_key() {
13379        let messages = crate::typechecker::rules::test_util::compile_messages(
13380            "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",
13381        );
13382        assert!(messages.is_empty(), "{messages:?}");
13383    }
13384
13385    #[test]
13386    fn for_await_names_the_rewrite_once() {
13387        assert_eq!(
13388            crate::typechecker::rules::test_util::compile_messages(
13389                "import * as fs from \"submilli:fs\";\nfunction main(): string {\n  for await (const e of fs.list(\".\", { recursive: false })) { }\n  return \"x\";\n}\n"
13390            ),
13391            ["`for await` is not supported"]
13392        );
13393        let (ast, diags) = parse_str("for await (const e of [1]) { }");
13394        assert_eq!(
13395            diags[0].help,
13396            ["calls return their values directly: remove `await` and write `for (const e of …)`"]
13397        );
13398        assert_eq!(ast.top_level.len(), 1, "the loop is still read");
13399    }
13400
13401    #[test]
13402    fn a_broken_switch_leaves_later_declarations_at_the_top_level() {
13403        assert_eq!(
13404            crate::typechecker::rules::test_util::compile_messages(
13405                "function g(): void { switch (1) { case 1 } }\nclass C {}\nfunction main() { return \"x\"; }\n"
13406            ),
13407            [
13408                "expected `:` after `case` label",
13409                "expected expression",
13410                "expected `:` and return type"
13411            ]
13412        );
13413    }
13414
13415    #[test]
13416    fn every_switch_arm_error_restores_the_nesting() {
13417        for switch in [
13418            "switch (1) { case 1 }",
13419            "switch (1) { case : break; }",
13420            "switch (1) { default: break; default: break; }",
13421            "switch (1) { default }",
13422            "switch (1) { x; }",
13423            "switch (1) { case 1: }",
13424        ] {
13425            let source = format!("function g(): void {{ {switch} }}\nclass C {{}}\n");
13426            let (_ast, diags) = parse_str(&source);
13427            assert!(
13428                diags.iter().all(|d| !d.message.contains("top level")),
13429                "{switch}: {diags:?}"
13430            );
13431        }
13432    }
13433
13434    #[test]
13435    fn protected_member_is_rejected() {
13436        let (_ast, diags) = parse_str("class C { protected x: number; }");
13437        assert!(
13438            diags
13439                .iter()
13440                .any(|d| d.message.contains("`protected` is not supported")),
13441            "got: {diags:?}",
13442        );
13443    }
13444
13445    #[test]
13446    fn abstract_member_is_rejected() {
13447        let (_ast, diags) = parse_str("class C { abstract run(): void; }");
13448        assert!(
13449            diags
13450                .iter()
13451                .any(|d| d.message.contains("abstract classes are not supported")),
13452            "got: {diags:?}",
13453        );
13454    }
13455
13456    #[test]
13457    fn getter_parses_as_accessor() {
13458        let (ast, diags) = parse_str("class C { get value(): number { return 1; } }");
13459        assert!(diags.is_empty(), "got: {diags:?}");
13460        let crate::ClassMember::Accessor { kind, name, .. } = &class_members(&ast)[0] else {
13461            panic!("expected an accessor member");
13462        };
13463        assert_eq!(*kind, crate::AccessorKind::Get);
13464        assert_eq!(name.name, "value");
13465    }
13466
13467    #[test]
13468    fn getter_without_return_type_is_rejected() {
13469        let (ast, diags) = parse_str("class C { get value() { return 1; } }");
13470        assert_eq!(diags.len(), 1, "got: {diags:?}");
13471        assert_eq!(diags[0].message, "expected `:` and return type");
13472        assert_eq!(diags[0].help, vec!["get name(): T { … }".to_string()]);
13473        assert!(
13474            matches!(
13475                &class_members(&ast)[0],
13476                crate::ClassMember::Accessor {
13477                    return_type: None,
13478                    ..
13479                }
13480            ),
13481            "the getter is still recovered as a member"
13482        );
13483    }
13484
13485    #[test]
13486    fn setter_parses_as_accessor() {
13487        let (ast, diags) = parse_str("class C { set value(v: number) {} }");
13488        assert!(diags.is_empty(), "got: {diags:?}");
13489        let crate::ClassMember::Accessor { kind, param, .. } = &class_members(&ast)[0] else {
13490            panic!("expected an accessor member");
13491        };
13492        assert_eq!(*kind, crate::AccessorKind::Set);
13493        assert!(param.is_some(), "setter should have a parameter");
13494    }
13495
13496    #[test]
13497    fn param_property_modifier_outside_constructor_is_rejected() {
13498        let (_ast, diags) = parse_str("function f(public x: number): void {}");
13499        assert!(
13500            diags.iter().any(|d| d
13501                .message
13502                .contains("parameter properties are only allowed in a constructor")),
13503            "got: {diags:?}",
13504        );
13505    }
13506
13507    #[test]
13508    fn constructor_return_type_is_rejected() {
13509        let (_ast, diags) = parse_str("class C { constructor(): void {} }");
13510        assert!(
13511            diags.iter().any(|d| d
13512                .message
13513                .contains("constructor cannot declare a return type")),
13514            "got: {diags:?}",
13515        );
13516    }
13517
13518    #[test]
13519    fn field_initializer_without_type_is_rejected() {
13520        let (_ast, diags) = parse_str("class C { x = 1; }");
13521        assert!(
13522            diags
13523                .iter()
13524                .any(|d| d.message.contains("class fields require a type annotation")),
13525            "got: {diags:?}",
13526        );
13527    }
13528
13529    #[test]
13530    fn this_outside_method_is_left_to_typechecker() {
13531        let (_ast, diags) = parse_str("function f(): string { return this.name; }");
13532        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13533    }
13534
13535    #[test]
13536    fn super_outside_method_is_rejected() {
13537        let (_ast, diags) = parse_str("function f(): void { super(1); }");
13538        assert!(
13539            diags.iter().any(|d| d
13540                .message
13541                .contains("`super` is only valid inside a class method or constructor body")),
13542            "got: {diags:?}",
13543        );
13544    }
13545
13546    #[test]
13547    fn this_inside_method_is_accepted_by_parser() {
13548        let (_ast, diags) = parse_str("class C { x: number; read(): number { return this.x; } }");
13549        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13550    }
13551    #[test]
13552    fn parse_void_expression_preserves_operand_and_precedence() {
13553        let (ast, diags) = parse_str("void effect() === undefined;");
13554        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13555        let expression = expr_of_single_stmt(&ast);
13556        let crate::ExprKind::Binary { lhs, rhs, .. } = expression.kind else {
13557            panic!("expected equality")
13558        };
13559        let crate::ExprKind::Void { operand } = ast.try_expr(lhs).unwrap().kind else {
13560            panic!("expected void")
13561        };
13562        assert!(matches!(
13563            ast.try_expr(operand).unwrap().kind,
13564            crate::ExprKind::Call { .. }
13565        ));
13566        assert!(matches!(
13567            ast.try_expr(rhs).unwrap().kind,
13568            crate::ExprKind::Identifier(_)
13569        ));
13570    }
13571
13572    #[test]
13573    fn parse_void_newline_and_object_operands() {
13574        for source in [
13575            "void\n0;",
13576            "void { value: 1 };",
13577            "void\n{ value: 1 };",
13578            "void /x/;",
13579            "void void 0;",
13580        ] {
13581            let (_, diags) = parse_str(source);
13582            assert!(diags.is_empty(), "{source}: {diags:?}");
13583        }
13584        let (_, diags) = parse_str("void; 0;");
13585        assert!(
13586            !diags.is_empty(),
13587            "an explicit semicolon cannot split void from its operand"
13588        );
13589    }
13590
13591    #[test]
13592    fn parse_optional_parameters_and_defaults_in_callable_forms() {
13593        for source in [
13594            "function f(x = 1): number { return x; }",
13595            "function f(x: number = 1, y: number): number { return y; }",
13596            "function f({ x } = { x: 1 }): number { return x; }",
13597            "const f = (x?: number) => x;",
13598            "const f = (x: number = effect()) => x;",
13599            "const f = function(x: number = effect()) { return x; };",
13600            "const f = { m(x: number = effect()): number { return x; } };",
13601            "const f = ([x = 1]: number[] = values) => x;",
13602            "let f: (x?: number, ...rest: string[]) => void = value;",
13603            "let f: { method?(x?: number): void } = value;",
13604            "function f(x: number = 1, y?: number): void {}",
13605        ] {
13606            let (_, diags) = parse_str(source);
13607            assert!(diags.is_empty(), "{source}: {diags:?}");
13608        }
13609    }
13610
13611    #[test]
13612    fn parse_invalid_optional_parameters() {
13613        for source in [
13614            "function f(x?: number, y: number): void {}",
13615            "const f = (x?: number, y: number) => y;",
13616            "let f: (x?: number, y: number) => void = value;",
13617            "function f(...x?: number[]): void {}",
13618            "function f(x?: number = 1): void {}",
13619            "const f = (x?: number = 1) => x;",
13620            "function f({x}?: T): void {}",
13621        ] {
13622            let (_, diags) = parse_str(source);
13623            assert!(!diags.is_empty(), "expected diagnostic for {source}");
13624        }
13625    }
13626
13627    #[test]
13628    fn parse_optional_tuple_forms_and_order() {
13629        for source in [
13630            "let x: [number, string?] = value;",
13631            "let x: [x?: number, y?: string] = value;",
13632            "let x: readonly [(number | null)?, string?] = value;",
13633        ] {
13634            let (_, diags) = parse_str(source);
13635            assert!(diags.is_empty(), "{source}: {diags:?}");
13636        }
13637        for source in [
13638            "let x: [number?, string] = value;",
13639            "let x: [x: number?] = value;",
13640        ] {
13641            let (_, diags) = parse_str(source);
13642            assert!(!diags.is_empty(), "expected diagnostic for {source}");
13643        }
13644    }
13645
13646    #[test]
13647    fn parse_array_pattern_defaults_preserve_holes() {
13648        let (ast, diags) = parse_str("const [, a = effect(), b] = values;");
13649        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13650        let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
13651            panic!("expected pattern")
13652        };
13653        let crate::Binding::Array {
13654            elems, defaults, ..
13655        } = binding
13656        else {
13657            panic!("expected array")
13658        };
13659        assert_eq!(elems.len(), 3);
13660        assert_eq!(defaults.len(), elems.len());
13661        assert!(elems[0].is_none());
13662        assert!(defaults[0].is_none());
13663        assert!(defaults[1].is_some());
13664        assert!(defaults[2].is_none());
13665    }
13666
13667    #[test]
13668    fn parse_optional_class_method_forms() {
13669        let (ast, diags) =
13670            parse_str("class C { declared?(x?: number): void; implemented?(): void\n{} }");
13671        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13672        let members = class_members(&ast);
13673        assert!(matches!(
13674            &members[0],
13675            crate::ClassMember::Field {
13676                optional: true,
13677                initializer: None,
13678                ty: crate::TypeAnnotation {
13679                    kind: crate::TypeAnnotationKind::Function { .. },
13680                    ..
13681                },
13682                ..
13683            }
13684        ));
13685        assert!(matches!(
13686            &members[1],
13687            crate::ClassMember::Method { optional: true, .. }
13688        ));
13689    }
13690    #[test]
13691    fn defaults_retain_their_callable_receiver_scope() {
13692        for source in [
13693            "class C { value: number = 1; method(value: number = this.value): number { return value; } }",
13694            "class C { constructor(value: number = this.value) {} value: number = 1; }",
13695            "const f = function(this: { value: number }, value: number = this.value): number { return value; };",
13696        ] {
13697            let (ast, diagnostics) = parse_str(source);
13698            assert!(diagnostics.is_empty(), "{source}: {diagnostics:?}");
13699            assert!(
13700                ast.source_expressions()
13701                    .iter()
13702                    .any(|expr| matches!(expr.kind, ExprKind::This))
13703            );
13704            assert!(
13705                !ast.source_expressions()
13706                    .iter()
13707                    .any(|expr| matches!(expr.kind, ExprKind::ThisOutsideReceiver))
13708            );
13709        }
13710    }
13711}