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        file,
69        pos: 0,
70        ast: Ast::new(),
71        diagnostics: Vec::new(),
72        block_depth: 0,
73        class_member_body_depth: 0,
74        function_expression_body_depth: 0,
75        recursion_depth: 0,
76        recursion_limit_span: None,
77        resume_at_next_member: false,
78        last_as_type_end: None,
79        trailing_declarators: Vec::new(),
80        eof,
81        fatal: None,
82    };
83    p.parse_program();
84    if p.fatal.is_none() {
85        p.reject_use_strict_with_non_simple_params();
86    }
87    if let Some(span) = p.recursion_limit_span {
88        p.fatal = Some(CompilerFailure::Limit {
89            stage: CompilerStage::Parse,
90            span: Some(span),
91            message: "parser recursion limit exceeded".into(),
92            help: vec!["simplify nested syntax or split it into separate declarations".into()],
93        });
94    }
95    if let Some(fatal) = p.fatal {
96        return Err(CompileError {
97            diagnostics: p.diagnostics,
98            fatal: Some(fatal),
99        });
100    }
101    p.ast
102        .validate_source(source, file)
103        .map_err(|error| CompileError {
104            diagnostics: p.diagnostics.clone(),
105            fatal: Some(error.into_compiler_failure(CompilerStage::Parse)),
106        })?;
107    crate::tree_height::check_syntax(&p.ast).map_err(|fatal| CompileError {
108        diagnostics: p.diagnostics.clone(),
109        fatal: Some(fatal),
110    })?;
111    Ok((p.ast, p.diagnostics))
112}
113
114fn validate_token_stream(source: &str, tokens: &[Token], file: FileId) -> Result<(), &'static str> {
115    if !matches!(tokens.last().map(|token| &token.kind), Some(TokenKind::Eof)) {
116        return Err("token stream must end with EOF");
117    }
118    let mut previous = 0;
119    for (index, token) in tokens.iter().enumerate() {
120        let span = token.span;
121        if span.file != file
122            || span.start < previous
123            || span.start > span.end
124            || source.get(span.start as usize..span.end as usize).is_none()
125        {
126            return Err("token span is outside its source or out of order");
127        }
128        if matches!(token.kind, TokenKind::Eof) && index != tokens.len() - 1 {
129            return Err("tokens follow EOF");
130        }
131        if let Some(doc) = &token.leading_doc {
132            let span = doc.span;
133            if span.file != file
134                || span.start > span.end
135                || span.end > token.span.start
136                || source.get(span.start as usize..span.end as usize) != Some(doc.text.as_str())
137                || !doc.text.starts_with("/**")
138                || !doc.text.ends_with("*/")
139            {
140                return Err("documentation metadata does not match its source");
141            }
142        }
143        previous = span.start;
144    }
145    Ok(())
146}
147
148pub(crate) struct Parser<'a> {
149    source: &'a str,
150    tokens: Vec<Token>,
151    file: FileId,
152    pos: usize,
153    ast: Ast,
154    diagnostics: Vec<Diagnostic>,
155    /// Zero at top level; used to reject `import` inside nested blocks.
156    block_depth: u32,
157    /// Nonzero while parsing a class method or constructor body; gates `this`/`super`.
158    class_member_body_depth: u32,
159    function_expression_body_depth: u32,
160    recursion_depth: usize,
161    recursion_limit_span: Option<Span>,
162    /// Set by a class member that reported its error after reading its whole
163    /// body (a method missing its return type): the class loop takes it and
164    /// continues at the next member instead of abandoning the class.
165    resume_at_next_member: bool,
166    /// End offset of the type in the most recent `x as T`. A statement ending there
167    /// may end at a line break, since the type stops at one (see
168    /// [`Parser::at_statement_end`]).
169    last_as_type_end: Option<u32>,
170    /// See [`Parser::take_trailing_declarators`].
171    trailing_declarators: Vec<StmtId>,
172    eof: Token,
173    fatal: Option<CompilerFailure>,
174}
175
176impl<'a> Parser<'a> {
177    fn span(&self, start: u32, end: u32) -> Span {
178        Span {
179            file: self.file,
180            start,
181            end,
182        }
183    }
184
185    fn type_name(&mut self, span: Span) -> Option<Ident> {
186        match span.text(self.source, self.file) {
187            Ok(name) => Some(Ident {
188                name: name.to_string(),
189                span,
190            }),
191            Err(error) => {
192                self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
193                None
194            }
195        }
196    }
197
198    fn parse_program(&mut self) {
199        while !self.is_at_eof() {
200            if self.fatal.is_some() || self.error_count() >= MAX_ERRORS {
201                break;
202            }
203            if matches!(self.peek().kind, TokenKind::Semicolon) {
204                self.advance();
205                continue;
206            }
207            let pos_before = self.pos;
208            if let Some(id) = self.parse_statement() {
209                self.ast.top_level.push(id);
210                let trailing = self.take_trailing_declarators();
211                self.ast.top_level.extend(trailing);
212            } else {
213                if self.fatal.is_some() {
214                    break;
215                }
216                // A failed statement that moved and stopped at the start of a
217                // top-level item leaves that item to be parsed, for example after a
218                // class whose last method body took its `}`. Recovering would skip
219                // `class`, `type`, `interface`, `enum` or `import`.
220                if self.pos != pos_before && self.peek_starts_top_level_item() {
221                    continue;
222                }
223                self.recover();
224                if self.pos != pos_before && matches!(self.peek().kind, TokenKind::RightBrace) {
225                    self.advance();
226                    continue;
227                }
228                // Forward-progress guarantee: advance if recover didn't move.
229                if self.pos == pos_before && !self.is_at_eof() {
230                    self.advance();
231                }
232            }
233        }
234    }
235
236    fn parse_statement(&mut self) -> Option<StmtId> {
237        self.trailing_declarators.clear();
238        self.with_recursion_limit(Self::parse_statement_inner)
239    }
240
241    fn parse_statement_inner(&mut self) -> Option<StmtId> {
242        self.reject_async_modifier();
243        match self.peek().kind {
244            TokenKind::Let => self.parse_let_or_const(false),
245            TokenKind::Const => self.parse_let_or_const(true),
246            TokenKind::Function => self.parse_function_decl(),
247            TokenKind::Class => self.parse_class_decl(),
248            TokenKind::Interface => self.parse_interface_decl(),
249            TokenKind::Enum => self.parse_enum_decl(),
250            TokenKind::Identifier if self.at_type_alias_head() => self.parse_type_alias_decl(),
251            TokenKind::Export => self.parse_export(),
252            TokenKind::Import => self.parse_import_decl(),
253            TokenKind::If => self.parse_if(),
254            TokenKind::While => self.parse_while(),
255            TokenKind::Do => self.parse_do_while(),
256            TokenKind::For => self.parse_for(),
257            TokenKind::Switch => self.parse_switch(),
258            TokenKind::Break => self.parse_break(),
259            TokenKind::Continue => self.parse_continue(),
260            TokenKind::Return => self.parse_return(),
261            TokenKind::Throw => self.parse_throw(),
262            TokenKind::Try => self.parse_try(),
263            TokenKind::LeftBrace => self.parse_block(),
264            _ => self.parse_expression_statement(),
265        }
266    }
267
268    fn parse_expression_statement(&mut self) -> Option<StmtId> {
269        let expr_id = self.parse_expression()?;
270        let expr_span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
271        if !self.at_statement_end() {
272            let what = if matches!(
273                parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.kind,
274                ExprKind::Assign { .. }
275            ) {
276                "assignment"
277            } else {
278                "expression"
279            };
280            self.error_at_peek(format!("expected `;` after {what}"));
281            return None;
282        }
283        let end = self.finish_statement();
284        let span = self.span(expr_span.start, end);
285        let kind = self.statement_kind_for(expr_id)?;
286        parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
287    }
288
289    /// An assignment in statement position becomes an assignment statement;
290    /// only one used as a value stays an `ExprKind::Assign`.
291    fn statement_kind_for(&mut self, expr_id: ExprId) -> Option<StmtKind> {
292        let ExprKind::Assign {
293            target,
294            op,
295            op_span,
296            value,
297        } = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?
298            .kind
299            .clone()
300        else {
301            return Some(StmtKind::Expr(expr_id));
302        };
303        Some(
304            match (
305                parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?
306                    .kind
307                    .clone(),
308                op,
309            ) {
310                (ExprKind::Identifier(target), None) => StmtKind::Assign { target, value },
311                (ExprKind::Identifier(target), Some(op)) => StmtKind::CompoundAssign {
312                    target,
313                    op,
314                    op_span,
315                    value,
316                },
317                (ExprKind::FieldAccess { receiver, name }, None) => StmtKind::AssignField {
318                    receiver,
319                    field_name: name,
320                    value,
321                },
322                (ExprKind::FieldAccess { receiver, name }, Some(op)) => {
323                    StmtKind::CompoundAssignField {
324                        receiver,
325                        field_name: name,
326                        op,
327                        op_span,
328                        value,
329                    }
330                }
331                (ExprKind::IndexAccess { receiver, index }, None) => StmtKind::AssignIndex {
332                    receiver,
333                    index,
334                    value,
335                },
336                (ExprKind::IndexAccess { receiver, index }, Some(op)) => {
337                    StmtKind::CompoundAssignIndex {
338                        receiver,
339                        index,
340                        op,
341                        op_span,
342                        value,
343                    }
344                }
345                _ => return self.invariant_failure("invalid assignment target"),
346            },
347        )
348    }
349
350    /// Parses `let`/`const` with one or more comma-separated declarators. Each
351    /// declarator becomes its own statement, in source order; the first is
352    /// returned and the rest wait in `trailing_declarators` for the caller to
353    /// place after it.
354    fn parse_let_or_const(&mut self, is_const: bool) -> Option<StmtId> {
355        let mut doc = self.take_leading_doc();
356        let kw = self.advance();
357
358        let mut declarators = Vec::new();
359        let mut start = kw.span.start;
360        loop {
361            let Some(kind) = self.parse_declarator(is_const, doc.take()) else {
362                self.discard_detached_patterns(&declarators);
363                return None;
364            };
365            if !matches!(self.peek().kind, TokenKind::Comma) {
366                if !self.at_statement_end() {
367                    self.error_at_peek("expected `;` after declaration");
368                    self.discard_detached_patterns(&declarators);
369                    return None;
370                }
371                let end = self.finish_statement();
372                declarators.push(self.push_declarator(kind, start, end)?);
373                break;
374            }
375            let end = self.prev_token_end();
376            declarators.push(self.push_declarator(kind, start, end)?);
377            self.advance();
378            start = self.peek().span.start;
379        }
380
381        let first = declarators.remove(0);
382        self.trailing_declarators = declarators;
383        Some(first)
384    }
385
386    fn push_declarator(&mut self, kind: StmtKind, start: u32, end: u32) -> Option<StmtId> {
387        let span = self.span(start, end);
388        parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
389    }
390
391    /// The declarators after the first of the declaration `parse_statement` just
392    /// returned. Every caller of `parse_statement` takes them, so none can
393    /// attach to a later statement.
394    fn take_trailing_declarators(&mut self) -> Vec<StmtId> {
395        std::mem::take(&mut self.trailing_declarators)
396    }
397
398    /// One declarator: a name or pattern, an optional annotation, and its initializer.
399    fn parse_declarator(
400        &mut self,
401        is_const: bool,
402        doc: Option<crate::DocComment>,
403    ) -> Option<StmtKind> {
404        let binding = match self.peek().kind {
405            TokenKind::LeftBrace | TokenKind::LeftBracket => Some(self.parse_binding()?),
406            _ => None,
407        };
408
409        let name = if binding.is_none() {
410            let name_tok =
411                self.expect_binding_identifier("expected identifier after `let`/`const`")?;
412            Some(self.ident_from_token(&name_tok))
413        } else {
414            None
415        };
416
417        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
418            self.advance();
419            Some(self.parse_type_annotation()?)
420        } else {
421            None
422        };
423
424        let implicit = !is_const
425            && ty.is_some()
426            && matches!(self.peek().kind, TokenKind::Semicolon | TokenKind::Comma);
427        let value = if let (true, Some(name)) = (implicit, &name) {
428            // `let x: T;` starts as `undefined`; the typechecker requires `T` to allow it.
429            let value = parse_arena_result(
430                self.ast.try_push_expr(Expr {
431                    kind: ExprKind::Identifier(Ident {
432                        name: "undefined".to_string(),
433                        span: name.span,
434                    }),
435                    span: name.span,
436                }),
437                &mut self.fatal,
438            )?;
439            self.ast.implicit_initializers.insert(value);
440            self.ast.synthetic_undefined.insert(value);
441            value
442        } else {
443            if !matches!(self.peek().kind, TokenKind::Equals) {
444                let (msg, help) = if is_const {
445                    (
446                        "`const` declaration requires an initializer",
447                        vec!["const x: T = expr;".to_string()],
448                    )
449                } else {
450                    (
451                        "`let` declaration requires an initializer",
452                        vec![
453                            "let x: T = expr;".to_string(),
454                            "or declare one that starts empty: `let x: T | undefined;`".to_string(),
455                        ],
456                    )
457                };
458                self.error_at_peek_with_help(msg, help);
459                return None;
460            }
461            self.advance();
462            self.parse_expression()?
463        };
464
465        Some(match (binding, name) {
466            (Some(binding), _) => {
467                if is_const {
468                    StmtKind::ConstPattern {
469                        binding,
470                        ty,
471                        value,
472                        doc,
473                    }
474                } else {
475                    StmtKind::LetPattern {
476                        binding,
477                        ty,
478                        value,
479                        doc,
480                    }
481                }
482            }
483            (None, Some(name)) => {
484                if is_const {
485                    StmtKind::Const {
486                        name,
487                        ty,
488                        value,
489                        doc,
490                    }
491                } else {
492                    StmtKind::Let {
493                        name,
494                        ty,
495                        value,
496                        doc,
497                    }
498                }
499            }
500            (None, None) => return self.invariant_failure("either binding or name must be Some"),
501        })
502    }
503
504    // One level only; nested patterns are rejected.
505    fn parse_binding(&mut self) -> Option<Binding> {
506        match self.peek().kind {
507            TokenKind::LeftBrace => self.parse_object_binding(),
508            TokenKind::LeftBracket => self.parse_array_binding(),
509            _ => {
510                self.error_at_peek("expected `{` or `[` to start a destructuring pattern");
511                None
512            }
513        }
514    }
515
516    fn parse_object_binding(&mut self) -> Option<Binding> {
517        let open = self.advance();
518        let mut fields: Vec<ObjectPatternField> = Vec::new();
519        let mut rest: Option<Ident> = None;
520
521        if matches!(self.peek().kind, TokenKind::RightBrace) {
522            self.error_at_peek("empty object destructuring pattern");
523            return None;
524        }
525
526        loop {
527            if matches!(self.peek().kind, TokenKind::DotDotDot) {
528                let dots = self.advance();
529                let name_tok = self.expect_binding_identifier("expected identifier after `...`")?;
530                let name = self.ident_from_token(&name_tok);
531                if !matches!(self.peek().kind, TokenKind::RightBrace) {
532                    self.error_at_peek_with_help(
533                        "rest element must be the last element in a destructuring pattern",
534                        vec!["move `...` after every other binding".to_string()],
535                    );
536                    return None;
537                }
538                rest = Some(Ident {
539                    name: name.name,
540                    span: self.span(dots.span.start, name.span.end),
541                });
542                break;
543            }
544
545            let source_tok = self.expect_property_name("expected field name in object pattern")?;
546            let source = self.ident_from_token(&source_tok);
547            let field_span_start = source.span.start;
548
549            let (local, field_end) = if matches!(self.peek().kind, TokenKind::Colon) {
550                self.advance();
551                match self.peek().kind {
552                    TokenKind::LeftBrace | TokenKind::LeftBracket => {
553                        self.error_at_peek_with_help(
554                                "nested destructuring is not supported",
555                                vec![
556                                    "destructure once and access nested fields explicitly: const { a } = obj; const inner = a.b;"
557                                        .to_string(),
558                                ],
559                            );
560                        return None;
561                    }
562                    _ => {
563                        let local_tok = self.expect_binding_identifier(
564                            "expected identifier after `:` in pattern",
565                        )?;
566                        let local = self.ident_from_token(&local_tok);
567                        let end = local.span.end;
568                        (local, end)
569                    }
570                }
571            } else {
572                if !matches!(source_tok.kind, TokenKind::Identifier) {
573                    self.error_at_peek("expected `:` after keyword field name in object pattern");
574                    return None;
575                }
576                self.reject_strict_mode_reserved_word(&source_tok);
577                self.reject_eval_or_arguments(source_tok.span);
578                let end = source.span.end;
579                (source.clone(), end)
580            };
581
582            let default = if matches!(self.peek().kind, TokenKind::Equals) {
583                self.advance();
584                Some(self.parse_expression()?)
585            } else {
586                None
587            };
588            let field_end = match default {
589                Some(value) => {
590                    parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?
591                        .span
592                        .end
593                }
594                None => field_end,
595            };
596
597            fields.push(ObjectPatternField {
598                source,
599                local,
600                default,
601                span: self.span(field_span_start, field_end),
602            });
603
604            match self.peek().kind {
605                TokenKind::Comma => {
606                    self.advance();
607                    if matches!(self.peek().kind, TokenKind::RightBrace) {
608                        break;
609                    }
610                }
611                TokenKind::RightBrace => break,
612                _ => {
613                    self.error_at_peek("expected `,` or `}` in object pattern");
614                    return None;
615                }
616            }
617        }
618
619        if !matches!(self.peek().kind, TokenKind::RightBrace) {
620            self.error_at_peek("expected `}` to close object pattern");
621            return None;
622        }
623        let close = self.advance();
624
625        Some(Binding::Object {
626            fields,
627            rest,
628            span: self.span(open.span.start, close.span.end),
629        })
630    }
631
632    fn parse_array_binding(&mut self) -> Option<Binding> {
633        let open = self.advance();
634        let mut elems: Vec<Option<Ident>> = Vec::new();
635        let mut defaults = Vec::new();
636        let mut rest: Option<Ident> = None;
637
638        if matches!(self.peek().kind, TokenKind::RightBracket) {
639            self.error_at_peek("empty array destructuring pattern");
640            return None;
641        }
642
643        loop {
644            match self.peek().kind {
645                TokenKind::Comma => {
646                    elems.push(None);
647                    defaults.push(None);
648                    self.advance();
649                    if matches!(self.peek().kind, TokenKind::RightBracket) {
650                        break;
651                    }
652                    continue;
653                }
654                TokenKind::RightBracket => break,
655                TokenKind::DotDotDot => {
656                    let dots = self.advance();
657                    let name_tok =
658                        self.expect_binding_identifier("expected identifier after `...`")?;
659                    let name = self.ident_from_token(&name_tok);
660                    if !matches!(self.peek().kind, TokenKind::RightBracket) {
661                        self.error_at_peek_with_help(
662                            "rest element must be the last element in a destructuring pattern",
663                            vec!["move `...` after every other binding".to_string()],
664                        );
665                        return None;
666                    }
667                    rest = Some(Ident {
668                        name: name.name,
669                        span: self.span(dots.span.start, name.span.end),
670                    });
671                    break;
672                }
673                TokenKind::LeftBrace | TokenKind::LeftBracket => {
674                    self.error_at_peek_with_help(
675                        "nested destructuring is not supported",
676                        vec![
677                            "destructure once and access nested elements explicitly: const [a] = arr; const inner = a[0];"
678                                .to_string(),
679                        ],
680                    );
681                    return None;
682                }
683                _ => {
684                    let name_tok =
685                        self.expect_binding_identifier("expected identifier in array pattern")?;
686                    let name = self.ident_from_token(&name_tok);
687
688                    let default = if matches!(self.peek().kind, TokenKind::Equals) {
689                        self.advance();
690                        Some(self.parse_expression()?)
691                    } else {
692                        None
693                    };
694                    defaults.push(default);
695                    elems.push(Some(name));
696
697                    match self.peek().kind {
698                        TokenKind::Comma => {
699                            self.advance();
700                            if matches!(self.peek().kind, TokenKind::RightBracket) {
701                                break;
702                            }
703                        }
704                        TokenKind::RightBracket => break,
705                        _ => {
706                            self.error_at_peek("expected `,` or `]` in array pattern");
707                            return None;
708                        }
709                    }
710                }
711            }
712        }
713
714        if !matches!(self.peek().kind, TokenKind::RightBracket) {
715            self.error_at_peek("expected `]` to close array pattern");
716            return None;
717        }
718        let close = self.advance();
719
720        Some(Binding::Array {
721            elems,
722            defaults,
723            rest,
724            span: self.span(open.span.start, close.span.end),
725        })
726    }
727
728    fn parse_function_decl(&mut self) -> Option<StmtId> {
729        let doc = self.take_leading_doc();
730        let kw = self.advance();
731
732        let name_tok = self.expect_binding_identifier("expected function name")?;
733        let name = self.ident_from_token(&name_tok);
734        let is_main = self.block_depth == 0 && name.name == "main";
735        let decl = self.parse_function_decl_after_name(doc, kw, name);
736        if decl.is_none() && is_main {
737            self.ast.unparsed_main = true;
738        }
739        decl
740    }
741
742    fn parse_function_decl_after_name(
743        &mut self,
744        doc: Option<crate::DocComment>,
745        kw: Token,
746        name: Ident,
747    ) -> Option<StmtId> {
748        let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
749            self.parse_generic_param_list()?
750        } else {
751            Vec::new()
752        };
753
754        if !matches!(self.peek().kind, TokenKind::LeftParen) {
755            self.error_at_peek("expected `(` after function name");
756            return None;
757        }
758        self.advance();
759
760        let params = self.parse_param_list(false)?;
761        self.advance(); // `)` — parse_param_list left us on it
762
763        if !matches!(self.peek().kind, TokenKind::Colon) {
764            self.error_at_peek_with_help(
765                "expected `:` and return type",
766                vec![format!(
767                    "write the return type: `function {}(…): T {{ … }}`, with `void` when it returns nothing",
768                    name.name
769                )],
770            );
771            // Read the body anyway, so its own errors are reported and recovery
772            // does not resume inside it and misread its closing `}`.
773            if matches!(self.peek().kind, TokenKind::LeftBrace) {
774                let _ = self.parse_outer_this_boundary(Self::parse_block);
775            }
776            return None;
777        }
778        self.advance();
779        let (return_type, type_predicate) =
780            self.parse_predicate_or_return_type(TypePos::Anywhere)?;
781
782        // A declaration nested in a method has no receiver of its own, and
783        // must not see the method's.
784        let body = self.parse_outer_this_boundary(Self::parse_block)?;
785        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
786            .span
787            .end;
788
789        parse_arena_result(
790            self.ast.try_push_stmt(Stmt {
791                kind: StmtKind::Function {
792                    name,
793                    generics,
794                    params,
795                    return_type,
796                    type_predicate,
797                    body,
798                    doc,
799                },
800                span: self.span(kw.span.start, body_end),
801            }),
802            &mut self.fatal,
803        )
804    }
805
806    fn parse_class_decl(&mut self) -> Option<StmtId> {
807        let doc = self.take_leading_doc();
808        let kw = self.advance();
809        // Classes are collected from the module's top level only; a nested one
810        // would otherwise be dropped unchecked.
811        if self.block_depth > 0 {
812            self.error_at_with_help(
813                kw.span,
814                "a `class` must be declared at the top level of the module",
815                vec!["move this `class` outside any block or function body".to_string()],
816            );
817        }
818        let name = self.parse_type_decl_name("a", "class")?;
819
820        let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
821            self.parse_generic_param_list()?
822        } else {
823            Vec::new()
824        };
825
826        let extends = if matches!(self.peek().kind, TokenKind::Extends) {
827            self.advance();
828            Some(self.parse_type_annotation()?)
829        } else {
830            None
831        };
832
833        let mut implements: Vec<crate::TypeAnnotation> = Vec::new();
834        if matches!(self.peek().kind, TokenKind::Implements) {
835            self.advance();
836            loop {
837                implements.push(self.parse_type_annotation()?);
838                if matches!(self.peek().kind, TokenKind::Comma) {
839                    self.advance();
840                    continue;
841                }
842                break;
843            }
844        }
845
846        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
847            self.error_at_peek("expected `{` after class header");
848            return None;
849        }
850        self.advance();
851
852        let mut members: Vec<crate::ClassMember> = Vec::new();
853        let mut member_failed = false;
854        // After a failed member, a body missing its own `}` may have taken the
855        // class's: a declaration or import there is the next top-level item, not a
856        // member, so the class stops and reports the missing `}`.
857        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
858            && !(member_failed && self.peek_starts_top_level_item())
859        {
860            // Stray `;` between members is accepted (TS allows it).
861            if matches!(self.peek().kind, TokenKind::Semicolon) {
862                self.advance();
863                continue;
864            }
865            match self.parse_class_member() {
866                Some(member) => members.push(member),
867                None if std::mem::take(&mut self.resume_at_next_member) => member_failed = true,
868                None => return None,
869            }
870        }
871
872        if !matches!(self.peek().kind, TokenKind::RightBrace) {
873            self.error_at_peek("expected `}` to close class");
874            return None;
875        }
876        let close = self.advance();
877        if member_failed {
878            return None;
879        }
880
881        parse_arena_result(
882            self.ast.try_push_stmt(Stmt {
883                kind: StmtKind::ClassDecl {
884                    name,
885                    generics,
886                    extends,
887                    implements,
888                    members,
889                    doc,
890                },
891                span: self.span(kw.span.start, close.span.end),
892            }),
893            &mut self.fatal,
894        )
895    }
896
897    fn parse_class_member(&mut self) -> Option<crate::ClassMember> {
898        let doc = self.take_leading_doc();
899        let member_start = self.peek().span.start;
900
901        // Excluded modifiers — reject before anything else, naming the fix.
902        if self.peek_identifier_text_is("protected") {
903            let tok = self.advance();
904            self.error_at_with_help(
905                tok.span,
906                "`protected` is not supported",
907                vec!["use `private` or `public`".to_string()],
908            );
909            return None;
910        }
911        if self.peek_identifier_text_is("abstract") {
912            let tok = self.advance();
913            self.error_at_with_help(
914                tok.span,
915                "abstract classes are not supported",
916                vec!["provide a concrete implementation instead of `abstract`".to_string()],
917            );
918            return None;
919        }
920
921        let modifiers = self.parse_class_modifiers();
922        self.reject_async_member_modifier();
923
924        // Getter/setter: `get`/`set` <name> ( … ). A member literally named `get`/`set`
925        // (followed directly by `(`) is an ordinary method and stays accepted.
926        for (kw, kind) in [
927            ("get", crate::AccessorKind::Get),
928            ("set", crate::AccessorKind::Set),
929        ] {
930            if self.peek_identifier_text_is(kw)
931                && is_property_name(&self.peek_at(1).kind)
932                && matches!(self.peek_at(2).kind, TokenKind::LeftParen)
933            {
934                if let Some(span) = modifiers.readonly {
935                    self.reject_readonly_modifier(span);
936                }
937                if let Some(span) = modifiers.static_span {
938                    self.error_at_with_help(
939                        span,
940                        "static accessors are not supported",
941                        vec!["use a static method: `static name(): T { … }`".to_string()],
942                    );
943                    return None;
944                }
945                return self.parse_accessor(member_start, modifiers, kind, doc);
946            }
947        }
948
949        // Constructor.
950        if self.peek_identifier_text_is("constructor")
951            && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
952        {
953            if let Some(span) = modifiers.static_span {
954                self.error_at_with_help(
955                    span,
956                    "a constructor cannot be `static`",
957                    vec![
958                        "remove `static` — the constructor already belongs to the class, not \
959                         instances"
960                            .to_string(),
961                    ],
962                );
963                return None;
964            }
965            if let Some(span) = modifiers.readonly {
966                self.error_at_with_help(
967                    span,
968                    "a constructor cannot be `readonly`",
969                    vec!["remove `readonly`; it applies to fields".to_string()],
970                );
971                return None;
972            }
973            return self.parse_constructor(member_start, modifiers.visibility, doc);
974        }
975
976        let name = self.expect_property_ident("expected class member name")?;
977        let optional_span = if matches!(self.peek().kind, TokenKind::Question) {
978            Some(self.advance().span)
979        } else {
980            None
981        };
982        let optional = optional_span.is_some();
983
984        // Method.
985        if matches!(self.peek().kind, TokenKind::LessThan | TokenKind::LeftParen) {
986            if let Some(span) = modifiers.readonly {
987                self.reject_readonly_modifier(span);
988            }
989            let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
990                self.parse_generic_param_list()?
991            } else {
992                Vec::new()
993            };
994            if !matches!(self.peek().kind, TokenKind::LeftParen) {
995                self.error_at_peek("expected `(` to start the method parameter list");
996                return None;
997            }
998            self.advance();
999            let params = self.parse_class_member_params(false)?;
1000            self.advance(); // `)` — parse_param_list left us on it
1001            if !matches!(self.peek().kind, TokenKind::Colon) {
1002                self.error_at_peek_with_help(
1003                    "expected `:` and return type",
1004                    vec![format!(
1005                        "write the return type: `{}(…): T {{ … }}`, with `void` when it returns nothing",
1006                        name.name
1007                    )],
1008                );
1009                // As for a function: read the body so recovery resumes after it.
1010                if matches!(self.peek().kind, TokenKind::LeftBrace)
1011                    && self.parse_class_member_body().is_some()
1012                {
1013                    self.resume_at_next_member = true;
1014                }
1015                return None;
1016            }
1017            self.advance();
1018            let return_type = self.parse_type_annotation()?;
1019            if self.at_inserted_semicolon() && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1020            {
1021                self.advance();
1022            }
1023            if optional
1024                && matches!(
1025                    self.peek().kind,
1026                    TokenKind::Semicolon | TokenKind::RightBrace
1027                )
1028            {
1029                if !generics.is_empty() {
1030                    self.error_at(
1031                        name.span,
1032                        "generic optional method declarations are not supported",
1033                    );
1034                    return None;
1035                }
1036                let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1037                    self.advance().span.end
1038                } else {
1039                    return_type.span.end
1040                };
1041                let ty = self.method_declaration_type(
1042                    params,
1043                    return_type,
1044                    self.span(name.span.start, end),
1045                )?;
1046                return Some(crate::ClassMember::Field {
1047                    name,
1048                    modifiers,
1049                    optional: true,
1050                    method_signature: true,
1051                    ty,
1052                    initializer: None,
1053                    span: self.span(member_start, end),
1054                    doc,
1055                });
1056            }
1057            let body = self.parse_class_member_body()?;
1058            let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1059                .span
1060                .end;
1061            return Some(crate::ClassMember::Method {
1062                name,
1063                optional,
1064                modifiers,
1065                generics,
1066                params,
1067                return_type,
1068                body,
1069                span: self.span(member_start, body_end),
1070                doc,
1071            });
1072        }
1073
1074        // Field.
1075        if let Some(span) = optional_span
1076            && modifiers.static_span.is_some()
1077        {
1078            self.error_at_with_help(
1079                span,
1080                "a static field cannot be optional",
1081                vec!["a static field must be initialized; give it a value with `= …`".to_string()],
1082            );
1083            return None;
1084        }
1085        if !matches!(self.peek().kind, TokenKind::Colon) {
1086            if matches!(self.peek().kind, TokenKind::Equals) {
1087                self.error_at_peek_with_help(
1088                    "class fields require a type annotation",
1089                    vec!["add `: T` before the initializer, e.g. `name: string = …`".to_string()],
1090                );
1091            } else {
1092                self.error_at_peek("expected `:` and a type for the class field");
1093            }
1094            return None;
1095        }
1096        self.advance();
1097        let ty = self.parse_type_annotation()?;
1098        let initializer = if matches!(self.peek().kind, TokenKind::Equals) {
1099            self.advance();
1100            // A field initializer may read `this` (the typechecker binds it); gate
1101            // it like a member body so `this.x` parses.
1102            self.class_member_body_depth += 1;
1103            let init = self.parse_expression();
1104            self.class_member_body_depth -= 1;
1105            Some(init?)
1106        } else {
1107            None
1108        };
1109        if !matches!(self.peek().kind, TokenKind::Semicolon) {
1110            self.error_at_peek("expected `;` after class field");
1111            return None;
1112        }
1113        let semi = self.advance();
1114        Some(crate::ClassMember::Field {
1115            name,
1116            modifiers,
1117            optional,
1118            method_signature: false,
1119            ty,
1120            initializer,
1121            span: self.span(member_start, semi.span.end),
1122            doc,
1123        })
1124    }
1125
1126    fn parse_constructor(
1127        &mut self,
1128        member_start: u32,
1129        visibility: crate::Visibility,
1130        doc: Option<crate::DocComment>,
1131    ) -> Option<crate::ClassMember> {
1132        self.advance(); // `constructor`
1133        self.advance(); // `(` — guaranteed by the caller
1134        let params = self.parse_class_member_params(true)?;
1135        self.advance(); // `)` — parse_param_list left us on it
1136        if matches!(self.peek().kind, TokenKind::Colon) {
1137            let colon = self.advance();
1138            let _ = self.parse_type_annotation();
1139            self.error_at_with_help(
1140                colon.span,
1141                "a constructor cannot declare a return type",
1142                vec![
1143                    "drop the `: T` — a constructor always returns the class instance".to_string(),
1144                ],
1145            );
1146        }
1147        let body = self.parse_class_member_body()?;
1148        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1149            .span
1150            .end;
1151        Some(crate::ClassMember::Constructor {
1152            visibility,
1153            params,
1154            body,
1155            span: self.span(member_start, body_end),
1156            doc,
1157        })
1158    }
1159
1160    /// `get name(): T { … }` / `set name(v: T) { … }`. The caller has confirmed the
1161    /// `get`/`set` keyword, a property name, and a `(` follow.
1162    fn parse_accessor(
1163        &mut self,
1164        member_start: u32,
1165        modifiers: crate::ClassModifiers,
1166        kind: crate::AccessorKind,
1167        doc: Option<crate::DocComment>,
1168    ) -> Option<crate::ClassMember> {
1169        self.advance(); // `get` / `set`
1170        let name = self.expect_property_ident("expected accessor name")?;
1171        self.advance(); // `(` — guaranteed by the caller
1172        let mut params = self.parse_class_member_params(false)?;
1173        self.advance(); // `)`
1174        let param = match kind {
1175            crate::AccessorKind::Get => {
1176                if !params.is_empty() {
1177                    self.error_at(name.span, "a getter cannot declare parameters");
1178                }
1179                None
1180            }
1181            crate::AccessorKind::Set => {
1182                if params.len() != 1 {
1183                    self.error_at(name.span, "a setter must declare exactly one parameter");
1184                    return None;
1185                }
1186                let param = params.remove(0);
1187                // An assignment always passes the value, so a setter's parameter
1188                // can't be omitted or collect several.
1189                if param.rest {
1190                    self.error_at(
1191                        param.name.span,
1192                        "a setter's parameter can't be a rest parameter",
1193                    );
1194                } else if param.default.is_some() {
1195                    self.error_at(name.span, "a setter's parameter can't have a default value");
1196                } else if param.optional {
1197                    self.error_at(param.name.span, "a setter's parameter can't be optional");
1198                }
1199                Some(Box::new(param))
1200            }
1201        };
1202        let return_type = if matches!(self.peek().kind, TokenKind::Colon) {
1203            self.advance();
1204            Some(self.parse_type_annotation()?)
1205        } else {
1206            // A getter's type is its return type, which, as for a method, is
1207            // written rather than inferred. The body still parses, so the
1208            // rest of the class is checked.
1209            if kind == crate::AccessorKind::Get {
1210                self.error_at_peek_with_help(
1211                    "expected `:` and return type",
1212                    vec!["get name(): T { … }".to_string()],
1213                );
1214            }
1215            None
1216        };
1217        let body = self.parse_class_member_body()?;
1218        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1219            .span
1220            .end;
1221        Some(crate::ClassMember::Accessor {
1222            name,
1223            modifiers,
1224            kind,
1225            param,
1226            return_type,
1227            body,
1228            span: self.span(member_start, body_end),
1229            doc,
1230        })
1231    }
1232
1233    /// `public`/`private`/`static`/`readonly`. Duplicate or conflicting modifiers are
1234    /// diagnosed but parsing continues so the member shape is still recovered.
1235    /// Visibility must precede `static` and `readonly` (the TypeScript order);
1236    /// `readonly` is accepted on either side of `static` — no semantic difference
1237    /// under our subset.
1238    fn parse_class_modifiers(&mut self) -> crate::ClassModifiers {
1239        let mut visibility = crate::Visibility::Public;
1240        let mut visibility_span: Option<Span> = None;
1241        let mut readonly: Option<Span> = None;
1242        let mut static_span: Option<Span> = None;
1243        loop {
1244            if self.peek_word_is_class_modifier("public")
1245                || self.peek_word_is_class_modifier("private")
1246            {
1247                let (kind, word) = if self.peek_identifier_text_is("private") {
1248                    (crate::Visibility::Private, "private")
1249                } else {
1250                    (crate::Visibility::Public, "public")
1251                };
1252                let tok = self.advance();
1253                if visibility_span.is_some() {
1254                    self.error_at_with_help(
1255                        tok.span,
1256                        "a class member may have at most one visibility modifier",
1257                        vec!["keep a single `public` or `private`".to_string()],
1258                    );
1259                    continue;
1260                }
1261                // TypeScript reports one misplaced modifier per member; `static` is
1262                // named first when both precede.
1263                let preceding = static_span
1264                    .map(|_| "static")
1265                    .or(readonly.map(|_| "readonly"));
1266                if let Some(preceding) = preceding {
1267                    self.error_at_with_help(
1268                        tok.span,
1269                        format!("`{word}` must come before `{preceding}`"),
1270                        vec![format!("write `{word} {preceding} <name>`")],
1271                    );
1272                }
1273                visibility = kind;
1274                visibility_span = Some(tok.span);
1275                continue;
1276            }
1277            if self.peek_word_is_class_modifier("static") {
1278                let tok = self.advance();
1279                if static_span.is_some() {
1280                    self.error_at(tok.span, "duplicate `static` modifier");
1281                } else {
1282                    static_span = Some(tok.span);
1283                }
1284                continue;
1285            }
1286            if self.peek_word_is_class_modifier("readonly") {
1287                let tok = self.advance();
1288                if readonly.is_some() {
1289                    self.error_at(tok.span, "duplicate `readonly` modifier");
1290                } else {
1291                    readonly = Some(tok.span);
1292                }
1293                continue;
1294            }
1295            break;
1296        }
1297        crate::ClassModifiers {
1298            visibility,
1299            visibility_span,
1300            readonly,
1301            static_span,
1302        }
1303    }
1304
1305    /// A contextual modifier keyword counts as a modifier only when another member token
1306    /// (the real name, or a further modifier) follows — otherwise the word is the member
1307    /// name itself (e.g. a field named `private`). Mirrors `eat_readonly_property_modifier`.
1308    /// As in TypeScript, only `static` may be followed by a line break; in a class body
1309    /// ASI already ends the member there, but a constructor parameter list has no ASI.
1310    fn peek_word_is_class_modifier(&self, word: &str) -> bool {
1311        if !self.peek_identifier_text_is(word) {
1312            return false;
1313        }
1314        if word != "static" && self.line_break_after_peek() {
1315            return false;
1316        }
1317        let next = &self.peek_at(1).kind;
1318        is_property_name(next) || matches!(next, TokenKind::StringLiteral(_))
1319    }
1320
1321    fn parse_class_member_params(
1322        &mut self,
1323        allow_param_properties: bool,
1324    ) -> Option<Vec<ParamDecl>> {
1325        let saved = self.class_member_body_depth;
1326        self.class_member_body_depth = 1;
1327        let params = self.parse_param_list(allow_param_properties);
1328        self.class_member_body_depth = saved;
1329        params
1330    }
1331
1332    fn parse_class_member_body(&mut self) -> Option<StmtId> {
1333        self.class_member_body_depth += 1;
1334        let body = self.parse_block();
1335        self.class_member_body_depth -= 1;
1336        body
1337    }
1338
1339    fn parse_interface_decl(&mut self) -> Option<StmtId> {
1340        let doc = self.take_leading_doc();
1341        let kw = self.advance();
1342        let name = self.parse_type_decl_name("an", "interface")?;
1343
1344        let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1345            self.parse_generic_param_list()?
1346        } else {
1347            Vec::new()
1348        };
1349
1350        let mut extends = Vec::new();
1351        if matches!(self.peek().kind, TokenKind::Extends) {
1352            self.advance();
1353            loop {
1354                extends.push(self.parse_type_annotation()?);
1355                if !matches!(self.peek().kind, TokenKind::Comma) {
1356                    break;
1357                }
1358                self.advance();
1359            }
1360        }
1361        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1362            self.error_at_peek("expected `{` after interface name");
1363            return None;
1364        }
1365        self.advance();
1366
1367        let mut members: Vec<crate::InterfaceMember> = Vec::new();
1368        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
1369            && !self.peek_starts_top_level_item()
1370        {
1371            // Call signatures `(params): ret;` are stored under the sentinel name `@call`.
1372            // The `@` prefix is not a valid identifier start, so collisions with user methods
1373            // are impossible.
1374            let member_doc = self.take_leading_doc();
1375            if matches!(self.peek().kind, TokenKind::LeftParen) {
1376                let open = self.advance();
1377                let params = self.parse_param_list(false)?;
1378                self.advance();
1379                if !matches!(self.peek().kind, TokenKind::Colon) {
1380                    self.error_at_peek("expected `:` and return type");
1381                    return None;
1382                }
1383                self.advance();
1384                let return_type = self.parse_type_annotation()?;
1385                let end = self.finish_interface_member(return_type.span.end)?;
1386                let span = self.span(open.span.start, end);
1387                let sentinel = Ident {
1388                    name: "@call".to_string(),
1389                    span: open.span,
1390                };
1391                members.push(crate::InterfaceMember::Method {
1392                    name: sentinel,
1393                    optional: false,
1394                    generics: Vec::new(),
1395                    params,
1396                    return_type,
1397                    span,
1398                    doc: member_doc,
1399                });
1400                continue;
1401            }
1402            if matches!(self.peek().kind, TokenKind::LessThan) {
1403                self.error_at_peek("generic call signatures are not yet supported");
1404                return None;
1405            }
1406            let member_start = self.peek().span.start;
1407            let readonly = self.eat_readonly_property_modifier();
1408            if self.peek_is_parameterless_index_signature() {
1409                self.skip_parameterless_index_signature(member_start)?;
1410                self.finish_interface_member(self.prev_token_end())?;
1411                continue;
1412            }
1413            if matches!(self.peek().kind, TokenKind::LeftBracket) {
1414                let signature = self.parse_index_signature(readonly)?;
1415                self.finish_interface_member(signature.span.end)?;
1416                members.push(crate::InterfaceMember::IndexSignature(signature));
1417                continue;
1418            }
1419            let member_name = self.expect_property_ident("expected interface member name")?;
1420            let optional = matches!(self.peek().kind, TokenKind::Question);
1421            if optional {
1422                self.advance();
1423            }
1424            if matches!(self.peek().kind, TokenKind::Colon) {
1425                self.advance();
1426                let ty = self.parse_type_annotation()?;
1427                let end = self.finish_interface_member(ty.span.end)?;
1428                let span = self.span(member_name.span.start, end);
1429                members.push(crate::InterfaceMember::Property {
1430                    name: member_name,
1431                    ty,
1432                    optional,
1433                    readonly,
1434                    span,
1435                    doc: member_doc,
1436                });
1437                continue;
1438            }
1439            if readonly {
1440                self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
1441            }
1442            let m_generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1443                self.parse_generic_param_list()?
1444            } else {
1445                Vec::new()
1446            };
1447            if !matches!(self.peek().kind, TokenKind::LeftParen) {
1448                self.error_at_peek(
1449                    "expected `(` to start a method signature or `:` to start a property",
1450                );
1451                return None;
1452            }
1453            self.advance();
1454            let params = self.parse_param_list(false)?;
1455            self.advance();
1456            if !matches!(self.peek().kind, TokenKind::Colon) {
1457                self.error_at_peek("expected `:` and return type");
1458                return None;
1459            }
1460            self.advance();
1461            let return_type = self.parse_type_annotation()?;
1462            let end = self.finish_interface_member(return_type.span.end)?;
1463            let span = self.span(member_name.span.start, end);
1464            if optional {
1465                if !m_generics.is_empty() {
1466                    self.error_at(
1467                        member_name.span,
1468                        "generic optional method declarations are not supported",
1469                    );
1470                    return None;
1471                }
1472                let ty = self.method_declaration_type(params, return_type, span)?;
1473                members.push(crate::InterfaceMember::Property {
1474                    name: member_name,
1475                    ty,
1476                    optional: true,
1477                    readonly,
1478                    span,
1479                    doc: member_doc,
1480                });
1481            } else {
1482                members.push(crate::InterfaceMember::Method {
1483                    name: member_name,
1484                    optional,
1485                    generics: m_generics,
1486                    params,
1487                    return_type,
1488                    span,
1489                    doc: member_doc,
1490                });
1491            }
1492        }
1493
1494        if !matches!(self.peek().kind, TokenKind::RightBrace) {
1495            self.error_at_peek("expected `}` to close interface");
1496            return None;
1497        }
1498        let close = self.advance();
1499
1500        parse_arena_result(
1501            self.ast.try_push_stmt(Stmt {
1502                kind: StmtKind::InterfaceDecl {
1503                    name,
1504                    generics,
1505                    extends,
1506                    members,
1507                    doc,
1508                },
1509                span: self.span(kw.span.start, close.span.end),
1510            }),
1511            &mut self.fatal,
1512        )
1513    }
1514
1515    fn method_declaration_type(
1516        &mut self,
1517        params: Vec<ParamDecl>,
1518        return_type: TypeAnnotation,
1519        span: Span,
1520    ) -> Option<TypeAnnotation> {
1521        let mut fields = Vec::new();
1522        for param in params {
1523            if param.pattern.is_some() || param.default.is_some() {
1524                self.error_at(
1525                    param.name.span,
1526                    "a method declaration cannot contain a parameter pattern or default value",
1527                );
1528                return None;
1529            }
1530            let Some(ty) = param.ty else {
1531                return self.invariant_failure("method parameter annotation was not retained");
1532            };
1533            fields.push(TypeAnnotationField {
1534                name: param.name,
1535                ty,
1536                optional: param.optional,
1537                readonly: false,
1538                rest: param.rest,
1539                method: false,
1540            });
1541        }
1542        Some(TypeAnnotation {
1543            kind: TypeAnnotationKind::Function {
1544                params: fields,
1545                return_type: Box::new(return_type),
1546            },
1547            span,
1548        })
1549    }
1550
1551    fn finish_interface_member(&mut self, body_end: u32) -> Option<u32> {
1552        self.finish_type_member(body_end, "expected `;`, `,`, or `}` after interface member")
1553    }
1554
1555    /// Consume the separator after an interface or type-literal member and return
1556    /// where the member ends. `;` and `,` are interchangeable and the last member's
1557    /// is optional. A line break also ends a member, as in TypeScript: ASI inserts
1558    /// `;` there, but keeps some tokens, such as `[` and `(`, attached. `body_end` is
1559    /// where the member ends when it carries no separator of its own.
1560    fn finish_type_member(&mut self, body_end: u32, message: &str) -> Option<u32> {
1561        match self.peek().kind {
1562            TokenKind::Semicolon | TokenKind::Comma => Some(self.advance().span.end),
1563            TokenKind::RightBrace => Some(body_end),
1564            _ if self.line_break_before_peek() => Some(body_end),
1565            _ => {
1566                self.error_at_peek(message);
1567                None
1568            }
1569        }
1570    }
1571
1572    fn parse_type_alias_decl(&mut self) -> Option<StmtId> {
1573        let doc = self.take_leading_doc();
1574        let kw = self.advance();
1575        let name = self.parse_type_decl_name("a", "type alias")?;
1576
1577        let generics: Vec<Ident> = if matches!(self.peek().kind, TokenKind::LessThan) {
1578            self.parse_generic_param_list()?
1579        } else {
1580            Vec::new()
1581        };
1582
1583        if !matches!(self.peek().kind, TokenKind::Equals) {
1584            self.error_at_peek("expected `=` after type alias name");
1585            return None;
1586        }
1587        self.advance();
1588
1589        let ty = self.parse_type_annotation()?;
1590
1591        // ASI keeps a `[` on the next line attached, but the type already ended at
1592        // the line break (see `parse_type_array_inner`), so the break ends the alias.
1593        let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1594            self.advance().span.end
1595        } else if self.line_break_before_peek() {
1596            self.prev_token_end()
1597        } else {
1598            self.error_at_peek("expected `;` after type alias body");
1599            return None;
1600        };
1601
1602        parse_arena_result(
1603            self.ast.try_push_stmt(Stmt {
1604                kind: StmtKind::TypeAliasDecl {
1605                    name,
1606                    generics,
1607                    ty,
1608                    doc,
1609                },
1610                span: self.span(kw.span.start, end),
1611            }),
1612            &mut self.fatal,
1613        )
1614    }
1615
1616    // Parser enforces kind-coherence: an enum cannot mix numeric and string initializers.
1617    fn parse_enum_decl(&mut self) -> Option<StmtId> {
1618        #[derive(Copy, Clone)]
1619        enum Kind {
1620            Number,
1621            String,
1622        }
1623        let doc = self.take_leading_doc();
1624        let kw = self.advance();
1625        let name = self.parse_type_decl_name("an", "enum")?;
1626
1627        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1628            self.error_at_peek("expected `{` after enum name");
1629            return None;
1630        }
1631        self.advance();
1632
1633        let mut members: Vec<EnumMember> = Vec::new();
1634        let mut first_explicit: Option<(Kind, Span)> = None;
1635        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1636            // ASI inserts `;` after `,` on a newline — tolerate it so multi-line enums parse.
1637            if matches!(self.peek().kind, TokenKind::Semicolon) {
1638                self.advance();
1639                continue;
1640            }
1641            let member_doc = self.take_leading_doc();
1642            let member_name_tok = self.expect_identifier_name("expected enum member name")?;
1643            let member_name = self.ident_from_token(&member_name_tok);
1644
1645            let value = if matches!(self.peek().kind, TokenKind::Equals) {
1646                self.advance();
1647                let init = self.parse_enum_initializer()?;
1648                let (this_kind, this_span) = match &init {
1649                    EnumInitializer::Number { span, .. } => (Kind::Number, *span),
1650                    EnumInitializer::String { span, .. } => (Kind::String, *span),
1651                };
1652                match first_explicit {
1653                    None => first_explicit = Some((this_kind, this_span)),
1654                    Some((prev, _)) => {
1655                        let mismatched = matches!(
1656                            (prev, this_kind),
1657                            (Kind::Number, Kind::String) | (Kind::String, Kind::Number)
1658                        );
1659                        if mismatched {
1660                            self.error_at(
1661                                this_span,
1662                                "mixed numeric and string enum members are not allowed",
1663                            );
1664                        }
1665                    }
1666                }
1667                Some(init)
1668            } else {
1669                None
1670            };
1671
1672            let end_span = value
1673                .as_ref()
1674                .map_or(member_name.span, super::ast::EnumInitializer::span);
1675            let member_span = self.span(member_name.span.start, end_span.end);
1676            members.push(EnumMember {
1677                name: member_name,
1678                value,
1679                span: member_span,
1680                doc: member_doc,
1681            });
1682
1683            match self.peek().kind {
1684                // ASI supplies the `;` before a closing `}` and after a newline, so an
1685                // enum body sees one wherever a comma was omitted.
1686                TokenKind::Comma | TokenKind::Semicolon => {
1687                    self.advance();
1688                }
1689                TokenKind::RightBrace | TokenKind::Eof => {}
1690                _ => {
1691                    self.error_at_peek("expected `,` or `}` after enum member");
1692                    return None;
1693                }
1694            }
1695        }
1696
1697        if !matches!(self.peek().kind, TokenKind::RightBrace) {
1698            self.error_at_peek("expected `}` to close enum");
1699            return None;
1700        }
1701        let close = self.advance();
1702
1703        parse_arena_result(
1704            self.ast.try_push_stmt(Stmt {
1705                kind: StmtKind::EnumDecl { name, members, doc },
1706                span: self.span(kw.span.start, close.span.end),
1707            }),
1708            &mut self.fatal,
1709        )
1710    }
1711
1712    /// `export` on a top-level declaration. Two forms (multi-file.md §3):
1713    /// Form 1 marks a declaration (`export function …`) — the declaration is
1714    /// parsed normally and recorded in `exported_decls`; Form 2 is a re-export
1715    /// (`export { a, b as c } from "./util";` / `export { x };`) parsed into an
1716    /// `ExportFrom` node. `export` is top-level only; inside a function body it
1717    /// is a parse error. `export default` stays rejected.
1718    fn parse_export(&mut self) -> Option<StmtId> {
1719        let export_tok = self.advance();
1720        let export_span = export_tok.span;
1721
1722        let nested = self.block_depth > 0;
1723        if nested {
1724            self.error_at_with_help(
1725                export_span,
1726                "`export` statements must appear at the top of the file",
1727                vec!["move this `export` to the top level of the module".to_string()],
1728            );
1729            // Fall through to parse the declaration / list anyway so recovery
1730            // stays in sync, but return None below so it isn't recorded.
1731        }
1732
1733        // `export type { … }` — TS's type-only re-export, accepted as a no-op
1734        // synonym: exports carry both value and type spaces here.
1735        if self.peek_identifier_text_is("type")
1736            && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1737        {
1738            self.advance();
1739        }
1740
1741        self.reject_async_modifier();
1742        let declares = matches!(
1743            self.peek().kind,
1744            TokenKind::Function
1745                | TokenKind::Class
1746                | TokenKind::Const
1747                | TokenKind::Let
1748                | TokenKind::Interface
1749                | TokenKind::Enum
1750        ) || self.at_type_alias_head();
1751        if declares {
1752            // Carry any doc comment that attached to `export` onto the
1753            // declaration token so the declaration parser still sees it.
1754            if export_tok.leading_doc.is_some() && self.peek().leading_doc.is_none() {
1755                if let Some(token) = self.tokens.get_mut(self.pos) {
1756                    token.leading_doc = export_tok.leading_doc;
1757                } else {
1758                    return self.invariant_failure("export cursor is outside token stream");
1759                }
1760            }
1761            let id = self.parse_statement()?;
1762            if nested {
1763                let detached: Vec<StmtId> = std::iter::once(id)
1764                    .chain(self.take_trailing_declarators())
1765                    .collect();
1766                self.discard_detached_patterns(&detached);
1767                return None;
1768            }
1769            // The caller places the trailing declarators; each is exported too.
1770            for stmt in std::iter::once(id).chain(self.trailing_declarators.clone()) {
1771                self.ast
1772                    .exported_decls
1773                    .push(ExportedDecl { stmt, export_span });
1774            }
1775            return Some(id);
1776        }
1777
1778        match self.peek().kind {
1779            TokenKind::LeftBrace => {
1780                // A doc comment attaches to the `export` keyword, which we've
1781                // already consumed — carry it onto the re-export node.
1782                let doc = export_tok
1783                    .leading_doc
1784                    .as_ref()
1785                    .and_then(|raw| self.parse_doc(raw))
1786                    .or_else(|| self.take_leading_doc());
1787                self.parse_export_from(export_span, nested, doc)
1788            }
1789            TokenKind::Default => {
1790                self.error_at_peek_with_help(
1791                    "`export default` is not supported",
1792                    vec![
1793                        "default exports are out of scope; drop `default` — a named \
1794                         `export` marks the declaration visible to sibling modules"
1795                            .to_string(),
1796                    ],
1797                );
1798                None
1799            }
1800            _ => {
1801                self.error_at_peek_with_help(
1802                    "expected a declaration after `export`",
1803                    vec![
1804                        "`export` may precede `function`, `class`, `const`, `let`, `type`, \
1805                         `interface`, or `enum`, or take the form `export { … }`"
1806                            .to_string(),
1807                    ],
1808                );
1809                None
1810            }
1811        }
1812    }
1813
1814    /// Parse Form 2 re-export: `export { a, b as c } from "./util";` or the
1815    /// bare `export { x };`. The leading `export` has already been consumed.
1816    fn parse_export_from(
1817        &mut self,
1818        export_span: Span,
1819        nested: bool,
1820        doc: Option<crate::DocComment>,
1821    ) -> Option<StmtId> {
1822        let (specs, open_span) = self.parse_specifier_list(SpecifierList::Export)?;
1823        if specs.is_empty() {
1824            self.error_at_with_help(
1825                open_span,
1826                "empty export specifier list",
1827                vec!["name what you re-export: `export { name } from \"./util\";`".to_string()],
1828            );
1829            return None;
1830        }
1831
1832        let source = if self.peek_identifier_text_is("from") {
1833            self.advance();
1834            let module_tok = self.peek().clone();
1835            let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1836                s.clone()
1837            } else {
1838                self.error_at_peek("expected module specifier string after `from`");
1839                return None;
1840            };
1841            self.advance();
1842            Some((module, module_tok.span))
1843        } else {
1844            None
1845        };
1846
1847        if !matches!(self.peek().kind, TokenKind::Semicolon) {
1848            self.error_at_peek("expected `;` after export statement");
1849            return None;
1850        }
1851        let semi = self.advance();
1852
1853        if nested {
1854            return None;
1855        }
1856
1857        parse_arena_result(
1858            self.ast.try_push_stmt(Stmt {
1859                kind: StmtKind::ExportFrom { specs, source, doc },
1860                span: self.span(export_span.start, semi.span.end),
1861            }),
1862            &mut self.fatal,
1863        )
1864    }
1865
1866    fn parse_import_decl(&mut self) -> Option<StmtId> {
1867        let doc = self.take_leading_doc();
1868        let kw = self.advance();
1869        let kw_span = kw.span;
1870
1871        let nested = self.block_depth > 0;
1872        if nested {
1873            self.error_at_with_help(
1874                kw_span,
1875                "`import` statements must appear at the top of the file",
1876                vec!["move this `import` outside any block / function body".to_string()],
1877            );
1878            // Continue parsing to consume the whole import so recovery isn't confused.
1879        }
1880
1881        let kind = self.parse_import_kind()?;
1882
1883        if !self.peek_identifier_text_is("from") {
1884            self.error_at_peek("expected `from` after import specifier list");
1885            return None;
1886        }
1887        self.advance();
1888
1889        let module_tok = self.peek().clone();
1890        let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1891            s.clone()
1892        } else {
1893            self.error_at_peek("expected module specifier string after `from`");
1894            return None;
1895        };
1896        let module_span = module_tok.span;
1897        self.advance();
1898
1899        if !matches!(self.peek().kind, TokenKind::Semicolon) {
1900            self.error_at_peek("expected `;` after import statement");
1901            return None;
1902        }
1903        let semi = self.advance();
1904
1905        if nested {
1906            return None;
1907        }
1908
1909        parse_arena_result(
1910            self.ast.try_push_stmt(Stmt {
1911                kind: StmtKind::Import {
1912                    module,
1913                    module_span,
1914                    kind,
1915                    doc,
1916                },
1917                span: self.span(kw_span.start, semi.span.end),
1918            }),
1919            &mut self.fatal,
1920        )
1921    }
1922
1923    fn parse_import_kind(&mut self) -> Option<ImportKind> {
1924        // `import type …` — TS's type-only import, accepted as a no-op synonym:
1925        // imports carry both value and type spaces here. `import type from "m"`
1926        // stays a namespace import named `type`, so `type` is a modifier only
1927        // when what follows can't be a complete import clause without it.
1928        if self.peek_identifier_text_is("type")
1929            && match self.peek_at(1).kind {
1930                TokenKind::LeftBrace | TokenKind::Star => true,
1931                TokenKind::Identifier => {
1932                    !(self.peek_at_is_word(1, "from")
1933                        && matches!(self.peek_at(2).kind, TokenKind::StringLiteral(_)))
1934                }
1935                _ => false,
1936            }
1937        {
1938            self.advance();
1939        }
1940
1941        match self.peek().kind {
1942            TokenKind::LeftBrace => self.parse_named_imports(),
1943            TokenKind::Identifier => {
1944                let local_tok = self.advance();
1945                let local_name = self.ident_from_token(&local_tok);
1946                if matches!(self.peek().kind, TokenKind::Comma) {
1947                    self.error_at_peek_with_help(
1948                        "combining default and named imports is not supported",
1949                        vec!["use two separate `import` statements".to_string()],
1950                    );
1951                    return None;
1952                }
1953                Some(ImportKind::Namespace { local_name })
1954            }
1955            // `import * as ns from "pkg"` — forgiveness synonym for `import ns from "pkg"`.
1956            // Both are identical since we have no default exports.
1957            TokenKind::Star => {
1958                self.advance();
1959                if !self.peek_identifier_text_is("as") {
1960                    self.error_at_peek_with_help(
1961                        "expected `as <name>` after `import *`",
1962                        vec![
1963                            "name the namespace binding: \
1964                             `import * as ns from \"pkg\";` (or use the \
1965                             short form: `import ns from \"pkg\";`)"
1966                                .to_string(),
1967                        ],
1968                    );
1969                    return None;
1970                }
1971                self.advance();
1972                let local_tok = self.expect_identifier("expected namespace name after `as`")?;
1973                let local_name = self.ident_from_token(&local_tok);
1974                Some(ImportKind::Namespace { local_name })
1975            }
1976            TokenKind::StringLiteral(_) => {
1977                self.error_at_peek_with_help(
1978                    "side-effect-only imports are not supported",
1979                    vec![
1980                        "name what you import: `import { name } from \"pkg\";` \
1981                         or `import ns from \"pkg\";`"
1982                            .to_string(),
1983                    ],
1984                );
1985                None
1986            }
1987            _ => {
1988                self.error_at_peek("expected `{` or namespace name after `import`");
1989                None
1990            }
1991        }
1992    }
1993
1994    fn parse_named_imports(&mut self) -> Option<ImportKind> {
1995        let (specs, open_span) = self.parse_specifier_list(SpecifierList::Import)?;
1996        if specs.is_empty() {
1997            self.error_at_with_help(
1998                open_span,
1999                "empty import specifier list",
2000                vec!["name what you import: `import { name } from \"pkg\";`".to_string()],
2001            );
2002            return None;
2003        }
2004        Some(ImportKind::Named(specs))
2005    }
2006
2007    /// Parse a `{ a, b as c }` specifier list shared by `import` and re-`export`.
2008    /// `list` says whether the specifiers bind local names (an import) and names the
2009    /// construct in diagnostics. Returns the specifiers (possibly
2010    /// empty — callers reject empty with construct-specific help) and the span of
2011    /// the opening brace.
2012    fn parse_specifier_list(
2013        &mut self,
2014        list: SpecifierList,
2015    ) -> Option<(Vec<ImportSpecifier>, Span)> {
2016        let what = list.noun();
2017        let open = self.advance();
2018        let mut specs: Vec<ImportSpecifier> = Vec::new();
2019        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2020            // Inline type-only modifier (`{ type X }`), accepted as a no-op
2021            // synonym. `{ type as t }` instead names the binding `type`.
2022            if self.peek_identifier_text_is("type")
2023                && matches!(self.peek_at(1).kind, TokenKind::Identifier)
2024                && !self.peek_at_is_word(1, "as")
2025            {
2026                self.advance();
2027            }
2028            let imported_tok =
2029                self.expect_identifier_name(&format!("expected {what} specifier name"))?;
2030            let imported_name = self.ident_from_token(&imported_tok);
2031
2032            let binds_local = list == SpecifierList::Import;
2033            let local_name = if self.peek_identifier_text_is("as") {
2034                self.advance();
2035                let local_tok = self.expect_identifier_name("expected local name after `as`")?;
2036                if binds_local {
2037                    self.reject_strict_mode_reserved_word(&local_tok);
2038                }
2039                self.ident_from_token(&local_tok)
2040            } else {
2041                if binds_local {
2042                    self.reject_strict_mode_reserved_word(&imported_tok);
2043                }
2044                imported_name.clone()
2045            };
2046
2047            specs.push(ImportSpecifier {
2048                imported_name,
2049                local_name,
2050            });
2051
2052            match self.peek().kind {
2053                TokenKind::Comma => {
2054                    self.advance();
2055                }
2056                TokenKind::RightBrace | TokenKind::Eof => {}
2057                _ => {
2058                    self.error_at_peek(format!("expected `,` or `}}` after {what} specifier"));
2059                    return None;
2060                }
2061            }
2062        }
2063
2064        if !matches!(self.peek().kind, TokenKind::RightBrace) {
2065            self.error_at_peek(format!("expected `}}` to close {what} specifier list"));
2066            return None;
2067        }
2068        self.advance();
2069
2070        Some((specs, open.span))
2071    }
2072
2073    fn parse_enum_initializer(&mut self) -> Option<EnumInitializer> {
2074        let neg = if matches!(self.peek().kind, TokenKind::Minus) {
2075            Some(self.advance())
2076        } else {
2077            None
2078        };
2079        let tok = self.advance();
2080        match &tok.kind {
2081            TokenKind::NumberLiteral(n) => {
2082                let value = if neg.is_some() { -n } else { *n };
2083                // tsc constant-folds enum initializers, which turns `-0` into `0`.
2084                let value = if value == 0.0 { 0.0 } else { value };
2085                let start = neg.as_ref().map_or(tok.span.start, |t| t.span.start);
2086                Some(EnumInitializer::Number {
2087                    value,
2088                    span: self.span(start, tok.span.end),
2089                })
2090            }
2091            TokenKind::StringLiteral(s) => {
2092                if let Some(neg_tok) = &neg {
2093                    self.error_at(
2094                        self.span(neg_tok.span.start, tok.span.end),
2095                        "cannot negate a string enum initializer",
2096                    );
2097                    return None;
2098                }
2099                Some(EnumInitializer::String {
2100                    value: s.clone(),
2101                    span: tok.span,
2102                })
2103            }
2104            _ => {
2105                let span = neg.map_or(tok.span, |t| self.span(t.span.start, tok.span.end));
2106                self.error_at(
2107                    span,
2108                    "expected a number or string literal for enum initializer",
2109                );
2110                None
2111            }
2112        }
2113    }
2114
2115    /// Whether the token at `index` is `operator` (`keyof`, `readonly`) used as a
2116    /// prefix type operator.
2117    ///
2118    /// Both are contextual keywords — valid as ordinary identifiers in TypeScript and
2119    /// Submilli — so they are only operators when a type follows. A bare type name is
2120    /// never followed by another type name, so requiring one separates `keyof T` from
2121    /// a type literally named `keyof`.
2122    fn is_contextual_type_operator(&self, index: usize, operator: &str) -> bool {
2123        let Some(tok) = self.tokens.get(index) else {
2124            return false;
2125        };
2126        if !self.token_is_word(tok, operator) {
2127            return false;
2128        }
2129        matches!(
2130            self.tokens.get(index + 1).map(|t| &t.kind),
2131            Some(
2132                TokenKind::Identifier
2133                    | TokenKind::Void
2134                    | TokenKind::LeftBrace
2135                    | TokenKind::LeftParen
2136                    | TokenKind::LeftBracket
2137                    | TokenKind::StringLiteral(_)
2138                    | TokenKind::NumberLiteral(_)
2139                    | TokenKind::BooleanLiteral(_)
2140                    | TokenKind::NullLiteral
2141            )
2142        )
2143    }
2144
2145    fn parse_type_argument_list(&mut self) -> Option<(Vec<TypeAnnotation>, u32)> {
2146        let lt = self.advance();
2147        if matches!(self.peek().kind, TokenKind::GreaterThan) {
2148            self.error_at(lt.span, "empty generic argument list `<>` is not allowed");
2149            return None;
2150        }
2151        let mut args = Vec::new();
2152        loop {
2153            args.push(self.parse_type_annotation()?);
2154            match self.peek().kind {
2155                TokenKind::Comma => {
2156                    self.advance();
2157                    if matches!(self.peek().kind, TokenKind::GreaterThan) {
2158                        let gt = self.advance();
2159                        return Some((args, gt.span.end));
2160                    }
2161                }
2162                TokenKind::GreaterThan => {
2163                    let gt = self.advance();
2164                    return Some((args, gt.span.end));
2165                }
2166                _ => {
2167                    self.error_at(
2168                        lt.span,
2169                        "expected `,` or `>` to close generic argument list",
2170                    );
2171                    return None;
2172                }
2173            }
2174        }
2175    }
2176
2177    /// The name a `class`, `interface`, `type` or `enum` declares, `noun` naming
2178    /// which. A built-in type's name is reported, as in TypeScript: `class number {}`
2179    /// would read as the built-in in some positions and the class in others.
2180    fn parse_type_decl_name(&mut self, article: &str, noun: &str) -> Option<Ident> {
2181        let name_tok = self.expect_identifier(&format!("expected {noun} name"))?;
2182        let name = self.ident_from_token(&name_tok);
2183        if BUILT_IN_TYPE_NAMES.contains(&name.name.as_str()) {
2184            self.error_at_with_help(
2185                name.span,
2186                format!(
2187                    "`{}` is a built-in type and can't be used as {article} {noun} name",
2188                    name.name
2189                ),
2190                vec![format!("rename the {noun}")],
2191            );
2192        }
2193        Some(name)
2194    }
2195
2196    fn parse_generic_param_list(&mut self) -> Option<Vec<Ident>> {
2197        let lt = self.advance();
2198        if matches!(self.peek().kind, TokenKind::GreaterThan) {
2199            self.error_at(lt.span, "empty generic parameter list `<>` is not allowed");
2200            return None;
2201        }
2202        let mut generics = Vec::new();
2203        loop {
2204            let name_tok = self.expect_identifier("expected generic parameter name")?;
2205            let name = self.ident_from_token(&name_tok);
2206            // A duplicate is reported and dropped, as `tsc` drops it: the first
2207            // parameter keeps the name, and the type arguments count against the
2208            // parameters that remain.
2209            if generics.iter().any(|g: &Ident| g.name == name.name) {
2210                self.error_at_with_help(
2211                    name.span,
2212                    format!("duplicate type parameter `{}`", name.name),
2213                    vec!["give each type parameter its own name".to_string()],
2214                );
2215            } else {
2216                generics.push(name);
2217            }
2218            match self.peek().kind {
2219                TokenKind::Comma => {
2220                    self.advance();
2221                    if matches!(self.peek().kind, TokenKind::GreaterThan) {
2222                        self.advance();
2223                        return Some(generics);
2224                    }
2225                }
2226                TokenKind::GreaterThan => {
2227                    self.advance();
2228                    return Some(generics);
2229                }
2230                _ => {
2231                    self.error_at_peek("expected `,` or `>`");
2232                    return None;
2233                }
2234            }
2235        }
2236    }
2237
2238    fn parse_param_list(&mut self, allow_param_properties: bool) -> Option<Vec<ParamDecl>> {
2239        if matches!(self.peek().kind, TokenKind::RightParen) {
2240            return Some(Vec::new());
2241        }
2242        let mut params = Vec::new();
2243        loop {
2244            let param = self.parse_param(allow_param_properties)?;
2245            let is_rest = param.rest;
2246            params.push(param);
2247            match self.peek().kind {
2248                TokenKind::Comma => {
2249                    if is_rest {
2250                        let comma = self.advance();
2251                        self.error_at(comma.span, "rest parameter must be the last parameter");
2252                        return None;
2253                    }
2254                    self.advance();
2255                    if matches!(self.peek().kind, TokenKind::RightParen) {
2256                        break;
2257                    }
2258                }
2259                TokenKind::RightParen => break,
2260                _ => {
2261                    self.error_at_peek("expected `,` or `)`");
2262                    return None;
2263                }
2264            }
2265        }
2266        self.check_parameter_order(params.iter().map(ParamOrder::of_param));
2267        Some(params)
2268    }
2269
2270    fn parse_param(&mut self, allow_param_properties: bool) -> Option<ParamDecl> {
2271        // Parameter properties: `constructor(public x: T)` declares + assigns a
2272        // field. A modifier keyword counts only when a parameter name follows it
2273        // (so a param literally named `public`/`readonly` still parses).
2274        let modifiers = if self.peek_word_is_class_modifier("public")
2275            || self.peek_word_is_class_modifier("private")
2276            || self.peek_word_is_class_modifier("readonly")
2277        {
2278            let span = self.peek().span;
2279            let parsed = self.parse_class_modifiers();
2280            if allow_param_properties {
2281                Some(parsed)
2282            } else {
2283                self.error_at_with_help(
2284                    span,
2285                    "parameter properties are only allowed in a constructor",
2286                    vec![
2287                        "move the `public`/`private`/`readonly` modifier to a constructor parameter"
2288                            .to_string(),
2289                    ],
2290                );
2291                None
2292            }
2293        } else {
2294            None
2295        };
2296
2297        let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
2298            self.advance();
2299            true
2300        } else {
2301            false
2302        };
2303        if modifiers.is_some() && rest {
2304            self.error_at_peek("a parameter property cannot be a rest parameter");
2305            return None;
2306        }
2307
2308        // Placeholder `name` is the empty identifier at the pattern's span;
2309        // the pre-infer lowering pass replaces it with a fresh `#pattern_p_N`.
2310        let (name, pattern) = match self.peek().kind {
2311            TokenKind::LeftBrace | TokenKind::LeftBracket => {
2312                if rest {
2313                    self.error_at_peek("rest parameter cannot be destructured");
2314                    return None;
2315                }
2316                if modifiers.is_some() {
2317                    self.error_at_peek("a parameter property cannot be destructured");
2318                    return None;
2319                }
2320                let binding = self.parse_binding()?;
2321                let placeholder = Ident {
2322                    name: String::new(),
2323                    span: binding.span(),
2324                };
2325                (placeholder, Some(binding))
2326            }
2327            _ => {
2328                let name_tok = self.expect_binding_identifier(if rest {
2329                    "expected identifier after `...`"
2330                } else {
2331                    "expected parameter name"
2332                })?;
2333                let name = self.ident_from_token(&name_tok);
2334                (name, None)
2335            }
2336        };
2337
2338        let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
2339        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2340            self.advance();
2341            Some(self.parse_type_annotation()?)
2342        } else if !rest && matches!(self.peek().kind, TokenKind::Equals) {
2343            None
2344        } else {
2345            if rest {
2346                self.error_at_peek_with_help(
2347                    "rest parameter requires a type annotation",
2348                    vec!["function name(...args: T[]): R { … }".to_string()],
2349                );
2350            } else {
2351                self.error_at_peek_with_help(
2352                    "parameter requires a type annotation",
2353                    vec!["add a type (`x: T`) or a default initializer (`x = value`)".to_string()],
2354                );
2355            }
2356            return None;
2357        };
2358        let default = self.parse_parameter_default(rest, optional)?;
2359        Some(ParamDecl {
2360            name,
2361            ty,
2362            default,
2363            optional,
2364            pattern,
2365            rest,
2366            modifiers,
2367        })
2368    }
2369
2370    fn parse_parameter_optional(&mut self, rest: bool, pattern: bool) -> Option<bool> {
2371        if !matches!(self.peek().kind, TokenKind::Question) {
2372            return Some(false);
2373        }
2374        let question = self.advance();
2375        if rest || pattern {
2376            self.error_at(
2377                question.span,
2378                if rest {
2379                    "a rest parameter cannot be optional"
2380                } else {
2381                    "a destructured parameter cannot be optional; use a default value"
2382                },
2383            );
2384            return None;
2385        }
2386        Some(true)
2387    }
2388
2389    /// The `= value` after a parameter, if any. An optional parameter's default is
2390    /// reported but still parsed, so the rest of the list is checked too.
2391    fn parse_parameter_default(&mut self, rest: bool, optional: bool) -> Option<Option<ExprId>> {
2392        if !matches!(self.peek().kind, TokenKind::Equals) {
2393            return Some(None);
2394        }
2395        if rest {
2396            self.error_rest_parameter_default();
2397            return None;
2398        }
2399        if optional {
2400            self.error_at_peek_with_help(
2401                "an optional parameter cannot have a default value",
2402                vec!["drop the `?`: a parameter with a default is already optional".to_string()],
2403            );
2404        }
2405        self.advance();
2406        Some(Some(self.parse_expression()?))
2407    }
2408
2409    /// Rest's default is the empty array, so an explicit one is rejected.
2410    fn error_rest_parameter_default(&mut self) {
2411        self.error_at_peek_with_help(
2412            "rest parameter cannot have a default value",
2413            vec!["omit the `= …`; an unspecified rest defaults to `[]`".to_string()],
2414        );
2415    }
2416
2417    /// A caller cannot skip an optional argument and still pass a later one, so a
2418    /// required parameter after an optional one is reported. Only the first is, and
2419    /// parsing continues.
2420    fn check_parameter_order<'p>(&mut self, params: impl IntoIterator<Item = ParamOrder<'p>>) {
2421        let mut follows_optional = false;
2422        for param in params {
2423            if param.optional {
2424                follows_optional = true;
2425                continue;
2426            }
2427            if !follows_optional || !param.required {
2428                continue;
2429            }
2430            let label = match param.name {
2431                Some(name) => format!("parameter `{name}`"),
2432                None => "destructured parameter".to_string(),
2433            };
2434            self.error_at_with_help(
2435                param.span,
2436                format!("required {label} cannot follow an optional parameter"),
2437                vec!["make it optional too (`x?: T`), give it a default (`x: T = …`), or move it before the optional parameters".to_string()],
2438            );
2439            return;
2440        }
2441    }
2442
2443    /// Drop a `;` that ASI inserted directly before `next`, where the grammar requires
2444    /// `next` and so no statement can have ended. ASI is a token-stream pre-pass with no
2445    /// parser feedback, so it cannot see that a `}` ends a `do` body rather than a
2446    /// statement, or that the `{` after a signature's return annotation opens its body.
2447    fn eat_asi_semicolon_before(&mut self, next: TokenKind) {
2448        if matches!(self.peek().kind, TokenKind::Semicolon) && self.peek_at(1).kind == next {
2449            self.advance();
2450        }
2451    }
2452
2453    fn parse_block(&mut self) -> Option<StmtId> {
2454        self.eat_asi_semicolon_before(TokenKind::LeftBrace);
2455        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2456            self.error_at_peek("expected `{`");
2457            return None;
2458        }
2459        let open = self.advance();
2460        self.block_depth += 1;
2461
2462        let mut stmts = Vec::new();
2463        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2464            if matches!(self.peek().kind, TokenKind::Semicolon) {
2465                self.advance();
2466                continue;
2467            }
2468            let pos_before = self.pos;
2469            if let Some(id) = self.parse_statement() {
2470                stmts.push(id);
2471                stmts.extend(self.take_trailing_declarators());
2472            } else {
2473                self.recover();
2474                if self.pos == pos_before && !self.is_at_eof() {
2475                    self.advance();
2476                }
2477            }
2478        }
2479
2480        self.block_depth -= 1;
2481        if !matches!(self.peek().kind, TokenKind::RightBrace) {
2482            self.error_at_peek("expected `}`");
2483            return None;
2484        }
2485        let close = self.advance();
2486
2487        let span = self.span(open.span.start, close.span.end);
2488        parse_arena_result(
2489            self.ast.try_push_stmt(Stmt {
2490                kind: StmtKind::Block(stmts),
2491                span,
2492            }),
2493            &mut self.fatal,
2494        )
2495    }
2496
2497    // Control-flow bodies accept either a braced block or a single statement;
2498    // `if (c) return x;` is identical to `if (c) { return x; }` under our type
2499    // system (forgiveness principle). Downstream passes assume a `Block`, so a
2500    // braceless statement is wrapped in one.
2501    fn parse_control_body(&mut self) -> Option<StmtId> {
2502        if matches!(self.peek().kind, TokenKind::LeftBrace) {
2503            return self.parse_block();
2504        }
2505        // A binding here would go out of scope as soon as it was made. TypeScript
2506        // or JavaScript rejects each of these, so it is reported and then parsed
2507        // as usual. Only a declaration is reported: a `let` followed by
2508        // anything else already fails to parse.
2509        let keyword = match self.peek().kind {
2510            TokenKind::Let => Some(("a", "let")),
2511            TokenKind::Const => Some(("a", "const")),
2512            TokenKind::Function => Some(("a", "function")),
2513            // A nested `class` is reported wherever it is: see `parse_class_decl`.
2514            TokenKind::Interface => Some(("an", "interface")),
2515            TokenKind::Enum => Some(("an", "enum")),
2516            _ if self.peek_identifier_text_is("type") => Some(("a", "type")),
2517            _ => None,
2518        };
2519        // After `let`/`const`, a bracket or brace starts a destructuring
2520        // pattern; after `type`, which is also an ordinary identifier, it is an
2521        // index or nothing a declaration can be.
2522        let declares = match self.peek_at(1).kind {
2523            TokenKind::Identifier => true,
2524            TokenKind::LeftBracket | TokenKind::LeftBrace => !matches!(keyword, Some((_, "type"))),
2525            _ => false,
2526        };
2527        if let Some((article, keyword)) = keyword
2528            && declares
2529        {
2530            self.error_at_peek_with_help(
2531                format!(
2532                    "{article} `{keyword}` declaration can't be the body of a statement without braces"
2533                ),
2534                vec![format!("wrap it in braces: `{{ {keyword} … }}`")],
2535            );
2536        }
2537        self.block_depth += 1;
2538        let stmt = self.parse_statement();
2539        self.block_depth -= 1;
2540        let stmt = stmt?;
2541        let mut stmts = vec![stmt];
2542        stmts.extend(self.take_trailing_declarators());
2543        let first = parse_arena_result(self.ast.try_stmt(stmt), &mut self.fatal)?.span;
2544        let last_id = stmts.last().copied().unwrap_or(stmt);
2545        let last = parse_arena_result(self.ast.try_stmt(last_id), &mut self.fatal)?.span;
2546        let span = self.span(first.start, last.end);
2547        parse_arena_result(
2548            self.ast.try_push_stmt(Stmt {
2549                kind: StmtKind::Block(stmts),
2550                span,
2551            }),
2552            &mut self.fatal,
2553        )
2554    }
2555
2556    /// A loop body may also be a lone `;`: `while (advance());` does all its work in
2557    /// the header. Branches do not accept it; see `parse_branch_body`.
2558    fn parse_loop_body(&mut self) -> Option<StmtId> {
2559        if !matches!(self.peek().kind, TokenKind::Semicolon) {
2560            return self.parse_control_body();
2561        }
2562        self.empty_body_at_semicolon()
2563    }
2564
2565    /// An `if` or `else` body. Unlike a loop, a branch cannot be a lone `;`: `if (c);`
2566    /// silently detaches the block meant to follow it. The `;` is reported, then
2567    /// consumed as an empty body so an `else` after it doesn't cascade into a second error.
2568    fn parse_branch_body(&mut self, keyword: &str) -> Option<StmtId> {
2569        if !matches!(self.peek().kind, TokenKind::Semicolon) {
2570            return self.parse_control_body();
2571        }
2572        self.error_at_peek_with_help(
2573            format!("`{keyword}` has an empty body"),
2574            vec![format!("remove the `;` after `{keyword}`")],
2575        );
2576        self.empty_body_at_semicolon()
2577    }
2578
2579    /// Consumes a lone `;` as an empty block spanning it.
2580    fn empty_body_at_semicolon(&mut self) -> Option<StmtId> {
2581        let semi = self.advance();
2582        parse_arena_result(
2583            self.ast.try_push_stmt(Stmt {
2584                kind: StmtKind::Block(vec![]),
2585                span: semi.span,
2586            }),
2587            &mut self.fatal,
2588        )
2589    }
2590
2591    fn parse_if(&mut self) -> Option<StmtId> {
2592        self.with_recursion_limit(Self::parse_if_inner)
2593    }
2594
2595    fn parse_if_inner(&mut self) -> Option<StmtId> {
2596        let kw = self.advance();
2597        let condition = self.parse_paren_condition()?;
2598        let then_block = self.parse_branch_body("if")?;
2599
2600        let else_block = if matches!(self.peek().kind, TokenKind::Else) {
2601            self.advance();
2602            if matches!(self.peek().kind, TokenKind::If) {
2603                Some(self.parse_if()?)
2604            } else {
2605                Some(self.parse_branch_body("else")?)
2606            }
2607        } else {
2608            None
2609        };
2610
2611        let end = match else_block {
2612            Some(id) => {
2613                parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
2614                    .span
2615                    .end
2616            }
2617            None => {
2618                parse_arena_result(self.ast.try_stmt(then_block), &mut self.fatal)?
2619                    .span
2620                    .end
2621            }
2622        };
2623
2624        parse_arena_result(
2625            self.ast.try_push_stmt(Stmt {
2626                kind: StmtKind::If {
2627                    condition,
2628                    then_block,
2629                    else_block,
2630                },
2631                span: self.span(kw.span.start, end),
2632            }),
2633            &mut self.fatal,
2634        )
2635    }
2636
2637    fn parse_while(&mut self) -> Option<StmtId> {
2638        let kw = self.advance();
2639        let condition = self.parse_paren_condition()?;
2640        let body = self.parse_loop_body()?;
2641        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2642            .span
2643            .end;
2644
2645        parse_arena_result(
2646            self.ast.try_push_stmt(Stmt {
2647                kind: StmtKind::While { condition, body },
2648                span: self.span(kw.span.start, body_end),
2649            }),
2650            &mut self.fatal,
2651        )
2652    }
2653
2654    // Disambiguates C-style for and for-of via non-consuming lookahead for `of`.
2655    fn parse_for(&mut self) -> Option<StmtId> {
2656        let kw = self.advance();
2657        // `for await (…)`: there are no promises to wait for, so name the
2658        // rewrite and read the loop as a plain `for`.
2659        if self.peek_identifier_text_is("await")
2660            && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
2661        {
2662            let tok = self.advance();
2663            self.error_at_with_help(
2664                tok.span,
2665                "`for await` is not supported",
2666                vec![
2667                    "calls return their values directly: remove `await` and write \
2668                     `for (const e of …)`"
2669                        .to_string(),
2670                ],
2671            );
2672        }
2673        if !matches!(self.peek().kind, TokenKind::LeftParen) {
2674            self.error_at_peek("expected `(` after `for`");
2675            return None;
2676        }
2677        self.advance();
2678
2679        if self.scan_for_of_shape() {
2680            self.parse_for_of_tail(kw)
2681        } else {
2682            self.parse_c_for_tail(kw)
2683        }
2684    }
2685
2686    // Counts bracket/angle depth to skip past optional type annotations.
2687    fn scan_for_of_shape(&self) -> bool {
2688        if !matches!(self.peek().kind, TokenKind::Let | TokenKind::Const) {
2689            return false;
2690        }
2691        // Object patterns are tentatively accepted here; `parse_for_of_tail` rejects them.
2692        let (start, mut depth) = match self.peek_at(1).kind {
2693            TokenKind::Identifier => match self.peek_at(2).kind {
2694                TokenKind::Identifier if self.peek_at_is_word(2, "of") => return true,
2695                TokenKind::Colon => (3, 0i32),
2696                _ => return false,
2697            },
2698            TokenKind::LeftBracket | TokenKind::LeftBrace => (2, 1i32),
2699            _ => return false,
2700        };
2701        let mut i = start;
2702        loop {
2703            match self.peek_at(i).kind {
2704                TokenKind::Eof => return false,
2705                TokenKind::LessThan
2706                | TokenKind::LeftParen
2707                | TokenKind::LeftBracket
2708                | TokenKind::LeftBrace => {
2709                    depth += 1;
2710                }
2711                TokenKind::GreaterThan
2712                | TokenKind::RightParen
2713                | TokenKind::RightBracket
2714                | TokenKind::RightBrace => {
2715                    if depth == 0 {
2716                        return false;
2717                    }
2718                    depth -= 1;
2719                }
2720                // A user type literally named `of` in the annotation would
2721                // misclassify here and error loudly downstream — acceptable.
2722                TokenKind::Identifier if depth == 0 && self.peek_at_is_word(i, "of") => {
2723                    return true;
2724                }
2725                TokenKind::Equals | TokenKind::Semicolon if depth == 0 => return false,
2726                _ => {}
2727            }
2728            i += 1;
2729        }
2730    }
2731
2732    // The trailing `;` of init-statement parsers doubles as the first `;` of the for header.
2733    fn parse_c_for_tail(&mut self, kw: Token) -> Option<StmtId> {
2734        let init = match self.peek().kind {
2735            TokenKind::Semicolon => {
2736                self.advance();
2737                Vec::new()
2738            }
2739            TokenKind::Let | TokenKind::Const => {
2740                self.trailing_declarators.clear();
2741                let is_const = matches!(self.peek().kind, TokenKind::Const);
2742                let mut init = vec![self.parse_let_or_const(is_const)?];
2743                init.extend(self.take_trailing_declarators());
2744                init
2745            }
2746            _ => self.parse_for_init_expressions()?,
2747        };
2748        let for_stmt = self.finish_c_for(kw, init.clone());
2749        if for_stmt.is_none() {
2750            self.discard_detached_patterns(&init);
2751        }
2752        for_stmt
2753    }
2754
2755    fn finish_c_for(&mut self, kw: Token, init: Vec<StmtId>) -> Option<StmtId> {
2756        let condition = if matches!(self.peek().kind, TokenKind::Semicolon) {
2757            None
2758        } else {
2759            Some(self.parse_expression()?)
2760        };
2761        if !matches!(self.peek().kind, TokenKind::Semicolon) {
2762            self.error_at_peek("expected `;` after `for` condition");
2763            return None;
2764        }
2765        self.advance();
2766
2767        let update = if matches!(self.peek().kind, TokenKind::RightParen) {
2768            None
2769        } else {
2770            Some(self.parse_for_update_stmt()?)
2771        };
2772        if !matches!(self.peek().kind, TokenKind::RightParen) {
2773            self.error_at_peek("expected `)` after `for` update");
2774            return None;
2775        }
2776        self.advance();
2777
2778        let body = self.parse_loop_body()?;
2779        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2780            .span
2781            .end;
2782        parse_arena_result(
2783            self.ast.try_push_stmt(Stmt {
2784                kind: StmtKind::For {
2785                    init,
2786                    condition,
2787                    update,
2788                    body,
2789                },
2790                span: self.span(kw.span.start, body_end),
2791            }),
2792            &mut self.fatal,
2793        )
2794    }
2795
2796    /// Pattern lowering expands a destructuring declaration only where a block, the
2797    /// program or a `for` head holds it, so one parsed into the arena whose
2798    /// statement then failed (a `for` loop, a later declarator, a nested `export`)
2799    /// would otherwise reach inference unlowered.
2800    fn discard_detached_patterns(&mut self, stmts: &[StmtId]) {
2801        for &id in stmts {
2802            let Some(stmt) = parse_arena_result(self.ast.try_stmt_mut(id), &mut self.fatal) else {
2803                return;
2804            };
2805            if matches!(
2806                stmt.kind,
2807                StmtKind::LetPattern { .. } | StmtKind::ConstPattern { .. }
2808            ) {
2809                stmt.kind = StmtKind::Block(Vec::new());
2810            }
2811        }
2812    }
2813
2814    /// `i = 0, j = n` in a `for` initializer runs each expression in turn, so each
2815    /// becomes its own statement, as each declarator of a `let` head does.
2816    fn parse_for_init_expressions(&mut self) -> Option<Vec<StmtId>> {
2817        let mut init = vec![self.parse_for_clause_expr()?];
2818        while matches!(self.peek().kind, TokenKind::Comma) {
2819            self.advance();
2820            init.push(self.parse_for_clause_expr()?);
2821        }
2822        if !self.at_statement_end() {
2823            let last = parse_arena_result(self.ast.try_stmt(*init.last()?), &mut self.fatal)?;
2824            let what = if matches!(last.kind, StmtKind::Expr(_)) {
2825                "expression"
2826            } else {
2827                "assignment"
2828            };
2829            self.error_at_peek(format!("expected `;` after {what}"));
2830            return None;
2831        }
2832        self.finish_statement();
2833        Some(init)
2834    }
2835
2836    /// `i++, j--` in a `for` update runs each expression in turn, so it becomes a
2837    /// block of their statements; it declares nothing, so the block scopes nothing.
2838    fn parse_for_update_stmt(&mut self) -> Option<StmtId> {
2839        let first = self.parse_for_clause_expr()?;
2840        if !matches!(self.peek().kind, TokenKind::Comma) {
2841            return Some(first);
2842        }
2843        let mut stmts = vec![first];
2844        while matches!(self.peek().kind, TokenKind::Comma) {
2845            self.advance();
2846            stmts.push(self.parse_for_clause_expr()?);
2847        }
2848        let start = parse_arena_result(self.ast.try_stmt(first), &mut self.fatal)?
2849            .span
2850            .start;
2851        let span = self.span(start, self.prev_token_end());
2852        parse_arena_result(
2853            self.ast.try_push_stmt(Stmt {
2854                kind: StmtKind::Block(stmts),
2855                span,
2856            }),
2857            &mut self.fatal,
2858        )
2859    }
2860
2861    fn parse_for_clause_expr(&mut self) -> Option<StmtId> {
2862        let expr_id = self.parse_expression()?;
2863        let span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
2864        let kind = self.statement_kind_for(expr_id)?;
2865        parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
2866    }
2867
2868    fn parse_for_of_tail(&mut self, kw: Token) -> Option<StmtId> {
2869        let binding_tok = self.advance();
2870        let binding_kind = match binding_tok.kind {
2871            TokenKind::Let => crate::BindingKind::Let,
2872            TokenKind::Const => crate::BindingKind::Const,
2873            _ => return self.invariant_failure("scan_for_of_shape gated this"),
2874        };
2875        let (name, binding) = match self.peek().kind {
2876            TokenKind::LeftBracket => {
2877                let b = self.parse_binding()?;
2878                (None, Some(b))
2879            }
2880            TokenKind::LeftBrace => {
2881                let b = self.parse_binding()?;
2882                self.error_at(
2883                    b.span(),
2884                    "object destructuring is not supported in `for-of`; use array destructuring or unpack inside the loop body",
2885                );
2886                return None;
2887            }
2888            _ => {
2889                let name_tok = self.expect_binding_identifier(
2890                    "expected identifier after `let`/`const` in `for-of`",
2891                )?;
2892                (Some(self.ident_from_token(&name_tok)), None)
2893            }
2894        };
2895        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2896            self.advance();
2897            Some(self.parse_type_annotation()?)
2898        } else {
2899            None
2900        };
2901        if !self.peek_identifier_text_is("of") {
2902            self.error_at_peek("expected `of` in `for-of`");
2903            return None;
2904        }
2905        self.advance();
2906
2907        let iter = self.parse_expression()?;
2908        if !matches!(self.peek().kind, TokenKind::RightParen) {
2909            self.error_at_peek("expected `)` after `for-of` iterable");
2910            return None;
2911        }
2912        self.advance();
2913
2914        let body = self.parse_loop_body()?;
2915        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2916            .span
2917            .end;
2918        let kind = match (binding, name) {
2919            (Some(binding), _) => StmtKind::ForOfPattern {
2920                binding_kind,
2921                binding,
2922                ty,
2923                iter,
2924                body,
2925            },
2926            (None, Some(name)) => StmtKind::ForOf {
2927                binding_kind,
2928                name,
2929                ty,
2930                iter,
2931                body,
2932            },
2933            (None, None) => return self.invariant_failure("either binding or name must be Some"),
2934        };
2935        parse_arena_result(
2936            self.ast.try_push_stmt(Stmt {
2937                kind,
2938                span: self.span(kw.span.start, body_end),
2939            }),
2940            &mut self.fatal,
2941        )
2942    }
2943
2944    fn parse_do_while(&mut self) -> Option<StmtId> {
2945        let kw = self.advance();
2946        let body = self.parse_loop_body()?;
2947        self.eat_asi_semicolon_before(TokenKind::While);
2948        if !matches!(self.peek().kind, TokenKind::While) {
2949            self.error_at_peek("expected `while` after `do` body");
2950            return None;
2951        }
2952        self.advance();
2953        let condition = self.parse_paren_condition()?;
2954        // ECMAScript inserts the terminator after a do-while's `)` unconditionally, so the
2955        // `;` is optional here — ASI can't supply it, since that `)` closes a `while`
2956        // header everywhere else.
2957        let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
2958            self.advance().span.end
2959        } else {
2960            parse_arena_result(self.ast.try_expr(condition), &mut self.fatal)?
2961                .span
2962                .end
2963        };
2964        parse_arena_result(
2965            self.ast.try_push_stmt(Stmt {
2966                kind: StmtKind::DoWhile { body, condition },
2967                span: self.span(kw.span.start, end),
2968            }),
2969            &mut self.fatal,
2970        )
2971    }
2972
2973    // Multiple consecutive `case`/`default` labels share the next body.
2974    fn parse_switch(&mut self) -> Option<StmtId> {
2975        let kw = self.advance();
2976        if !matches!(self.peek().kind, TokenKind::LeftParen) {
2977            self.error_at_peek("expected `(` after `switch`");
2978            return None;
2979        }
2980        self.advance();
2981        let discriminant = self.parse_expression()?;
2982        if !matches!(self.peek().kind, TokenKind::RightParen) {
2983            self.error_at_peek("expected `)` after `switch` discriminant");
2984            return None;
2985        }
2986        self.advance();
2987
2988        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2989            self.error_at_peek("expected `{` to start `switch` body");
2990            return None;
2991        }
2992        self.advance();
2993        self.block_depth += 1;
2994        let arms = self.parse_switch_arms();
2995        self.block_depth -= 1;
2996        let (cases, default) = arms?;
2997
2998        if !matches!(self.peek().kind, TokenKind::RightBrace) {
2999            self.error_at_peek("expected `}` to close `switch` body");
3000            return None;
3001        }
3002        let close = self.advance();
3003
3004        parse_arena_result(
3005            self.ast.try_push_stmt(Stmt {
3006                kind: StmtKind::Switch {
3007                    discriminant,
3008                    cases,
3009                    default,
3010                },
3011                span: self.span(kw.span.start, close.span.end),
3012            }),
3013            &mut self.fatal,
3014        )
3015    }
3016
3017    // Reads the arms up to the closing `}`, which is left for the caller.
3018    fn parse_switch_arms(&mut self) -> Option<(Vec<SwitchCase>, Option<SwitchDefault>)> {
3019        let mut cases: Vec<SwitchCase> = Vec::new();
3020        let mut default: Option<SwitchDefault> = None;
3021        let mut pending_values: Vec<ExprId> = Vec::new();
3022        let mut pending_label_start: Option<u32> = None;
3023        let mut pending_default: Option<Span> = None;
3024
3025        loop {
3026            match self.peek().kind {
3027                TokenKind::Eof => break,
3028                // ASI inserts `;` after `:` on a newline — treat it as a no-op here.
3029                TokenKind::Semicolon => {
3030                    self.advance();
3031                }
3032                TokenKind::RightBrace => {
3033                    // Case bodies are required (typechecker enforces break/return termination).
3034                    if pending_label_start.is_some() {
3035                        self.error_at_peek(
3036                            "expected case body before `}` — case labels must be followed by statements",
3037                        );
3038                        return None;
3039                    }
3040                    break;
3041                }
3042                TokenKind::Case => {
3043                    let case_kw = self.advance();
3044                    if pending_label_start.is_none() {
3045                        pending_label_start = Some(case_kw.span.start);
3046                    }
3047                    let value = self.parse_expression()?;
3048                    if !matches!(self.peek().kind, TokenKind::Colon) {
3049                        self.error_at_peek("expected `:` after `case` label");
3050                        return None;
3051                    }
3052                    self.advance();
3053                    pending_values.push(value);
3054                }
3055                TokenKind::Default => {
3056                    let kw_tok = self.advance();
3057                    if default.is_some() {
3058                        self.error_at(kw_tok.span, "duplicate `default` clause in `switch`");
3059                        return None;
3060                    }
3061                    if !matches!(self.peek().kind, TokenKind::Colon) {
3062                        self.error_at_peek("expected `:` after `default`");
3063                        return None;
3064                    }
3065                    self.advance();
3066                    if pending_label_start.is_none() {
3067                        pending_label_start = Some(kw_tok.span.start);
3068                    }
3069                    pending_default = Some(kw_tok.span);
3070                }
3071                _ => {
3072                    if pending_label_start.is_none() {
3073                        self.error_at_peek(
3074                            "expected `case` or `default` at the start of a `switch` body",
3075                        );
3076                        return None;
3077                    }
3078                    let Some(label_start) = pending_label_start else {
3079                        return self.invariant_failure("switch arm has no label");
3080                    };
3081                    let body = self.parse_switch_arm_body(label_start)?;
3082                    let arm_span =
3083                        parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
3084                    if !pending_values.is_empty() {
3085                        let first = parse_arena_result(
3086                            self.ast.try_expr(pending_values[0]),
3087                            &mut self.fatal,
3088                        )?
3089                        .span
3090                        .start;
3091                        let case_span = self.span(first, arm_span.end);
3092                        cases.push(SwitchCase {
3093                            values: std::mem::take(&mut pending_values),
3094                            body,
3095                            span: case_span,
3096                        });
3097                    }
3098                    if let Some(def_kw_span) = pending_default.take() {
3099                        default = Some(SwitchDefault {
3100                            body,
3101                            span: self.span(def_kw_span.start, arm_span.end),
3102                        });
3103                    }
3104                    pending_label_start = None;
3105                }
3106            }
3107        }
3108        Some((cases, default))
3109    }
3110
3111    // `body_start` is the label keyword's offset so diagnostics point at the label.
3112    fn parse_switch_arm_body(&mut self, body_start: u32) -> Option<StmtId> {
3113        let mut stmts: Vec<StmtId> = Vec::new();
3114        let mut end_offset = body_start;
3115        loop {
3116            if matches!(
3117                self.peek().kind,
3118                TokenKind::Case | TokenKind::Default | TokenKind::RightBrace | TokenKind::Eof
3119            ) {
3120                break;
3121            }
3122            if matches!(self.peek().kind, TokenKind::Semicolon) {
3123                end_offset = self.peek().span.end;
3124                self.advance();
3125                continue;
3126            }
3127            let pos_before = self.pos;
3128            if let Some(id) = self.parse_statement() {
3129                stmts.push(id);
3130                stmts.extend(self.take_trailing_declarators());
3131                let last = stmts.last().copied().unwrap_or(id);
3132                end_offset = parse_arena_result(self.ast.try_stmt(last), &mut self.fatal)?
3133                    .span
3134                    .end;
3135            } else {
3136                self.recover();
3137                if self.pos == pos_before && !self.is_at_eof() {
3138                    self.advance();
3139                }
3140                // Recovery consumed tokens but produced no statement; advance the body
3141                // end past them so the arm span can't end before its own case value.
3142                end_offset = end_offset.max(self.prev_token_end());
3143            }
3144        }
3145        let span = self.span(body_start, end_offset);
3146        parse_arena_result(
3147            self.ast.try_push_stmt(Stmt {
3148                kind: StmtKind::Block(stmts),
3149                span,
3150            }),
3151            &mut self.fatal,
3152        )
3153    }
3154
3155    fn parse_break(&mut self) -> Option<StmtId> {
3156        let kw = self.advance();
3157        if !matches!(self.peek().kind, TokenKind::Semicolon) {
3158            self.error_at_peek("expected `;` after `break`");
3159            return None;
3160        }
3161        let semi = self.advance();
3162        parse_arena_result(
3163            self.ast.try_push_stmt(Stmt {
3164                kind: StmtKind::Break,
3165                span: self.span(kw.span.start, semi.span.end),
3166            }),
3167            &mut self.fatal,
3168        )
3169    }
3170
3171    fn parse_continue(&mut self) -> Option<StmtId> {
3172        let kw = self.advance();
3173        if !matches!(self.peek().kind, TokenKind::Semicolon) {
3174            self.error_at_peek("expected `;` after `continue`");
3175            return None;
3176        }
3177        let semi = self.advance();
3178        parse_arena_result(
3179            self.ast.try_push_stmt(Stmt {
3180                kind: StmtKind::Continue,
3181                span: self.span(kw.span.start, semi.span.end),
3182            }),
3183            &mut self.fatal,
3184        )
3185    }
3186
3187    fn parse_return(&mut self) -> Option<StmtId> {
3188        let kw = self.advance();
3189
3190        let value = if matches!(self.peek().kind, TokenKind::Semicolon) {
3191            None
3192        } else {
3193            Some(self.parse_expression()?)
3194        };
3195
3196        if !self.at_statement_end() {
3197            self.error_at_peek("expected `;` after return");
3198            return None;
3199        }
3200        let end = self.finish_statement();
3201
3202        parse_arena_result(
3203            self.ast.try_push_stmt(Stmt {
3204                kind: StmtKind::Return(value),
3205                span: self.span(kw.span.start, end),
3206            }),
3207            &mut self.fatal,
3208        )
3209    }
3210
3211    fn parse_throw(&mut self) -> Option<StmtId> {
3212        let kw = self.advance();
3213
3214        if matches!(self.peek().kind, TokenKind::Semicolon) {
3215            self.error_at_peek_with_help(
3216                "expected expression after `throw`",
3217                vec!["throw new Error(\"message\");".to_string()],
3218            );
3219            return None;
3220        }
3221
3222        let value = self.parse_expression()?;
3223
3224        if !self.at_statement_end() {
3225            self.error_at_peek("expected `;` after `throw`");
3226            return None;
3227        }
3228        let end = self.finish_statement();
3229
3230        parse_arena_result(
3231            self.ast.try_push_stmt(Stmt {
3232                kind: StmtKind::Throw { value },
3233                span: self.span(kw.span.start, end),
3234            }),
3235            &mut self.fatal,
3236        )
3237    }
3238
3239    fn parse_try(&mut self) -> Option<StmtId> {
3240        let kw = self.advance();
3241
3242        let body = self.parse_block()?;
3243
3244        let mut catches: Vec<CatchClause> = Vec::new();
3245        let mut finally: Option<StmtId> = None;
3246        let mut tail_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3247            .span
3248            .end;
3249
3250        loop {
3251            match self.peek().kind {
3252                TokenKind::Catch => {
3253                    if finally.is_some() {
3254                        self.error_at_peek("`catch` clause must appear before `finally`");
3255                        return None;
3256                    }
3257                    let clause = self.parse_catch_clause()?;
3258                    tail_end = clause.span.end;
3259                    catches.push(clause);
3260                }
3261                TokenKind::Finally => {
3262                    if finally.is_some() {
3263                        self.error_at_peek("duplicate `finally` clause");
3264                        return None;
3265                    }
3266                    self.advance();
3267                    let block = self.parse_block()?;
3268                    tail_end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
3269                        .span
3270                        .end;
3271                    finally = Some(block);
3272                }
3273                _ => break,
3274            }
3275        }
3276
3277        if catches.is_empty() && finally.is_none() {
3278            self.error_at(
3279                self.span(kw.span.start, tail_end),
3280                "try requires at least one of `catch` or `finally`",
3281            );
3282            return None;
3283        }
3284
3285        parse_arena_result(
3286            self.ast.try_push_stmt(Stmt {
3287                kind: StmtKind::Try {
3288                    body,
3289                    catches,
3290                    finally,
3291                },
3292                span: self.span(kw.span.start, tail_end),
3293            }),
3294            &mut self.fatal,
3295        )
3296    }
3297
3298    fn parse_catch_clause(&mut self) -> Option<CatchClause> {
3299        let kw = self.advance();
3300
3301        let (binding, ty) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
3302            // This binding is inaccessible to source code, including nested catches.
3303            (
3304                Ident {
3305                    name: "#catch".into(),
3306                    span: kw.span,
3307                },
3308                None,
3309            )
3310        } else {
3311            self.parse_catch_binding()?
3312        };
3313
3314        let body = self.parse_block()?;
3315        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3316            .span
3317            .end;
3318
3319        Some(CatchClause {
3320            binding,
3321            ty,
3322            body,
3323            span: self.span(kw.span.start, body_end),
3324        })
3325    }
3326
3327    fn parse_catch_binding(&mut self) -> Option<(Ident, Option<TypeAnnotation>)> {
3328        if !matches!(self.peek().kind, TokenKind::LeftParen) {
3329            self.error_at_peek("expected `(` after `catch`");
3330            return None;
3331        }
3332        self.advance();
3333        let name_tok = self.expect_binding_identifier("expected catch binding name")?;
3334        let binding = self.ident_from_token(&name_tok);
3335        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3336            self.advance();
3337            if self.reject_any_catch_annotation() {
3338                // The error is reported; parsing continues as an unannotated catch.
3339                None
3340            } else {
3341                Some(self.parse_type_annotation()?)
3342            }
3343        } else {
3344            None
3345        };
3346        if !matches!(self.peek().kind, TokenKind::RightParen) {
3347            self.error_at_peek("expected `)` after catch binding");
3348            return None;
3349        }
3350        self.advance();
3351        Some((binding, ty))
3352    }
3353
3354    /// Reports `catch (e: any)` and consumes the `any`, returning whether it did.
3355    /// Only exactly `any)` is handled; compound types keep the general diagnostic.
3356    fn reject_any_catch_annotation(&mut self) -> bool {
3357        let is_bare_any = self.peek_identifier_text_is("any")
3358            && matches!(self.peek_at(1).kind, TokenKind::RightParen);
3359        if !is_bare_any {
3360            return false;
3361        }
3362        let any_tok = self.advance();
3363        // The general `any` help suggests `unknown`, which a catch binding refuses.
3364        self.error_at_with_help(
3365            any_tok.span,
3366            "`any` is not supported",
3367            vec![
3368                "write `catch (e)` to catch every thrown error, or name an error class, \
3369                 as in `catch (e: RangeError)`, to catch only that type"
3370                    .to_string(),
3371            ],
3372        );
3373        true
3374    }
3375
3376    fn parse_paren_condition(&mut self) -> Option<ExprId> {
3377        if !matches!(self.peek().kind, TokenKind::LeftParen) {
3378            self.error_at_peek("expected `(`");
3379            return None;
3380        }
3381        self.advance();
3382        let expr = self.parse_expression()?;
3383        if !matches!(self.peek().kind, TokenKind::RightParen) {
3384            self.error_at_peek("expected `)`");
3385            return None;
3386        }
3387        self.advance();
3388        Some(expr)
3389    }
3390
3391    fn parse_predicate_or_return_type(
3392        &mut self,
3393        type_pos: TypePos,
3394    ) -> Option<(
3395        Option<TypeAnnotation>,
3396        Option<crate::TypePredicateAnnotation>,
3397    )> {
3398        if matches!(self.peek().kind, TokenKind::Identifier) && self.peek_at_is_word(1, "is") {
3399            let param_tok = self.advance();
3400            let param = self.ident_from_token(&param_tok);
3401            self.advance();
3402            let asserted = self.parse_type_annotation_in(type_pos)?;
3403            let span = self.span(param.span.start, asserted.span.end);
3404            return Some((
3405                None,
3406                Some(crate::TypePredicateAnnotation {
3407                    param,
3408                    asserted,
3409                    span,
3410                }),
3411            ));
3412        }
3413        Some((Some(self.parse_type_annotation_in(type_pos)?), None))
3414    }
3415
3416    fn parse_type_annotation(&mut self) -> Option<TypeAnnotation> {
3417        self.parse_type_annotation_in(TypePos::Anywhere)
3418    }
3419
3420    fn parse_type_annotation_in(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3421        // A leading `|` (prettier's multiline format) is accepted and discarded.
3422        let leading_pipe = matches!(self.peek().kind, TokenKind::Pipe);
3423        if leading_pipe {
3424            self.advance();
3425        }
3426        let first = self.parse_type_array(type_pos)?;
3427        if !matches!(self.peek().kind, TokenKind::Pipe) {
3428            return Some(first);
3429        }
3430        let start = first.span.start;
3431        let mut end = first.span.end;
3432        let mut members = vec![first];
3433        while matches!(self.peek().kind, TokenKind::Pipe) {
3434            self.advance();
3435            let member = self.parse_type_array(type_pos)?;
3436            end = member.span.end;
3437            members.push(member);
3438        }
3439        Some(TypeAnnotation {
3440            kind: TypeAnnotationKind::Union(members),
3441            span: self.span(start, end),
3442        })
3443    }
3444
3445    fn parse_type_array(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3446        self.with_recursion_limit(|parser| parser.parse_type_array_inner(type_pos))
3447    }
3448
3449    fn parse_type_array_inner(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3450        // `keyof` is contextual, not reserved: TypeScript allows it as an ordinary
3451        // identifier, and so does Submilli. It is an operator only when another type
3452        // follows it, which an identifier in type position never does.
3453        if self.is_contextual_type_operator(self.pos, "keyof") {
3454            let kw = self.advance();
3455            let operand = self.parse_type_array(type_pos)?;
3456            let span = self.span(kw.span.start, operand.span.end);
3457            return Some(TypeAnnotation {
3458                kind: TypeAnnotationKind::KeyOf(Box::new(operand)),
3459                span,
3460            });
3461        }
3462        if self.is_contextual_type_operator(self.pos, "readonly") {
3463            return self.parse_readonly_type(type_pos);
3464        }
3465
3466        let mut ty = match self.peek().kind {
3467            // `typeof x` names the type of a *value*. Unlike `keyof` this is already a
3468            // reserved word (the `typeof x === "string"` guard uses it), so it needs no
3469            // contextual check — in type position it is only ever the operator. Built
3470            // here rather than returned early so the `[]` suffix loop below still runs,
3471            // making `typeof a[]` an array of it, as TypeScript parses it too.
3472            TokenKind::Typeof => {
3473                let kw = self.advance();
3474                let first = self.expect_identifier("expected a value name after `typeof`")?;
3475                let mut path = vec![self.type_name(first.span)?];
3476                let mut end = first.span.end;
3477                while matches!(self.peek().kind, TokenKind::Dot) {
3478                    self.advance();
3479                    let seg = self.expect_identifier_name("expected a property name after `.`")?;
3480                    end = seg.span.end;
3481                    path.push(self.type_name(seg.span)?);
3482                }
3483                TypeAnnotation {
3484                    kind: TypeAnnotationKind::TypeOf { path },
3485                    span: self.span(kw.span.start, end),
3486                }
3487            }
3488            TokenKind::Identifier | TokenKind::Void => {
3489                // `void` never starts a qualified path.
3490                let leading_is_identifier = matches!(self.peek().kind, TokenKind::Identifier);
3491                let tok = self.advance();
3492                let first_span = tok.span;
3493                let mut path_end = first_span.end;
3494                let first_name = self.type_name(first_span)?;
3495                let mut path = vec![first_name.clone()];
3496                if leading_is_identifier {
3497                    while matches!(self.peek().kind, TokenKind::Dot) {
3498                        self.advance();
3499                        if !matches!(self.peek().kind, TokenKind::Identifier) {
3500                            self.error_at_peek("expected identifier after `.` in type name");
3501                            return None;
3502                        }
3503                        let seg = self.advance();
3504                        path_end = seg.span.end;
3505                        path.push(self.type_name(seg.span)?);
3506                    }
3507                }
3508                // Type arguments, like the `[]` suffix, must start on the type's line.
3509                let (args, end) = if matches!(self.peek().kind, TokenKind::LessThan)
3510                    && !self.line_break_before_peek()
3511                {
3512                    self.parse_type_argument_list()?
3513                } else {
3514                    (Vec::new(), path_end)
3515                };
3516                let outer_span = self.span(first_span.start, end);
3517                let kind = if path.len() == 1 {
3518                    if first_name.name == "any" {
3519                        self.error_at_with_help(
3520                            first_span,
3521                            "`any` is not supported",
3522                            vec![
3523                                "annotate a concrete type instead — an object shape \
3524                                 like `{ id: string }`, a named `interface`, or \
3525                                 `unknown` (a safe dynamic type you narrow before use)"
3526                                    .to_string(),
3527                            ],
3528                        );
3529                    }
3530                    TypeAnnotationKind::Name {
3531                        name: first_name,
3532                        args,
3533                    }
3534                } else {
3535                    TypeAnnotationKind::Qualified { path, args }
3536                };
3537                TypeAnnotation {
3538                    kind,
3539                    span: outer_span,
3540                }
3541            }
3542            // `null<T>` is meaningless — no type args after `null`.
3543            TokenKind::NullLiteral => {
3544                let tok = self.advance();
3545                TypeAnnotation {
3546                    kind: TypeAnnotationKind::Name {
3547                        name: self.type_name(tok.span)?,
3548                        args: Vec::new(),
3549                    },
3550                    span: tok.span,
3551                }
3552            }
3553            TokenKind::StringLiteral(_) => {
3554                let tok = self.advance();
3555                let span = tok.span;
3556                let TokenKind::StringLiteral(s) = tok.kind else {
3557                    // advance returns the token just matched, before moving the cursor.
3558                    unreachable!("advance preserves the matched StringLiteral variant");
3559                };
3560                TypeAnnotation {
3561                    kind: TypeAnnotationKind::StringLiteral(s),
3562                    span,
3563                }
3564            }
3565            TokenKind::NumberLiteral(_) => {
3566                let tok = self.advance();
3567                let span = tok.span;
3568                let TokenKind::NumberLiteral(v) = tok.kind else {
3569                    // advance returns the token just matched, before moving the cursor.
3570                    unreachable!("advance preserves the matched NumberLiteral variant");
3571                };
3572                // Canonicalize `-0.0` → `0.0` so literal type `0` matches both signs.
3573                let canonical = if v == 0.0 { 0.0 } else { v };
3574                TypeAnnotation {
3575                    kind: TypeAnnotationKind::NumberLiteral(crate::types::LiteralF64(canonical)),
3576                    span,
3577                }
3578            }
3579            TokenKind::BigIntLiteral(_) => {
3580                let tok = self.advance();
3581                let TokenKind::BigIntLiteral(digits) = tok.kind else {
3582                    // advance returns the token just matched, before moving the cursor.
3583                    unreachable!("advance preserves the matched BigIntLiteral variant");
3584                };
3585                TypeAnnotation {
3586                    kind: TypeAnnotationKind::BigIntLiteral(digits),
3587                    span: tok.span,
3588                }
3589            }
3590            TokenKind::Minus
3591                if matches!(
3592                    self.peek_at(1).kind,
3593                    TokenKind::NumberLiteral(_) | TokenKind::BigIntLiteral(_)
3594                ) =>
3595            {
3596                let minus = self.advance().span;
3597                let tok = self.advance();
3598                let span = self.span(minus.start, tok.span.end);
3599                let kind =
3600                    match tok.kind {
3601                        TokenKind::NumberLiteral(v) => TypeAnnotationKind::NumberLiteral(
3602                            crate::types::LiteralF64(if v == 0.0 { 0.0 } else { -v }),
3603                        ),
3604                        TokenKind::BigIntLiteral(digits) => TypeAnnotationKind::BigIntLiteral(
3605                            crate::types::negate_bigint_digits(&digits),
3606                        ),
3607                        // The guard admits only the two numeric literal tokens.
3608                        _ => unreachable!("guarded to a numeric literal after `-`"),
3609                    };
3610                TypeAnnotation { kind, span }
3611            }
3612            TokenKind::BooleanLiteral(value) => {
3613                let span = self.advance().span;
3614                TypeAnnotation {
3615                    kind: TypeAnnotationKind::BooleanLiteral(value),
3616                    span,
3617                }
3618            }
3619            TokenKind::LeftBrace => self.parse_object_type_annotation()?,
3620            TokenKind::LeftParen if self.paren_at_opens_function_type(self.pos, type_pos) => {
3621                self.parse_function_type_annotation()?
3622            }
3623            TokenKind::LeftParen => self.parse_grouped_or_function_type()?,
3624            // Leading `[` is unambiguously a tuple (postfix `[]` can only follow a base type).
3625            TokenKind::LeftBracket => self.parse_tuple_type_annotation()?,
3626            _ => {
3627                self.error_at_peek("expected type");
3628                return None;
3629            }
3630        };
3631        // Array suffixes nest boxed annotations without recursing, so each one
3632        // spends the recursive grammar budget to keep the chain's drop bounded.
3633        let mut array_suffixes = 0usize;
3634        // As in TypeScript, a line break ends the type before `[`: in an object type,
3635        // `a: B` ⏎ `[k: string]: V` is a field followed by an index signature.
3636        while matches!(self.peek().kind, TokenKind::LeftBracket) && !self.line_break_before_peek() {
3637            array_suffixes += 1;
3638            if self.recursion_depth.saturating_add(array_suffixes) >= MAX_PARSE_DEPTH {
3639                self.stop_at_recursion_limit();
3640                return None;
3641            }
3642            let open = self.advance();
3643            if !matches!(self.peek().kind, TokenKind::RightBracket) {
3644                self.error_at(open.span, "expected `]` to close array type");
3645                return None;
3646            }
3647            let close = self.advance();
3648            let span = self.span(ty.span.start, close.span.end);
3649            ty = TypeAnnotation {
3650                kind: TypeAnnotationKind::Array(Box::new(ty)),
3651                span,
3652            };
3653        }
3654        Some(ty)
3655    }
3656
3657    /// `readonly T[]` or `readonly [A, B]`. The operand binds like `keyof`'s, so
3658    /// `readonly T[][]` is a readonly array of mutable arrays and
3659    /// `readonly T[] | null` a union with a readonly member.
3660    fn parse_readonly_type(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3661        let kw = self.advance();
3662        let opens_group = matches!(self.peek().kind, TokenKind::LeftParen);
3663        let operand = self.parse_type_array(type_pos)?;
3664        // `readonly (T[])` is rejected as TypeScript rejects it: the operand must be
3665        // written as an array or tuple type, not grouped into one.
3666        let grouped = opens_group
3667            && matches!(
3668                self.pos
3669                    .checked_sub(1)
3670                    .and_then(|index| self.tokens.get(index))
3671                    .map(|token| &token.kind),
3672                Some(TokenKind::RightParen)
3673            );
3674        if grouped
3675            || !matches!(
3676                operand.kind,
3677                TypeAnnotationKind::Array(_) | TypeAnnotationKind::Tuple(_)
3678            )
3679        {
3680            self.error_at_with_help(
3681                kw.span,
3682                "`readonly` only applies to array and tuple types",
3683                vec![
3684                    "write `readonly T[]` or `readonly [A, B]`; to protect an object's \
3685                     properties, mark each one `readonly` instead"
3686                        .to_string(),
3687                ],
3688            );
3689            return Some(operand);
3690        }
3691        let span = self.span(kw.span.start, operand.span.end);
3692        Some(TypeAnnotation {
3693            kind: TypeAnnotationKind::Readonly(Box::new(operand)),
3694            span,
3695        })
3696    }
3697
3698    /// In type position `(` is ambiguous: a function type's parameter list, or a
3699    /// grouping paren the postfix `[]` / `|` then composes with. Decided by what the
3700    /// parens *contain*, not by what follows them — a trailing `=>` can belong to an
3701    /// enclosing arrow function's body (`(): (string | null) => null`), so looking
3702    /// past the `)` would read that return type as a parameter list. The exception is
3703    /// `(T)` / `(T, U)`, spelled identically either way, which only a trailing `=>`
3704    /// can break.
3705    ///
3706    /// Empty parens count as a parameter list: `()` groups nothing, so the
3707    /// function-type parse's "expected `=>`" is the message that names the fix.
3708    fn paren_at_opens_function_type(&self, index: usize, type_pos: TypePos) -> bool {
3709        let after = |offset: usize| self.tokens.get(index + offset).map(|t| &t.kind);
3710        match (after(1), after(2)) {
3711            // Only a parameter list is empty or starts with a rest element.
3712            (Some(TokenKind::RightParen | TokenKind::DotDotDot), _) => true,
3713            // A named, annotated parameter — no type starts this way.
3714            (Some(TokenKind::Identifier), Some(TokenKind::Colon | TokenKind::Question)) => true,
3715            // `(T)` and `(T, U)` are spelled exactly like a grouped type. v1
3716            // rejects bare parameter names, and a trailing `=>` is the only sign
3717            // the writer meant one — routing those to the function-type parse
3718            // keeps its "params require named annotations" message reachable in
3719            // declaration position (`type F = (T) => R`). In an arrow's *return*
3720            // annotation the trailing `=>` is the body's, so `(): (T) => x` loses
3721            // that message and falls back to the expression parse; dropping the
3722            // redundant parens is the fix there.
3723            (Some(TokenKind::Identifier), Some(TokenKind::Comma | TokenKind::RightParen)) => {
3724                type_pos == TypePos::Anywhere
3725                    && self.index_after_matching_paren(index).is_some_and(|i| {
3726                        matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow))
3727                    })
3728            }
3729            // Anything else can only begin a type, so the parens group.
3730            _ => false,
3731        }
3732    }
3733
3734    /// Parse a `(` the contents rule called a group. If that fails and a `=>` follows
3735    /// the matching `)`, the author was writing a function type after all — a typo in
3736    /// its parameter list is what made it look like a group — so re-parse that way to
3737    /// recover the diagnostic naming the parameter rule instead of a stray-`)` error.
3738    fn parse_grouped_or_function_type(&mut self) -> Option<TypeAnnotation> {
3739        let open = self.pos;
3740        let diagnostics_before = self.diagnostics.len();
3741        if let Some(ty) = self.parse_grouped_type_annotation() {
3742            return Some(ty);
3743        }
3744        let arrow_follows = self
3745            .index_after_matching_paren(open)
3746            .is_some_and(|i| matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)));
3747        if !arrow_follows {
3748            return None;
3749        }
3750        self.pos = open;
3751        self.diagnostics.truncate(diagnostics_before);
3752        self.parse_function_type_annotation()
3753    }
3754
3755    /// A parenthesized type is its inner type — the parens only group, so no node
3756    /// records them. The span covers them so a diagnostic points at what was written.
3757    fn parse_grouped_type_annotation(&mut self) -> Option<TypeAnnotation> {
3758        let open = self.advance();
3759        let inner = self.parse_type_annotation()?;
3760        if !matches!(self.peek().kind, TokenKind::RightParen) {
3761            self.error_at_peek("expected `)` to close a parenthesized type");
3762            return None;
3763        }
3764        let close = self.advance();
3765        Some(TypeAnnotation {
3766            span: self.span(open.span.start, close.span.end),
3767            ..inner
3768        })
3769    }
3770
3771    // Param names are required — bare `(T, U) => R` is rejected.
3772    fn parse_function_type_annotation(&mut self) -> Option<TypeAnnotation> {
3773        let open = self.advance();
3774        let params = self.parse_function_type_params()?;
3775        if !matches!(self.peek().kind, TokenKind::Arrow) {
3776            self.error_at_peek("expected `=>` in function-type annotation");
3777            return None;
3778        }
3779        self.advance();
3780        let return_type = self.parse_type_annotation()?;
3781        let end = return_type.span.end;
3782        Some(TypeAnnotation {
3783            kind: TypeAnnotationKind::Function {
3784                params,
3785                return_type: Box::new(return_type),
3786            },
3787            span: self.span(open.span.start, end),
3788        })
3789    }
3790
3791    /// The `(…)` of a function type, its `(` already consumed and its `)` consumed here.
3792    /// Shared with a type literal's method members, which spell the same parameter list
3793    /// before a `:` rather than a `=>`.
3794    fn parse_function_type_params(&mut self) -> Option<Vec<TypeAnnotationField>> {
3795        let mut params: Vec<TypeAnnotationField> = Vec::new();
3796        if !matches!(self.peek().kind, TokenKind::RightParen) {
3797            loop {
3798                let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
3799                    self.advance();
3800                    true
3801                } else {
3802                    false
3803                };
3804                let name_tok = self.expect_binding_identifier(if rest {
3805                    "expected identifier after `...` in function-type annotation"
3806                } else {
3807                    "expected parameter name in function-type annotation"
3808                })?;
3809                let name = self.ident_from_token(&name_tok);
3810                let optional = self.parse_parameter_optional(rest, false)?;
3811                if !matches!(self.peek().kind, TokenKind::Colon) {
3812                    if rest {
3813                        self.error_at_peek_with_help(
3814                            "rest parameter requires a type annotation",
3815                            vec!["(...args: T[]) => R".to_string()],
3816                        );
3817                    } else {
3818                        self.error_at_peek(
3819                            "expected `:` after parameter name (function-type params \
3820                             require named annotations in v1)",
3821                        );
3822                    }
3823                    return None;
3824                }
3825                self.advance();
3826                let ty = self.parse_type_annotation()?;
3827                if rest && !is_rest_array_annotation(&ty) {
3828                    self.error_at(ty.span, "rest parameter type must be an array");
3829                    return None;
3830                }
3831                if matches!(self.peek().kind, TokenKind::Equals) {
3832                    if rest {
3833                        self.error_rest_parameter_default();
3834                    } else {
3835                        self.error_at_peek(
3836                            "default values in function-type annotations are not yet supported",
3837                        );
3838                    }
3839                    return None;
3840                }
3841                params.push(TypeAnnotationField {
3842                    name,
3843                    ty,
3844                    optional,
3845                    readonly: false,
3846                    rest,
3847                    method: false,
3848                });
3849                match self.peek().kind {
3850                    TokenKind::Comma => {
3851                        if rest {
3852                            let comma = self.advance();
3853                            self.error_at(comma.span, "rest parameter must be the last parameter");
3854                            return None;
3855                        }
3856                        self.advance();
3857                        if matches!(self.peek().kind, TokenKind::RightParen) {
3858                            break;
3859                        }
3860                    }
3861                    TokenKind::RightParen => break,
3862                    _ => {
3863                        self.error_at_peek("expected `,` or `)`");
3864                        return None;
3865                    }
3866                }
3867            }
3868            self.check_parameter_order(params.iter().map(ParamOrder::of_field));
3869        }
3870        if !matches!(self.peek().kind, TokenKind::RightParen) {
3871            self.error_at_peek("expected `)`");
3872            return None;
3873        }
3874        self.advance();
3875        Some(params)
3876    }
3877
3878    fn parse_tuple_type_annotation(&mut self) -> Option<TypeAnnotation> {
3879        let open = self.advance();
3880        if matches!(self.peek().kind, TokenKind::RightBracket) {
3881            let close = self.peek().span;
3882            self.error_at(
3883                self.span(open.span.start, close.end),
3884                "tuple types must have at least one element",
3885            );
3886            return None;
3887        }
3888        let mut elements: Vec<TypeAnnotation> = Vec::new();
3889        loop {
3890            if matches!(self.peek().kind, TokenKind::DotDotDot) {
3891                self.error_at_with_help(
3892                    self.peek().span,
3893                    "rest elements in tuple types are not supported",
3894                    vec!["use an array type `T[]` for a list of varying length".to_string()],
3895                );
3896                return None;
3897            }
3898            // Labels may be mixed with unlabeled elements, as TypeScript allows
3899            // since 5.2.
3900            let label = self.skip_tuple_element_label();
3901            let mut ty = self.parse_type_annotation()?;
3902            let suffix_optional = matches!(self.peek().kind, TokenKind::Question);
3903            if suffix_optional && label != TupleElementLabel::None {
3904                self.error_at_peek_with_help(
3905                    "put the optional marker after the tuple element label",
3906                    vec!["write `name?: T` instead of `name: T?`".to_string()],
3907                );
3908                return None;
3909            }
3910            let optional = label == TupleElementLabel::Optional || suffix_optional;
3911            if optional {
3912                let end = if suffix_optional {
3913                    self.advance().span.end
3914                } else {
3915                    ty.span.end
3916                };
3917                let span = self.span(ty.span.start, end);
3918                ty = TypeAnnotation {
3919                    kind: TypeAnnotationKind::Optional(Box::new(ty)),
3920                    span,
3921                };
3922            } else if elements
3923                .iter()
3924                .any(|element| matches!(element.kind, TypeAnnotationKind::Optional(_)))
3925            {
3926                self.error_at(
3927                    ty.span,
3928                    "a required tuple element cannot follow an optional element",
3929                );
3930                return None;
3931            }
3932            elements.push(ty);
3933            match self.peek().kind {
3934                TokenKind::Comma => {
3935                    self.advance();
3936                    if matches!(self.peek().kind, TokenKind::RightBracket) {
3937                        break;
3938                    }
3939                }
3940                TokenKind::RightBracket => break,
3941                _ => {
3942                    self.error_at_peek("expected `,` or `]`");
3943                    return None;
3944                }
3945            }
3946        }
3947        if !matches!(self.peek().kind, TokenKind::RightBracket) {
3948            self.error_at_peek("expected `]`");
3949            return None;
3950        }
3951        let close = self.advance();
3952        Some(TypeAnnotation {
3953            kind: TypeAnnotationKind::Tuple(elements),
3954            span: self.span(open.span.start, close.span.end),
3955        })
3956    }
3957
3958    /// Labels document positions; retain only whether the label was optional.
3959    fn skip_tuple_element_label(&mut self) -> TupleElementLabel {
3960        if !is_property_name(&self.peek().kind) {
3961            return TupleElementLabel::None;
3962        }
3963        match self.peek_at(1).kind {
3964            TokenKind::Colon => {
3965                self.advance();
3966                self.advance();
3967                TupleElementLabel::Required
3968            }
3969            TokenKind::Question if matches!(self.peek_at(2).kind, TokenKind::Colon) => {
3970                self.advance();
3971                self.advance();
3972                self.advance();
3973                TupleElementLabel::Optional
3974            }
3975            _ => TupleElementLabel::None,
3976        }
3977    }
3978
3979    /// `[]` or `[]: V` — an index signature with no parameter, which TypeScript
3980    /// reads and rejects.
3981    fn peek_is_parameterless_index_signature(&self) -> bool {
3982        matches!(self.peek().kind, TokenKind::LeftBracket)
3983            && matches!(self.peek_at(1).kind, TokenKind::RightBracket)
3984    }
3985
3986    /// Diagnoses and skips a parameterless index signature (see
3987    /// `peek_is_parameterless_index_signature`) so the members after it still parse.
3988    /// `member_start` is where the member begins, at a `readonly` modifier if any.
3989    fn skip_parameterless_index_signature(&mut self, member_start: u32) -> Option<()> {
3990        self.advance();
3991        let close = self.advance();
3992        self.error_at_with_help(
3993            self.span(member_start, close.span.end),
3994            "an index signature must declare exactly one parameter",
3995            vec!["write `[key: string]: V`".to_string()],
3996        );
3997        if matches!(self.peek().kind, TokenKind::Colon) {
3998            self.advance();
3999            self.parse_type_annotation()?;
4000        }
4001        Some(())
4002    }
4003
4004    fn parse_index_signature(&mut self, readonly: bool) -> Option<crate::IndexSignatureAnnotation> {
4005        let open = self.advance();
4006        let parameter = self.expect_property_ident("expected index parameter name")?;
4007        self.reject_eval_or_arguments(parameter.span);
4008        if !matches!(self.peek().kind, TokenKind::Colon) {
4009            self.error_at_peek("expected `:` after index parameter name");
4010            return None;
4011        }
4012        self.advance();
4013        if !self.peek_identifier_text_is("string") {
4014            self.error_at_peek("index signatures require string keys; use `[key: string]: V`");
4015            return None;
4016        }
4017        self.advance();
4018        if !matches!(self.peek().kind, TokenKind::RightBracket) {
4019            self.error_at_peek("expected `]` after string index key type");
4020            return None;
4021        }
4022        self.advance();
4023        if !matches!(self.peek().kind, TokenKind::Colon) {
4024            self.error_at_peek("expected `:` before index value type");
4025            return None;
4026        }
4027        self.advance();
4028        let value = self.parse_type_annotation()?;
4029        Some(crate::IndexSignatureAnnotation {
4030            span: self.span(open.span.start, value.span.end),
4031            value,
4032            readonly,
4033        })
4034    }
4035
4036    fn parse_object_type_annotation(&mut self) -> Option<TypeAnnotation> {
4037        let open = self.advance();
4038        let mut fields: Vec<TypeAnnotationField> = Vec::new();
4039        let mut index = None;
4040        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
4041            && !self.peek_starts_top_level_item()
4042        {
4043            let member_start = self.peek().span.start;
4044            let readonly = self.eat_readonly_property_modifier();
4045            if self.peek_is_parameterless_index_signature() {
4046                self.skip_parameterless_index_signature(member_start)?;
4047            } else if self.peek_is_construct_signature() {
4048                self.reject_construct_signature()?;
4049            } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
4050                let signature = self.parse_index_signature(readonly)?;
4051                if index.is_some() {
4052                    self.error_at_with_help(
4053                        signature.span,
4054                        "duplicate string index signature",
4055                        vec![],
4056                    );
4057                }
4058                index = Some(Box::new(signature));
4059            } else {
4060                let field = self.parse_object_type_field(readonly, member_start)?;
4061                if let Some(existing) = fields.iter().find(|f| f.name.name == field.name.name) {
4062                    self.diagnostics.push(Diagnostic {
4063                        severity: Severity::Error,
4064                        span: field.name.span,
4065                        message: format!("duplicate field `{}` in object type", field.name.name),
4066                        help: vec![],
4067                        notes: vec![(existing.name.span, "first defined here".into())],
4068                    });
4069                }
4070                fields.push(field);
4071            }
4072            self.finish_type_member(self.prev_token_end(), "expected `;`, `,`, or `}`")?;
4073        }
4074        if !matches!(self.peek().kind, TokenKind::RightBrace) {
4075            self.error_at_peek("expected `}`");
4076            return None;
4077        }
4078        let close = self.advance();
4079        Some(TypeAnnotation {
4080            kind: TypeAnnotationKind::Object { index, fields },
4081            span: self.span(open.span.start, close.span.end),
4082        })
4083    }
4084
4085    /// A declaration or an `import` statement: what a top-level item starts with.
4086    /// `import(…)` and `import.meta` are expressions, not statements.
4087    fn peek_starts_top_level_item(&self) -> bool {
4088        let import_statement = matches!(self.peek().kind, TokenKind::Import)
4089            && matches!(
4090                self.peek_at(1).kind,
4091                TokenKind::LeftBrace
4092                    | TokenKind::Star
4093                    | TokenKind::Identifier
4094                    | TokenKind::StringLiteral(_)
4095            );
4096        import_statement || self.peek_starts_declaration()
4097    }
4098
4099    /// Whether the next tokens start a declaration (`function f`, `class C`, `type T`,
4100    /// …, optionally after `export`, `declare`, `async` or `abstract`) or an export
4101    /// statement (`export {`, `export *`, `export default`), which no type or class
4102    /// member can: a keyword is a member name only before `:`, `?` or `(`. A member
4103    /// list that reaches one was left unclosed, so it stops there and leaves the
4104    /// declaration to be parsed.
4105    fn peek_starts_declaration(&self) -> bool {
4106        if matches!(self.peek().kind, TokenKind::Export)
4107            && matches!(
4108                self.peek_at(1).kind,
4109                TokenKind::Default | TokenKind::LeftBrace | TokenKind::Star
4110            )
4111        {
4112            return true;
4113        }
4114        let mut offset = 0;
4115        while self.peek_at_is_declaration_modifier(offset) {
4116            offset += 1;
4117        }
4118        let declaration_keyword = matches!(
4119            self.peek_at(offset).kind,
4120            TokenKind::Function
4121                | TokenKind::Class
4122                | TokenKind::Let
4123                | TokenKind::Const
4124                | TokenKind::Enum
4125                | TokenKind::Interface
4126        ) || self.peek_at_is_word(offset, "type");
4127        declaration_keyword && matches!(self.peek_at(offset + 1).kind, TokenKind::Identifier)
4128    }
4129
4130    /// `export`, `declare`, `async` or `abstract` before a declaration keyword.
4131    fn peek_at_is_declaration_modifier(&self, offset: usize) -> bool {
4132        matches!(self.peek_at(offset).kind, TokenKind::Export)
4133            || ["declare", "async", "abstract"]
4134                .iter()
4135                .any(|word| self.peek_at_is_word(offset, word))
4136    }
4137
4138    /// `new (params): T` or `new <T>(params): T` inside a type literal: a construct
4139    /// signature, which TypeScript reads where it would otherwise see a method named
4140    /// `new`. A quoted `"new"()`, an optional `new?()` and a field `new: T` are
4141    /// ordinary members, as in TypeScript.
4142    fn peek_is_construct_signature(&self) -> bool {
4143        matches!(self.peek().kind, TokenKind::New)
4144            && matches!(
4145                self.peek_at(1).kind,
4146                TokenKind::LeftParen | TokenKind::LessThan
4147            )
4148    }
4149
4150    /// Only interfaces declare construct signatures, so one in a type literal (see
4151    /// `peek_is_construct_signature`) is diagnosed, then skipped rather than misread
4152    /// as a method named `new`.
4153    fn reject_construct_signature(&mut self) -> Option<()> {
4154        let keyword = self.advance();
4155        self.error_at_with_help(
4156            keyword.span,
4157            "construct signatures are not supported in object types",
4158            vec![
4159                "declare an `interface` with the `new (…)` signature, or pass a factory \
4160                 function such as `() => T`"
4161                    .to_string(),
4162            ],
4163        );
4164        if matches!(self.peek().kind, TokenKind::LessThan) {
4165            self.parse_generic_param_list()?;
4166        }
4167        if !matches!(self.peek().kind, TokenKind::LeftParen) {
4168            self.error_at_peek("expected `(` after construct signature type parameters");
4169            return None;
4170        }
4171        self.parse_object_type_method_signature(keyword.span.start)?;
4172        Some(())
4173    }
4174
4175    /// `name: T`, `name?: T` or the method signature `name(params): T`.
4176    fn parse_object_type_field(
4177        &mut self,
4178        readonly: bool,
4179        member_start: u32,
4180    ) -> Option<TypeAnnotationField> {
4181        let name = self.expect_property_ident("expected field name in object type")?;
4182        // `name?: T` — omittable at construction; reads widen to `T | undefined`.
4183        let optional = matches!(self.peek().kind, TokenKind::Question);
4184        if optional {
4185            self.advance();
4186        }
4187        let method = matches!(self.peek().kind, TokenKind::LeftParen);
4188        let ty = if method {
4189            if readonly {
4190                self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
4191            }
4192            self.parse_object_type_method_signature(name.span.start)?
4193        } else {
4194            if !matches!(self.peek().kind, TokenKind::Colon) {
4195                self.error_at_peek("expected `:` after field name");
4196                return None;
4197            }
4198            self.advance();
4199            self.parse_type_annotation()?
4200        };
4201        Some(TypeAnnotationField {
4202            name,
4203            ty,
4204            optional,
4205            readonly,
4206            rest: false,
4207            method,
4208        })
4209    }
4210
4211    /// `m(params): T` inside a type literal, spelled as the function-typed field
4212    /// `m: (params) => T` — the two are interchangeable in a structural shape, and
4213    /// interfaces already accept both spellings for the same member. `start` is the
4214    /// member name's offset, so the annotation's span covers the name too.
4215    fn parse_object_type_method_signature(&mut self, start: u32) -> Option<TypeAnnotation> {
4216        self.advance();
4217        let params = self.parse_function_type_params()?;
4218        if !matches!(self.peek().kind, TokenKind::Colon) {
4219            self.error_at_peek("expected `:` and return type after method parameters");
4220            return None;
4221        }
4222        self.advance();
4223        let return_type = self.parse_type_annotation()?;
4224        let end = return_type.span.end;
4225        Some(TypeAnnotation {
4226            kind: TypeAnnotationKind::Function {
4227                params,
4228                return_type: Box::new(return_type),
4229            },
4230            span: self.span(start, end),
4231        })
4232    }
4233
4234    /// An identifier that names a binding or a type, which rejects the words strict
4235    /// mode reserves (see [`STRICT_MODE_RESERVED_WORDS`]).
4236    fn expect_identifier(&mut self, message: &str) -> Option<Token> {
4237        let tok = self.expect_identifier_name(message)?;
4238        self.reject_strict_mode_reserved_word(&tok);
4239        Some(tok)
4240    }
4241
4242    /// The name a variable, parameter, function or `catch` binds. Strict mode also
4243    /// forbids `eval` and `arguments` there, though not as a class, enum or type
4244    /// name (TypeScript's TS1100 covers the same positions).
4245    fn expect_binding_identifier(&mut self, message: &str) -> Option<Token> {
4246        let tok = self.expect_identifier(message)?;
4247        self.reject_eval_or_arguments(tok.span);
4248        Some(tok)
4249    }
4250
4251    /// An identifier in a position where strict mode's reserved words are allowed: an
4252    /// enum member, a property, or the name an import specifier refers to.
4253    fn expect_identifier_name(&mut self, message: &str) -> Option<Token> {
4254        if matches!(self.peek().kind, TokenKind::Identifier) {
4255            return Some(self.advance());
4256        }
4257        if is_reserved_identifier_word(&self.peek().kind) {
4258            let tok = self.advance();
4259            let keyword = &self.source[tok.span.start as usize..tok.span.end as usize];
4260            self.error_at_with_help(
4261                tok.span,
4262                format!("`{keyword}` is a reserved keyword and can't be used as a name"),
4263                vec![format!(
4264                    "rename it, for example: `{}`",
4265                    reserved_keyword_rename_example(keyword)
4266                )],
4267            );
4268            return None;
4269        }
4270        self.error_at_peek(message.to_string());
4271        None
4272    }
4273
4274    /// Reports a binding named by a word strict mode reserves. Parsing continues
4275    /// with the name: nothing else about it is wrong.
4276    fn reject_strict_mode_reserved_word(&mut self, tok: &Token) {
4277        let word = &self.source[tok.span.start as usize..tok.span.end as usize];
4278        if !STRICT_MODE_RESERVED_WORDS.contains(&word) {
4279            return;
4280        }
4281        self.error_at_with_help(
4282            tok.span,
4283            format!("`{word}` is a reserved word in strict mode and can't be used as a name"),
4284            vec![format!(
4285                "rename it, for example: `{}`",
4286                reserved_keyword_rename_example(word)
4287            )],
4288        );
4289    }
4290
4291    /// Reports a binding named `eval` or `arguments`. Parsing continues with the
4292    /// name: nothing else about it is wrong.
4293    fn reject_eval_or_arguments(&mut self, span: Span) {
4294        let word = &self.source[span.start as usize..span.end as usize];
4295        if !matches!(word, "eval" | "arguments") {
4296            return;
4297        }
4298        self.error_at_with_help(
4299            span,
4300            format!("`{word}` can't name a binding in strict mode"),
4301            vec![format!(
4302                "rename it, for example: `{}`",
4303                reserved_keyword_rename_example(word)
4304            )],
4305        );
4306    }
4307
4308    fn ident_from_token(&self, tok: &Token) -> Ident {
4309        Ident {
4310            name: self.source[tok.span.start as usize..tok.span.end as usize].to_string(),
4311            span: tok.span,
4312        }
4313    }
4314
4315    fn property_ident_from_token(&self, tok: &Token) -> Ident {
4316        if let TokenKind::StringLiteral(s) = &tok.kind {
4317            Ident {
4318                name: s.clone(),
4319                span: tok.span,
4320            }
4321        } else {
4322            self.ident_from_token(tok)
4323        }
4324    }
4325
4326    fn expect_property_name(&mut self, message: &str) -> Option<Token> {
4327        if !is_property_name(&self.peek().kind) {
4328            self.error_at_peek(message.to_string());
4329            return None;
4330        }
4331        Some(self.advance())
4332    }
4333
4334    fn expect_property_ident(&mut self, message: &str) -> Option<Ident> {
4335        if !is_property_name(&self.peek().kind)
4336            && !matches!(self.peek().kind, TokenKind::StringLiteral(_))
4337        {
4338            self.error_at_peek(message.to_string());
4339            return None;
4340        }
4341        let tok = self.advance();
4342        Some(self.property_ident_from_token(&tok))
4343    }
4344
4345    /// The span of a `readonly` modifier that [`Parser::eat_readonly_property_modifier`]
4346    /// consumed at `member_start`.
4347    fn readonly_modifier_span(&self, member_start: u32) -> Span {
4348        self.span(member_start, member_start + "readonly".len() as u32)
4349    }
4350
4351    /// Reports `readonly` on a method, accessor or method signature, as TypeScript
4352    /// does (TS1024). Parsing continues: the modifier changes nothing else.
4353    fn reject_readonly_modifier(&mut self, span: Span) {
4354        self.error_at_with_help(
4355            span,
4356            "`readonly` can only modify a property or index signature",
4357            vec!["remove `readonly`".to_string()],
4358        );
4359    }
4360
4361    fn eat_readonly_property_modifier(&mut self) -> bool {
4362        if self.peek_identifier_text_is("readonly")
4363            && !self.line_break_after_peek()
4364            && (self.peek_starts_property_after_readonly()
4365                || matches!(self.peek_at(1).kind, TokenKind::LeftBracket))
4366        {
4367            self.advance();
4368            return true;
4369        }
4370        false
4371    }
4372
4373    fn peek_starts_property_after_readonly(&self) -> bool {
4374        let name = &self.peek_at(1).kind;
4375        let after_name = &self.peek_at(2).kind;
4376        let after_optional = &self.peek_at(3).kind;
4377        // A member starts its type with `:`, or its parameter list with `(` — otherwise
4378        // `readonly` is the member's own name.
4379        let starts_member_type =
4380            |kind: &TokenKind| matches!(kind, TokenKind::Colon | TokenKind::LeftParen);
4381        (is_property_name(name) || matches!(name, TokenKind::StringLiteral(_)))
4382            && (starts_member_type(after_name)
4383                || matches!(after_name, TokenKind::Question) && starts_member_type(after_optional))
4384    }
4385
4386    /// `as` and `satisfies`, which lex as identifiers but continue an expression.
4387    fn peek_at_is_binary_word(&self, offset: usize) -> bool {
4388        self.peek_at_is_word(offset, "as") || self.peek_at_is_word(offset, "satisfies")
4389    }
4390
4391    fn peek_identifier_text_is(&self, expected: &str) -> bool {
4392        self.token_is_word(self.peek(), expected)
4393    }
4394
4395    fn peek_at_is_word(&self, offset: usize, expected: &str) -> bool {
4396        self.token_is_word(self.peek_at(offset), expected)
4397    }
4398
4399    fn token_is_word(&self, tok: &Token, expected: &str) -> bool {
4400        matches!(tok.kind, TokenKind::Identifier)
4401            && &self.source[tok.span.start as usize..tok.span.end as usize] == expected
4402    }
4403
4404    /// `delete` is lexed as an identifier: `map.delete(k)`, a `delete()` method and
4405    /// `submilli:http`'s `delete(url)` function all use the name. So it is the
4406    /// operator only when an operand that can't be an argument list follows it, and
4407    /// `delete (o.x)` stays a call. `as` and `satisfies` are lexed as identifiers too:
4408    /// `delete as unknown` casts the function value, while `delete as.x` deletes from a
4409    /// binding named `as`.
4410    fn at_delete_operator(&self) -> bool {
4411        if !self.peek_identifier_text_is("delete") {
4412            return false;
4413        }
4414        if !matches!(
4415            self.peek_at(1).kind,
4416            TokenKind::Identifier | TokenKind::This
4417        ) {
4418            return false;
4419        }
4420        let next_is_cast_keyword =
4421            self.peek_at_is_word(1, "as") || self.peek_at_is_word(1, "satisfies");
4422        !next_is_cast_keyword || cannot_start_type(&self.peek_at(2).kind)
4423    }
4424
4425    /// `type X …` commits to a type-alias declaration only when followed by an
4426    /// identifier, mirroring TypeScript; `type = 5`, `type;`, `type(x)` stay
4427    /// expression statements over a binding named `type`.
4428    fn at_type_alias_head(&self) -> bool {
4429        self.peek_identifier_text_is("type")
4430            && matches!(self.peek_at(1).kind, TokenKind::Identifier)
4431    }
4432
4433    /// Assignment is the lowest-precedence expression and right-associative:
4434    /// `a = b = c` assigns `c` to both, and yields it.
4435    fn parse_expression(&mut self) -> Option<ExprId> {
4436        self.with_recursion_limit(Self::parse_expression_inner)
4437    }
4438
4439    fn parse_expression_inner(&mut self) -> Option<ExprId> {
4440        let written = self.parse_conditional()?;
4441        let shift_assignment = self.peek_shift().filter(|shift| shift.assignment);
4442        if !is_assign_lookahead(&self.peek().kind) && shift_assignment.is_none() {
4443            return Some(written);
4444        }
4445        let target_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4446        let target = self.assignment_target(written)?;
4447        let op_tok = self.advance();
4448        let mut op_span = op_tok.span;
4449        let op = if let Some(ShiftToken { op, tokens, .. }) = shift_assignment {
4450            for _ in 1..tokens {
4451                op_span.end = self.advance().span.end;
4452            }
4453            Some(op)
4454        } else {
4455            compound_op_for_token(&op_tok.kind)
4456        };
4457        let value = self.parse_expression()?;
4458        let value_span = parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
4459        parse_arena_result(
4460            self.ast.try_push_expr(Expr {
4461                kind: ExprKind::Assign {
4462                    target,
4463                    op,
4464                    op_span,
4465                    value,
4466                },
4467                span: self.span(target_span.start, value_span.end),
4468            }),
4469            &mut self.fatal,
4470        )
4471    }
4472
4473    /// The binding, field, or element `written` names, through any parentheses:
4474    /// `(a) = 1` assigns `a`, as in JavaScript.
4475    fn assignment_target(&mut self, written: ExprId) -> Option<ExprId> {
4476        let mut target = written;
4477        while let ExprKind::Paren(inner) =
4478            parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind
4479        {
4480            target = inner;
4481        }
4482        if matches!(
4483            parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind,
4484            ExprKind::Identifier(_) | ExprKind::FieldAccess { .. } | ExprKind::IndexAccess { .. }
4485        ) {
4486            return Some(target);
4487        }
4488        let error_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4489        self.error_at_with_help(
4490            error_span,
4491            "invalid assignment target",
4492            vec![
4493                "assign to a variable, a field (`o.f = …`), or an element (`a[i] = …`)".to_string(),
4494            ],
4495        );
4496        None
4497    }
4498
4499    fn parse_conditional(&mut self) -> Option<ExprId> {
4500        self.reject_async_modifier();
4501        if self.is_arrow_start() {
4502            return self.parse_arrow();
4503        }
4504        let cond = self.parse_binary(0)?;
4505        // Ternary sits below every binary op; right-associative.
4506        if !matches!(self.peek().kind, TokenKind::Question) {
4507            return Some(cond);
4508        }
4509        self.advance();
4510        let then_ = self.parse_expression()?;
4511        if !matches!(self.peek().kind, TokenKind::Colon) {
4512            self.error_at_peek_with_help(
4513                "expected `:` to complete ternary expression",
4514                vec!["const x = cond ? then : else;".to_string()],
4515            );
4516            return None;
4517        }
4518        self.advance();
4519        let else_ = self.parse_expression()?;
4520        let cond_span = parse_arena_result(self.ast.try_expr(cond), &mut self.fatal)?.span;
4521        let else_span = parse_arena_result(self.ast.try_expr(else_), &mut self.fatal)?.span;
4522        parse_arena_result(
4523            self.ast.try_push_expr(Expr {
4524                kind: ExprKind::Ternary { cond, then_, else_ },
4525                span: self.span(cond_span.start, else_span.end),
4526            }),
4527            &mut self.fatal,
4528        )
4529    }
4530
4531    fn is_arrow_start(&self) -> bool {
4532        match self.peek().kind {
4533            TokenKind::Identifier => matches!(self.peek_at(1).kind, TokenKind::Arrow),
4534            TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos),
4535            _ => false,
4536        }
4537    }
4538
4539    /// Reject an `async` that modifies a function, arrow or method, and keep a
4540    /// binding named `async` as it is.
4541    fn reject_async_modifier(&mut self) {
4542        if !self.peek_identifier_text_is("async") {
4543            return;
4544        }
4545        let modifies = match self.peek_at(1).kind {
4546            TokenKind::Function => true,
4547            // `async (x) => …`, not a call of a function named `async`.
4548            TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos + 1),
4549            // `async x => …`, or a method `async run(…)` / `async run<T>(…)`.
4550            // `async in (o)` and `async as (T)` are operators on a binding named
4551            // `async`.
4552            ref name
4553                if is_property_name(name)
4554                    && !matches!(name, TokenKind::In)
4555                    && !self.peek_at_is_binary_word(1) =>
4556            {
4557                matches!(
4558                    self.peek_at(2).kind,
4559                    TokenKind::Arrow | TokenKind::LeftParen | TokenKind::LessThan
4560                )
4561            }
4562            _ => false,
4563        };
4564        if modifies {
4565            self.report_async_modifier();
4566        }
4567    }
4568
4569    /// Reject an `async` that starts a class or object-literal method. A member's
4570    /// `async` is never a value an operator applies to, so `async *run`,
4571    /// `async in()` and `async "s"()` are methods too. After a line break,
4572    /// `async` is a member of its own, as in TypeScript.
4573    fn reject_async_member_modifier(&mut self) {
4574        if !self.peek_identifier_text_is("async") || self.line_break_after_peek() {
4575            return;
4576        }
4577        let modifies = match self.peek_at(1).kind {
4578            TokenKind::Star
4579            | TokenKind::LeftBracket
4580            | TokenKind::StringLiteral(_)
4581            | TokenKind::NumberLiteral(_) => true,
4582            ref name if is_property_name(name) => {
4583                matches!(
4584                    self.peek_at(2).kind,
4585                    TokenKind::LeftParen | TokenKind::LessThan
4586                )
4587            }
4588            _ => false,
4589        };
4590        if modifies {
4591            self.report_async_modifier();
4592        }
4593    }
4594
4595    /// There are no promises here, so name the rewrite, then consume `async`
4596    /// and parse on as if it were absent: the body's `await`s are reported in
4597    /// the same run.
4598    fn report_async_modifier(&mut self) {
4599        let tok = self.advance();
4600        self.error_at_with_help(
4601            tok.span,
4602            "`async` functions are not supported",
4603            vec![
4604                "calls return their value directly: remove `async`, write the return type \
4605                 `T` instead of `Promise<T>`, and drop `await`"
4606                    .to_string(),
4607            ],
4608        );
4609    }
4610
4611    /// `await` followed on the same line by an operand that cannot continue a
4612    /// binding named `await`: an identifier, a keyword, a literal or a template. `await(x)`
4613    /// and `await[i]` stay a call and an index; the typechecker names the fix if
4614    /// `await` then does not resolve. Calls are synchronous, so report it and
4615    /// parse on: the operand stays, and one run names every `await` in the program.
4616    fn reject_await_operator(&mut self) -> bool {
4617        if !self.peek_identifier_text_is("await") {
4618            return false;
4619        }
4620        let operand_starts = matches!(
4621            self.peek_at(1).kind,
4622            TokenKind::Identifier
4623                | TokenKind::This
4624                | TokenKind::Super
4625                | TokenKind::New
4626                | TokenKind::Function
4627                | TokenKind::StringLiteral(_)
4628                | TokenKind::NumberLiteral(_)
4629                | TokenKind::NullLiteral
4630                | TokenKind::BooleanLiteral(_)
4631                // A tagged template is not supported, so a template after `await`
4632                // can only be its operand.
4633                | TokenKind::TemplateHead(_)
4634                | TokenKind::TemplateNoSubstitution(_)
4635        ) && !self.peek_at_is_binary_word(1);
4636        if !operand_starts || self.line_break_after_peek() {
4637            return false;
4638        }
4639        let tok = self.advance();
4640        self.error_at_with_help(
4641            tok.span,
4642            "`await` is not supported",
4643            vec!["calls return their value directly: remove `await`".to_string()],
4644        );
4645        true
4646    }
4647
4648    /// Index just past the `)` matching the `(` at `index`; `None` when the parens
4649    /// never balance. Pure lookahead — emits no diagnostics.
4650    fn index_after_matching_paren(&self, index: usize) -> Option<usize> {
4651        if !matches!(
4652            self.tokens.get(index).map(|t| &t.kind),
4653            Some(TokenKind::LeftParen)
4654        ) {
4655            return None;
4656        }
4657        self.scan_past_balanced(
4658            index,
4659            |k| matches!(k, TokenKind::LeftParen),
4660            |k| matches!(k, TokenKind::RightParen),
4661        )
4662    }
4663
4664    fn find_arrow_after_matching_paren(&self, open: usize) -> bool {
4665        let Some(i) = self.index_after_matching_paren(open) else {
4666            return false;
4667        };
4668        let after_type = if matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Colon)) {
4669            match self.scan_past_type_annotation(i + 1, TypePos::ArrowReturn) {
4670                Some(p) => p,
4671                None => return false,
4672            }
4673        } else {
4674            i
4675        };
4676        matches!(
4677            self.tokens.get(after_type).map(|t| &t.kind),
4678            Some(TokenKind::Arrow),
4679        )
4680    }
4681
4682    // Pure lookahead used only by the arrow-disambiguator. No diagnostics.
4683    // Must accept every form `parse_type_annotation` accepts: a form missing
4684    // here makes the disambiguator reject a valid arrow, so the expression
4685    // parser reports a misleading `expected expression` at the return type.
4686    // `arrow_return_type_scanner_matches_type_grammar` pins the two together.
4687    fn scan_past_type_annotation(&self, start: usize, type_pos: TypePos) -> Option<usize> {
4688        self.scan_past_type_annotation_at_depth(start, type_pos, 0)
4689    }
4690
4691    fn scan_past_type_annotation_at_depth(
4692        &self,
4693        start: usize,
4694        type_pos: TypePos,
4695        depth: usize,
4696    ) -> Option<usize> {
4697        if depth >= MAX_PARSE_DEPTH {
4698            return None;
4699        }
4700        if matches!(
4701            self.tokens.get(start).map(|t| &t.kind),
4702            Some(TokenKind::Identifier)
4703        ) && self
4704            .tokens
4705            .get(start + 1)
4706            .is_some_and(|t| self.token_is_word(t, "is"))
4707        {
4708            return self.scan_past_type_annotation_at_depth(start + 2, type_pos, depth + 1);
4709        }
4710        // Leading `|` (prettier's multiline union format).
4711        let start = if matches!(
4712            self.tokens.get(start).map(|t| &t.kind),
4713            Some(TokenKind::Pipe)
4714        ) {
4715            start + 1
4716        } else {
4717            start
4718        };
4719        let mut i = self.scan_past_single_type_member(start, type_pos, depth + 1)?;
4720        while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Pipe)) {
4721            i = self.scan_past_single_type_member(i + 1, type_pos, depth + 1)?;
4722        }
4723        Some(i)
4724    }
4725
4726    fn scan_past_single_type_member(
4727        &self,
4728        start: usize,
4729        type_pos: TypePos,
4730        depth: usize,
4731    ) -> Option<usize> {
4732        if depth >= MAX_PARSE_DEPTH {
4733            return None;
4734        }
4735        if self.is_contextual_type_operator(start, "keyof")
4736            || self.is_contextual_type_operator(start, "readonly")
4737        {
4738            return self.scan_past_single_type_member(start + 1, type_pos, depth + 1);
4739        }
4740        let mut i = start;
4741        match self.tokens.get(i)?.kind {
4742            TokenKind::Identifier | TokenKind::Void => {
4743                i += 1;
4744                while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Dot)) {
4745                    if !matches!(
4746                        self.tokens.get(i + 1).map(|t| &t.kind),
4747                        Some(TokenKind::Identifier)
4748                    ) {
4749                        return None;
4750                    }
4751                    i += 2;
4752                }
4753                if matches!(
4754                    self.tokens.get(i).map(|t| &t.kind),
4755                    Some(TokenKind::LessThan)
4756                ) {
4757                    i = self.scan_past_balanced(
4758                        i,
4759                        |k| matches!(k, TokenKind::LessThan),
4760                        |k| matches!(k, TokenKind::GreaterThan),
4761                    )?;
4762                }
4763            }
4764            TokenKind::NullLiteral
4765            | TokenKind::StringLiteral(_)
4766            | TokenKind::NumberLiteral(_)
4767            | TokenKind::BooleanLiteral(_) => {
4768                i += 1;
4769            }
4770            TokenKind::LeftBrace => {
4771                i = self.scan_past_balanced(
4772                    i,
4773                    |k| matches!(k, TokenKind::LeftBrace),
4774                    |k| matches!(k, TokenKind::RightBrace),
4775                )?;
4776            }
4777            TokenKind::LeftBracket => {
4778                i = self.scan_past_balanced(
4779                    i,
4780                    |k| matches!(k, TokenKind::LeftBracket),
4781                    |k| matches!(k, TokenKind::RightBracket),
4782                )?;
4783            }
4784            // Same `(`-disambiguation `parse_type_array` uses, and it has to agree:
4785            // a function type carries its own `=>`, so everything after it is the
4786            // return type and the scan ends with that. Grouping parens instead fall
4787            // through to the postfix `[]` loop below.
4788            TokenKind::LeftParen => {
4789                let is_function_type = self.paren_at_opens_function_type(i, type_pos);
4790                i = self.scan_past_balanced(
4791                    i,
4792                    |k| matches!(k, TokenKind::LeftParen),
4793                    |k| matches!(k, TokenKind::RightParen),
4794                )?;
4795                if is_function_type {
4796                    if !matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)) {
4797                        return None;
4798                    }
4799                    return self.scan_past_type_annotation_at_depth(
4800                        i + 1,
4801                        TypePos::Anywhere,
4802                        depth + 1,
4803                    );
4804                }
4805            }
4806            _ => return None,
4807        }
4808        while matches!(
4809            self.tokens.get(i).map(|t| &t.kind),
4810            Some(TokenKind::LeftBracket)
4811        ) {
4812            i += 1;
4813            if !matches!(
4814                self.tokens.get(i).map(|t| &t.kind),
4815                Some(TokenKind::RightBracket)
4816            ) {
4817                return None;
4818            }
4819            i += 1;
4820        }
4821        Some(i)
4822    }
4823
4824    // `start` must index the opening token; returns the index just past the
4825    // matching close, or None if the tokens run out first.
4826    fn scan_past_balanced(
4827        &self,
4828        start: usize,
4829        is_open: fn(&TokenKind) -> bool,
4830        is_close: fn(&TokenKind) -> bool,
4831    ) -> Option<usize> {
4832        let mut depth: i32 = 1;
4833        let mut i = start + 1;
4834        while i < self.tokens.len() && depth > 0 {
4835            let token = self.tokens.get(i)?;
4836            let kind = &token.kind;
4837            if is_open(kind) {
4838                depth += 1;
4839            } else if is_close(kind) {
4840                depth -= 1;
4841            } else if matches!(kind, TokenKind::Eof) {
4842                return None;
4843            }
4844            i += 1;
4845        }
4846        if depth == 0 { Some(i) } else { None }
4847    }
4848
4849    fn parse_arrow(&mut self) -> Option<ExprId> {
4850        let start_span = self.peek().span;
4851
4852        let params = match self.peek().kind {
4853            TokenKind::Identifier => {
4854                let name_tok = self.advance();
4855                self.reject_eval_or_arguments(name_tok.span);
4856                let name = self.ident_from_token(&name_tok);
4857                vec![ParamDecl {
4858                    name,
4859                    ty: None,
4860                    default: None,
4861                    optional: false,
4862                    pattern: None,
4863                    rest: false,
4864                    modifiers: None,
4865                }]
4866            }
4867            TokenKind::LeftParen => {
4868                self.advance();
4869                let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4870                    Vec::new()
4871                } else {
4872                    self.parse_arrow_param_list()?
4873                };
4874                if !matches!(self.peek().kind, TokenKind::RightParen) {
4875                    self.error_at_peek("expected `)`");
4876                    return None;
4877                }
4878                self.advance();
4879                params
4880            }
4881            _ => return self.invariant_failure("is_arrow_start guarded the entry"),
4882        };
4883
4884        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4885            self.advance();
4886            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4887        } else {
4888            (None, None)
4889        };
4890
4891        if !matches!(self.peek().kind, TokenKind::Arrow) {
4892            self.error_at_peek("expected `=>` in arrow function");
4893            return None;
4894        }
4895        // Like TypeScript, the arrow still parses, so later errors keep their spans.
4896        if self.line_break_before_peek() {
4897            let arrow_span = self.peek().span;
4898            self.error_at(arrow_span, "a line break is not allowed before `=>`");
4899        }
4900        self.advance();
4901
4902        let (body, end_pos) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
4903            let block = self.parse_block()?;
4904            let span = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?.span;
4905            (ArrowBody::Block(block), span.end)
4906        } else {
4907            let expr = self.parse_expression()?;
4908            let span = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?.span;
4909            (ArrowBody::Expr(expr), span.end)
4910        };
4911
4912        parse_arena_result(
4913            self.ast.try_push_expr(Expr {
4914                kind: ExprKind::Arrow {
4915                    params,
4916                    return_type,
4917                    type_predicate,
4918                    body,
4919                },
4920                span: self.span(start_span.start, end_pos),
4921            }),
4922            &mut self.fatal,
4923        )
4924    }
4925
4926    /// Function expressions share parameter/body parsing with arrows, but retain
4927    /// their own receiver and optional recursive-name binding.
4928    fn parse_function_expression(&mut self) -> Option<ExprId> {
4929        let kw = self.advance();
4930
4931        if matches!(self.peek().kind, TokenKind::LessThan) {
4932            self.error_at_peek("generic function expressions are not supported");
4933            return None;
4934        }
4935
4936        let self_name = if matches!(self.peek().kind, TokenKind::Identifier) {
4937            let name_tok = self.advance();
4938            self.reject_eval_or_arguments(name_tok.span);
4939            Some(self.ident_from_token(&name_tok))
4940        } else {
4941            None
4942        };
4943
4944        if !matches!(self.peek().kind, TokenKind::LeftParen) {
4945            self.error_at_peek("expected `(` after `function`");
4946            return None;
4947        }
4948        self.advance();
4949
4950        let this_type = if matches!(self.peek().kind, TokenKind::This) {
4951            self.advance();
4952            if !matches!(self.peek().kind, TokenKind::Colon) {
4953                self.error_at_peek("expected a type annotation after `this`");
4954                return None;
4955            }
4956            self.advance();
4957            let ty = self.parse_type_annotation()?;
4958            if matches!(self.peek().kind, TokenKind::Comma) {
4959                self.advance();
4960            } else if !matches!(self.peek().kind, TokenKind::RightParen) {
4961                self.error_at_peek("expected `,` or `)` after the `this` parameter");
4962                return None;
4963            }
4964            Some(ty)
4965        } else {
4966            None
4967        };
4968        let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4969            Vec::new()
4970        } else {
4971            let saved_class = self.class_member_body_depth;
4972            let saved_function = self.function_expression_body_depth;
4973            self.class_member_body_depth = 0;
4974            self.function_expression_body_depth = 1;
4975            let params = self.parse_arrow_param_list();
4976            self.class_member_body_depth = saved_class;
4977            self.function_expression_body_depth = saved_function;
4978            params?
4979        };
4980        if !matches!(self.peek().kind, TokenKind::RightParen) {
4981            self.error_at_peek("expected `)`");
4982            return None;
4983        }
4984        self.advance();
4985
4986        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4987            self.advance();
4988            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4989        } else {
4990            (None, None)
4991        };
4992
4993        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
4994            self.error_at_peek("expected `{` to open the function body");
4995            return None;
4996        }
4997        let saved = self.class_member_body_depth;
4998        self.class_member_body_depth = 0;
4999        self.function_expression_body_depth += 1;
5000        let block = self.parse_block();
5001        self.function_expression_body_depth -= 1;
5002        self.class_member_body_depth = saved;
5003        let block = block?;
5004        let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
5005            .span
5006            .end;
5007
5008        let function = parse_arena_result(
5009            self.ast.try_push_expr(Expr {
5010                kind: ExprKind::Arrow {
5011                    params,
5012                    return_type,
5013                    type_predicate,
5014                    body: ArrowBody::Block(block),
5015                },
5016                span: self.span(kw.span.start, end),
5017            }),
5018            &mut self.fatal,
5019        )?;
5020        parse_arena_result(
5021            self.ast.try_push_expr(Expr {
5022                kind: ExprKind::FunctionExpression {
5023                    name: self_name,
5024                    this_type,
5025                    function,
5026                },
5027                span: self.span(kw.span.start, end),
5028            }),
5029            &mut self.fatal,
5030        )
5031    }
5032
5033    /// Shorthand methods still use arrow lowering. Reject their receiver syntax
5034    /// so an enclosing class or function receiver cannot leak into the body.
5035    fn parse_outer_this_boundary<T>(
5036        &mut self,
5037        parse: impl FnOnce(&mut Self) -> Option<T>,
5038    ) -> Option<T> {
5039        let saved = self.class_member_body_depth;
5040        self.class_member_body_depth = 0;
5041        let saved_function = self.function_expression_body_depth;
5042        self.function_expression_body_depth = 0;
5043        let parsed = parse(self);
5044        self.class_member_body_depth = saved;
5045        self.function_expression_body_depth = saved_function;
5046        parsed
5047    }
5048
5049    fn parse_arrow_param_list(&mut self) -> Option<Vec<ParamDecl>> {
5050        let mut params = Vec::new();
5051        loop {
5052            let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
5053                self.advance();
5054                true
5055            } else {
5056                false
5057            };
5058            // The lowering pass rewrites expression-body arrows to block form when any param
5059            // has a destructuring pattern.
5060            let (name, pattern) = match self.peek().kind {
5061                TokenKind::LeftBrace | TokenKind::LeftBracket => {
5062                    if rest {
5063                        self.error_at_peek("rest parameter cannot be destructured");
5064                        return None;
5065                    }
5066                    let binding = self.parse_binding()?;
5067                    let placeholder = Ident {
5068                        name: String::new(),
5069                        span: binding.span(),
5070                    };
5071                    (placeholder, Some(binding))
5072                }
5073                _ => {
5074                    let name_tok = self.expect_binding_identifier(if rest {
5075                        "expected identifier after `...`"
5076                    } else {
5077                        "expected parameter name"
5078                    })?;
5079                    let name = self.ident_from_token(&name_tok);
5080                    (name, None)
5081                }
5082            };
5083            let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
5084            let ty = if matches!(self.peek().kind, TokenKind::Colon) {
5085                self.advance();
5086                Some(self.parse_type_annotation()?)
5087            } else {
5088                // Rest without annotation is OK at parse time; the typechecker fills it
5089                // from a contextual function-type hint if available.
5090                None
5091            };
5092            let default = self.parse_parameter_default(rest, optional)?;
5093            params.push(ParamDecl {
5094                name,
5095                ty,
5096                default,
5097                optional,
5098                pattern,
5099                rest,
5100                modifiers: None,
5101            });
5102            match self.peek().kind {
5103                TokenKind::Comma => {
5104                    if rest {
5105                        let comma = self.advance();
5106                        self.error_at(comma.span, "rest parameter must be the last parameter");
5107                        return None;
5108                    }
5109                    self.advance();
5110                    if matches!(self.peek().kind, TokenKind::RightParen) {
5111                        break;
5112                    }
5113                }
5114                TokenKind::RightParen => break,
5115                _ => {
5116                    self.error_at_peek("expected `,` or `)`");
5117                    return None;
5118                }
5119            }
5120        }
5121        self.check_parameter_order(params.iter().map(ParamOrder::of_param));
5122        Some(params)
5123    }
5124
5125    fn parse_binary(&mut self, min_prec: u8) -> Option<ExprId> {
5126        self.with_recursion_limit(|parser| parser.parse_binary_inner(min_prec))
5127    }
5128
5129    /// Angle tokens stay separate for nested type arguments. Only adjacent
5130    /// source tokens form a shift; whitespace and comments cannot join them.
5131    fn peek_shift(&self) -> Option<ShiftToken> {
5132        let first = self.peek();
5133        let second = self.peek_at(1);
5134        if first.span.end != second.span.start {
5135            return None;
5136        }
5137        match (&first.kind, &second.kind) {
5138            (TokenKind::LessThan, TokenKind::LessThan) => {
5139                Some(ShiftToken::new(BinOp::Shl, 2, false))
5140            }
5141            (TokenKind::LessThan, TokenKind::LessEquals) => {
5142                Some(ShiftToken::new(BinOp::Shl, 2, true))
5143            }
5144            (TokenKind::GreaterThan, TokenKind::GreaterEquals) => {
5145                Some(ShiftToken::new(BinOp::Shr, 2, true))
5146            }
5147            (TokenKind::GreaterThan, TokenKind::GreaterThan) => {
5148                let third = self.peek_at(2);
5149                if second.span.end == third.span.start {
5150                    match third.kind {
5151                        TokenKind::GreaterThan => {
5152                            return Some(ShiftToken::new(BinOp::UnsignedShr, 3, false));
5153                        }
5154                        TokenKind::GreaterEquals => {
5155                            return Some(ShiftToken::new(BinOp::UnsignedShr, 3, true));
5156                        }
5157                        _ => {}
5158                    }
5159                }
5160                Some(ShiftToken::new(BinOp::Shr, 2, false))
5161            }
5162            _ => None,
5163        }
5164    }
5165
5166    fn peek_binary(&self) -> Option<BinaryToken> {
5167        if let Some(shift) = self.peek_shift() {
5168            return (!shift.assignment).then_some(BinaryToken {
5169                op: shift.op,
5170                precedence: SHIFT_PRECEDENCE,
5171                tokens: shift.tokens,
5172            });
5173        }
5174        peek_binop(&self.peek().kind).map(|(op, precedence)| BinaryToken {
5175            op,
5176            precedence,
5177            tokens: 1,
5178        })
5179    }
5180
5181    fn parse_binary_inner(&mut self, min_prec: u8) -> Option<ExprId> {
5182        let mut lhs = self.parse_cast()?;
5183        // Track the previous op to reject `a || b ?? c` / `a ?? b || c` mixes. Both
5184        // reach this loop: `??` parses its right operand above `&&` (see
5185        // `right_operand_min_prec`), so `||` / `&&` after it is left for this check.
5186        let mut last_op: Option<BinOp> = None;
5187        let mut reported_mixing = false;
5188        while let Some(BinaryToken {
5189            op,
5190            precedence: prec,
5191            tokens: count,
5192        }) = self.peek_binary()
5193        {
5194            if prec < min_prec {
5195                break;
5196            }
5197            if !reported_mixing
5198                && let Some(last) = last_op
5199                && mixed_logical(last, op)
5200            {
5201                reported_mixing = true;
5202                let op_span = self.peek().span;
5203                // Parsing continues as if the left side were grouped, so one error
5204                // inside an `if (...)` header doesn't cascade through the statement.
5205                // Later mixes in the same chain add nothing, so only the first is reported.
5206                self.error_at_with_help(
5207                    op_span,
5208                    "mixing `??` with `||` / `&&` requires parentheses",
5209                    vec![mixed_logical_help(last, op)],
5210                );
5211            }
5212            if op == BinOp::Pow
5213                && matches!(
5214                    parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.kind,
5215                    ExprKind::Unary { .. }
5216                        | ExprKind::Typeof { .. }
5217                        | ExprKind::Delete { .. }
5218                        | ExprKind::Void { .. }
5219                )
5220            {
5221                let error_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5222                self.error_at_with_help(
5223                    error_span,
5224                    "an unparenthesized unary expression cannot be the left operand of `**`",
5225                    vec!["choose the grouping explicitly: `(-x) ** 2` or `-(x ** 2)`".into()],
5226                );
5227                return None;
5228            }
5229            let loose = matches!(self.peek().kind, TokenKind::EqEq | TokenKind::BangEq);
5230            for _ in 0..count {
5231                self.advance();
5232            }
5233            let rhs = self.parse_binary(right_operand_min_prec(op, prec))?;
5234            let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5235            let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
5236            if loose {
5237                self.reject_loose_nullish_comparison(op, lhs, rhs)?;
5238            }
5239            lhs = parse_arena_result(
5240                self.ast.try_push_expr(Expr {
5241                    kind: ExprKind::Binary { op, lhs, rhs },
5242                    span: self.span(lhs_span.start, rhs_span.end),
5243                }),
5244                &mut self.fatal,
5245            )?;
5246            last_op = Some(op);
5247        }
5248        Some(lhs)
5249    }
5250
5251    // Higher than every binary op, lower than every unary prefix. Left-associative.
5252    fn parse_cast(&mut self) -> Option<ExprId> {
5253        let mut expr = self.parse_unary()?;
5254        while matches!(self.peek().kind, TokenKind::Instanceof)
5255            || self.peek_identifier_text_is("as")
5256        {
5257            let is_instanceof = matches!(self.peek().kind, TokenKind::Instanceof);
5258            self.advance();
5259            let ty = self.parse_type_annotation()?;
5260            let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5261                .span
5262                .start;
5263            let end = ty.span.end;
5264            let kind = if is_instanceof {
5265                ExprKind::InstanceOf { value: expr, ty }
5266            } else {
5267                self.last_as_type_end = Some(end);
5268                ExprKind::As { expr, ty }
5269            };
5270            expr = parse_arena_result(
5271                self.ast.try_push_expr(Expr {
5272                    kind,
5273                    span: self.span(start, end),
5274                }),
5275                &mut self.fatal,
5276            )?;
5277        }
5278        Some(expr)
5279    }
5280
5281    fn parse_unary(&mut self) -> Option<ExprId> {
5282        self.with_recursion_limit(Self::parse_unary_inner)
5283    }
5284
5285    fn parse_unary_inner(&mut self) -> Option<ExprId> {
5286        if self.reject_await_operator() {
5287            return self.parse_unary();
5288        }
5289        // `typeof` is parsed as a unary prefix so `typeof x === "T"` works without a new layer;
5290        // the typechecker rejects `Typeof` outside the recognized equality fold position.
5291        if matches!(self.peek().kind, TokenKind::Typeof) {
5292            return self.parse_prefix_expr(|operand| ExprKind::Typeof { operand });
5293        }
5294        if matches!(self.peek().kind, TokenKind::Void) {
5295            return self.parse_prefix_expr(|operand| ExprKind::Void { operand });
5296        }
5297        if self.at_delete_operator() {
5298            return self.parse_prefix_expr(|operand| ExprKind::Delete { operand });
5299        }
5300        if matches!(self.peek().kind, TokenKind::New) {
5301            let op_tok = self.advance();
5302            let mut callee = self.parse_atom()?;
5303            while matches!(self.peek().kind, TokenKind::Dot) {
5304                self.advance();
5305                if !matches!(self.peek().kind, TokenKind::Identifier) {
5306                    self.error_at_peek("expected identifier after `.` in constructor name");
5307                    return None;
5308                }
5309                let ident_tok = self.advance();
5310                let name = self.ident_from_token(&ident_tok);
5311                let receiver_span =
5312                    parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5313                let combined_span = self.span(receiver_span.start, ident_tok.span.end);
5314                callee = parse_arena_result(
5315                    self.ast.try_push_expr(Expr {
5316                        kind: ExprKind::FieldAccess {
5317                            receiver: callee,
5318                            name,
5319                        },
5320                        span: combined_span,
5321                    }),
5322                    &mut self.fatal,
5323                )?;
5324            }
5325            let type_args = if matches!(self.peek().kind, TokenKind::LessThan) {
5326                let saved_pos = self.pos;
5327                let saved_diag_len = self.diagnostics.len();
5328                if let Some(args) = self.try_parse_type_args_only() {
5329                    Some(args)
5330                } else {
5331                    self.pos = saved_pos;
5332                    self.diagnostics.truncate(saved_diag_len);
5333                    None
5334                }
5335            } else {
5336                None
5337            };
5338            if !matches!(self.peek().kind, TokenKind::LeftParen) {
5339                self.error_at_peek("expected `(` after constructor name in `new` expression");
5340                return None;
5341            }
5342            self.advance();
5343            let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5344                Vec::new()
5345            } else {
5346                self.parse_call_args()?
5347            };
5348            if !matches!(self.peek().kind, TokenKind::RightParen) {
5349                self.error_at_peek("expected `)`");
5350                return None;
5351            }
5352            let close = self.advance();
5353            let new_expr = parse_arena_result(
5354                self.ast.try_push_expr(Expr {
5355                    kind: ExprKind::New {
5356                        callee,
5357                        type_args,
5358                        args,
5359                    },
5360                    span: self.span(op_tok.span.start, close.span.end),
5361                }),
5362                &mut self.fatal,
5363            )?;
5364            // `new Foo()` is a postfix primary: `new Map().set(...)`, `new Map().size`,
5365            // `new Foo()[i]` all continue the chain off the constructor result.
5366            return self.parse_postfix_from(new_expr);
5367        }
5368        let op = match self.peek().kind {
5369            TokenKind::Bang => UnOp::Not,
5370            TokenKind::Tilde => UnOp::BitNot,
5371            TokenKind::Minus => UnOp::Neg,
5372            TokenKind::Plus => UnOp::Pos,
5373            _ => return self.parse_postfix(),
5374        };
5375        self.parse_prefix_expr(|operand| ExprKind::Unary { op, operand })
5376    }
5377
5378    /// Consume a prefix operator and its operand, spanning both.
5379    fn parse_prefix_expr(&mut self, kind: impl FnOnce(ExprId) -> ExprKind) -> Option<ExprId> {
5380        let op_tok = self.advance();
5381        // ASI cannot distinguish the `void` type from the expression operator.
5382        // In expression position a newline after the keyword still awaits an operand.
5383        if matches!(op_tok.kind, TokenKind::Void) && self.at_inserted_semicolon() {
5384            self.advance();
5385        }
5386        let operand = self.parse_unary()?;
5387        let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5388        parse_arena_result(
5389            self.ast.try_push_expr(Expr {
5390                kind: kind(operand),
5391                span: self.span(op_tok.span.start, operand_span.end),
5392            }),
5393            &mut self.fatal,
5394        )
5395    }
5396
5397    fn parse_postfix(&mut self) -> Option<ExprId> {
5398        let expr = self.parse_atom()?;
5399        self.parse_postfix_from(expr)
5400    }
5401
5402    fn parse_postfix_from(&mut self, mut expr: ExprId) -> Option<ExprId> {
5403        // OptionalChain is emitted once at end-of-loop so a speculative-`<` rewind
5404        // never strands a half-built chain. Everything before the first `?.` accumulates
5405        // in `expr` normally; from the first `?.` onward ops go into `parts`.
5406        let mut chain_parts: Option<(ExprId, Vec<crate::ChainPart>)> = None;
5407        loop {
5408            match self.peek().kind {
5409                TokenKind::LeftParen => {
5410                    if let Some((_, parts)) = &mut chain_parts {
5411                        let part = self.parse_chain_call_tail(/*optional=*/ false)?;
5412                        parts.push(part);
5413                    } else {
5414                        expr = self.parse_call_tail(expr, None)?;
5415                    }
5416                }
5417                TokenKind::Dot => {
5418                    if let Some((_, parts)) = &mut chain_parts {
5419                        let part = self.parse_chain_field_tail(/*optional=*/ false)?;
5420                        parts.push(part);
5421                    } else {
5422                        expr = self.parse_field_access_tail(expr)?;
5423                    }
5424                }
5425                TokenKind::LeftBracket => {
5426                    if let Some((_, parts)) = &mut chain_parts {
5427                        let part = self.parse_chain_index_tail(/*optional=*/ false)?;
5428                        parts.push(part);
5429                    } else {
5430                        expr = self.parse_index_access_tail(expr)?;
5431                    }
5432                }
5433                TokenKind::QuestionDot => {
5434                    if chain_parts.is_none() {
5435                        chain_parts = Some((expr, Vec::new()));
5436                    }
5437                    self.advance();
5438                    let part = match self.peek().kind {
5439                        TokenKind::LeftParen => self.parse_chain_call_tail(true)?,
5440                        TokenKind::LeftBracket => self.parse_chain_index_tail(true)?,
5441                        _ if is_property_name(&self.peek().kind) => {
5442                            self.parse_chain_field_tail(true)?
5443                        }
5444                        _ => {
5445                            self.error_at_peek(
5446                                "expected field name, `(` for call, or `[` for index after `?.`",
5447                            );
5448                            return None;
5449                        }
5450                    };
5451                    if let Some((_, parts)) = &mut chain_parts {
5452                        parts.push(part);
5453                    }
5454                }
5455                // `<` after a callable expression might be a generic call `f<T>(args)`.
5456                // Speculatively parse; on failure restore state and let `parse_binary`
5457                // treat `<` as a comparison.
5458                TokenKind::LessThan => {
5459                    if chain_parts.is_some() {
5460                        // `a?.<T>()` not yet supported — terminate the chain.
5461                        break;
5462                    }
5463                    let saved_pos = self.pos;
5464                    let saved_diag_len = self.diagnostics.len();
5465                    if let Some(call_id) = self.try_parse_generic_call_tail(expr) {
5466                        expr = call_id;
5467                    } else {
5468                        self.pos = saved_pos;
5469                        self.diagnostics.truncate(saved_diag_len);
5470                        break;
5471                    }
5472                }
5473                TokenKind::Bang => {
5474                    if let Some((_, parts)) = &mut chain_parts {
5475                        let tok = self.advance();
5476                        parts.push(crate::ChainPart::NonNull { span: tok.span });
5477                        continue;
5478                    }
5479                    let tok = self.advance();
5480                    let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5481                        .span
5482                        .start;
5483                    expr = parse_arena_result(
5484                        self.ast.try_push_expr(Expr {
5485                            kind: ExprKind::PostfixUnary {
5486                                op: PostfixOp::NonNullAssert,
5487                                operand: expr,
5488                            },
5489                            span: self.span(start, tok.span.end),
5490                        }),
5491                        &mut self.fatal,
5492                    )?;
5493                }
5494                // Postfix `++`/`--` terminates the chain; `a?.b++` is rejected.
5495                TokenKind::PlusPlus | TokenKind::MinusMinus => {
5496                    if chain_parts.is_some() {
5497                        break;
5498                    }
5499                    let tok = self.advance();
5500                    let op = if matches!(tok.kind, TokenKind::PlusPlus) {
5501                        PostfixOp::Inc
5502                    } else {
5503                        PostfixOp::Dec
5504                    };
5505                    let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5506                        .span
5507                        .start;
5508                    expr = parse_arena_result(
5509                        self.ast.try_push_expr(Expr {
5510                            kind: ExprKind::PostfixUnary { op, operand: expr },
5511                            span: self.span(start, tok.span.end),
5512                        }),
5513                        &mut self.fatal,
5514                    )?;
5515                    break;
5516                }
5517                _ => break,
5518            }
5519        }
5520        if let Some((base, parts)) = chain_parts {
5521            let base_span = parse_arena_result(self.ast.try_expr(base), &mut self.fatal)?.span;
5522            let end = parts.last().map_or(base_span.end, |p| p.span().end);
5523            Some(parse_arena_result(
5524                self.ast.try_push_expr(Expr {
5525                    kind: ExprKind::OptionalChain { base, parts },
5526                    span: self.span(base_span.start, end),
5527                }),
5528                &mut self.fatal,
5529            )?)
5530        } else {
5531            Some(expr)
5532        }
5533    }
5534
5535    // `.` is consumed here for the non-optional case; `?.` was consumed by the caller.
5536    fn parse_chain_field_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5537        let start_pos = self.peek().span.start;
5538        if !optional {
5539            self.advance();
5540        }
5541        let name_tok = if is_property_name(&self.peek().kind) {
5542            self.advance()
5543        } else {
5544            let what = if optional { "?." } else { "." };
5545            self.error_at_peek(format!("expected field name after `{what}`"));
5546            return None;
5547        };
5548        let name = self.ident_from_token(&name_tok);
5549        let end = name.span.end;
5550        Some(crate::ChainPart::Field {
5551            name,
5552            optional,
5553            span: self.span(start_pos, end),
5554        })
5555    }
5556
5557    fn parse_chain_index_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5558        let open = self.advance();
5559        let idx = self.parse_expression()?;
5560        if !matches!(self.peek().kind, TokenKind::RightBracket) {
5561            self.error_at_peek("expected `]`");
5562            return None;
5563        }
5564        let close = self.advance();
5565        Some(crate::ChainPart::Index {
5566            idx,
5567            optional,
5568            span: self.span(open.span.start, close.span.end),
5569        })
5570    }
5571
5572    fn parse_chain_call_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5573        let open = self.advance();
5574        let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5575            Vec::new()
5576        } else {
5577            self.parse_call_args()?
5578        };
5579        if !matches!(self.peek().kind, TokenKind::RightParen) {
5580            self.error_at_peek("expected `)`");
5581            return None;
5582        }
5583        let close = self.advance();
5584        Some(crate::ChainPart::Call {
5585            args,
5586            type_args: None,
5587            optional,
5588            span: self.span(open.span.start, close.span.end),
5589        })
5590    }
5591
5592    fn parse_field_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5593        self.advance();
5594        // Keywords are valid field names — `Foo.new(args)` is the canonical constructor call.
5595        let name_tok = if is_property_name(&self.peek().kind) {
5596            self.advance()
5597        } else {
5598            self.error_at_peek("expected field name after `.`");
5599            return None;
5600        };
5601        let name = self.ident_from_token(&name_tok);
5602        let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5603        parse_arena_result(
5604            self.ast.try_push_expr(Expr {
5605                kind: ExprKind::FieldAccess {
5606                    receiver,
5607                    name: name.clone(),
5608                },
5609                span: self.span(receiver_span.start, name.span.end),
5610            }),
5611            &mut self.fatal,
5612        )
5613    }
5614
5615    fn parse_index_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5616        self.advance();
5617        let index = self.parse_expression()?;
5618        if !matches!(self.peek().kind, TokenKind::RightBracket) {
5619            self.error_at_peek("expected `]`");
5620            return None;
5621        }
5622        let close = self.advance();
5623        let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5624        parse_arena_result(
5625            self.ast.try_push_expr(Expr {
5626                kind: ExprKind::IndexAccess { receiver, index },
5627                span: self.span(receiver_span.start, close.span.end),
5628            }),
5629            &mut self.fatal,
5630        )
5631    }
5632
5633    fn parse_call_tail(
5634        &mut self,
5635        callee: ExprId,
5636        type_args: Option<Vec<TypeAnnotation>>,
5637    ) -> Option<ExprId> {
5638        self.advance();
5639        let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5640            Vec::new()
5641        } else {
5642            self.parse_call_args()?
5643        };
5644        if !matches!(self.peek().kind, TokenKind::RightParen) {
5645            self.error_at_peek("expected `)`");
5646            return None;
5647        }
5648        let close = self.advance();
5649
5650        let callee_span = parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5651        parse_arena_result(
5652            self.ast.try_push_expr(Expr {
5653                kind: ExprKind::Call {
5654                    callee,
5655                    type_args,
5656                    args,
5657                },
5658                span: self.span(callee_span.start, close.span.end),
5659            }),
5660            &mut self.fatal,
5661        )
5662    }
5663
5664    // Caller restores position + diagnostics on `None` so `<` is re-interpreted as comparison.
5665    fn try_parse_generic_call_tail(&mut self, callee: ExprId) -> Option<ExprId> {
5666        let type_args = self.try_parse_type_args_only()?;
5667        if !matches!(self.peek().kind, TokenKind::LeftParen) {
5668            return None;
5669        }
5670        self.parse_call_tail(callee, Some(type_args))
5671    }
5672
5673    fn try_parse_type_args_only(&mut self) -> Option<Vec<TypeAnnotation>> {
5674        if !matches!(self.peek().kind, TokenKind::LessThan) {
5675            return self.invariant_failure("type argument parser expected `<`");
5676        }
5677        self.advance();
5678
5679        if matches!(self.peek().kind, TokenKind::GreaterThan) {
5680            return None;
5681        }
5682
5683        let mut type_args = Vec::new();
5684        loop {
5685            let ta = self.parse_type_annotation()?;
5686            type_args.push(ta);
5687            match self.peek().kind {
5688                TokenKind::Comma => {
5689                    self.advance();
5690                    if matches!(self.peek().kind, TokenKind::GreaterThan) {
5691                        self.advance();
5692                        break;
5693                    }
5694                }
5695                TokenKind::GreaterThan => {
5696                    self.advance();
5697                    break;
5698                }
5699                _ => return None,
5700            }
5701        }
5702        Some(type_args)
5703    }
5704
5705    fn parse_call_args(&mut self) -> Option<Vec<ExprId>> {
5706        let mut args = Vec::new();
5707        loop {
5708            let arg = self.parse_expression()?;
5709            args.push(arg);
5710            match self.peek().kind {
5711                TokenKind::Comma => {
5712                    self.advance();
5713                    if matches!(self.peek().kind, TokenKind::RightParen) {
5714                        return Some(args);
5715                    }
5716                }
5717                TokenKind::RightParen => return Some(args),
5718                _ => {
5719                    self.error_at_peek("expected `,` or `)`");
5720                    return None;
5721                }
5722            }
5723        }
5724    }
5725
5726    /// `<T>expr`, TypeScript's original cast syntax, lowered to the same
5727    /// [`ExprKind::As`] node `expr as T` produces — so it is checked and emitted
5728    /// identically, including the runtime `ref.test` our `as` performs.
5729    ///
5730    /// The operand is a unary expression, which is what binds `<T>-1` as a cast of
5731    /// `-1` rather than a cast of `1` that is then negated.
5732    fn parse_angle_cast(&mut self) -> Option<ExprId> {
5733        let open = self.advance();
5734        let ty = self.parse_type_annotation()?;
5735        if !matches!(self.peek().kind, TokenKind::GreaterThan) {
5736            self.error_at_peek("expected `>` to close a type assertion");
5737            return None;
5738        }
5739        self.advance();
5740        let expr = self.parse_unary()?;
5741        let end = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5742            .span
5743            .end;
5744        parse_arena_result(
5745            self.ast.try_push_expr(Expr {
5746                kind: ExprKind::As { expr, ty },
5747                span: self.span(open.span.start, end),
5748            }),
5749            &mut self.fatal,
5750        )
5751    }
5752
5753    fn parse_atom(&mut self) -> Option<ExprId> {
5754        match self.peek().kind {
5755            // `<T>expr` — the older spelling of `expr as T`, lowered to the same node.
5756            // Unambiguous only because this is an atom's first token: a `<` that means
5757            // comparison always has a left operand, and one that opens a generic
5758            // argument list always follows a callee, so neither reaches here.
5759            TokenKind::LessThan => return self.parse_angle_cast(),
5760            TokenKind::LeftParen => return self.parse_paren(),
5761            // `{` here is an object literal; statement-start `{` is dispatched to
5762            // `parse_block` before `parse_atom` runs, so the two stay disjoint.
5763            TokenKind::LeftBrace => return self.parse_object_literal(),
5764            TokenKind::LeftBracket => return self.parse_array_literal(),
5765            TokenKind::TemplateHead(_) => return self.parse_template_literal(),
5766            TokenKind::This => return self.parse_this_or_super(true),
5767            TokenKind::Super => return self.parse_this_or_super(false),
5768            TokenKind::Function => return self.parse_function_expression(),
5769            _ => {}
5770        }
5771        if !matches!(
5772            self.peek().kind,
5773            TokenKind::NumberLiteral(_)
5774                | TokenKind::BigIntLiteral(_)
5775                | TokenKind::StringLiteral(_)
5776                | TokenKind::TemplateNoSubstitution(_)
5777                | TokenKind::BooleanLiteral(_)
5778                | TokenKind::NullLiteral
5779                | TokenKind::Identifier
5780                | TokenKind::RegexLiteral { .. }
5781        ) {
5782            self.error_at_peek("expected expression");
5783            return None;
5784        }
5785        let tok = self.advance();
5786        let span = tok.span;
5787        let kind = match tok.kind {
5788            TokenKind::NumberLiteral(v) => ExprKind::Number(v),
5789            TokenKind::BigIntLiteral(s) => ExprKind::BigInt(s),
5790            TokenKind::StringLiteral(s) => ExprKind::String(s),
5791            TokenKind::TemplateNoSubstitution(s) => ExprKind::String(s),
5792            TokenKind::BooleanLiteral(b) => ExprKind::Boolean(b),
5793            TokenKind::NullLiteral => ExprKind::Null,
5794            TokenKind::Identifier => ExprKind::Identifier(Ident {
5795                name: self.source[span.start as usize..span.end as usize].to_string(),
5796                span,
5797            }),
5798            TokenKind::RegexLiteral { source, flags } => ExprKind::Regex { source, flags },
5799            // No cursor movement occurs between the accepted-kind check and advance.
5800            _ => unreachable!("advance preserves the accepted expression token"),
5801        };
5802        parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5803    }
5804
5805    /// Ordinary functions establish a `this` boundary and exclude an enclosing
5806    /// class's `super`; arrows inherit the current parsing context.
5807    fn parse_this_or_super(&mut self, is_this: bool) -> Option<ExprId> {
5808        let tok = self.advance();
5809        let span = tok.span;
5810        if !is_this && self.class_member_body_depth == 0 {
5811            self.error_at_with_help(
5812                span,
5813                "`super` is only valid inside a class method or constructor body",
5814                vec![
5815                    "call `super(...)` in a subclass constructor or `super.method(...)` \
5816                     in a subclass method"
5817                        .to_string(),
5818                ],
5819            );
5820        }
5821        let kind = if is_this
5822            && self.class_member_body_depth == 0
5823            && self.function_expression_body_depth == 0
5824        {
5825            ExprKind::ThisOutsideReceiver
5826        } else if is_this {
5827            ExprKind::This
5828        } else {
5829            ExprKind::Super
5830        };
5831        parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5832    }
5833
5834    // parse_atom dispatches immediately after matching TemplateHead.
5835    // The remaining template tokens are still parsed and diagnosed normally.
5836    fn parse_template_literal(&mut self) -> Option<ExprId> {
5837        let head_tok = self.advance();
5838        let TokenKind::TemplateHead(head) = head_tok.kind else {
5839            unreachable!("template dispatch requires TemplateHead");
5840        };
5841        let start = head_tok.span.start;
5842        let mut parts: Vec<String> = vec![head];
5843        let mut exprs: Vec<ExprId> = Vec::new();
5844        let mut substitution_spans: Vec<Span> = Vec::new();
5845        // The token before a substitution ends just past its `${`; the one after
5846        // starts at its `}`.
5847        let mut substitution_start = head_tok.span.end.saturating_sub(2);
5848        loop {
5849            let expr = self.parse_expression()?;
5850            exprs.push(expr);
5851            let closing = self.peek().span.start;
5852            let substitution_span = self.span(substitution_start, closing.saturating_add(1));
5853            match self.peek().kind.clone() {
5854                TokenKind::TemplateMiddle(s) => {
5855                    let tok = self.advance();
5856                    substitution_spans.push(substitution_span);
5857                    substitution_start = tok.span.end.saturating_sub(2);
5858                    parts.push(s);
5859                }
5860                TokenKind::TemplateTail(s) => {
5861                    let tok = self.advance();
5862                    substitution_spans.push(substitution_span);
5863                    parts.push(s);
5864                    return parse_arena_result(
5865                        self.ast.try_push_expr(Expr {
5866                            kind: ExprKind::TemplateLiteral {
5867                                parts,
5868                                exprs,
5869                                substitution_spans,
5870                            },
5871                            span: self.span(start, tok.span.end),
5872                        }),
5873                        &mut self.fatal,
5874                    );
5875                }
5876                _ => {
5877                    self.error_at_peek("expected `}` to close template interpolation");
5878                    return None;
5879                }
5880            }
5881        }
5882    }
5883
5884    fn parse_object_literal(&mut self) -> Option<ExprId> {
5885        let open = self.advance();
5886
5887        let mut members: Vec<ObjectLiteralMember> = Vec::new();
5888        if !matches!(self.peek().kind, TokenKind::RightBrace) {
5889            loop {
5890                if matches!(self.peek().kind, TokenKind::DotDotDot) {
5891                    let dots = self.advance();
5892                    let value = self.parse_expression()?;
5893                    let value_span =
5894                        parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5895                    members.push(ObjectLiteralMember::Spread {
5896                        value,
5897                        span: self.span(dots.span.start, value_span.end),
5898                    });
5899                } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
5900                    self.advance();
5901                    let key = self.parse_expression()?;
5902                    if !matches!(self.peek().kind, TokenKind::RightBracket) {
5903                        self.error_at_peek("expected `]` after computed property key");
5904                        return None;
5905                    }
5906                    self.advance();
5907                    if !matches!(self.peek().kind, TokenKind::Colon) {
5908                        self.error_at_peek("expected `:` after computed property key; computed methods are not supported");
5909                        return None;
5910                    }
5911                    self.advance();
5912                    let value = self.parse_expression()?;
5913                    members.push(ObjectLiteralMember::Computed { key, value });
5914                } else {
5915                    let field = self.parse_object_literal_field()?;
5916                    // Spread members are dynamic — only literal fields participate in
5917                    // the duplicate-key check.
5918                    if let Some(existing) = members.iter().find_map(|m| match m {
5919                        ObjectLiteralMember::Field(f) if f.name.name == field.name.name => Some(f),
5920                        _ => None,
5921                    }) {
5922                        self.diagnostics.push(Diagnostic {
5923                            severity: Severity::Error,
5924                            span: field.name.span,
5925                            message: format!(
5926                                "duplicate field `{}` in object literal",
5927                                field.name.name
5928                            ),
5929                            help: vec![],
5930                            notes: vec![(existing.name.span, "first defined here".into())],
5931                        });
5932                        // Keep parsing so the user sees all duplicates at once.
5933                    }
5934                    members.push(ObjectLiteralMember::Field(field));
5935                }
5936                if self.at_inserted_semicolon()
5937                    && matches!(self.peek_at(1).kind, TokenKind::RightBrace)
5938                {
5939                    self.advance();
5940                }
5941                match self.peek().kind {
5942                    TokenKind::Comma => {
5943                        self.advance();
5944                        if matches!(self.peek().kind, TokenKind::RightBrace) {
5945                            break;
5946                        }
5947                    }
5948                    TokenKind::RightBrace => break,
5949                    _ => {
5950                        self.error_at_peek("expected `,` or `}`");
5951                        return None;
5952                    }
5953                }
5954            }
5955        }
5956
5957        if !matches!(self.peek().kind, TokenKind::RightBrace) {
5958            self.error_at_peek("expected `}`");
5959            return None;
5960        }
5961        let close = self.advance();
5962
5963        parse_arena_result(
5964            self.ast.try_push_expr(Expr {
5965                kind: ExprKind::ObjectLiteral { members },
5966                span: self.span(open.span.start, close.span.end),
5967            }),
5968            &mut self.fatal,
5969        )
5970    }
5971
5972    fn parse_object_literal_field(&mut self) -> Option<ObjectLiteralField> {
5973        self.reject_async_member_modifier();
5974        let (name, shorthandable) = match self.peek().kind.clone() {
5975            TokenKind::Identifier => {
5976                let tok = self.advance();
5977                (self.ident_from_token(&tok), true)
5978            }
5979            kind if is_property_name(&kind) => {
5980                let tok = self.advance();
5981                (self.ident_from_token(&tok), false)
5982            }
5983            TokenKind::StringLiteral(_) => {
5984                let tok = self.advance();
5985                (self.property_ident_from_token(&tok), false)
5986            }
5987            _ => {
5988                self.error_at_peek("expected field name");
5989                return None;
5990            }
5991        };
5992
5993        // `{ m(x: T): R { … } }` is shorthand for `{ m: (x: T): R => { … } }`. Method
5994        // shorthand carries no `this` of its own here, for the same reason a function
5995        // expression does not, so the arrow is an exact lowering. Checked before the
5996        // `:` branch and independently of `shorthandable`, because a keyword or string
5997        // key can name a method too.
5998        if matches!(self.peek().kind, TokenKind::LeftParen) {
5999            let value = self.parse_method_shorthand_body(name.span)?;
6000            return Some(ObjectLiteralField { name, value });
6001        }
6002
6003        if !matches!(self.peek().kind, TokenKind::Colon) {
6004            // `{ x }` is shorthand for `{ x: x }`. Only identifier keys can use
6005            // it — a string key like `{ "x" }` has no binding to reference.
6006            if shorthandable {
6007                let value = parse_arena_result(
6008                    self.ast.try_push_expr(Expr {
6009                        kind: ExprKind::Identifier(name.clone()),
6010                        span: name.span,
6011                    }),
6012                    &mut self.fatal,
6013                )?;
6014                return Some(ObjectLiteralField { name, value });
6015            }
6016            self.error_at_peek("expected `:` after field name");
6017            return None;
6018        }
6019        self.advance();
6020
6021        let value = self.parse_expression()?;
6022        Some(ObjectLiteralField { name, value })
6023    }
6024
6025    /// The `(params): R { … }` tail of an object-literal method, lowered to an arrow.
6026    /// `name_span` is the key's span, so the arrow spans the whole member.
6027    fn parse_method_shorthand_body(&mut self, name_span: Span) -> Option<ExprId> {
6028        self.advance();
6029
6030        let params = if matches!(self.peek().kind, TokenKind::RightParen) {
6031            Vec::new()
6032        } else {
6033            self.parse_arrow_param_list()?
6034        };
6035        if !matches!(self.peek().kind, TokenKind::RightParen) {
6036            self.error_at_peek("expected `)`");
6037            return None;
6038        }
6039        self.advance();
6040
6041        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
6042            self.advance();
6043            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
6044        } else {
6045            (None, None)
6046        };
6047
6048        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
6049            self.error_at_peek("expected `{` to open the method body");
6050            return None;
6051        }
6052        let block = self.parse_outer_this_boundary(Self::parse_block)?;
6053        let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
6054            .span
6055            .end;
6056
6057        parse_arena_result(
6058            self.ast.try_push_expr(Expr {
6059                kind: ExprKind::Arrow {
6060                    params,
6061                    return_type,
6062                    type_predicate,
6063                    body: ArrowBody::Block(block),
6064                },
6065                span: self.span(name_span.start, end),
6066            }),
6067            &mut self.fatal,
6068        )
6069    }
6070
6071    fn parse_array_literal(&mut self) -> Option<ExprId> {
6072        let open = self.advance();
6073
6074        let mut elements: Vec<ArrayLiteralElement> = Vec::new();
6075        if !matches!(self.peek().kind, TokenKind::RightBracket) {
6076            loop {
6077                let element = if matches!(self.peek().kind, TokenKind::DotDotDot) {
6078                    let dots = self.advance();
6079                    let value = self.parse_expression()?;
6080                    let value_span =
6081                        parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
6082                    ArrayLiteralElement::Spread {
6083                        value,
6084                        span: self.span(dots.span.start, value_span.end),
6085                    }
6086                } else {
6087                    ArrayLiteralElement::Value(self.parse_expression()?)
6088                };
6089                elements.push(element);
6090                match self.peek().kind {
6091                    TokenKind::Comma => {
6092                        self.advance();
6093                        if matches!(self.peek().kind, TokenKind::RightBracket) {
6094                            break;
6095                        }
6096                    }
6097                    TokenKind::RightBracket => break,
6098                    _ => {
6099                        self.error_at_peek("expected `,` or `]`");
6100                        return None;
6101                    }
6102                }
6103            }
6104        }
6105
6106        if !matches!(self.peek().kind, TokenKind::RightBracket) {
6107            self.error_at_peek("expected `]`");
6108            return None;
6109        }
6110        let close = self.advance();
6111
6112        parse_arena_result(
6113            self.ast.try_push_expr(Expr {
6114                kind: ExprKind::ArrayLiteral { elements },
6115                span: self.span(open.span.start, close.span.end),
6116            }),
6117            &mut self.fatal,
6118        )
6119    }
6120
6121    fn parse_paren(&mut self) -> Option<ExprId> {
6122        let open = self.advance();
6123        let inner = self.parse_expression()?;
6124        if !matches!(self.peek().kind, TokenKind::RightParen) {
6125            self.error_at_peek("expected `)`");
6126            return None;
6127        }
6128        let close = self.advance();
6129        let span = self.span(open.span.start, close.span.end);
6130        parse_arena_result(
6131            self.ast.try_push_expr(Expr {
6132                kind: ExprKind::Paren(inner),
6133                span,
6134            }),
6135            &mut self.fatal,
6136        )
6137    }
6138
6139    fn with_recursion_limit<T>(&mut self, parse: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
6140        if self.fatal.is_some() {
6141            return None;
6142        }
6143        if self.recursion_limit_span.is_some() || self.recursion_depth >= MAX_PARSE_DEPTH {
6144            self.stop_at_recursion_limit();
6145            return None;
6146        }
6147        self.recursion_depth += 1;
6148        let result = parse(self);
6149        self.recursion_depth -= 1;
6150        result
6151    }
6152
6153    fn stop_at_recursion_limit(&mut self) {
6154        self.recursion_limit_span.get_or_insert(self.peek().span);
6155        // Stop recovery and enclosing block loops as well as recursive calls.
6156        self.pos = self.tokens.len().saturating_sub(1);
6157    }
6158
6159    fn recover(&mut self) {
6160        while !self.is_at_eof() {
6161            match self.peek().kind {
6162                TokenKind::Semicolon => {
6163                    self.advance();
6164                    return;
6165                }
6166                TokenKind::RightBrace
6167                | TokenKind::Let
6168                | TokenKind::Const
6169                | TokenKind::Function
6170                | TokenKind::Export
6171                | TokenKind::If
6172                | TokenKind::While
6173                | TokenKind::Return
6174                | TokenKind::Try
6175                | TokenKind::Throw => return,
6176                _ => {
6177                    self.advance();
6178                }
6179            }
6180        }
6181    }
6182
6183    fn peek(&self) -> &Token {
6184        self.tokens.get(self.pos).unwrap_or(&self.eof)
6185    }
6186
6187    fn peek_at(&self, offset: usize) -> &Token {
6188        self.pos
6189            .checked_add(offset)
6190            .and_then(|index| self.tokens.get(index))
6191            .unwrap_or(&self.eof)
6192    }
6193
6194    /// ASI inserts its `;` as a zero-width token; a written `;` always has width.
6195    fn at_inserted_semicolon(&self) -> bool {
6196        let token = self.peek();
6197        matches!(token.kind, TokenKind::Semicolon) && token.span.start == token.span.end
6198    }
6199
6200    fn advance(&mut self) -> Token {
6201        let tok = self.peek().clone();
6202        if self.pos < self.tokens.len().saturating_sub(1) {
6203            self.pos += 1;
6204        }
6205        tok
6206    }
6207
6208    fn is_at_eof(&self) -> bool {
6209        self.fatal.is_some() || matches!(self.peek().kind, TokenKind::Eof)
6210    }
6211
6212    /// Whether a line break separates the next token from the one before it.
6213    fn line_break_before_peek(&self) -> bool {
6214        self.source_has_line_break(self.prev_token_end(), self.peek().span.start)
6215    }
6216
6217    /// Whether a line break separates the next token from the one after it.
6218    fn line_break_after_peek(&self) -> bool {
6219        self.source_has_line_break(self.peek().span.end, self.peek_at(1).span.start)
6220    }
6221
6222    /// The ASI pass drops newline tokens, so line breaks between tokens are read
6223    /// from the source.
6224    fn source_has_line_break(&self, start: u32, end: u32) -> bool {
6225        self.source
6226            .get(start as usize..end as usize)
6227            .is_some_and(|gap| gap.contains(['\n', '\r']))
6228    }
6229
6230    /// Whether the statement being parsed ends here: at a `;`, or at a line break
6231    /// right after an `as T`. ASI keeps a `[` or `(` on the next line attached, but
6232    /// the type already stopped at the break (see `parse_type_array_inner`), and no
6233    /// expression continues past a type, so TypeScript ends the statement there.
6234    fn at_statement_end(&self) -> bool {
6235        matches!(self.peek().kind, TokenKind::Semicolon)
6236            || (self.last_as_type_end == Some(self.prev_token_end())
6237                && self.line_break_before_peek())
6238    }
6239
6240    /// Consumes the `;` that [`Parser::at_statement_end`] found, if there is one, and
6241    /// returns the statement's end offset.
6242    fn finish_statement(&mut self) -> u32 {
6243        if matches!(self.peek().kind, TokenKind::Semicolon) {
6244            return self.advance().span.end;
6245        }
6246        self.prev_token_end()
6247    }
6248
6249    /// End offset of the most recently consumed token (the body of the file's first
6250    /// token if nothing has been consumed yet).
6251    fn prev_token_end(&self) -> u32 {
6252        self.tokens
6253            .get(self.pos.saturating_sub(1))
6254            .unwrap_or(&self.eof)
6255            .span
6256            .end
6257    }
6258
6259    fn take_leading_doc(&mut self) -> Option<crate::DocComment> {
6260        let parsed = self
6261            .peek()
6262            .leading_doc
6263            .as_ref()
6264            .map(crate::parse_doc_comment)
6265            .transpose();
6266        match parsed {
6267            Ok(doc) => doc,
6268            Err(error) => {
6269                self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6270                None
6271            }
6272        }
6273    }
6274
6275    fn parse_doc(&mut self, raw: &crate::RawDoc) -> Option<crate::DocComment> {
6276        match crate::parse_doc_comment(raw) {
6277            Ok(doc) => Some(doc),
6278            Err(error) => {
6279                self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6280                None
6281            }
6282        }
6283    }
6284
6285    /// JavaScript forbids a `"use strict"` directive in a function whose parameters
6286    /// aren't all plain names: one with a default, a pattern, or `...`. As TypeScript
6287    /// does, each such parameter and the directive are reported.
6288    fn reject_use_strict_with_non_simple_params(&mut self) {
6289        let mut functions: Vec<(Vec<Span>, StmtId)> = Vec::new();
6290        let non_simple = |param: &ParamDecl| {
6291            (param.default.is_some() || param.pattern.is_some() || param.rest).then(|| {
6292                param
6293                    .pattern
6294                    .as_ref()
6295                    .map_or(param.name.span, Binding::span)
6296            })
6297        };
6298        for expr in self.ast.source_expressions() {
6299            if let ExprKind::Arrow {
6300                params,
6301                body: ArrowBody::Block(body),
6302                ..
6303            } = &expr.kind
6304            {
6305                functions.push((params.iter().filter_map(non_simple).collect(), *body));
6306            }
6307        }
6308        for stmt in self.ast.source_statements() {
6309            match &stmt.kind {
6310                StmtKind::Function { params, body, .. } => {
6311                    functions.push((params.iter().filter_map(non_simple).collect(), *body));
6312                }
6313                StmtKind::ClassDecl { members, .. } => {
6314                    for member in members {
6315                        match member {
6316                            ClassMember::Method { params, body, .. }
6317                            | ClassMember::Constructor { params, body, .. } => {
6318                                functions
6319                                    .push((params.iter().filter_map(non_simple).collect(), *body));
6320                            }
6321                            ClassMember::Accessor { param, body, .. } => {
6322                                let params = param.as_deref().and_then(non_simple);
6323                                functions.push((params.into_iter().collect(), *body));
6324                            }
6325                            _ => {}
6326                        }
6327                    }
6328                }
6329                _ => {}
6330            }
6331        }
6332        for (params, body) in functions {
6333            if params.is_empty() {
6334                continue;
6335            }
6336            let Some(directive) = self.use_strict_directive(body) else {
6337                continue;
6338            };
6339            for span in params {
6340                self.error_at(
6341                    span,
6342                    "this parameter is not allowed with a `\"use strict\"` directive",
6343                );
6344            }
6345            self.error_at_with_help(
6346                directive,
6347                "a `\"use strict\"` directive can't be used with a parameter list that has defaults, patterns, or a rest parameter",
6348                vec!["remove the directive; Submilli code is always strict".into()],
6349            );
6350        }
6351    }
6352
6353    /// The span of a `"use strict"` directive in the prologue of a function body:
6354    /// the string-literal statements it starts with. As in TypeScript, the string's
6355    /// value decides, so an escape spelling it counts too; a template doesn't.
6356    fn use_strict_directive(&self, body: StmtId) -> Option<Span> {
6357        let StmtKind::Block(stmts) = &self.ast.try_stmt(body).ok()?.kind else {
6358            return None;
6359        };
6360        for stmt in stmts {
6361            let stmt = self.ast.try_stmt(*stmt).ok()?;
6362            let StmtKind::Expr(expr) = stmt.kind else {
6363                return None;
6364            };
6365            let expr = self.ast.try_expr(expr).ok()?;
6366            let ExprKind::String(value) = &expr.kind else {
6367                return None;
6368            };
6369            if self.source.as_bytes().get(expr.span.start as usize) == Some(&b'`') {
6370                return None;
6371            }
6372            if value == "use strict" {
6373                return Some(stmt.span);
6374            }
6375        }
6376        None
6377    }
6378
6379    fn error_at(&mut self, span: Span, message: impl Into<String>) {
6380        self.diagnostics.push(Diagnostic {
6381            severity: Severity::Error,
6382            span,
6383            message: message.into(),
6384            help: vec![],
6385            notes: vec![],
6386        });
6387    }
6388
6389    /// `==` is `===` here, so `x == null` would silently miss `undefined` — the
6390    /// one case where TypeScript's loose equality differs in a way code relies on.
6391    fn reject_loose_nullish_comparison(
6392        &mut self,
6393        op: BinOp,
6394        lhs: ExprId,
6395        rhs: ExprId,
6396    ) -> Option<()> {
6397        let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
6398        let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
6399        let (nullish, operand, nullish_first) =
6400            match (self.nullish_operand(lhs)?, self.nullish_operand(rhs)?) {
6401                (Some(left), Some(right)) => {
6402                    self.reject_nullish_against_nullish(op, lhs_span, rhs_span, [left, right]);
6403                    return Some(());
6404                }
6405                (None, Some(nullish)) => (nullish, lhs, false),
6406                (Some(nullish), None) => (nullish, rhs, true),
6407                (None, None) => return Some(()),
6408            };
6409        let comparison = LooseComparison {
6410            nullish,
6411            nullish_first,
6412            negated: op == BinOp::NotEq,
6413        };
6414        let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
6415        let x = self.single_line_source(operand_span).unwrap_or("x");
6416        let strict = self.strict_suggestion(&comparison, x);
6417        let either = self.either_suggestion(&comparison, operand, x)?;
6418        let literal = comparison.nullish.literal.text();
6419        // Quote the comparison as written, so `null == z` stays in that order.
6420        let written = self
6421            .single_line_source(self.span(lhs_span.start, rhs_span.end))
6422            .map_or_else(
6423                || {
6424                    let written_op = if comparison.negated { "!=" } else { "==" };
6425                    comparison.ordered(x, written_op, literal)
6426                },
6427                str::to_string,
6428            );
6429        self.error_at_with_help(
6430            self.span(lhs_span.start, rhs_span.end),
6431            format!(
6432                "`{written}` does not also match `{}`: `==` compares like `===`",
6433                comparison.nullish.literal.other().text()
6434            ),
6435            vec![
6436                either,
6437                format!("to test for `{literal}` alone, write `{strict}`"),
6438            ],
6439        );
6440        Some(())
6441    }
6442
6443    /// Both sides are always nullish. TypeScript's `==` treats `null` and
6444    /// `undefined` as equal, so the comparison has one value, which the help
6445    /// names; a `void` operand with effects still runs, as a statement.
6446    fn reject_nullish_against_nullish(
6447        &mut self,
6448        op: BinOp,
6449        lhs: Span,
6450        rhs: Span,
6451        sides: [NullishOperand; 2],
6452    ) {
6453        let span = self.span(lhs.start, rhs.end);
6454        // A comparison written over several lines can't be quoted on one, so
6455        // the message names it without the source.
6456        let subject = self.single_line_source(span).map_or_else(
6457            || "this comparison".to_string(),
6458            |written| format!("`{written}`"),
6459        );
6460        let value = if op == BinOp::NotEq { "false" } else { "true" };
6461        let effects: Vec<String> = sides
6462            .iter()
6463            .filter_map(|side| side.effect)
6464            .map(|effect| {
6465                self.single_line_source(effect.operand).map_or_else(
6466                    || "the `void` operand's expression".to_string(),
6467                    |statement| format!("`{statement};`"),
6468                )
6469            })
6470            .collect();
6471        let help = if effects.is_empty() {
6472            format!("in TypeScript this is always `{value}`; write `{value}`")
6473        } else {
6474            format!(
6475                "in TypeScript this is always `{value}`; run {} {}, then use `{value}`",
6476                effects.join(" and "),
6477                if effects.len() > 1 {
6478                    "each as its own statement"
6479                } else {
6480                    "as its own statement"
6481                }
6482            )
6483        };
6484        self.error_at_with_help(
6485            span,
6486            format!("{subject} compares two nullish values: `==` compares like `===`"),
6487            vec![help],
6488        );
6489    }
6490
6491    /// The strict comparison against the written nullish value. A `void`
6492    /// operand with effects stays as written, so the operand runs once.
6493    fn strict_suggestion(&self, comparison: &LooseComparison, x: &str) -> String {
6494        let eq = comparison.strict_op();
6495        match comparison.nullish.effect {
6496            Some(effect) => {
6497                let void = self.single_line_source(effect.void).unwrap_or("void expr");
6498                comparison.ordered(x, eq, void)
6499            }
6500            None => format!("{x} {eq} {}", comparison.nullish.literal.text()),
6501        }
6502    }
6503
6504    /// "To test for either" names the operand twice, so one with effects is
6505    /// bound to a `const` first, and a `void` operand's expression runs as a
6506    /// statement of its own.
6507    fn either_suggestion(
6508        &mut self,
6509        comparison: &LooseComparison,
6510        operand: ExprId,
6511        x: &str,
6512    ) -> Option<String> {
6513        let mut steps = Vec::new();
6514        let tested = if self.repeats_without_effect(operand)? {
6515            x
6516        } else {
6517            steps.push(format!("bind `{x}` to a `const` (`const value = {x};`)"));
6518            "value"
6519        };
6520        if let Some(effect) = comparison.nullish.effect {
6521            let statement = self.single_line_source(effect.operand).unwrap_or("expr");
6522            let step = format!("run `{statement};` as its own statement");
6523            if comparison.nullish_first {
6524                steps.insert(0, step);
6525            } else {
6526                steps.push(step);
6527            }
6528        }
6529        let eq = comparison.strict_op();
6530        let joiner = if comparison.negated { "&&" } else { "||" };
6531        let both = format!("{tested} {eq} null {joiner} {tested} {eq} undefined");
6532        Some(if steps.is_empty() {
6533            format!("to test for either, write `{both}`")
6534        } else {
6535            format!(
6536                "to test for either, first {}, then write `{both}`",
6537                steps.join(", then ")
6538            )
6539        })
6540    }
6541
6542    /// Whether naming `id` twice evaluates it twice with no effect: a literal,
6543    /// or a reference path.
6544    fn repeats_without_effect(&mut self, id: ExprId) -> Option<bool> {
6545        let unwrapped = self.unparenthesized(id)?;
6546        let expr = parse_arena_result(self.ast.try_expr(unwrapped), &mut self.fatal)?;
6547        if is_literal(&expr.kind) {
6548            return Some(true);
6549        }
6550        if self
6551            .nullish_operand(unwrapped)?
6552            .is_some_and(|nullish| nullish.effect.is_none())
6553        {
6554            return Some(true);
6555        }
6556        self.is_reference_path(id)
6557    }
6558
6559    /// `null`, `undefined` or `void e`, looking through parentheses.
6560    fn nullish_operand(&mut self, id: ExprId) -> Option<Option<NullishOperand>> {
6561        let id = self.unparenthesized(id)?;
6562        let expr = parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?;
6563        let span = expr.span;
6564        Some(match &expr.kind {
6565            ExprKind::Null => Some(NullishOperand {
6566                literal: Nullish::Null,
6567                effect: None,
6568            }),
6569            ExprKind::Identifier(ident) if ident.name == "undefined" => Some(NullishOperand {
6570                literal: Nullish::Undefined,
6571                effect: None,
6572            }),
6573            ExprKind::Void { operand } => {
6574                let operand_id = self.unparenthesized(*operand)?;
6575                let operand =
6576                    parse_arena_result(self.ast.try_expr(operand_id), &mut self.fatal)?.clone();
6577                // A literal or a reference path reads with no effect to keep.
6578                let effect_free =
6579                    is_literal(&operand.kind) || self.is_reference_path(operand_id)?;
6580                let effect = (!effect_free).then_some(VoidEffect {
6581                    void: span,
6582                    operand: operand.span,
6583                });
6584                Some(NullishOperand {
6585                    literal: Nullish::Undefined,
6586                    effect,
6587                })
6588            }
6589            _ => None,
6590        })
6591    }
6592
6593    /// An identifier, `this`, or a `.field` chain on one: naming it twice evaluates
6594    /// it twice with no effect.
6595    fn is_reference_path(&mut self, id: ExprId) -> Option<bool> {
6596        let mut id = id;
6597        loop {
6598            match &parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6599                ExprKind::Identifier(_) | ExprKind::This => return Some(true),
6600                ExprKind::FieldAccess { receiver, .. } => id = *receiver,
6601                ExprKind::Paren(inner) => id = *inner,
6602                _ => return Some(false),
6603            }
6604        }
6605    }
6606
6607    fn unparenthesized(&mut self, id: ExprId) -> Option<ExprId> {
6608        let mut id = id;
6609        loop {
6610            match parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6611                ExprKind::Paren(inner) => id = inner,
6612                _ => return Some(id),
6613            }
6614        }
6615    }
6616
6617    fn single_line_source(&self, span: Span) -> Option<&'a str> {
6618        self.source
6619            .get(span.start as usize..span.end as usize)
6620            .filter(|text| !text.contains('\n'))
6621    }
6622
6623    fn error_at_with_help(&mut self, span: Span, message: impl Into<String>, help: Vec<String>) {
6624        self.diagnostics.push(Diagnostic {
6625            severity: Severity::Error,
6626            span,
6627            message: message.into(),
6628            help,
6629            notes: vec![],
6630        });
6631    }
6632
6633    fn invariant_failure<T>(&mut self, message: &str) -> Option<T> {
6634        self.fatal.get_or_insert_with(|| CompilerFailure::Internal {
6635            stage: CompilerStage::Parse,
6636            span: None,
6637            message: message.into(),
6638        });
6639        self.pos = self.tokens.len();
6640        None
6641    }
6642
6643    fn error_at_peek(&mut self, message: impl Into<String>) {
6644        let span = self.peek().span;
6645        self.error_at(span, message);
6646    }
6647
6648    fn error_at_peek_with_help(&mut self, message: impl Into<String>, help: Vec<String>) {
6649        let span = self.peek().span;
6650        self.error_at_with_help(span, message, help);
6651    }
6652
6653    fn error_count(&self) -> usize {
6654        self.diagnostics
6655            .iter()
6656            .filter(|d| d.severity == Severity::Error)
6657            .count()
6658    }
6659}
6660
6661pub(crate) fn is_property_name(kind: &TokenKind) -> bool {
6662    matches!(
6663        kind,
6664        TokenKind::Identifier
6665            | TokenKind::BooleanLiteral(_)
6666            | TokenKind::NullLiteral
6667            | TokenKind::Let
6668            | TokenKind::Const
6669            | TokenKind::Function
6670            | TokenKind::If
6671            | TokenKind::Else
6672            | TokenKind::While
6673            | TokenKind::Do
6674            | TokenKind::For
6675            | TokenKind::Break
6676            | TokenKind::Continue
6677            | TokenKind::Return
6678            | TokenKind::Switch
6679            | TokenKind::Case
6680            | TokenKind::Default
6681            | TokenKind::Void
6682            | TokenKind::Interface
6683            | TokenKind::Enum
6684            | TokenKind::In
6685            | TokenKind::Typeof
6686            | TokenKind::Import
6687            | TokenKind::Export
6688            | TokenKind::New
6689            | TokenKind::Try
6690            | TokenKind::Catch
6691            | TokenKind::Finally
6692            | TokenKind::Throw
6693            | TokenKind::Class
6694            | TokenKind::Extends
6695            | TokenKind::Implements
6696            | TokenKind::Super
6697            | TokenKind::This
6698    )
6699}
6700
6701fn is_reserved_identifier_word(kind: &TokenKind) -> bool {
6702    matches!(
6703        kind,
6704        TokenKind::BooleanLiteral(_)
6705            | TokenKind::NullLiteral
6706            | TokenKind::Let
6707            | TokenKind::Const
6708            | TokenKind::Function
6709            | TokenKind::If
6710            | TokenKind::Else
6711            | TokenKind::While
6712            | TokenKind::Do
6713            | TokenKind::For
6714            | TokenKind::Break
6715            | TokenKind::Continue
6716            | TokenKind::Return
6717            | TokenKind::Switch
6718            | TokenKind::Case
6719            | TokenKind::Default
6720            | TokenKind::Export
6721            | TokenKind::Void
6722            | TokenKind::Interface
6723            | TokenKind::Enum
6724            | TokenKind::In
6725            | TokenKind::Typeof
6726            | TokenKind::Import
6727            | TokenKind::New
6728            | TokenKind::Try
6729            | TokenKind::Catch
6730            | TokenKind::Finally
6731            | TokenKind::Throw
6732            | TokenKind::Class
6733            | TokenKind::Extends
6734            | TokenKind::Implements
6735            | TokenKind::Super
6736            | TokenKind::This
6737    )
6738}
6739
6740/// Which statement a `{ … }` specifier list belongs to. An import binds local
6741/// names, which strict mode restricts; an export's are module export names,
6742/// which it doesn't.
6743#[derive(Clone, Copy, PartialEq, Eq)]
6744enum SpecifierList {
6745    Import,
6746    Export,
6747}
6748
6749impl SpecifierList {
6750    fn noun(self) -> &'static str {
6751        match self {
6752            SpecifierList::Import => "import",
6753            SpecifierList::Export => "export",
6754        }
6755    }
6756}
6757
6758/// Words strict mode reserves that Submilli otherwise lexes as identifiers: they
6759/// can't name a binding or a type, but stay valid as property names and modifiers.
6760/// (`implements`, `interface` and `let` are keywords already.)
6761const STRICT_MODE_RESERVED_WORDS: [&str; 6] = [
6762    "package",
6763    "private",
6764    "protected",
6765    "public",
6766    "static",
6767    "yield",
6768];
6769
6770fn reserved_keyword_rename_example(keyword: &str) -> &'static str {
6771    match keyword {
6772        "default" => "defaultValue",
6773        _ => "valueName",
6774    }
6775}
6776
6777fn mixed_logical(prev: BinOp, next: BinOp) -> bool {
6778    let prev_logical = matches!(prev, BinOp::Or | BinOp::And);
6779    let next_logical = matches!(next, BinOp::Or | BinOp::And);
6780    let prev_nullish = matches!(prev, BinOp::NullishCoalesce);
6781    let next_nullish = matches!(next, BinOp::NullishCoalesce);
6782    (prev_logical && next_nullish) || (prev_nullish && next_logical)
6783}
6784
6785/// Spells out both groupings in the order the operators were written. Called
6786/// only for pairs `mixed_logical` accepts, so the non-`??` operator is `||` or `&&`.
6787fn mixed_logical_help(prev: BinOp, next: BinOp) -> String {
6788    let symbol = |op: BinOp| if op == BinOp::And { "&&" } else { "||" };
6789    let (first, second) = if prev == BinOp::NullishCoalesce {
6790        ("??", symbol(next))
6791    } else {
6792        (symbol(prev), "??")
6793    };
6794    format!(
6795        "add parentheses to choose the grouping: `(a {first} b) {second} c` or `a {first} (b {second} c)`"
6796    )
6797}
6798
6799const LOGICAL_AND_PREC: u8 = 2;
6800
6801/// One side of a loose comparison that is always nullish.
6802#[derive(Clone, Copy)]
6803struct NullishOperand {
6804    literal: Nullish,
6805    /// Set for `void e` when `e` is not a literal and may have effects.
6806    effect: Option<VoidEffect>,
6807}
6808
6809#[derive(Clone, Copy, PartialEq, Eq)]
6810enum Nullish {
6811    Null,
6812    Undefined,
6813}
6814
6815impl Nullish {
6816    fn text(self) -> &'static str {
6817        match self {
6818            Nullish::Null => "null",
6819            Nullish::Undefined => "undefined",
6820        }
6821    }
6822
6823    /// The nullish value a loose comparison against `self` fails to match.
6824    fn other(self) -> Self {
6825        match self {
6826            Nullish::Null => Nullish::Undefined,
6827            Nullish::Undefined => Nullish::Null,
6828        }
6829    }
6830}
6831
6832/// A rejected `x == null`-style comparison, as written.
6833struct LooseComparison {
6834    nullish: NullishOperand,
6835    /// Whether the nullish side is written first, as in `null == x`.
6836    nullish_first: bool,
6837    /// `!=` rather than `==`.
6838    negated: bool,
6839}
6840
6841impl LooseComparison {
6842    fn strict_op(&self) -> &'static str {
6843        if self.negated { "!==" } else { "===" }
6844    }
6845
6846    /// `x op nullish`, in the order the comparison was written.
6847    fn ordered(&self, x: &str, op: &str, nullish: &str) -> String {
6848        if self.nullish_first {
6849            format!("{nullish} {op} {x}")
6850        } else {
6851            format!("{x} {op} {nullish}")
6852        }
6853    }
6854}
6855
6856#[derive(Clone, Copy)]
6857struct VoidEffect {
6858    /// The whole `void e`.
6859    void: Span,
6860    /// The `e`, without enclosing parentheses.
6861    operand: Span,
6862}
6863
6864fn is_literal(kind: &ExprKind) -> bool {
6865    matches!(
6866        kind,
6867        ExprKind::Number(_)
6868            | ExprKind::BigInt(_)
6869            | ExprKind::String(_)
6870            | ExprKind::Boolean(_)
6871            | ExprKind::Null
6872    )
6873}
6874
6875/// What `Parser::check_parameter_order` needs from either kind of parameter list.
6876struct ParamOrder<'p> {
6877    optional: bool,
6878    /// Neither optional, defaulted, nor rest.
6879    required: bool,
6880    /// `None` for a destructuring pattern.
6881    name: Option<&'p str>,
6882    span: Span,
6883}
6884
6885impl<'p> ParamOrder<'p> {
6886    fn of_param(param: &'p ParamDecl) -> Self {
6887        let (name, span) = match &param.pattern {
6888            Some(binding) => (None, binding.span()),
6889            None => (Some(param.name.name.as_str()), param.name.span),
6890        };
6891        Self {
6892            optional: param.optional,
6893            required: !param.is_omittable() && !param.rest,
6894            name,
6895            span,
6896        }
6897    }
6898
6899    fn of_field(field: &'p TypeAnnotationField) -> Self {
6900        Self {
6901            optional: field.optional,
6902            required: !field.optional && !field.rest,
6903            name: Some(field.name.name.as_str()),
6904            span: field.name.span,
6905        }
6906    }
6907}
6908
6909/// A binary operator at the cursor, which may span several tokens.
6910struct BinaryToken {
6911    op: BinOp,
6912    precedence: u8,
6913    tokens: usize,
6914}
6915
6916/// `<<`, `>>`, `>>>` or their compound assignments, read from adjacent angle tokens.
6917struct ShiftToken {
6918    op: BinOp,
6919    tokens: usize,
6920    /// The operator ends in `=`: `<<=`, `>>=`, `>>>=`.
6921    assignment: bool,
6922}
6923
6924impl ShiftToken {
6925    fn new(op: BinOp, tokens: usize, assignment: bool) -> Self {
6926        Self {
6927            op,
6928            tokens,
6929            assignment,
6930        }
6931    }
6932}
6933
6934/// Between relational (7) and additive (9) in the `peek_binop` table.
6935const SHIFT_PRECEDENCE: u8 = 8;
6936
6937#[derive(Clone, Copy, PartialEq)]
6938enum TupleElementLabel {
6939    None,
6940    /// `name: T`
6941    Required,
6942    /// `name?: T`
6943    Optional,
6944}
6945
6946fn peek_binop(kind: &TokenKind) -> Option<(BinOp, u8)> {
6947    Some(match kind {
6948        TokenKind::QuestionQuestion => (BinOp::NullishCoalesce, 0),
6949        TokenKind::PipePipe => (BinOp::Or, 1),
6950        TokenKind::AmpAmp => (BinOp::And, LOGICAL_AND_PREC),
6951        TokenKind::Pipe => (BinOp::BitOr, 3),
6952        TokenKind::Caret => (BinOp::BitXor, 4),
6953        TokenKind::Amp => (BinOp::BitAnd, 5),
6954        TokenKind::EqEqEq | TokenKind::EqEq => (BinOp::Eq, 6),
6955        TokenKind::BangEqEq | TokenKind::BangEq => (BinOp::NotEq, 6),
6956        TokenKind::LessThan => (BinOp::Lt, 7),
6957        TokenKind::GreaterThan => (BinOp::Gt, 7),
6958        TokenKind::LessEquals => (BinOp::Le, 7),
6959        TokenKind::GreaterEquals => (BinOp::Ge, 7),
6960        TokenKind::In => (BinOp::In, 7),
6961        // Shifts take `SHIFT_PRECEDENCE` (8); they span several angle tokens, so
6962        // `Parser::peek_shift` recognizes them rather than this table.
6963        TokenKind::Plus => (BinOp::Add, 9),
6964        TokenKind::Minus => (BinOp::Sub, 9),
6965        TokenKind::Star => (BinOp::Mul, 10),
6966        TokenKind::Slash => (BinOp::Div, 10),
6967        TokenKind::Percent => (BinOp::Rem, 10),
6968        // `**` recurses with `prec` not `prec + 1` — that's what makes it right-associative.
6969        TokenKind::StarStar => (BinOp::Pow, 11),
6970        _ => return None,
6971    })
6972}
6973
6974/// After `as` or `satisfies`, a token that can only extend or end an operand, never
6975/// begin a type. `[` counts, as TypeScript reads `delete as [0]` as an index, so a
6976/// tuple cast of a binding named `delete` needs parentheses.
6977fn cannot_start_type(kind: &TokenKind) -> bool {
6978    matches!(
6979        kind,
6980        TokenKind::Dot
6981            | TokenKind::QuestionDot
6982            | TokenKind::LeftBracket
6983            | TokenKind::Bang
6984            | TokenKind::Semicolon
6985            | TokenKind::RightParen
6986            | TokenKind::RightBrace
6987            | TokenKind::Comma
6988            | TokenKind::Eof
6989    )
6990}
6991
6992/// The lowest precedence the right operand of `op` may contain. `**` is
6993/// right-associative, so it recurses at its own level. An operand of `??` may
6994/// not be an unparenthesized `||` / `&&` expression, so it stops below them too.
6995fn right_operand_min_prec(op: BinOp, prec: u8) -> u8 {
6996    match op {
6997        BinOp::Pow => prec,
6998        BinOp::NullishCoalesce => LOGICAL_AND_PREC + 1,
6999        _ => prec + 1,
7000    }
7001}
7002
7003fn compound_op_for_token(kind: &TokenKind) -> Option<BinOp> {
7004    Some(match kind {
7005        TokenKind::PlusEquals => BinOp::Add,
7006        TokenKind::MinusEquals => BinOp::Sub,
7007        TokenKind::StarEquals => BinOp::Mul,
7008        TokenKind::SlashEquals => BinOp::Div,
7009        TokenKind::PercentEquals => BinOp::Rem,
7010        TokenKind::StarStarEquals => BinOp::Pow,
7011        TokenKind::AmpEquals => BinOp::BitAnd,
7012        TokenKind::PipeEquals => BinOp::BitOr,
7013        TokenKind::CaretEquals => BinOp::BitXor,
7014        _ => return None,
7015    })
7016}
7017
7018fn is_assign_lookahead(kind: &TokenKind) -> bool {
7019    matches!(kind, TokenKind::Equals) || compound_op_for_token(kind).is_some()
7020}
7021
7022/// Parser productions use `None` for recovery; arena errors also latch a fatal
7023/// cause so speculative parsing cannot turn them into ordinary syntax errors.
7024fn parse_arena_result<T>(
7025    result: Result<T, crate::arena::ArenaError>,
7026    fatal: &mut Option<CompilerFailure>,
7027) -> Option<T> {
7028    match result {
7029        Ok(value) => Some(value),
7030        Err(error) => {
7031            fatal.get_or_insert_with(|| error.into_compiler_failure(CompilerStage::Parse));
7032            None
7033        }
7034    }
7035}
7036
7037/// Whether a rest parameter's annotation names an array: `T[]`, or `readonly T[]`.
7038fn is_rest_array_annotation(ty: &crate::TypeAnnotation) -> bool {
7039    use crate::ast::TypeAnnotationKind;
7040    match &ty.kind {
7041        TypeAnnotationKind::Array(_) => true,
7042        TypeAnnotationKind::Readonly(inner) => matches!(inner.kind, TypeAnnotationKind::Array(_)),
7043        _ => false,
7044    }
7045}
7046
7047#[cfg(test)]
7048mod tests {
7049    use super::{MAX_ERRORS, MAX_PARSE_DEPTH, Parser, parse};
7050    use crate::source::Sources;
7051    use crate::{Asi, Ast, ExprKind, FileId, ImportKind, StmtKind, Token, TokenKind, diagnostics};
7052
7053    const F: FileId = FileId(0);
7054
7055    #[test]
7056    fn malformed_direct_token_streams_return_typed_failures() {
7057        use crate::compiler_error::CompilerFailure;
7058        for tokens in [
7059            Vec::new(),
7060            vec![Token::new(
7061                TokenKind::Identifier,
7062                crate::Span::new(F, 0, 2).unwrap(),
7063            )],
7064            vec![
7065                Token::new(
7066                    TokenKind::Identifier,
7067                    crate::Span {
7068                        file: F,
7069                        start: 1,
7070                        end: 2,
7071                    },
7072                ),
7073                Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
7074            ],
7075            vec![
7076                Token::new(TokenKind::Eof, crate::Span::at(F)),
7077                Token::new(TokenKind::Eof, crate::Span::at(F)),
7078            ],
7079            vec![
7080                Token::new(
7081                    TokenKind::Identifier,
7082                    crate::Span {
7083                        file: F,
7084                        start: 2,
7085                        end: 0,
7086                    },
7087                ),
7088                Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
7089            ],
7090            vec![Token::new(TokenKind::Eof, crate::Span::at(FileId(9)))],
7091        ] {
7092            let error = super::parse_checked("é", tokens, F).expect_err("invalid token metadata");
7093            assert!(matches!(
7094                error.fatal,
7095                Some(CompilerFailure::Internal { .. })
7096            ));
7097        }
7098    }
7099
7100    #[test]
7101    fn malformed_documentation_metadata_returns_a_fatal_error() {
7102        let source = "/** hello */ function main(): void {}";
7103        let valid = tokens_of(source);
7104        for doc in [
7105            crate::RawDoc {
7106                text: "/** hi */".into(),
7107                span: crate::Span {
7108                    file: F,
7109                    start: u32::MAX,
7110                    end: u32::MAX,
7111                },
7112            },
7113            crate::RawDoc {
7114                text: "/** different */".into(),
7115                span: crate::Span::new(F, 0, 12).unwrap(),
7116            },
7117            crate::RawDoc {
7118                text: "/** hello */".into(),
7119                span: crate::Span::new(FileId(9), 0, 12).unwrap(),
7120            },
7121        ] {
7122            let mut tokens = valid.clone();
7123            tokens[0].leading_doc = Some(doc);
7124            let error = super::parse_checked(source, tokens, F).expect_err("invalid doc metadata");
7125            assert!(error.to_string().contains("documentation metadata"));
7126        }
7127        assert!(super::parse_checked(source, valid, F).is_ok());
7128    }
7129
7130    #[test]
7131    fn invalid_parser_dispatch_survives_diagnostic_rollback() {
7132        let mut parser = parser_from_source("let x = 1;");
7133        assert!(parser.try_parse_type_args_only().is_none());
7134        parser.diagnostics.clear();
7135        assert!(parser.fatal.is_some());
7136        assert!(parser.parse_expression().is_none());
7137    }
7138
7139    #[test]
7140    fn invalid_arena_read_stops_parser_and_survives_diagnostic_rollback() {
7141        use crate::{
7142            ExprId,
7143            compiler_error::{CompilerFailure, CompilerStage},
7144        };
7145        let mut parser = parser_from_source("let x = 1;");
7146        assert!(parser.statement_kind_for(ExprId(u32::MAX)).is_none());
7147        assert!(matches!(
7148            parser.fatal,
7149            Some(CompilerFailure::Internal {
7150                stage: CompilerStage::Parse,
7151                span: None,
7152                ..
7153            })
7154        ));
7155        parser.diagnostics.clear();
7156        let position = parser.pos;
7157        assert!(parser.parse_statement().is_none());
7158        parser.recover();
7159        assert_eq!(parser.pos, position);
7160        assert!(parser.ast.expr_ids().unwrap().next().is_none());
7161    }
7162
7163    fn tokens_of(source: &str) -> Vec<Token> {
7164        let mut asi = Asi::new(source, crate::FileId(0));
7165        let mut tokens = Vec::new();
7166        loop {
7167            let tok = asi.next_token();
7168            let is_eof = matches!(tok.kind, TokenKind::Eof);
7169            tokens.push(tok);
7170            if is_eof {
7171                break;
7172            }
7173        }
7174        let diags = asi.into_diagnostics();
7175        assert!(diags.is_empty(), "unexpected lexer diagnostics: {diags:?}");
7176        tokens
7177    }
7178
7179    fn parse_str(source: &str) -> (Ast, Vec<crate::Diagnostic>) {
7180        parse(source, tokens_of(source), crate::FileId(0))
7181    }
7182
7183    fn parser_from_source(source: &str) -> Parser<'_> {
7184        Parser {
7185            source,
7186            tokens: tokens_of(source),
7187            file: F,
7188            pos: 0,
7189            ast: Ast::new(),
7190            diagnostics: Vec::new(),
7191            block_depth: 0,
7192            class_member_body_depth: 0,
7193            function_expression_body_depth: 0,
7194            recursion_depth: 0,
7195            recursion_limit_span: None,
7196            resume_at_next_member: false,
7197            last_as_type_end: None,
7198            trailing_declarators: Vec::new(),
7199            eof: Token::new(
7200                TokenKind::Eof,
7201                crate::Span::new(F, source.len() as u32, source.len() as u32).unwrap(),
7202            ),
7203            fatal: None,
7204        }
7205    }
7206
7207    #[test]
7208    fn recursion_budget_accepts_boundary_and_restores_depth() {
7209        let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH - 1));
7210        let mut parser = parser_from_source(&source);
7211        assert!(parser.parse_unary().is_some());
7212        assert_eq!(parser.recursion_depth, 0);
7213        assert!(parser.recursion_limit_span.is_none());
7214
7215        let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH));
7216        let mut parser = parser_from_source(&source);
7217        assert!(parser.parse_unary().is_none());
7218        assert_eq!(parser.recursion_depth, 0);
7219        assert!(parser.recursion_limit_span.is_some());
7220    }
7221
7222    #[test]
7223    fn parse_empty_input() {
7224        let (ast, diags) = parse_str("");
7225        assert!(diags.is_empty());
7226        assert!(ast.top_level.is_empty());
7227    }
7228
7229    #[test]
7230    fn any_type_is_rejected() {
7231        // `any` is not supported — the parser rejects it in every type position
7232        // (annotation, cast target, array/generic element) and points to the fix.
7233        for src in [
7234            "function main(): void { const x: any = 1; }",
7235            "function main(): void { const x = 1 as any; }",
7236            "function main(): void { const x: any[] = []; }",
7237        ] {
7238            let (_ast, diags) = parse_str(src);
7239            assert!(
7240                diags
7241                    .iter()
7242                    .any(|d| d.message.contains("`any` is not supported")),
7243                "expected an `any` rejection for {src:?}, got: {diags:?}",
7244            );
7245            assert!(
7246                diags
7247                    .iter()
7248                    .any(|d| d.help.iter().any(|h| h.contains("`unknown`"))),
7249                "expected the fix-shape help for {src:?}, got: {diags:?}",
7250            );
7251        }
7252    }
7253
7254    #[test]
7255    fn any_catch_binding_help_suggests_catch_forms() {
7256        // `unknown` is not a valid catch binding type, so the general `any`
7257        // help would send the reader to a second error.
7258        let (_ast, diags) = parse_str("function main(): void { try { } catch (e: any) { } }");
7259        let any_diags: Vec<_> = diags
7260            .iter()
7261            .filter(|d| d.message.contains("`any` is not supported"))
7262            .collect();
7263        assert_eq!(any_diags.len(), 1, "got: {diags:?}");
7264        assert!(
7265            any_diags[0].help.iter().any(|h| h.contains("`catch (e)`")),
7266            "got: {diags:?}"
7267        );
7268        assert!(
7269            !any_diags[0].help.iter().any(|h| h.contains("`unknown`")),
7270            "got: {diags:?}"
7271        );
7272    }
7273
7274    #[test]
7275    fn arrow_return_type_scanner_matches_type_grammar() {
7276        // Every form the annotation grammar accepts must also be accepted by
7277        // the arrow-disambiguator's lookahead (scan_past_type_annotation):
7278        // a miss there turns a valid arrow into `expected expression`.
7279        // When adding a type form to parse_type_array, add a row here.
7280        for ty in [
7281            "number",
7282            "void",
7283            "null",
7284            "ns.Foo",
7285            "Map<string, number>",
7286            "ns.Foo<string>[]",
7287            "number[]",
7288            "[number, number]",
7289            "[number, [string, boolean]]",
7290            "[number, string][]",
7291            "{ a: number }",
7292            "{ a: number }[]",
7293            "\"on\" | \"off\"",
7294            "1 | 2",
7295            "Point | null",
7296            "| number | string",
7297            "(n: number) => number",
7298            "(n: number) => [number, number]",
7299            "readonly number[]",
7300            "readonly number[][]",
7301            "readonly [number, string]",
7302            "readonly number[] | null",
7303            "[first: number, second: string]",
7304            "[new: number, string]",
7305            "keyof { a: number }",
7306        ] {
7307            let annotation = format!("function f(): void {{ const a: {ty} = x; }}");
7308            let (_ast, diags) = parse_str(&annotation);
7309            assert!(
7310                diags.is_empty(),
7311                "annotation grammar rejected {ty:?}: {diags:?}",
7312            );
7313
7314            let arrow = format!("function f(): void {{ const g = (): {ty} => x; }}");
7315            let (_ast, diags) = parse_str(&arrow);
7316            assert!(
7317                diags.is_empty(),
7318                "arrow lookahead rejected return type {ty:?}: {diags:?}",
7319            );
7320        }
7321
7322        // Type-predicate returns ride the same scanner.
7323        let (_ast, diags) =
7324            parse_str("function f(): void { const g = (v: unknown): v is string => x; }");
7325        assert!(
7326            diags.is_empty(),
7327            "arrow lookahead rejected predicate return: {diags:?}",
7328        );
7329    }
7330
7331    #[test]
7332    fn bare_semicolons_are_silent_empty_statements() {
7333        let (ast, diags) = parse_str(";");
7334        assert!(diags.is_empty());
7335        assert!(ast.top_level.is_empty());
7336    }
7337
7338    #[test]
7339    fn parse_single_non_expression_token_errors() {
7340        let (ast, diags) = parse_str("}");
7341        assert_eq!(diags.len(), 1);
7342        assert_eq!(diags[0].message, "expected expression");
7343        assert!(ast.top_level.is_empty());
7344    }
7345
7346    #[test]
7347    fn recovery_across_many_bad_starts() {
7348        let (ast, diags) = parse_str("} } } x;");
7349        assert_eq!(diags.len(), 3);
7350        for d in &diags {
7351            assert_eq!(d.message, "expected expression");
7352        }
7353        assert_eq!(ast.top_level.len(), 1);
7354    }
7355
7356    #[test]
7357    fn recovery_consumes_semicolon() {
7358        let mut p = parser_from_source("foo ;");
7359        p.error_at_peek("test");
7360        p.recover();
7361        assert!(matches!(
7362            p.peek().kind,
7363            TokenKind::Semicolon | TokenKind::Eof
7364        ));
7365        assert!(matches!(p.peek().kind, TokenKind::Eof));
7366    }
7367
7368    #[test]
7369    fn recovery_stops_at_right_brace() {
7370        let mut p = parser_from_source("foo }");
7371        p.error_at_peek("test");
7372        p.recover();
7373        assert!(matches!(p.peek().kind, TokenKind::RightBrace));
7374    }
7375
7376    #[test]
7377    fn recovery_stops_at_let_keyword() {
7378        let mut p = parser_from_source("foo let x");
7379        p.error_at_peek("test");
7380        p.recover();
7381        assert!(matches!(p.peek().kind, TokenKind::Let));
7382    }
7383
7384    #[test]
7385    fn recovery_stops_at_function_keyword() {
7386        let mut p = parser_from_source("foo function bar");
7387        p.error_at_peek("test");
7388        p.recover();
7389        assert!(matches!(p.peek().kind, TokenKind::Function));
7390    }
7391
7392    #[test]
7393    fn error_cap_at_20() {
7394        let source = "} ".repeat(25);
7395        let (_, diags) = parse_str(&source);
7396        assert_eq!(diags.len(), MAX_ERRORS);
7397    }
7398
7399    #[test]
7400    fn driver_makes_progress_past_unmatched_brace() {
7401        let (ast, diags) = parse_str("} x;");
7402        assert_eq!(diags.len(), 1);
7403        assert_eq!(diags[0].message, "expected expression");
7404        assert_eq!(ast.top_level.len(), 1);
7405    }
7406
7407    #[test]
7408    fn snapshot_multi_error_render() {
7409        let source = "foo bar;\nbaz qux;\nhello world;";
7410        let (_, diags) = parse_str(source);
7411        let (sources, _) = Sources::single("script.subm", source).unwrap();
7412        let rendered: String = diags
7413            .iter()
7414            .map(|d| diagnostics::render(d, &sources))
7415            .collect::<Vec<_>>()
7416            .join("\n");
7417        insta::assert_snapshot!(rendered);
7418    }
7419
7420    fn single_stmt(ast: &Ast) -> &crate::Stmt {
7421        assert_eq!(ast.top_level.len(), 1);
7422        ast.try_stmt(ast.top_level[0]).unwrap()
7423    }
7424
7425    fn expr_of_single_stmt(ast: &Ast) -> &crate::Expr {
7426        let stmt = single_stmt(ast);
7427        match stmt.kind {
7428            crate::StmtKind::Expr(id) => ast.try_expr(id).unwrap(),
7429            _ => panic!("expected expression statement, got {:?}", stmt.kind),
7430        }
7431    }
7432
7433    #[test]
7434    fn parse_number_literal() {
7435        let (ast, diags) = parse_str("42;");
7436        assert!(diags.is_empty());
7437        let expr = expr_of_single_stmt(&ast);
7438        assert_eq!(expr.kind, crate::ExprKind::Number(42.0));
7439        assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
7440    }
7441
7442    #[test]
7443    fn parse_string_literal() {
7444        let (ast, diags) = parse_str(r#""s";"#);
7445        assert!(diags.is_empty());
7446        let expr = expr_of_single_stmt(&ast);
7447        assert_eq!(expr.kind, crate::ExprKind::String("s".to_string()));
7448        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7449    }
7450
7451    #[test]
7452    fn parse_boolean_true() {
7453        let (ast, diags) = parse_str("true;");
7454        assert!(diags.is_empty());
7455        let expr = expr_of_single_stmt(&ast);
7456        assert_eq!(expr.kind, crate::ExprKind::Boolean(true));
7457        assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7458    }
7459
7460    #[test]
7461    fn parse_boolean_false() {
7462        let (ast, diags) = parse_str("false;");
7463        assert!(diags.is_empty());
7464        let expr = expr_of_single_stmt(&ast);
7465        assert_eq!(expr.kind, crate::ExprKind::Boolean(false));
7466        assert_eq!(expr.span, crate::Span::new(F, 0, 5).unwrap());
7467    }
7468
7469    #[test]
7470    fn parse_null_literal() {
7471        let (ast, diags) = parse_str("null;");
7472        assert!(diags.is_empty());
7473        let expr = expr_of_single_stmt(&ast);
7474        assert_eq!(expr.kind, crate::ExprKind::Null);
7475        assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7476    }
7477
7478    #[test]
7479    fn parse_identifier() {
7480        let (ast, diags) = parse_str("x;");
7481        assert!(diags.is_empty());
7482        let expr = expr_of_single_stmt(&ast);
7483        assert!(matches!(expr.kind, crate::ExprKind::Identifier(_)));
7484        assert_eq!(expr.span, crate::Span::new(F, 0, 1).unwrap());
7485    }
7486
7487    #[test]
7488    fn reserved_keywords_remain_invalid_declaration_names() {
7489        let (_ast, diags) = parse_str("function default(): void {}");
7490        assert_eq!(diags.len(), 1, "{diags:#?}");
7491        assert_eq!(
7492            diags[0].message,
7493            "`default` is a reserved keyword and can't be used as a name"
7494        );
7495        assert!(
7496            diags[0]
7497                .help
7498                .iter()
7499                .any(|h| h == "rename it, for example: `defaultValue`"),
7500            "expected rename help, got: {diags:?}"
7501        );
7502    }
7503
7504    #[test]
7505    fn eval_and_arguments_cannot_name_bindings() {
7506        let bindings = [
7507            ("let arguments = 1;", "arguments"),
7508            ("const [eval] = [1];", "eval"),
7509            ("const { a: eval } = { a: 1 };", "eval"),
7510            ("const { eval } = { eval: 1 };", "eval"),
7511            ("const [...arguments] = [1];", "arguments"),
7512            ("function eval(): void {}", "eval"),
7513            ("function f(arguments: number): void {}", "arguments"),
7514            ("const f = arguments => arguments;", "arguments"),
7515            ("const f = (...eval: number[]) => 1;", "eval"),
7516            ("const f = function eval(): void {};", "eval"),
7517            ("for (const eval of [1]) {}", "eval"),
7518            ("try {} catch (arguments) {}", "arguments"),
7519            ("type F = (eval: number) => void;", "eval"),
7520            ("type G = (...arguments: number[]) => void;", "arguments"),
7521            ("type H = { [eval: string]: number };", "eval"),
7522        ];
7523        for (source, word) in bindings {
7524            let (_ast, diags) = parse_str(source);
7525            assert!(
7526                diags
7527                    .iter()
7528                    .any(|d| d.message == format!("`{word}` can't name a binding in strict mode")),
7529                "{source}: {diags:#?}"
7530            );
7531        }
7532    }
7533
7534    #[test]
7535    fn eval_and_arguments_still_name_types_and_properties() {
7536        for source in [
7537            "class eval {}",
7538            "interface arguments { eval: number }",
7539            "type eval = number;",
7540            "enum arguments { eval }",
7541            "const o = { eval: 1, arguments: 2 };",
7542            "import { eval } from \"./m\";",
7543        ] {
7544            let (_ast, diags) = parse_str(source);
7545            assert!(diags.is_empty(), "{source}: {diags:#?}");
7546        }
7547    }
7548
7549    #[test]
7550    fn reserved_keywords_remain_invalid_parameter_names() {
7551        let (_ast, diags) = parse_str("function f(default: string): void {}");
7552        assert_eq!(diags.len(), 1, "{diags:#?}");
7553        assert_eq!(
7554            diags[0].message,
7555            "`default` is a reserved keyword and can't be used as a name"
7556        );
7557        assert!(
7558            diags[0]
7559                .help
7560                .iter()
7561                .any(|h| h == "rename it, for example: `defaultValue`"),
7562            "expected rename help, got: {diags:?}"
7563        );
7564    }
7565
7566    #[test]
7567    fn contextual_keywords_are_valid_names() {
7568        for src in [
7569            "const type = \"x\";",
7570            "const from = 1;",
7571            "let of = 2;",
7572            "const as = 3;",
7573            "const is = 4;",
7574            "function f(from: string): void {}",
7575        ] {
7576            let (_ast, diags) = parse_str(src);
7577            assert!(
7578                diags.is_empty(),
7579                "unexpected diagnostics for {src:?}: {diags:#?}"
7580            );
7581        }
7582    }
7583
7584    #[test]
7585    fn type_alias_dispatch_table() {
7586        let (ast, diags) = parse_str("type X = number;");
7587        assert!(diags.is_empty(), "{diags:#?}");
7588        assert!(matches!(
7589            ast.try_stmt(ast.top_level[0]).unwrap().kind,
7590            crate::StmtKind::TypeAliasDecl { .. }
7591        ));
7592
7593        // A binding named `type` keeps expression/assignment statements working.
7594        for src in ["type = 5;", "type;", "type(1);", "type < 3;", "type.foo;"] {
7595            let (ast, diags) = parse_str(src);
7596            assert!(
7597                diags.is_empty(),
7598                "unexpected diagnostics for {src:?}: {diags:#?}"
7599            );
7600            assert!(
7601                !matches!(
7602                    ast.try_stmt(ast.top_level[0]).unwrap().kind,
7603                    crate::StmtKind::TypeAliasDecl { .. }
7604                ),
7605                "{src:?} must not parse as a type alias"
7606            );
7607        }
7608
7609        let (_ast, diags) = parse_str("type X;");
7610        assert!(
7611            diags
7612                .iter()
7613                .any(|d| d.message == "expected `=` after type alias name"),
7614            "bare `type X` should commit to the alias diagnostic, got: {diags:#?}"
7615        );
7616    }
7617
7618    #[test]
7619    fn export_type_alias_dispatch() {
7620        let (ast, diags) = parse_str("export type X = number;");
7621        assert!(diags.is_empty(), "{diags:#?}");
7622        assert_eq!(ast.exported_decls.len(), 1);
7623        assert!(matches!(
7624            ast.try_stmt(ast.exported_decls[0].stmt).unwrap().kind,
7625            crate::StmtKind::TypeAliasDecl { .. }
7626        ));
7627
7628        let (_ast, diags) = parse_str("export type = 5;");
7629        assert!(
7630            diags
7631                .iter()
7632                .any(|d| d.message == "expected a declaration after `export`"),
7633            "`export type = 5` must not become an exported assignment, got: {diags:#?}"
7634        );
7635    }
7636
7637    #[test]
7638    fn parse_paren_simple() {
7639        let (ast, diags) = parse_str("(x);");
7640        assert!(diags.is_empty());
7641        let expr = expr_of_single_stmt(&ast);
7642        let crate::ExprKind::Paren(inner_id) = expr.kind else {
7643            panic!("expected Paren, got {:?}", expr.kind);
7644        };
7645        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7646        let inner = ast.try_expr(inner_id).unwrap();
7647        assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7648        assert_eq!(inner.span, crate::Span::new(F, 1, 2).unwrap());
7649    }
7650
7651    #[test]
7652    fn parse_paren_nested() {
7653        let (ast, diags) = parse_str("((x));");
7654        assert!(diags.is_empty());
7655        let outer = expr_of_single_stmt(&ast);
7656        let crate::ExprKind::Paren(mid_id) = outer.kind else {
7657            panic!("expected outer Paren");
7658        };
7659        assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7660        let mid = ast.try_expr(mid_id).unwrap();
7661        let crate::ExprKind::Paren(inner_id) = mid.kind else {
7662            panic!("expected inner Paren");
7663        };
7664        assert_eq!(mid.span, crate::Span::new(F, 1, 4).unwrap());
7665        let inner = ast.try_expr(inner_id).unwrap();
7666        assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7667        assert_eq!(inner.span, crate::Span::new(F, 2, 3).unwrap());
7668    }
7669
7670    #[test]
7671    fn parse_sequence_of_atoms() {
7672        let (ast, diags) = parse_str(r#"42; "hi"; true; null; x; (x);"#);
7673        assert!(diags.is_empty());
7674        assert_eq!(ast.top_level.len(), 6);
7675        insta::assert_debug_snapshot!(ast);
7676    }
7677
7678    #[test]
7679    fn missing_semicolon_after_expression() {
7680        let (ast, diags) = parse_str("42 43");
7681        assert_eq!(diags.len(), 1);
7682        assert_eq!(diags[0].message, "expected `;` after expression");
7683        assert!(ast.top_level.is_empty());
7684    }
7685
7686    #[test]
7687    fn empty_paren_diagnoses() {
7688        let (ast, diags) = parse_str("();");
7689        assert_eq!(diags.len(), 1);
7690        assert_eq!(diags[0].message, "expected expression");
7691        assert!(ast.top_level.is_empty());
7692    }
7693
7694    #[test]
7695    fn unterminated_paren_diagnoses() {
7696        let (ast, diags) = parse_str("(x;");
7697        assert_eq!(diags.len(), 1);
7698        assert_eq!(diags[0].message, "expected `)`");
7699        assert!(ast.top_level.is_empty());
7700    }
7701
7702    fn binary(e: &crate::Expr) -> (crate::BinOp, crate::ExprId, crate::ExprId) {
7703        match e.kind {
7704            crate::ExprKind::Binary { op, lhs, rhs } => (op, lhs, rhs),
7705            _ => panic!("expected Binary, got {:?}", e.kind),
7706        }
7707    }
7708
7709    #[test]
7710    fn parse_binary_add() {
7711        let (ast, diags) = parse_str("a + b;");
7712        assert!(diags.is_empty());
7713        let outer = expr_of_single_stmt(&ast);
7714        let (op, lhs, rhs) = binary(outer);
7715        assert_eq!(op, crate::BinOp::Add);
7716        assert!(matches!(
7717            ast.try_expr(lhs).unwrap().kind,
7718            crate::ExprKind::Identifier(_)
7719        ));
7720        assert!(matches!(
7721            ast.try_expr(rhs).unwrap().kind,
7722            crate::ExprKind::Identifier(_)
7723        ));
7724        assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7725    }
7726
7727    #[test]
7728    fn bitwise_precedence_and_shift_adjacency() {
7729        let (ast, diags) = parse_str("a | b ^ c & d == e >> f + g;");
7730        assert!(diags.is_empty(), "{diags:?}");
7731        let (op, _, mut rhs) = binary(expr_of_single_stmt(&ast));
7732        assert_eq!(op, crate::BinOp::BitOr);
7733        for expected in [
7734            crate::BinOp::BitXor,
7735            crate::BinOp::BitAnd,
7736            crate::BinOp::Eq,
7737            crate::BinOp::Shr,
7738            crate::BinOp::Add,
7739        ] {
7740            let (op, _, next) = binary(ast.try_expr(rhs).unwrap());
7741            assert_eq!(op, expected);
7742            rhs = next;
7743        }
7744        for source in ["a > > b;", "a >/*gap*/> b;", "a < < b;"] {
7745            let (_, diags) = parse_str(source);
7746            assert!(!diags.is_empty(), "{source}");
7747        }
7748    }
7749
7750    #[test]
7751    fn bitwise_shift_assignment_span() {
7752        let (ast, diags) = parse_str("a >>>= b;");
7753        assert!(diags.is_empty(), "{diags:?}");
7754        let crate::StmtKind::CompoundAssign { op, op_span, .. } = single_stmt(&ast).kind else {
7755            panic!("expected assignment");
7756        };
7757        assert_eq!(op, crate::BinOp::UnsignedShr);
7758        assert_eq!(op_span, crate::Span::new(F, 2, 6).unwrap());
7759    }
7760
7761    #[test]
7762    fn precedence_mul_binds_tighter_than_add() {
7763        let (ast, diags) = parse_str("a + b * c;");
7764        assert!(diags.is_empty());
7765        let outer = expr_of_single_stmt(&ast);
7766        let (op, lhs, rhs) = binary(outer);
7767        assert_eq!(op, crate::BinOp::Add);
7768        assert!(matches!(
7769            ast.try_expr(lhs).unwrap().kind,
7770            crate::ExprKind::Identifier(_)
7771        ));
7772        let (rop, rlhs, rrhs) = binary(ast.try_expr(rhs).unwrap());
7773        assert_eq!(rop, crate::BinOp::Mul);
7774        assert!(matches!(
7775            ast.try_expr(rlhs).unwrap().kind,
7776            crate::ExprKind::Identifier(_)
7777        ));
7778        assert!(matches!(
7779            ast.try_expr(rrhs).unwrap().kind,
7780            crate::ExprKind::Identifier(_)
7781        ));
7782    }
7783
7784    #[test]
7785    fn precedence_mul_left_of_add() {
7786        let (ast, diags) = parse_str("a * b + c;");
7787        assert!(diags.is_empty());
7788        let outer = expr_of_single_stmt(&ast);
7789        let (op, lhs, rhs) = binary(outer);
7790        assert_eq!(op, crate::BinOp::Add);
7791        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7792        assert_eq!(lop, crate::BinOp::Mul);
7793        assert!(matches!(
7794            ast.try_expr(rhs).unwrap().kind,
7795            crate::ExprKind::Identifier(_)
7796        ));
7797    }
7798
7799    #[test]
7800    fn left_associative_same_precedence() {
7801        let (ast, diags) = parse_str("a - b - c;");
7802        assert!(diags.is_empty());
7803        let outer = expr_of_single_stmt(&ast);
7804        let (op, lhs, rhs) = binary(outer);
7805        assert_eq!(op, crate::BinOp::Sub);
7806        let (lop, llhs, lrhs) = binary(ast.try_expr(lhs).unwrap());
7807        assert_eq!(lop, crate::BinOp::Sub);
7808        assert!(matches!(
7809            ast.try_expr(llhs).unwrap().kind,
7810            crate::ExprKind::Identifier(_)
7811        ));
7812        assert!(matches!(
7813            ast.try_expr(lrhs).unwrap().kind,
7814            crate::ExprKind::Identifier(_)
7815        ));
7816        assert!(matches!(
7817            ast.try_expr(rhs).unwrap().kind,
7818            crate::ExprKind::Identifier(_)
7819        ));
7820    }
7821
7822    #[test]
7823    fn equality_inside_logical() {
7824        let (ast, diags) = parse_str("a === b && c === d;");
7825        assert!(diags.is_empty());
7826        let outer = expr_of_single_stmt(&ast);
7827        let (op, lhs, rhs) = binary(outer);
7828        assert_eq!(op, crate::BinOp::And);
7829        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7830        assert_eq!(lop, crate::BinOp::Eq);
7831        let (rop, ..) = binary(ast.try_expr(rhs).unwrap());
7832        assert_eq!(rop, crate::BinOp::Eq);
7833    }
7834
7835    #[test]
7836    fn double_equal_is_same_variant_as_triple() {
7837        let (ast, _) = parse_str("a == b;");
7838        let outer = expr_of_single_stmt(&ast);
7839        let (op, ..) = binary(outer);
7840        assert_eq!(op, crate::BinOp::Eq);
7841    }
7842
7843    #[test]
7844    fn bang_eq_is_not_eq() {
7845        let (ast, _) = parse_str("a != b;");
7846        let outer = expr_of_single_stmt(&ast);
7847        let (op, ..) = binary(outer);
7848        assert_eq!(op, crate::BinOp::NotEq);
7849    }
7850
7851    #[test]
7852    fn comparison_has_higher_prec_than_logical() {
7853        let (ast, _) = parse_str("a < b && c;");
7854        let outer = expr_of_single_stmt(&ast);
7855        let (op, lhs, _) = binary(outer);
7856        assert_eq!(op, crate::BinOp::And);
7857        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7858        assert_eq!(lop, crate::BinOp::Lt);
7859    }
7860
7861    #[test]
7862    fn full_precedence_chain_snapshot() {
7863        let (ast, diags) = parse_str("a || b && c === d < e + f * g;");
7864        assert!(diags.is_empty());
7865        insta::assert_debug_snapshot!(ast);
7866    }
7867
7868    #[test]
7869    fn paren_overrides_precedence() {
7870        let (ast, _) = parse_str("(a + b) * c;");
7871        let outer = expr_of_single_stmt(&ast);
7872        let (op, lhs, rhs) = binary(outer);
7873        assert_eq!(op, crate::BinOp::Mul);
7874        let crate::ExprKind::Paren(inner_id) = ast.try_expr(lhs).unwrap().kind else {
7875            panic!("expected Paren on lhs");
7876        };
7877        let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7878        assert_eq!(inner_op, crate::BinOp::Add);
7879        assert!(matches!(
7880            ast.try_expr(rhs).unwrap().kind,
7881            crate::ExprKind::Identifier(_)
7882        ));
7883    }
7884
7885    #[test]
7886    fn binary_missing_rhs_diagnoses() {
7887        let (ast, diags) = parse_str("a +;");
7888        assert_eq!(diags.len(), 1);
7889        assert_eq!(diags[0].message, "expected expression");
7890        assert!(ast.top_level.is_empty());
7891    }
7892
7893    fn unary(e: &crate::Expr) -> (crate::UnOp, crate::ExprId) {
7894        match e.kind {
7895            crate::ExprKind::Unary { op, operand } => (op, operand),
7896            _ => panic!("expected Unary, got {:?}", e.kind),
7897        }
7898    }
7899
7900    #[test]
7901    fn parse_unary_not() {
7902        let (ast, _) = parse_str("!x;");
7903        let outer = expr_of_single_stmt(&ast);
7904        let (op, operand) = unary(outer);
7905        assert_eq!(op, crate::UnOp::Not);
7906        assert!(matches!(
7907            ast.try_expr(operand).unwrap().kind,
7908            crate::ExprKind::Identifier(_)
7909        ));
7910        assert_eq!(outer.span, crate::Span::new(F, 0, 2).unwrap());
7911    }
7912
7913    #[test]
7914    fn parse_unary_neg() {
7915        let (ast, _) = parse_str("-x;");
7916        let (op, _) = unary(expr_of_single_stmt(&ast));
7917        assert_eq!(op, crate::UnOp::Neg);
7918    }
7919
7920    #[test]
7921    fn parse_unary_pos() {
7922        let (ast, _) = parse_str("+x;");
7923        let (op, _) = unary(expr_of_single_stmt(&ast));
7924        assert_eq!(op, crate::UnOp::Pos);
7925    }
7926
7927    #[test]
7928    fn repeated_unary_not() {
7929        let (ast, _) = parse_str("!!x;");
7930        let outer = expr_of_single_stmt(&ast);
7931        let (op, operand) = unary(outer);
7932        assert_eq!(op, crate::UnOp::Not);
7933        let (inner_op, inner_operand) = unary(ast.try_expr(operand).unwrap());
7934        assert_eq!(inner_op, crate::UnOp::Not);
7935        assert!(matches!(
7936            ast.try_expr(inner_operand).unwrap().kind,
7937            crate::ExprKind::Identifier(_)
7938        ));
7939    }
7940
7941    #[test]
7942    fn unary_binds_tighter_than_binary() {
7943        let (ast, _) = parse_str("-x + 1;");
7944        let outer = expr_of_single_stmt(&ast);
7945        let (op, lhs, rhs) = binary(outer);
7946        assert_eq!(op, crate::BinOp::Add);
7947        let (unop, _) = unary(ast.try_expr(lhs).unwrap());
7948        assert_eq!(unop, crate::UnOp::Neg);
7949        assert_eq!(
7950            ast.try_expr(rhs).unwrap().kind,
7951            crate::ExprKind::Number(1.0)
7952        );
7953    }
7954
7955    #[test]
7956    fn unary_over_paren() {
7957        let (ast, _) = parse_str("-(x + 1);");
7958        let outer = expr_of_single_stmt(&ast);
7959        let (op, operand) = unary(outer);
7960        assert_eq!(op, crate::UnOp::Neg);
7961        let crate::ExprKind::Paren(inner_id) = ast.try_expr(operand).unwrap().kind else {
7962            panic!("expected Paren");
7963        };
7964        let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7965        assert_eq!(inner_op, crate::BinOp::Add);
7966    }
7967
7968    #[test]
7969    fn parse_is_as_expression_rejected() {
7970        let (_, diags) = parse_str("x is number;");
7971        assert!(!diags.is_empty(), "expected parse error for `x is T`");
7972    }
7973
7974    fn as_cast(e: &crate::Expr) -> (crate::ExprId, &crate::TypeAnnotation) {
7975        match &e.kind {
7976            crate::ExprKind::As { expr, ty } => (*expr, ty),
7977            other => panic!("expected ExprKind::As, got {other:?}"),
7978        }
7979    }
7980
7981    #[test]
7982    fn parse_instanceof_class() {
7983        let (ast, diags) = parse_str("x instanceof Foo;");
7984        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7985        let outer = expr_of_single_stmt(&ast);
7986        let crate::ExprKind::InstanceOf { value, ty } = &outer.kind else {
7987            panic!("expected ExprKind::InstanceOf, got {:?}", outer.kind);
7988        };
7989        assert!(matches!(
7990            ast.try_expr(*value).unwrap().kind,
7991            crate::ExprKind::Identifier(_)
7992        ));
7993        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7994    }
7995
7996    #[test]
7997    fn parse_instanceof_binds_tighter_than_logical_and() {
7998        // `a instanceof B && c` parses as `(a instanceof B) && c`.
7999        let (ast, diags) = parse_str("a instanceof B && c;");
8000        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8001        let outer = expr_of_single_stmt(&ast);
8002        let (op, lhs, rhs) = binary(outer);
8003        assert_eq!(op, crate::BinOp::And);
8004        assert!(matches!(
8005            ast.try_expr(lhs).unwrap().kind,
8006            crate::ExprKind::InstanceOf { .. }
8007        ));
8008        assert!(matches!(
8009            ast.try_expr(rhs).unwrap().kind,
8010            crate::ExprKind::Identifier(_)
8011        ));
8012    }
8013
8014    /// `<T>x` produces the same node as `x as T`, so everything downstream of the
8015    /// parser sees one construct rather than two.
8016    #[test]
8017    fn parse_angle_cast_lowers_to_the_as_node() {
8018        let (ast, diags) = parse_str("<number>x;");
8019        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8020        let outer = expr_of_single_stmt(&ast);
8021        let (inner, ty) = as_cast(outer);
8022        assert!(matches!(
8023            ast.try_expr(inner).unwrap().kind,
8024            crate::ExprKind::Identifier(_)
8025        ));
8026        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8027    }
8028
8029    /// A `<` that means comparison always has a left operand, so it never reaches
8030    /// the cast branch. This is the regression the cast syntax could most easily
8031    /// have caused.
8032    #[test]
8033    fn a_less_than_comparison_is_not_read_as_a_cast() {
8034        let (ast, diags) = parse_str("a < b;");
8035        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8036        let e = expr_of_single_stmt(&ast);
8037        assert!(matches!(e.kind, crate::ExprKind::Binary { .. }));
8038    }
8039
8040    #[test]
8041    fn parse_typeof_in_type_position() {
8042        let (_ast, diags) = parse_str("let a: typeof b = 1;");
8043        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8044    }
8045
8046    #[test]
8047    fn parse_typeof_with_a_dotted_path() {
8048        let (_ast, diags) = parse_str("let a: typeof b.c.d = 1;");
8049        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8050    }
8051
8052    #[test]
8053    fn parse_as_primitive() {
8054        let (ast, diags) = parse_str("x as number;");
8055        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8056        let outer = expr_of_single_stmt(&ast);
8057        let (inner, ty) = as_cast(outer);
8058        assert!(matches!(
8059            ast.try_expr(inner).unwrap().kind,
8060            crate::ExprKind::Identifier(_)
8061        ));
8062        match &ty.kind {
8063            crate::TypeAnnotationKind::Name { args, .. } => {
8064                assert!(args.is_empty());
8065            }
8066            other => panic!("expected Name target, got {other:?}"),
8067        }
8068        assert_eq!(outer.span, crate::Span::new(F, 0, 11).unwrap());
8069    }
8070
8071    #[test]
8072    fn parse_as_object_literal_type() {
8073        let (ast, diags) = parse_str("x as { a: number };");
8074        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8075        let outer = expr_of_single_stmt(&ast);
8076        let (_, ty) = as_cast(outer);
8077        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Object { .. }));
8078    }
8079
8080    #[test]
8081    fn parse_as_array_short() {
8082        let (ast, diags) = parse_str("x as string[];");
8083        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8084        let outer = expr_of_single_stmt(&ast);
8085        let (_, ty) = as_cast(outer);
8086        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
8087    }
8088
8089    #[test]
8090    fn parse_as_array_generic() {
8091        let (ast, diags) = parse_str("x as T[];");
8092        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8093        let outer = expr_of_single_stmt(&ast);
8094        let (_, ty) = as_cast(outer);
8095        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
8096    }
8097
8098    #[test]
8099    fn parse_as_generic_instantiation() {
8100        let (ast, diags) = parse_str("x as Foo<Bar>;");
8101        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8102        let outer = expr_of_single_stmt(&ast);
8103        let (_, ty) = as_cast(outer);
8104        match &ty.kind {
8105            crate::TypeAnnotationKind::Name { args, .. } => {
8106                assert_eq!(args.len(), 1, "Foo<Bar> has one type arg");
8107            }
8108            other => panic!("expected Name with type args, got {other:?}"),
8109        }
8110    }
8111
8112    #[test]
8113    fn parse_as_precedence_higher_than_additive() {
8114        let (ast, diags) = parse_str("x as number + 1;");
8115        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8116        let outer = expr_of_single_stmt(&ast);
8117        let (op, lhs, rhs) = binary(outer);
8118        assert_eq!(op, crate::BinOp::Add);
8119        assert!(matches!(
8120            ast.try_expr(lhs).unwrap().kind,
8121            crate::ExprKind::As { .. }
8122        ));
8123        assert!(matches!(
8124            ast.try_expr(rhs).unwrap().kind,
8125            crate::ExprKind::Number(_)
8126        ));
8127    }
8128
8129    #[test]
8130    fn parse_as_precedence_lower_than_unary() {
8131        let (ast, diags) = parse_str("!x as boolean;");
8132        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8133        let outer = expr_of_single_stmt(&ast);
8134        let (inner, _) = as_cast(outer);
8135        assert!(matches!(
8136            ast.try_expr(inner).unwrap().kind,
8137            crate::ExprKind::Unary {
8138                op: crate::UnOp::Not,
8139                ..
8140            }
8141        ));
8142    }
8143
8144    #[test]
8145    fn parse_as_left_associative_chain() {
8146        let (ast, diags) = parse_str("x as A as B;");
8147        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8148        let outer = expr_of_single_stmt(&ast);
8149        let (inner, _outer_ty) = as_cast(outer);
8150        let (innermost, _inner_ty) = as_cast(ast.try_expr(inner).unwrap());
8151        assert!(matches!(
8152            ast.try_expr(innermost).unwrap().kind,
8153            crate::ExprKind::Identifier(_)
8154        ));
8155    }
8156
8157    #[test]
8158    fn parse_as_inside_call_arg() {
8159        let (ast, diags) = parse_str("f(x as T);");
8160        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8161        let outer = expr_of_single_stmt(&ast);
8162        match &outer.kind {
8163            crate::ExprKind::Call { args, .. } => {
8164                assert_eq!(args.len(), 1);
8165                assert!(matches!(
8166                    ast.try_expr(args[0]).unwrap().kind,
8167                    crate::ExprKind::As { .. }
8168                ));
8169            }
8170            other => panic!("expected Call, got {other:?}"),
8171        }
8172    }
8173
8174    #[test]
8175    fn parse_as_after_field_access() {
8176        let (ast, diags) = parse_str("obj.field as T;");
8177        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8178        let outer = expr_of_single_stmt(&ast);
8179        let (inner, _) = as_cast(outer);
8180        assert!(matches!(
8181            ast.try_expr(inner).unwrap().kind,
8182            crate::ExprKind::FieldAccess { .. }
8183        ));
8184    }
8185
8186    #[test]
8187    fn parse_type_guard_return_annotation_on_function() {
8188        let src = "function isCircle(s: Shape): s is Circle { return true; }";
8189        let (ast, diags) = parse_str(src);
8190        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8191        let stmt = single_stmt(&ast);
8192        match stmt.kind {
8193            crate::StmtKind::Function {
8194                ref return_type,
8195                ref type_predicate,
8196                ..
8197            } => {
8198                assert!(
8199                    return_type.is_none(),
8200                    "predicate form leaves return_type empty",
8201                );
8202                let tp = type_predicate.as_ref().expect("predicate parsed");
8203                assert_eq!(tp.param.name, "s");
8204                assert!(matches!(
8205                    tp.asserted.kind,
8206                    crate::TypeAnnotationKind::Name { .. }
8207                ));
8208                assert_eq!(tp.span, crate::Span::new(F, 29, 40).unwrap());
8209            }
8210            _ => panic!("expected Function"),
8211        }
8212    }
8213
8214    #[test]
8215    fn parse_type_guard_return_annotation_on_arrow() {
8216        let src = "const f = (s: Shape): s is Circle => true;";
8217        let (ast, diags) = parse_str(src);
8218        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8219        let value = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
8220            crate::StmtKind::Const { value, .. } => *value,
8221            _ => panic!("expected const"),
8222        };
8223        match &ast.try_expr(value).unwrap().kind {
8224            crate::ExprKind::Arrow {
8225                return_type,
8226                type_predicate,
8227                ..
8228            } => {
8229                assert!(return_type.is_none());
8230                let tp = type_predicate.as_ref().expect("predicate parsed");
8231                assert_eq!(tp.param.name, "s");
8232            }
8233            other => panic!("expected Arrow, got {other:?}"),
8234        }
8235    }
8236
8237    #[test]
8238    fn parse_plain_return_type_unaffected_by_predicate_parser() {
8239        let src = "function f(s: Shape): boolean { return true; }";
8240        let (ast, diags) = parse_str(src);
8241        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8242        let stmt = single_stmt(&ast);
8243        match stmt.kind {
8244            crate::StmtKind::Function {
8245                ref return_type,
8246                ref type_predicate,
8247                ..
8248            } => {
8249                assert!(return_type.is_some(), "plain form has return_type");
8250                assert!(type_predicate.is_none(), "plain form has no type_predicate",);
8251            }
8252            _ => panic!("expected Function"),
8253        }
8254    }
8255
8256    #[test]
8257    fn parse_type_guard_rejects_non_return_position() {
8258        let (_, diags) = parse_str("let x: y is Foo = null;");
8259        assert!(
8260            !diags.is_empty(),
8261            "expected parse error for `is` outside return-type position",
8262        );
8263    }
8264
8265    #[test]
8266    fn parse_typeof_prefix() {
8267        let (ast, diags) = parse_str("typeof x;");
8268        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8269        let outer = expr_of_single_stmt(&ast);
8270        let crate::ExprKind::Typeof { operand } = outer.kind else {
8271            panic!("expected Typeof, got {:?}", outer.kind);
8272        };
8273        assert!(matches!(
8274            ast.try_expr(operand).unwrap().kind,
8275            crate::ExprKind::Identifier(_)
8276        ));
8277        assert_eq!(outer.span, crate::Span::new(F, 0, 8).unwrap());
8278    }
8279
8280    #[test]
8281    fn parse_typeof_in_equality() {
8282        let (ast, diags) = parse_str(r#"typeof x === "number";"#);
8283        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8284        let outer = expr_of_single_stmt(&ast);
8285        let (op, lhs, rhs) = binary(outer);
8286        assert_eq!(op, crate::BinOp::Eq);
8287        assert!(matches!(
8288            ast.try_expr(lhs).unwrap().kind,
8289            crate::ExprKind::Typeof { .. }
8290        ));
8291        assert!(matches!(
8292            ast.try_expr(rhs).unwrap().kind,
8293            crate::ExprKind::String(ref s) if s == "number"
8294        ));
8295    }
8296
8297    #[test]
8298    fn parse_let_without_type() {
8299        let (ast, diags) = parse_str("let x = 1;");
8300        assert!(diags.is_empty());
8301        let stmt = single_stmt(&ast);
8302        assert_eq!(stmt.span, crate::Span::new(F, 0, 10).unwrap());
8303        match stmt.kind {
8304            crate::StmtKind::Let {
8305                ref name,
8306                ref ty,
8307                value,
8308                ..
8309            } => {
8310                assert_eq!(name.name, "x");
8311                assert_eq!(name.span, crate::Span::new(F, 4, 5).unwrap());
8312                assert!(ty.is_none());
8313                assert_eq!(
8314                    ast.try_expr(value).unwrap().kind,
8315                    crate::ExprKind::Number(1.0)
8316                );
8317            }
8318            _ => panic!("expected Let"),
8319        }
8320    }
8321
8322    #[test]
8323    fn parse_const_with_type_and_string() {
8324        let (ast, diags) = parse_str(r#"const y: string = "hi";"#);
8325        assert!(diags.is_empty());
8326        let stmt = single_stmt(&ast);
8327        match stmt.kind {
8328            crate::StmtKind::Const {
8329                ref name,
8330                ref ty,
8331                value,
8332                ..
8333            } => {
8334                assert_eq!(name.name, "y");
8335                assert_eq!(name.span, crate::Span::new(F, 6, 7).unwrap());
8336                let ty = ty.as_ref().expect("expected type annotation");
8337                assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8338                assert_eq!(ty.span, crate::Span::new(F, 9, 15).unwrap());
8339                assert_eq!(
8340                    ast.try_expr(value).unwrap().kind,
8341                    crate::ExprKind::String("hi".to_string())
8342                );
8343            }
8344            _ => panic!("expected Const"),
8345        }
8346    }
8347
8348    #[test]
8349    fn parse_let_with_type_and_binary_initializer() {
8350        let (ast, diags) = parse_str("let z: number = 1 + 2;");
8351        assert!(diags.is_empty());
8352        let stmt = single_stmt(&ast);
8353        match stmt.kind {
8354            crate::StmtKind::Let { ref ty, value, .. } => {
8355                assert!(ty.is_some());
8356                let (op, ..) = binary(ast.try_expr(value).unwrap());
8357                assert_eq!(op, crate::BinOp::Add);
8358            }
8359            _ => panic!("expected Let"),
8360        }
8361    }
8362
8363    #[test]
8364    fn void_type_annotation_is_accepted_syntactically() {
8365        let (ast, diags) = parse_str("let v: void = null;");
8366        assert!(diags.is_empty());
8367        let stmt = single_stmt(&ast);
8368        match stmt.kind {
8369            crate::StmtKind::Let { ref ty, .. } => {
8370                let ty = ty.as_ref().unwrap();
8371                assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8372                assert_eq!(ty.span, crate::Span::new(F, 7, 11).unwrap());
8373            }
8374            _ => panic!("expected Let"),
8375        }
8376    }
8377
8378    #[test]
8379    fn const_missing_initializer_diagnoses() {
8380        let (ast, diags) = parse_str("const x;");
8381        assert_eq!(diags.len(), 1);
8382        assert_eq!(
8383            diags[0].message,
8384            "`const` declaration requires an initializer"
8385        );
8386        assert!(ast.top_level.is_empty());
8387    }
8388
8389    #[test]
8390    fn parse_const_object_shorthand_pattern() {
8391        let (ast, diags) = parse_str("const { a, b } = obj;");
8392        assert!(diags.is_empty(), "unexpected: {diags:?}");
8393        let stmt = single_stmt(&ast);
8394        match stmt.kind {
8395            crate::StmtKind::ConstPattern {
8396                binding:
8397                    crate::Binding::Object {
8398                        ref fields,
8399                        ref rest,
8400                        ..
8401                    },
8402                ..
8403            } => {
8404                assert_eq!(fields.len(), 2);
8405                assert_eq!(fields[0].source.name, "a");
8406                assert_eq!(fields[0].local.name, "a");
8407                assert_eq!(fields[1].source.name, "b");
8408                assert_eq!(fields[1].local.name, "b");
8409                assert!(rest.is_none());
8410            }
8411            _ => panic!("expected ConstPattern Object, got {:?}", stmt.kind),
8412        }
8413    }
8414
8415    #[test]
8416    fn parse_const_object_pattern_keyword_source_key() {
8417        let (ast, diags) = parse_str("const { type: statusType, default: fallback } = obj;");
8418        assert!(diags.is_empty(), "unexpected: {diags:?}");
8419        let stmt = single_stmt(&ast);
8420        let crate::StmtKind::ConstPattern {
8421            binding: crate::Binding::Object { ref fields, .. },
8422            ..
8423        } = stmt.kind
8424        else {
8425            panic!("expected object const pattern");
8426        };
8427
8428        let names: Vec<(&str, &str)> = fields
8429            .iter()
8430            .map(|f| (f.source.name.as_str(), f.local.name.as_str()))
8431            .collect();
8432        assert_eq!(names, vec![("type", "statusType"), ("default", "fallback")]);
8433    }
8434
8435    #[test]
8436    fn parse_const_object_pattern_keyword_shorthand_rejected() {
8437        let (_ast, diags) = parse_str("const { default } = obj;");
8438        assert!(
8439            diags
8440                .iter()
8441                .any(|d| d.message == "expected `:` after keyword field name in object pattern"),
8442            "expected keyword-shorthand diagnostic, got: {diags:?}"
8443        );
8444    }
8445
8446    #[test]
8447    fn parse_const_object_renamed_pattern() {
8448        let (ast, diags) = parse_str("const { a: x, b: y } = obj;");
8449        assert!(diags.is_empty(), "unexpected: {diags:?}");
8450        match single_stmt(&ast).kind {
8451            crate::StmtKind::ConstPattern {
8452                binding: crate::Binding::Object { ref fields, .. },
8453                ..
8454            } => {
8455                assert_eq!(fields[0].source.name, "a");
8456                assert_eq!(fields[0].local.name, "x");
8457                assert_eq!(fields[1].source.name, "b");
8458                assert_eq!(fields[1].local.name, "y");
8459            }
8460            ref other => panic!("expected ConstPattern Object, got {other:?}"),
8461        }
8462    }
8463
8464    #[test]
8465    fn parse_const_object_with_rest() {
8466        let (ast, diags) = parse_str("const { a, ...rest } = obj;");
8467        assert!(diags.is_empty(), "unexpected: {diags:?}");
8468        match single_stmt(&ast).kind {
8469            crate::StmtKind::ConstPattern {
8470                binding:
8471                    crate::Binding::Object {
8472                        ref fields,
8473                        ref rest,
8474                        ..
8475                    },
8476                ..
8477            } => {
8478                assert_eq!(fields.len(), 1);
8479                assert_eq!(fields[0].source.name, "a");
8480                let r = rest.as_ref().expect("expected rest");
8481                assert_eq!(r.name, "rest");
8482            }
8483            ref other => panic!("expected ConstPattern Object, got {other:?}"),
8484        }
8485    }
8486
8487    #[test]
8488    fn parse_const_array_pattern() {
8489        let (ast, diags) = parse_str("const [x, y] = arr;");
8490        assert!(diags.is_empty(), "unexpected: {diags:?}");
8491        match single_stmt(&ast).kind {
8492            crate::StmtKind::ConstPattern {
8493                binding:
8494                    crate::Binding::Array {
8495                        ref elems,
8496                        ref rest,
8497                        ..
8498                    },
8499                ..
8500            } => {
8501                assert_eq!(elems.len(), 2);
8502                assert_eq!(elems[0].as_ref().unwrap().name, "x");
8503                assert_eq!(elems[1].as_ref().unwrap().name, "y");
8504                assert!(rest.is_none());
8505            }
8506            ref other => panic!("expected ConstPattern Array, got {other:?}"),
8507        }
8508    }
8509
8510    #[test]
8511    fn parse_const_array_with_hole_and_rest() {
8512        let (ast, diags) = parse_str("const [, x, ...rest] = arr;");
8513        assert!(diags.is_empty(), "unexpected: {diags:?}");
8514        match single_stmt(&ast).kind {
8515            crate::StmtKind::ConstPattern {
8516                binding:
8517                    crate::Binding::Array {
8518                        ref elems,
8519                        ref rest,
8520                        ..
8521                    },
8522                ..
8523            } => {
8524                assert_eq!(elems.len(), 2);
8525                assert!(elems[0].is_none(), "first slot should be a hole");
8526                assert_eq!(elems[1].as_ref().unwrap().name, "x");
8527                assert_eq!(rest.as_ref().unwrap().name, "rest");
8528            }
8529            ref other => panic!("expected ConstPattern Array, got {other:?}"),
8530        }
8531    }
8532
8533    #[test]
8534    fn parse_let_object_pattern() {
8535        let (ast, diags) = parse_str("let { a, b } = obj;");
8536        assert!(diags.is_empty(), "unexpected: {diags:?}");
8537        assert!(matches!(
8538            single_stmt(&ast).kind,
8539            crate::StmtKind::LetPattern {
8540                binding: crate::Binding::Object { .. },
8541                ..
8542            }
8543        ));
8544    }
8545
8546    #[test]
8547    fn parse_function_param_object_pattern() {
8548        let src = "function f({ a, b }: T): void { return; }";
8549        let (ast, diags) = parse_str(src);
8550        assert!(diags.is_empty(), "unexpected: {diags:?}");
8551        match single_stmt(&ast).kind {
8552            crate::StmtKind::Function { ref params, .. } => {
8553                assert_eq!(params.len(), 1);
8554                let p = &params[0];
8555                assert!(p.pattern.is_some(), "expected pattern param");
8556                assert_eq!(p.name.name, "", "pattern placeholder uses empty name");
8557                match p.pattern.as_ref().unwrap() {
8558                    crate::Binding::Object { fields, .. } => {
8559                        assert_eq!(fields.len(), 2);
8560                        assert_eq!(fields[0].source.name, "a");
8561                    }
8562                    other => panic!("expected Object binding, got {other:?}"),
8563                }
8564            }
8565            ref other => panic!("expected Function, got {other:?}"),
8566        }
8567    }
8568
8569    #[test]
8570    fn parse_arrow_param_array_pattern() {
8571        let src = "const g = ([x, y]: number[]): number => x + y;";
8572        let (_ast, diags) = parse_str(src);
8573        assert!(diags.is_empty(), "unexpected: {diags:?}");
8574    }
8575
8576    #[test]
8577    fn parse_destructure_nested_rejected() {
8578        let (_ast, diags) = parse_str("const { a: { b } } = obj;");
8579        assert!(
8580            diags
8581                .iter()
8582                .any(|d| d.message.contains("nested destructuring")),
8583            "expected nested-destructuring diagnostic, got: {diags:?}",
8584        );
8585    }
8586
8587    #[test]
8588    fn parse_destructure_array_nested_rejected() {
8589        let (_ast, diags) = parse_str("const [[a]] = arr;");
8590        assert!(
8591            diags
8592                .iter()
8593                .any(|d| d.message.contains("nested destructuring")),
8594            "expected nested-destructuring diagnostic, got: {diags:?}",
8595        );
8596    }
8597
8598    #[test]
8599    fn parse_destructure_pattern_defaults() {
8600        let (ast, diags) = parse_str("const { a = 1, b: c = 2 } = obj;");
8601        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8602        let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
8603            panic!("expected pattern")
8604        };
8605        let crate::Binding::Object { fields, .. } = binding else {
8606            panic!("expected object")
8607        };
8608        assert_eq!(fields.len(), 2);
8609        assert!(fields.iter().all(|field| field.default.is_some()));
8610        assert_eq!(fields[1].local.name, "c");
8611    }
8612
8613    #[test]
8614    fn parse_destructure_param_default() {
8615        let (ast, diags) = parse_str("function f({ a = 1 }: T = obj): void { return; }");
8616        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8617        let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
8618            panic!("expected function")
8619        };
8620        assert!(params[0].default.is_some());
8621        assert!(params[0].pattern.is_some());
8622    }
8623
8624    #[test]
8625    fn parse_destructure_rest_not_last_rejected() {
8626        let (_ast, diags) = parse_str("const { ...rest, a } = obj;");
8627        assert!(
8628            diags
8629                .iter()
8630                .any(|d| d.message.contains("rest element must be the last element")),
8631            "expected rest-not-last diagnostic, got: {diags:?}",
8632        );
8633    }
8634
8635    #[test]
8636    fn parse_destructure_empty_object_rejected() {
8637        let (_ast, diags) = parse_str("const { } = obj;");
8638        assert!(
8639            diags
8640                .iter()
8641                .any(|d| d.message.contains("empty object destructuring pattern")),
8642            "expected empty-pattern diagnostic, got: {diags:?}",
8643        );
8644    }
8645
8646    #[test]
8647    fn parse_ternary_simple() {
8648        let (ast, diags) = parse_str("const x: number = a ? 1 : 2;");
8649        assert!(diags.is_empty(), "unexpected: {diags:?}");
8650        let stmt = single_stmt(&ast);
8651        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8652            panic!("expected Const, got {:?}", stmt.kind);
8653        };
8654        assert!(matches!(
8655            ast.try_expr(value).unwrap().kind,
8656            crate::ExprKind::Ternary { .. }
8657        ));
8658    }
8659
8660    #[test]
8661    fn parse_ternary_nested_right_assoc() {
8662        let (ast, diags) = parse_str("const x: number = a ? 1 : b ? 2 : 3;");
8663        assert!(diags.is_empty(), "unexpected: {diags:?}");
8664        let stmt = single_stmt(&ast);
8665        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8666            panic!("expected Const");
8667        };
8668        let crate::ExprKind::Ternary { else_, .. } = ast.try_expr(value).unwrap().kind else {
8669            panic!("expected outer Ternary");
8670        };
8671        assert!(matches!(
8672            ast.try_expr(else_).unwrap().kind,
8673            crate::ExprKind::Ternary { .. }
8674        ));
8675    }
8676
8677    #[test]
8678    fn parse_ternary_missing_colon_diagnoses() {
8679        let (_, diags) = parse_str("const x: number = a ? 1;");
8680        assert!(
8681            diags
8682                .iter()
8683                .any(|d| d.message.contains("expected `:` to complete ternary")),
8684            "expected colon diagnostic, got: {diags:?}",
8685        );
8686    }
8687
8688    #[test]
8689    fn parse_nullish_coalesce_binop() {
8690        let (ast, diags) = parse_str("const x: number = a ?? 1;");
8691        assert!(diags.is_empty(), "unexpected: {diags:?}");
8692        let stmt = single_stmt(&ast);
8693        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8694            panic!("expected Const");
8695        };
8696        match ast.try_expr(value).unwrap().kind {
8697            crate::ExprKind::Binary { op, .. } => {
8698                assert_eq!(op, crate::BinOp::NullishCoalesce);
8699            }
8700            ref other => panic!("expected Binary NullishCoalesce, got {other:?}"),
8701        }
8702    }
8703
8704    #[test]
8705    fn parse_nullish_left_associative() {
8706        let (ast, diags) = parse_str("const x: number = a ?? b ?? c;");
8707        assert!(diags.is_empty(), "unexpected: {diags:?}");
8708        let stmt = single_stmt(&ast);
8709        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8710            panic!("expected Const");
8711        };
8712        let crate::ExprKind::Binary { lhs, .. } = ast.try_expr(value).unwrap().kind else {
8713            panic!("expected outer Binary");
8714        };
8715        match ast.try_expr(lhs).unwrap().kind {
8716            crate::ExprKind::Binary { op, .. } => {
8717                assert_eq!(op, crate::BinOp::NullishCoalesce);
8718            }
8719            ref other => panic!("expected inner Binary, got {other:?}"),
8720        }
8721    }
8722
8723    #[test]
8724    fn parse_pow_is_right_associative() {
8725        let (ast, diags) = parse_str("const x: number = 2 ** 3 ** 2;");
8726        assert!(diags.is_empty(), "unexpected: {diags:?}");
8727        let stmt = single_stmt(&ast);
8728        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8729            panic!("expected Const");
8730        };
8731        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8732            panic!("expected outer Binary");
8733        };
8734        assert_eq!(op, crate::BinOp::Pow);
8735        match ast.try_expr(rhs).unwrap().kind {
8736            crate::ExprKind::Binary { op: inner_op, .. } => {
8737                assert_eq!(inner_op, crate::BinOp::Pow);
8738            }
8739            ref other => panic!("expected inner Pow Binary on RHS, got {other:?}"),
8740        }
8741    }
8742
8743    #[test]
8744    fn parse_pow_higher_than_multiply() {
8745        let (ast, diags) = parse_str("const x: number = 2 * 3 ** 2;");
8746        assert!(diags.is_empty(), "unexpected: {diags:?}");
8747        let stmt = single_stmt(&ast);
8748        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8749            panic!("expected Const");
8750        };
8751        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8752            panic!("expected outer Binary");
8753        };
8754        assert_eq!(op, crate::BinOp::Mul);
8755        match ast.try_expr(rhs).unwrap().kind {
8756            crate::ExprKind::Binary { op: inner_op, .. } => {
8757                assert_eq!(inner_op, crate::BinOp::Pow);
8758            }
8759            ref other => panic!("expected Pow on the RHS of *, got {other:?}"),
8760        }
8761    }
8762
8763    #[test]
8764    fn parse_compound_assign_ident() {
8765        let (ast, diags) = parse_str("function main(): void { let x: number = 0; x += 5; }");
8766        assert!(diags.is_empty(), "unexpected: {diags:?}");
8767        let func = ast.top_level.iter().find_map(|sid| {
8768            if let crate::StmtKind::Function { body, .. } = &ast.try_stmt(*sid).unwrap().kind {
8769                Some(*body)
8770            } else {
8771                None
8772            }
8773        });
8774        let crate::StmtKind::Block(stmts) = &ast.try_stmt(func.expect("main")).unwrap().kind else {
8775            panic!("expected function body to be a block");
8776        };
8777        let target = stmts
8778            .iter()
8779            .find_map(|sid| match &ast.try_stmt(*sid).unwrap().kind {
8780                crate::StmtKind::CompoundAssign { target, op, .. } => {
8781                    Some((target.name.clone(), *op))
8782                }
8783                _ => None,
8784            });
8785        let (name, op) = target.expect("expected a CompoundAssign in the body");
8786        assert_eq!(name, "x");
8787        assert_eq!(op, crate::BinOp::Add);
8788    }
8789
8790    #[test]
8791    fn parse_nullish_mixed_with_or_rejected() {
8792        let (_, diags) = parse_str("const x: number = a || b ?? c;");
8793        assert!(
8794            diags
8795                .iter()
8796                .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8797            "expected mixing diagnostic, got: {diags:?}",
8798        );
8799    }
8800
8801    #[test]
8802    fn parse_nullish_mixed_with_and_rejected() {
8803        let (_, diags) = parse_str("const x: number = a && b ?? c;");
8804        assert!(
8805            diags
8806                .iter()
8807                .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8808            "expected mixing diagnostic, got: {diags:?}",
8809        );
8810    }
8811
8812    #[test]
8813    fn parse_nullish_followed_by_logical_rejected() {
8814        for (source, help) in [
8815            (
8816                "const x: number = a ?? b || c;",
8817                "`(a ?? b) || c` or `a ?? (b || c)`",
8818            ),
8819            (
8820                "const x: number = a ?? b && c;",
8821                "`(a ?? b) && c` or `a ?? (b && c)`",
8822            ),
8823            (
8824                "const x: number = a ?? b ?? c || d;",
8825                "`(a ?? b) || c` or `a ?? (b || c)`",
8826            ),
8827            // One error per chain, however many mixes it has.
8828            (
8829                "const x: number = a || b ?? c || d;",
8830                "`(a || b) ?? c` or `a || (b ?? c)`",
8831            ),
8832        ] {
8833            let (_, diags) = parse_str(source);
8834            assert_eq!(diags.len(), 1, "diags for {source:?}: {diags:?}");
8835            assert!(
8836                diags[0].message.contains("mixing `??` with `||` / `&&`"),
8837                "message for {source:?}: {diags:?}",
8838            );
8839            assert!(
8840                diags[0].help.iter().any(|h| h.contains(help)),
8841                "help for {source:?}: {diags:?}",
8842            );
8843        }
8844    }
8845
8846    #[test]
8847    fn parse_nullish_operand_keeps_tighter_operators() {
8848        // `a ?? (b == c)`: only `||` and `&&` are excluded from a `??` operand.
8849        let (ast, diags) = parse_str("const x: boolean = a ?? b == c;");
8850        assert!(diags.is_empty(), "unexpected: {diags:?}");
8851        let stmt = single_stmt(&ast);
8852        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8853            panic!("expected Const");
8854        };
8855        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8856            panic!("expected outer Binary");
8857        };
8858        assert_eq!(op, crate::BinOp::NullishCoalesce);
8859        assert!(matches!(
8860            ast.try_expr(rhs).unwrap().kind,
8861            crate::ExprKind::Binary {
8862                op: crate::BinOp::Eq,
8863                ..
8864            }
8865        ));
8866    }
8867
8868    #[test]
8869    fn parse_nullish_with_parens_ok() {
8870        let (_, diags) = parse_str("const x: number = (a || b) ?? c;");
8871        assert!(diags.is_empty(), "unexpected: {diags:?}");
8872    }
8873
8874    #[test]
8875    fn parse_optional_field_chain() {
8876        let (ast, diags) = parse_str("const x: number = a?.b;");
8877        assert!(diags.is_empty(), "unexpected: {diags:?}");
8878        let stmt = single_stmt(&ast);
8879        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8880            panic!("expected Const");
8881        };
8882        match ast.try_expr(value).unwrap().kind {
8883            crate::ExprKind::OptionalChain { ref parts, .. } => {
8884                assert_eq!(parts.len(), 1);
8885                match &parts[0] {
8886                    crate::ChainPart::Field { name, optional, .. } => {
8887                        assert_eq!(name.name, "b");
8888                        assert!(*optional);
8889                    }
8890                    other => panic!("expected Field, got {other:?}"),
8891                }
8892            }
8893            ref other => panic!("expected OptionalChain, got {other:?}"),
8894        }
8895    }
8896
8897    #[test]
8898    fn parse_optional_chain_continues_after_first_dot() {
8899        let (ast, diags) = parse_str("const x: number = a?.b.c;");
8900        assert!(diags.is_empty(), "unexpected: {diags:?}");
8901        let stmt = single_stmt(&ast);
8902        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8903            panic!("expected Const");
8904        };
8905        match ast.try_expr(value).unwrap().kind {
8906            crate::ExprKind::OptionalChain { ref parts, .. } => {
8907                assert_eq!(parts.len(), 2);
8908                if let crate::ChainPart::Field { optional, .. } = &parts[0] {
8909                    assert!(*optional);
8910                }
8911                if let crate::ChainPart::Field { optional, .. } = &parts[1] {
8912                    assert!(!*optional);
8913                }
8914            }
8915            ref other => panic!("expected OptionalChain, got {other:?}"),
8916        }
8917    }
8918
8919    #[test]
8920    fn parse_optional_chain_multiple_short_circuits() {
8921        let (ast, diags) = parse_str("const x: number = a?.b?.c;");
8922        assert!(diags.is_empty(), "unexpected: {diags:?}");
8923        let stmt = single_stmt(&ast);
8924        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8925            panic!("expected Const");
8926        };
8927        let crate::ExprKind::OptionalChain { parts, .. } = &ast.try_expr(value).unwrap().kind
8928        else {
8929            panic!("expected OptionalChain");
8930        };
8931        assert_eq!(parts.len(), 2);
8932        for p in parts {
8933            if let crate::ChainPart::Field { optional, .. } = p {
8934                assert!(*optional, "both parts should be optional");
8935            }
8936        }
8937    }
8938
8939    #[test]
8940    fn parse_optional_call_and_index() {
8941        let (_, diags) = parse_str("const x: number = f?.();");
8942        assert!(diags.is_empty(), "unexpected: {diags:?}");
8943        let (_, diags) = parse_str("const y: number = arr?.[0];");
8944        assert!(diags.is_empty(), "unexpected: {diags:?}");
8945    }
8946
8947    #[test]
8948    fn parse_optional_chain_terminates_at_questionmark_for_ternary() {
8949        let (ast, diags) = parse_str("const x: number = a?.b ? c : d;");
8950        assert!(diags.is_empty(), "unexpected: {diags:?}");
8951        let stmt = single_stmt(&ast);
8952        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8953            panic!("expected Const");
8954        };
8955        let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8956            panic!(
8957                "expected top-level Ternary, got {:?}",
8958                ast.try_expr(value).unwrap().kind
8959            );
8960        };
8961        assert!(matches!(
8962            ast.try_expr(cond).unwrap().kind,
8963            crate::ExprKind::OptionalChain { .. }
8964        ));
8965    }
8966
8967    #[test]
8968    fn parse_nullish_below_ternary() {
8969        let (ast, diags) = parse_str("const x: number = a ?? b ? c : d;");
8970        assert!(diags.is_empty(), "unexpected: {diags:?}");
8971        let stmt = single_stmt(&ast);
8972        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8973            panic!("expected Const");
8974        };
8975        let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8976            panic!("expected top-level Ternary");
8977        };
8978        match ast.try_expr(cond).unwrap().kind {
8979            crate::ExprKind::Binary { op, .. } => {
8980                assert_eq!(op, crate::BinOp::NullishCoalesce);
8981            }
8982            ref other => panic!("expected Binary ??, got {other:?}"),
8983        }
8984    }
8985
8986    #[test]
8987    fn let_missing_initializer_diagnoses() {
8988        let (ast, diags) = parse_str("let x;");
8989        assert_eq!(diags.len(), 1);
8990        assert_eq!(
8991            diags[0].message,
8992            "`let` declaration requires an initializer"
8993        );
8994        assert!(ast.top_level.is_empty());
8995    }
8996
8997    #[test]
8998    fn missing_identifier_after_let_diagnoses() {
8999        let (ast, diags) = parse_str("let = 1;");
9000        assert_eq!(diags.len(), 1);
9001        assert_eq!(diags[0].message, "expected identifier after `let`/`const`");
9002        assert!(ast.top_level.is_empty());
9003    }
9004
9005    #[test]
9006    fn missing_type_after_colon_diagnoses() {
9007        let (ast, diags) = parse_str("let x: = 1;");
9008        assert_eq!(diags.len(), 1);
9009        assert_eq!(diags[0].message, "expected type");
9010        assert!(ast.top_level.is_empty());
9011    }
9012
9013    #[test]
9014    fn missing_semicolon_after_declaration_diagnoses() {
9015        let (ast, diags) = parse_str("let x = 1 y;");
9016        assert_eq!(diags.len(), 1);
9017        assert_eq!(diags[0].message, "expected `;` after declaration");
9018        assert!(ast.top_level.is_empty());
9019    }
9020
9021    #[test]
9022    fn expression_statements_still_parse() {
9023        let (ast, diags) = parse_str("42;");
9024        assert!(diags.is_empty());
9025        assert_eq!(ast.top_level.len(), 1);
9026    }
9027
9028    #[test]
9029    fn snapshot_mixed_declarations() {
9030        let (ast, diags) =
9031            parse_str(r#"let x = 1; const y: string = "hi"; let z: number = 1 + 2;"#);
9032        assert!(diags.is_empty());
9033        assert_eq!(ast.top_level.len(), 3);
9034        insta::assert_debug_snapshot!(ast);
9035    }
9036
9037    #[test]
9038    fn parse_function_typical() {
9039        let (ast, diags) = parse_str("function f(a: number, b: string): boolean { }");
9040        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9041        let stmt = single_stmt(&ast);
9042        match stmt.kind {
9043            crate::StmtKind::Function {
9044                ref name,
9045                ref generics,
9046                ref params,
9047                ref return_type,
9048                body,
9049                ..
9050            } => {
9051                assert_eq!(name.name, "f");
9052                assert_eq!(name.span, crate::Span::new(F, 9, 10).unwrap());
9053                assert!(generics.is_empty());
9054                assert_eq!(params.len(), 2);
9055                assert_eq!(params[0].name.name, "a");
9056                assert_eq!(params[0].name.span, crate::Span::new(F, 11, 12).unwrap());
9057                let p0_ty = params[0]
9058                    .ty
9059                    .as_ref()
9060                    .expect("function-decl param requires annotation");
9061                assert!(matches!(p0_ty.kind, crate::TypeAnnotationKind::Name { .. }));
9062                assert_eq!(p0_ty.span, crate::Span::new(F, 14, 20).unwrap()); // `number`
9063                assert_eq!(params[1].name.name, "b");
9064                assert_eq!(params[1].name.span, crate::Span::new(F, 22, 23).unwrap());
9065                let p1_ty = params[1]
9066                    .ty
9067                    .as_ref()
9068                    .expect("function-decl param requires annotation");
9069                assert_eq!(p1_ty.span, crate::Span::new(F, 25, 31).unwrap()); // `string`
9070                let return_type = return_type.as_ref().expect("plain return type");
9071                assert!(matches!(
9072                    return_type.kind,
9073                    crate::TypeAnnotationKind::Name { .. }
9074                ));
9075                assert_eq!(return_type.span, crate::Span::new(F, 34, 41).unwrap()); // `boolean`
9076                let block = ast.try_stmt(body).unwrap();
9077                assert!(matches!(block.kind, crate::StmtKind::Block(ref v) if v.is_empty()));
9078            }
9079            _ => panic!("expected Function"),
9080        }
9081    }
9082
9083    #[test]
9084    fn parse_function_with_doc_comment() {
9085        let (ast, diags) = parse_str(
9086            "/**\n * Sum two numbers.\n * @param a First.\n * @param b Second.\n */\nfunction add(a: number, b: number): number { return a + b; }",
9087        );
9088        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9089        let stmt = single_stmt(&ast);
9090        match &stmt.kind {
9091            crate::StmtKind::Function { doc, .. } => {
9092                let d = doc.as_ref().expect("doc attached");
9093                assert_eq!(d.summary, "Sum two numbers.");
9094                assert_eq!(d.params.len(), 2);
9095                assert_eq!(d.params[0].name, "a");
9096                assert_eq!(d.params[1].name, "b");
9097            }
9098            _ => panic!("expected Function"),
9099        }
9100    }
9101
9102    #[test]
9103    fn parse_interface_member_docs() {
9104        let (ast, diags) = parse_str(
9105            "interface Foo {\n  /** First method. */ first(): void;\n  /** A field. */ field: number;\n}",
9106        );
9107        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9108        let stmt = single_stmt(&ast);
9109        match &stmt.kind {
9110            crate::StmtKind::InterfaceDecl { members, .. } => {
9111                assert_eq!(members.len(), 2);
9112                match &members[0] {
9113                    crate::InterfaceMember::Method { doc, .. } => {
9114                        assert_eq!(doc.as_ref().unwrap().summary, "First method.");
9115                    }
9116                    _ => panic!("expected Method first"),
9117                }
9118                match &members[1] {
9119                    crate::InterfaceMember::Property { doc, .. } => {
9120                        assert_eq!(doc.as_ref().unwrap().summary, "A field.");
9121                    }
9122                    _ => panic!("expected Property second"),
9123                }
9124            }
9125            _ => panic!("expected InterfaceDecl"),
9126        }
9127    }
9128
9129    #[test]
9130    fn parse_numeric_enum_all_implicit() {
9131        let (ast, diags) = parse_str("enum Direction { Up, Down, Left, Right }");
9132        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9133        let stmt = single_stmt(&ast);
9134        match &stmt.kind {
9135            crate::StmtKind::EnumDecl { name, members, doc } => {
9136                assert_eq!(name.name, "Direction");
9137                assert!(doc.is_none());
9138                assert_eq!(members.len(), 4);
9139                for (m, expected) in members.iter().zip(["Up", "Down", "Left", "Right"]) {
9140                    assert_eq!(m.name.name, expected);
9141                    assert!(m.value.is_none(), "expected no initializer on {expected}");
9142                }
9143            }
9144            _ => panic!("expected EnumDecl"),
9145        }
9146    }
9147
9148    #[test]
9149    fn parse_string_enum_all_explicit() {
9150        let (ast, diags) =
9151            parse_str("enum Status { Active = \"active\", Inactive = \"inactive\" }");
9152        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9153        let stmt = single_stmt(&ast);
9154        match &stmt.kind {
9155            crate::StmtKind::EnumDecl { name, members, .. } => {
9156                assert_eq!(name.name, "Status");
9157                assert_eq!(members.len(), 2);
9158                match &members[0].value {
9159                    Some(crate::EnumInitializer::String { value, .. }) => {
9160                        assert_eq!(value, "active");
9161                    }
9162                    other => panic!("expected String init, got {other:?}"),
9163                }
9164                match &members[1].value {
9165                    Some(crate::EnumInitializer::String { value, .. }) => {
9166                        assert_eq!(value, "inactive");
9167                    }
9168                    other => panic!("expected String init, got {other:?}"),
9169                }
9170            }
9171            _ => panic!("expected EnumDecl"),
9172        }
9173    }
9174
9175    #[test]
9176    fn parse_numeric_enum_with_negative_init() {
9177        let (ast, diags) = parse_str("enum D { Up = 1, Down = -1 }");
9178        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9179        let stmt = single_stmt(&ast);
9180        match &stmt.kind {
9181            crate::StmtKind::EnumDecl { members, .. } => {
9182                assert_eq!(members.len(), 2);
9183                match &members[0].value {
9184                    Some(crate::EnumInitializer::Number { value, .. }) => {
9185                        assert_eq!(*value, 1.0);
9186                    }
9187                    other => panic!("expected Number init, got {other:?}"),
9188                }
9189                match &members[1].value {
9190                    Some(crate::EnumInitializer::Number { value, .. }) => {
9191                        assert_eq!(*value, -1.0);
9192                    }
9193                    other => panic!("expected Number init, got {other:?}"),
9194                }
9195            }
9196            _ => panic!("expected EnumDecl"),
9197        }
9198    }
9199
9200    #[test]
9201    fn parse_enum_mixed_kinds_is_parse_error() {
9202        let (_ast, diags) = parse_str("enum Mix { A = 1, B = \"two\" }");
9203        assert!(
9204            diags
9205                .iter()
9206                .any(|d| d.message.contains("mixed numeric and string")),
9207            "expected mixed-kind diagnostic, got: {diags:?}"
9208        );
9209    }
9210
9211    #[test]
9212    fn parse_enum_member_docs() {
9213        let (ast, diags) = parse_str(
9214            "enum D {\n  /** The first one. */ A = 1,\n  /** The second one. */ B = 2,\n}",
9215        );
9216        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9217        let stmt = single_stmt(&ast);
9218        match &stmt.kind {
9219            crate::StmtKind::EnumDecl { members, .. } => {
9220                assert_eq!(members.len(), 2);
9221                assert_eq!(
9222                    members[0].doc.as_ref().expect("first doc").summary,
9223                    "The first one."
9224                );
9225                assert_eq!(
9226                    members[1].doc.as_ref().expect("second doc").summary,
9227                    "The second one."
9228                );
9229            }
9230            _ => panic!("expected EnumDecl"),
9231        }
9232    }
9233
9234    #[test]
9235    fn parse_enum_trailing_comma_ok() {
9236        let (ast, diags) = parse_str("enum D { A, B, }");
9237        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9238        let stmt = single_stmt(&ast);
9239        match &stmt.kind {
9240            crate::StmtKind::EnumDecl { members, .. } => assert_eq!(members.len(), 2),
9241            _ => panic!("expected EnumDecl"),
9242        }
9243    }
9244
9245    #[test]
9246    fn parse_function_empty_params() {
9247        let (ast, diags) = parse_str("function noop(): void { }");
9248        assert!(diags.is_empty());
9249        let stmt = single_stmt(&ast);
9250        match stmt.kind {
9251            crate::StmtKind::Function { ref params, .. } => assert!(params.is_empty()),
9252            _ => panic!("expected Function"),
9253        }
9254    }
9255
9256    #[test]
9257    fn parse_generic_function_single_param() {
9258        let (ast, diags) = parse_str("function identity<T>(x: T): T { return x; }");
9259        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9260        let stmt = single_stmt(&ast);
9261        match stmt.kind {
9262            crate::StmtKind::Function {
9263                ref name,
9264                ref generics,
9265                ref params,
9266                ..
9267            } => {
9268                assert_eq!(name.name, "identity");
9269                assert_eq!(generics.len(), 1);
9270                assert_eq!(generics[0].name, "T");
9271                assert_eq!(params.len(), 1);
9272                assert_eq!(params[0].name.name, "x");
9273            }
9274            _ => panic!("expected Function"),
9275        }
9276    }
9277
9278    #[test]
9279    fn parse_generic_function_multiple_params() {
9280        let (ast, diags) = parse_str("function pair<T, U>(a: T, b: U): T { return a; }");
9281        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9282        let stmt = single_stmt(&ast);
9283        match stmt.kind {
9284            crate::StmtKind::Function { ref generics, .. } => {
9285                assert_eq!(generics.len(), 2);
9286                assert_eq!(generics[0].name, "T");
9287                assert_eq!(generics[1].name, "U");
9288            }
9289            _ => panic!("expected Function"),
9290        }
9291    }
9292
9293    #[test]
9294    fn parse_non_generic_function_has_empty_generics() {
9295        let (ast, diags) = parse_str("function noop(): void { }");
9296        assert!(diags.is_empty());
9297        let stmt = single_stmt(&ast);
9298        match stmt.kind {
9299            crate::StmtKind::Function { ref generics, .. } => assert!(generics.is_empty()),
9300            _ => panic!("expected Function"),
9301        }
9302    }
9303
9304    #[test]
9305    fn parse_empty_generic_list_diagnoses() {
9306        let (_ast, diags) = parse_str("function f<>(): void { }");
9307        assert!(
9308            diags
9309                .iter()
9310                .any(|d| d.message.contains("empty generic parameter list")),
9311            "expected empty-generic-list diagnostic, got: {diags:?}",
9312        );
9313    }
9314
9315    #[test]
9316    fn parse_trailing_comma_in_generic_list_ok() {
9317        let (ast, diags) = parse_str("function f<T,>(): void { }");
9318        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9319        match single_stmt(&ast).kind {
9320            crate::StmtKind::Function { ref generics, .. } => assert_eq!(generics.len(), 1),
9321            _ => panic!("expected Function"),
9322        }
9323    }
9324
9325    #[test]
9326    fn parse_generic_call_single_type_arg() {
9327        let (ast, diags) = parse_str("identity<number>(42);");
9328        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9329        let stmt = single_stmt(&ast);
9330        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9331            panic!("expected Expr stmt");
9332        };
9333        match &ast.try_expr(call_id).unwrap().kind {
9334            crate::ExprKind::Call {
9335                type_args, args, ..
9336            } => {
9337                let ta = type_args.as_ref().expect("type_args set for generic call");
9338                assert_eq!(ta.len(), 1);
9339                assert_eq!(args.len(), 1);
9340            }
9341            other => panic!("expected Call, got {other:?}"),
9342        }
9343    }
9344
9345    #[test]
9346    fn parse_generic_call_trailing_comma_ok() {
9347        let (ast, diags) = parse_str("identity<number,>(42);");
9348        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9349        let stmt = single_stmt(&ast);
9350        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9351            panic!("expected Expr stmt");
9352        };
9353        match &ast.try_expr(call_id).unwrap().kind {
9354            crate::ExprKind::Call {
9355                type_args, args, ..
9356            } => {
9357                assert_eq!(type_args.as_ref().expect("type_args set").len(), 1);
9358                assert_eq!(args.len(), 1);
9359            }
9360            other => panic!("expected Call, got {other:?}"),
9361        }
9362    }
9363
9364    #[test]
9365    fn parse_generic_call_multi_type_args() {
9366        let (ast, diags) = parse_str("pair<number, string>(1, \"x\");");
9367        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9368        let stmt = single_stmt(&ast);
9369        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9370            panic!("expected Expr stmt");
9371        };
9372        match &ast.try_expr(call_id).unwrap().kind {
9373            crate::ExprKind::Call { type_args, .. } => {
9374                let ta = type_args.as_ref().expect("type_args set for generic call");
9375                assert_eq!(ta.len(), 2);
9376            }
9377            other => panic!("expected Call, got {other:?}"),
9378        }
9379    }
9380
9381    #[test]
9382    fn parse_comparison_chain_unaffected_by_generic_lookahead() {
9383        let (ast, diags) = parse_str("let z = a < b > c;");
9384        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9385        assert!(
9386            !ast_contains_call_with_type_args(&ast),
9387            "should not have parsed any generic call",
9388        );
9389    }
9390
9391    #[test]
9392    fn parse_lt_with_paren_rhs_is_comparison_not_generic() {
9393        let (ast, diags) = parse_str("let z = a < (b);");
9394        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9395        assert!(!ast_contains_call_with_type_args(&ast));
9396    }
9397
9398    #[test]
9399    fn parse_generic_call_no_args() {
9400        let (ast, diags) = parse_str("none<number>();");
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().unwrap().len(), 1);
9411                assert!(args.is_empty());
9412            }
9413            other => panic!("expected Call, got {other:?}"),
9414        }
9415    }
9416
9417    fn ast_contains_call_with_type_args(ast: &crate::Ast) -> bool {
9418        for id in 0..ast.top_level.len() {
9419            let stmt = ast.try_stmt(ast.top_level[id]).unwrap();
9420            if walk_stmt_for_typed_call(ast, stmt) {
9421                return true;
9422            }
9423        }
9424        false
9425    }
9426
9427    fn walk_stmt_for_typed_call(ast: &crate::Ast, stmt: &crate::Stmt) -> bool {
9428        match &stmt.kind {
9429            crate::StmtKind::Let { value, .. } | crate::StmtKind::Const { value, .. } => {
9430                walk_expr_for_typed_call(ast, *value)
9431            }
9432            crate::StmtKind::Expr(e) => walk_expr_for_typed_call(ast, *e),
9433            crate::StmtKind::Block(stmts) => stmts
9434                .iter()
9435                .any(|sid| walk_stmt_for_typed_call(ast, ast.try_stmt(*sid).unwrap())),
9436            _ => false,
9437        }
9438    }
9439
9440    fn walk_expr_for_typed_call(ast: &crate::Ast, id: crate::ExprId) -> bool {
9441        match &ast.try_expr(id).unwrap().kind {
9442            crate::ExprKind::Call {
9443                type_args: Some(_), ..
9444            } => true,
9445            crate::ExprKind::Call { callee, args, .. } => {
9446                walk_expr_for_typed_call(ast, *callee)
9447                    || args.iter().any(|a| walk_expr_for_typed_call(ast, *a))
9448            }
9449            crate::ExprKind::Binary { lhs, rhs, .. } => {
9450                walk_expr_for_typed_call(ast, *lhs) || walk_expr_for_typed_call(ast, *rhs)
9451            }
9452            crate::ExprKind::Unary { operand, .. } => walk_expr_for_typed_call(ast, *operand),
9453            crate::ExprKind::Paren(e) => walk_expr_for_typed_call(ast, *e),
9454            crate::ExprKind::FieldAccess { receiver, .. } => {
9455                walk_expr_for_typed_call(ast, *receiver)
9456            }
9457            crate::ExprKind::IndexAccess { receiver, index } => {
9458                walk_expr_for_typed_call(ast, *receiver) || walk_expr_for_typed_call(ast, *index)
9459            }
9460            _ => false,
9461        }
9462    }
9463
9464    #[test]
9465    fn parse_function_body_with_statements() {
9466        let (ast, diags) = parse_str("function g(x: number): number { let y = x; y; }");
9467        assert!(diags.is_empty());
9468        let stmt = single_stmt(&ast);
9469        match stmt.kind {
9470            crate::StmtKind::Function { body, .. } => {
9471                let block = ast.try_stmt(body).unwrap();
9472                match block.kind {
9473                    crate::StmtKind::Block(ref v) => assert_eq!(v.len(), 2),
9474                    _ => panic!("expected Block"),
9475                }
9476            }
9477            _ => panic!("expected Function"),
9478        }
9479    }
9480
9481    #[test]
9482    fn function_missing_return_type_diagnoses() {
9483        let (ast, diags) = parse_str("function f() { }");
9484        assert!(!diags.is_empty());
9485        assert_eq!(diags[0].message, "expected `:` and return type");
9486        assert!(ast.top_level.is_empty());
9487    }
9488
9489    #[test]
9490    fn method_missing_return_type_still_reads_the_body() {
9491        let (_ast, diags) = parse_str(
9492            "class Box {\n  async run() {\n    const x = await f();\n    return x;\n  }\n  ok(): void {}\n}",
9493        );
9494        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9495        assert_eq!(
9496            messages,
9497            [
9498                "`async` functions are not supported",
9499                "expected `:` and return type",
9500                "`await` is not supported"
9501            ],
9502            "{diags:?}"
9503        );
9504        assert_eq!(
9505            diags[1].help,
9506            ["write the return type: `run(…): T { … }`, with `void` when it returns nothing"]
9507        );
9508    }
9509
9510    #[test]
9511    fn an_unclosed_method_body_leaves_the_next_declaration_alone() {
9512        let (ast, diags) =
9513            parse_str("class Box {\n  a() { return 1;\n}\nfunction main(): number { return 0; }");
9514        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9515        assert_eq!(
9516            messages,
9517            [
9518                "expected `:` and return type",
9519                "expected `}` to close class"
9520            ],
9521            "{diags:?}"
9522        );
9523        assert_eq!(ast.top_level.len(), 1, "`main` is still declared");
9524    }
9525
9526    #[test]
9527    fn every_declaration_after_an_unclosed_method_body_is_kept() {
9528        for next in [
9529            "class B { ok(): number { return 2; } }",
9530            "interface I { a: number; }",
9531            "enum E { A }",
9532            "type X = number;",
9533            "import * as fs from \"submilli:fs\";",
9534            "async function g(): number { return 1; }",
9535        ] {
9536            let source = format!(
9537                "class A {{\n  bad() {{\n    return 1;\n}}\n{next}\nfunction main(): void {{}}"
9538            );
9539            let (ast, diags) = parse_str(&source);
9540            let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9541            let expected_async = next.starts_with("async");
9542            let mut expected = vec![
9543                "expected `:` and return type",
9544                "expected `}` to close class",
9545            ];
9546            if expected_async {
9547                expected.push("`async` functions are not supported");
9548            }
9549            assert_eq!(messages, expected, "{next}: {diags:?}");
9550            assert_eq!(
9551                ast.top_level.len(),
9552                2,
9553                "{next} and `main` are kept: {diags:?}"
9554            );
9555        }
9556    }
9557
9558    #[test]
9559    fn a_failed_statement_leaves_the_next_declaration_to_be_parsed() {
9560        for (source, kept) in [
9561            ("let x = 1 +\nimport { exists } from \"submilli:fs\";", 1),
9562            ("const y =\nclass C {}\nfunction main(): void {}", 2),
9563            (
9564                "interface I {\n  a: number;\nimport * as fs from \"submilli:fs\";",
9565                1,
9566            ),
9567        ] {
9568            let (ast, diags) = parse_str(source);
9569            assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9570            assert_eq!(ast.top_level.len(), kept, "{source}: {diags:?}");
9571        }
9572    }
9573
9574    #[test]
9575    fn a_dynamic_import_after_a_failed_statement_reports_once() {
9576        for source in [
9577            "const m = import(\"submilli:fs\");\nclass C {}",
9578            "const d = import.meta.dirname;\nclass C {}",
9579        ] {
9580            let (_ast, diags) = parse_str(source);
9581            assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9582        }
9583    }
9584
9585    #[test]
9586    fn members_named_like_declarations_after_a_bad_method_stay_members() {
9587        let (_ast, diags) = parse_str(
9588            "class A {\n  bad() {}\n  type: number = 1;\n  function(): void {}\n  declare(): void {}\n  async(): void {}\n}",
9589        );
9590        assert_eq!(diags.len(), 1, "{diags:?}");
9591    }
9592
9593    #[test]
9594    fn every_member_after_a_bad_method_is_still_read() {
9595        let (_ast, diags) = parse_str(
9596            "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}",
9597        );
9598        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9599        assert_eq!(messages, ["expected `:` and return type"; 3], "{diags:?}");
9600    }
9601
9602    #[test]
9603    fn a_member_failing_after_a_type_literal_reports_once() {
9604        let (_ast, diags) = parse_str("class A {\n  x: { a: number } 5\n}");
9605        assert_eq!(diags.len(), 1, "{diags:?}");
9606    }
9607
9608    #[test]
9609    fn multiple_declarators_become_one_statement_each() {
9610        let (ast, diags) = parse_str("let a = 0, b: string = \"s\", [c] = [1];");
9611        assert!(diags.is_empty(), "{diags:?}");
9612        let kinds: Vec<&crate::StmtKind> = ast
9613            .top_level
9614            .iter()
9615            .map(|id| &ast.try_stmt(*id).unwrap().kind)
9616            .collect();
9617        assert!(
9618            matches!(
9619                kinds.as_slice(),
9620                [
9621                    crate::StmtKind::Let { name: a, ty: None, .. },
9622                    crate::StmtKind::Let { name: b, ty: Some(_), .. },
9623                    crate::StmtKind::LetPattern { .. },
9624                ] if a.name == "a" && b.name == "b"
9625            ),
9626            "{kinds:?}"
9627        );
9628    }
9629
9630    #[test]
9631    fn declarators_keep_their_own_spans() {
9632        let source = "const a = 1, b = 2;";
9633        let (ast, diags) = parse_str(source);
9634        assert!(diags.is_empty(), "{diags:?}");
9635        let spans: Vec<&str> = ast
9636            .top_level
9637            .iter()
9638            .map(|id| {
9639                let span = ast.try_stmt(*id).unwrap().span;
9640                &source[span.start as usize..span.end as usize]
9641            })
9642            .collect();
9643        assert_eq!(spans, ["const a = 1", "b = 2;"]);
9644    }
9645
9646    #[test]
9647    fn a_declarator_missing_its_initializer_is_reported_at_it() {
9648        let (_ast, diags) = parse_str("const a = 1, b;");
9649        assert_eq!(diags.len(), 1, "{diags:?}");
9650        assert_eq!(
9651            diags[0].message,
9652            "`const` declaration requires an initializer"
9653        );
9654        assert_eq!(diags[0].span.start, 14, "{diags:?}");
9655        let (_ast, diags) = parse_str("let a = 1, ;");
9656        assert_eq!(
9657            diags[0].message, "expected identifier after `let`/`const`",
9658            "{diags:?}"
9659        );
9660    }
9661
9662    #[test]
9663    fn a_typed_declarator_without_a_value_may_precede_another() {
9664        let (ast, diags) = parse_str("let a: number | undefined, b = 1;");
9665        assert!(diags.is_empty(), "{diags:?}");
9666        assert_eq!(ast.top_level.len(), 2);
9667    }
9668
9669    #[test]
9670    fn declarators_stay_in_their_block() {
9671        let (ast, diags) = parse_str("function f(): void { let a = 1, b = 2; }\nlet c = 3;");
9672        assert!(diags.is_empty(), "{diags:?}");
9673        assert_eq!(
9674            ast.top_level.len(),
9675            2,
9676            "the block's declarators don't leak out"
9677        );
9678    }
9679
9680    #[test]
9681    fn every_exported_declarator_is_exported() {
9682        let (ast, diags) = parse_str("export const a = 1, b = 2;");
9683        assert!(diags.is_empty(), "{diags:?}");
9684        assert_eq!(ast.top_level.len(), 2);
9685        let exported: Vec<crate::StmtId> =
9686            ast.exported_decls.iter().map(|decl| decl.stmt).collect();
9687        assert_eq!(exported, ast.top_level);
9688    }
9689
9690    #[test]
9691    fn a_for_head_holds_every_declarator() {
9692        let (ast, diags) = parse_str("for (let i = 0, j = 10; i < j; i++, j--) {}");
9693        assert!(diags.is_empty(), "{diags:?}");
9694        let crate::StmtKind::For { init, update, .. } = &single_stmt(&ast).kind else {
9695            panic!("expected For");
9696        };
9697        assert_eq!(init.len(), 2);
9698        let (ast2, diags) = parse_str("for (i = 0, j = 10; i < j; i++) {}");
9699        assert!(diags.is_empty(), "{diags:?}");
9700        let crate::StmtKind::For { init: assigns, .. } = &single_stmt(&ast2).kind else {
9701            panic!("expected For");
9702        };
9703        assert_eq!(assigns.len(), 2);
9704        let update = ast.try_stmt(update.unwrap()).unwrap();
9705        assert!(
9706            matches!(&update.kind, crate::StmtKind::Block(stmts) if stmts.len() == 2),
9707            "{update:?}"
9708        );
9709    }
9710
9711    #[test]
9712    fn missing_return_type_still_reads_the_body() {
9713        let (_ast, diags) = parse_str(
9714            "async function main() {\n  const x = await f();\n  if (x) { return 1; }\n  return 2;\n}\nfunction g(): void {}",
9715        );
9716        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9717        assert_eq!(
9718            messages,
9719            [
9720                "`async` functions are not supported",
9721                "expected `:` and return type",
9722                "`await` is not supported"
9723            ],
9724            "{diags:?}"
9725        );
9726        assert_eq!(
9727            diags[1].help,
9728            [
9729                "write the return type: `function main(…): T { … }`, with `void` when it returns nothing"
9730            ]
9731        );
9732    }
9733
9734    #[test]
9735    fn function_missing_param_type_diagnoses() {
9736        let (ast, diags) = parse_str("function f(a): void { }");
9737        assert!(!diags.is_empty());
9738        assert_eq!(diags[0].message, "parameter requires a type annotation");
9739        assert!(ast.top_level.is_empty());
9740    }
9741
9742    #[test]
9743    fn function_missing_name_diagnoses() {
9744        let (ast, diags) = parse_str("function (x: number): void { }");
9745        assert!(!diags.is_empty());
9746        assert_eq!(diags[0].message, "expected function name");
9747        assert!(ast.top_level.is_empty());
9748    }
9749
9750    #[test]
9751    fn function_trailing_param_comma_ok() {
9752        let (ast, diags) = parse_str("function f(a: number,): void { }");
9753        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9754        match single_stmt(&ast).kind {
9755            crate::StmtKind::Function { ref params, .. } => assert_eq!(params.len(), 1),
9756            _ => panic!("expected Function"),
9757        }
9758    }
9759
9760    #[test]
9761    fn function_missing_open_brace_diagnoses() {
9762        let (ast, diags) = parse_str("function f(): void");
9763        assert_eq!(diags.len(), 1);
9764        assert_eq!(diags[0].message, "expected `{`");
9765        assert!(ast.top_level.is_empty());
9766    }
9767
9768    #[test]
9769    fn snapshot_function_declaration() {
9770        let (ast, diags) =
9771            parse_str("function add(a: number, b: number): number { let sum = a + b; sum; }");
9772        assert!(diags.is_empty());
9773        insta::assert_debug_snapshot!(ast);
9774    }
9775
9776    #[test]
9777    fn rest_param_basic_parses() {
9778        let (_, diags) = parse_str("function sum(...nums: number[]): number { return 0; }");
9779        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9780    }
9781
9782    #[test]
9783    fn rest_param_with_fixed_prefix_parses() {
9784        let (_, diags) =
9785            parse_str("function tag(label: string, ...vals: number[]): string { return label; }");
9786        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9787    }
9788
9789    #[test]
9790    fn rest_param_in_arrow_parses() {
9791        let (_, diags) = parse_str("const f = (...n: number[]) => n.length;");
9792        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9793    }
9794
9795    #[test]
9796    fn rest_param_in_function_type_annotation_parses() {
9797        let (_, diags) =
9798            parse_str("let f: (a: number, ...rest: string[]) => void = (a, ...r) => {};");
9799        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9800    }
9801
9802    #[test]
9803    fn rest_param_not_last_rejected() {
9804        let (_, diags) = parse_str("function f(...xs: number[], y: number): void {}");
9805        assert!(
9806            diags.iter().any(|d| d
9807                .message
9808                .contains("rest parameter must be the last parameter")),
9809            "expected 'must be last' diagnostic, got {diags:?}",
9810        );
9811    }
9812
9813    #[test]
9814    fn rest_param_with_default_rejected() {
9815        let (_, diags) = parse_str("function f(...xs: number[] = []): void {}");
9816        assert!(
9817            diags.iter().any(|d| d
9818                .message
9819                .contains("rest parameter cannot have a default value")),
9820            "expected default-rejection diagnostic, got {diags:?}",
9821        );
9822    }
9823
9824    #[test]
9825    fn rest_param_without_annotation_rejected() {
9826        let (_, diags) = parse_str("function f(...xs): void {}");
9827        assert!(
9828            diags.iter().any(|d| d
9829                .message
9830                .contains("rest parameter requires a type annotation")),
9831            "expected missing-annotation diagnostic, got {diags:?}",
9832        );
9833    }
9834
9835    #[test]
9836    fn rest_param_destructured_rejected() {
9837        let (_, diags) = parse_str("function f(...{a, b}): void {}");
9838        assert!(
9839            diags
9840                .iter()
9841                .any(|d| d.message.contains("rest parameter cannot be destructured")),
9842            "expected destructured-rejection diagnostic, got {diags:?}",
9843        );
9844    }
9845
9846    #[test]
9847    fn rest_param_in_function_type_with_non_array_rejected() {
9848        let (_, diags) = parse_str("let f: (...xs: number) => void = () => {};");
9849        assert!(
9850            diags
9851                .iter()
9852                .any(|d| d.message.contains("rest parameter type must be an array")),
9853            "expected non-array-type diagnostic, got {diags:?}",
9854        );
9855    }
9856
9857    fn call(e: &crate::Expr) -> (crate::ExprId, &[crate::ExprId]) {
9858        match e.kind {
9859            crate::ExprKind::Call {
9860                callee, ref args, ..
9861            } => (callee, args.as_slice()),
9862            _ => panic!("expected Call, got {:?}", e.kind),
9863        }
9864    }
9865
9866    #[test]
9867    fn parse_call_no_args() {
9868        let (ast, diags) = parse_str("f();");
9869        assert!(diags.is_empty());
9870        let outer = expr_of_single_stmt(&ast);
9871        let (callee, args) = call(outer);
9872        assert!(matches!(
9873            ast.try_expr(callee).unwrap().kind,
9874            crate::ExprKind::Identifier(_)
9875        ));
9876        assert!(args.is_empty());
9877        assert_eq!(outer.span, crate::Span::new(F, 0, 3).unwrap());
9878    }
9879
9880    #[test]
9881    fn parse_call_one_arg() {
9882        let (ast, diags) = parse_str("f(1);");
9883        assert!(diags.is_empty());
9884        let outer = expr_of_single_stmt(&ast);
9885        let (callee, args) = call(outer);
9886        assert!(matches!(
9887            ast.try_expr(callee).unwrap().kind,
9888            crate::ExprKind::Identifier(_)
9889        ));
9890        assert_eq!(args.len(), 1);
9891        assert_eq!(
9892            ast.try_expr(args[0]).unwrap().kind,
9893            crate::ExprKind::Number(1.0)
9894        );
9895    }
9896
9897    #[test]
9898    fn parse_call_three_args() {
9899        let (ast, diags) = parse_str("f(1, 2, 3);");
9900        assert!(diags.is_empty());
9901        let outer = expr_of_single_stmt(&ast);
9902        let (_callee, args) = call(outer);
9903        assert_eq!(args.len(), 3);
9904        assert_eq!(
9905            ast.try_expr(args[0]).unwrap().kind,
9906            crate::ExprKind::Number(1.0)
9907        );
9908        assert_eq!(
9909            ast.try_expr(args[1]).unwrap().kind,
9910            crate::ExprKind::Number(2.0)
9911        );
9912        assert_eq!(
9913            ast.try_expr(args[2]).unwrap().kind,
9914            crate::ExprKind::Number(3.0)
9915        );
9916    }
9917
9918    #[test]
9919    fn parse_call_nested() {
9920        let (ast, diags) = parse_str("f(g(x));");
9921        assert!(diags.is_empty());
9922        let outer = expr_of_single_stmt(&ast);
9923        let (_callee, args) = call(outer);
9924        assert_eq!(args.len(), 1);
9925        let (_inner_callee, inner_args) = call(ast.try_expr(args[0]).unwrap());
9926        assert_eq!(inner_args.len(), 1);
9927        assert!(matches!(
9928            ast.try_expr(inner_args[0]).unwrap().kind,
9929            crate::ExprKind::Identifier(_)
9930        ));
9931    }
9932
9933    #[test]
9934    fn parse_curried_call() {
9935        let (ast, diags) = parse_str("f()(x);");
9936        assert!(diags.is_empty());
9937        let outer = expr_of_single_stmt(&ast);
9938        let (callee, args) = call(outer);
9939        assert_eq!(args.len(), 1);
9940        let (_inner_callee, inner_args) = call(ast.try_expr(callee).unwrap());
9941        assert!(inner_args.is_empty());
9942    }
9943
9944    #[test]
9945    fn unary_binds_looser_than_call() {
9946        let (ast, diags) = parse_str("-f();");
9947        assert!(diags.is_empty());
9948        let outer = expr_of_single_stmt(&ast);
9949        let (op, operand) = unary(outer);
9950        assert_eq!(op, crate::UnOp::Neg);
9951        let (_callee, args) = call(ast.try_expr(operand).unwrap());
9952        assert!(args.is_empty());
9953    }
9954
9955    #[test]
9956    fn call_inside_binary() {
9957        let (ast, diags) = parse_str("f() + 1;");
9958        assert!(diags.is_empty());
9959        let outer = expr_of_single_stmt(&ast);
9960        let (op, lhs, rhs) = binary(outer);
9961        assert_eq!(op, crate::BinOp::Add);
9962        let (_callee, args) = call(ast.try_expr(lhs).unwrap());
9963        assert!(args.is_empty());
9964        assert_eq!(
9965            ast.try_expr(rhs).unwrap().kind,
9966            crate::ExprKind::Number(1.0)
9967        );
9968    }
9969
9970    #[test]
9971    fn call_with_complex_args() {
9972        let (ast, diags) = parse_str("f(1 + 2, g(x));");
9973        assert!(diags.is_empty());
9974        let outer = expr_of_single_stmt(&ast);
9975        let (_callee, args) = call(outer);
9976        assert_eq!(args.len(), 2);
9977        let (op, ..) = binary(ast.try_expr(args[0]).unwrap());
9978        assert_eq!(op, crate::BinOp::Add);
9979        let (_, inner_args) = call(ast.try_expr(args[1]).unwrap());
9980        assert_eq!(inner_args.len(), 1);
9981    }
9982
9983    #[test]
9984    fn call_trailing_comma_ok() {
9985        let (ast, diags) = parse_str("f(1, 2,);");
9986        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9987        let (_callee, args) = call(expr_of_single_stmt(&ast));
9988        assert_eq!(args.len(), 2);
9989    }
9990
9991    #[test]
9992    fn unclosed_call_diagnoses() {
9993        let (ast, diags) = parse_str("f(1, 2");
9994        assert!(!diags.is_empty());
9995        assert!(diags[0].message == "expected `,` or `)`" || diags[0].message == "expected `)`");
9996        assert!(ast.top_level.is_empty());
9997    }
9998
9999    #[test]
10000    fn call_expression_statement() {
10001        let (ast, diags) = parse_str(r#"greet("world");"#);
10002        assert!(diags.is_empty());
10003        let outer = expr_of_single_stmt(&ast);
10004        let (callee, args) = call(outer);
10005        assert!(matches!(
10006            ast.try_expr(callee).unwrap().kind,
10007            crate::ExprKind::Identifier(_)
10008        ));
10009        assert_eq!(args.len(), 1);
10010        assert_eq!(
10011            ast.try_expr(args[0]).unwrap().kind,
10012            crate::ExprKind::String("world".to_string())
10013        );
10014    }
10015
10016    #[test]
10017    fn snapshot_multi_call_program() {
10018        let (ast, diags) = parse_str("f(); g(1); h(f(2), 3);");
10019        assert!(diags.is_empty());
10020        assert_eq!(ast.top_level.len(), 3);
10021        insta::assert_debug_snapshot!(ast);
10022    }
10023
10024    #[test]
10025    fn parse_if_simple() {
10026        let (ast, diags) = parse_str("if (a) { }");
10027        assert!(diags.is_empty());
10028        let stmt = single_stmt(&ast);
10029        match stmt.kind {
10030            crate::StmtKind::If {
10031                condition,
10032                then_block,
10033                else_block,
10034            } => {
10035                assert!(matches!(
10036                    ast.try_expr(condition).unwrap().kind,
10037                    crate::ExprKind::Identifier(_)
10038                ));
10039                assert!(matches!(
10040                    ast.try_stmt(then_block).unwrap().kind,
10041                    crate::StmtKind::Block(ref v) if v.is_empty()
10042                ));
10043                assert!(else_block.is_none());
10044            }
10045            _ => panic!("expected If"),
10046        }
10047    }
10048
10049    #[test]
10050    fn parse_if_else() {
10051        let (ast, diags) = parse_str("if (a) { } else { }");
10052        assert!(diags.is_empty());
10053        let stmt = single_stmt(&ast);
10054        match stmt.kind {
10055            crate::StmtKind::If { else_block, .. } => {
10056                let else_id = else_block.expect("expected else block");
10057                assert!(matches!(
10058                    ast.try_stmt(else_id).unwrap().kind,
10059                    crate::StmtKind::Block(ref v) if v.is_empty()
10060                ));
10061            }
10062            _ => panic!("expected If"),
10063        }
10064    }
10065
10066    #[test]
10067    fn parse_else_if_chain() {
10068        let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10069        assert!(diags.is_empty());
10070        let stmt = single_stmt(&ast);
10071        let crate::StmtKind::If {
10072            else_block: Some(inner_id),
10073            ..
10074        } = stmt.kind
10075        else {
10076            panic!("expected outer If with else_block");
10077        };
10078        let inner = ast.try_stmt(inner_id).unwrap();
10079        let crate::StmtKind::If {
10080            else_block: Some(tail_id),
10081            ..
10082        } = inner.kind
10083        else {
10084            panic!("expected inner If for `else if`");
10085        };
10086        assert!(matches!(
10087            ast.try_stmt(tail_id).unwrap().kind,
10088            crate::StmtKind::Block(_)
10089        ));
10090    }
10091
10092    #[test]
10093    fn parse_if_with_body_statements() {
10094        let (ast, diags) = parse_str("if (a) { let x = 1; }");
10095        assert!(diags.is_empty());
10096        let stmt = single_stmt(&ast);
10097        match stmt.kind {
10098            crate::StmtKind::If { then_block, .. } => {
10099                let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind
10100                else {
10101                    panic!("expected Block")
10102                };
10103                assert_eq!(body.len(), 1);
10104            }
10105            _ => panic!("expected If"),
10106        }
10107    }
10108
10109    #[test]
10110    fn parse_if_braceless_body() {
10111        let (ast, diags) = parse_str("if (a) return b;");
10112        assert!(diags.is_empty(), "{diags:?}");
10113        let stmt = single_stmt(&ast);
10114        let crate::StmtKind::If {
10115            then_block,
10116            else_block,
10117            ..
10118        } = stmt.kind
10119        else {
10120            panic!("expected If");
10121        };
10122        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10123            panic!("braceless body should wrap in a Block");
10124        };
10125        assert_eq!(body.len(), 1);
10126        assert!(matches!(
10127            ast.try_stmt(body[0]).unwrap().kind,
10128            crate::StmtKind::Return(_)
10129        ));
10130        assert!(else_block.is_none());
10131    }
10132
10133    #[test]
10134    fn parse_braceless_dangling_else_binds_nearest_if() {
10135        // `else` attaches to the inner `if`, not the outer one.
10136        let (ast, diags) = parse_str("if (a) if (b) x(); else y();");
10137        assert!(diags.is_empty(), "{diags:?}");
10138        let crate::StmtKind::If {
10139            then_block,
10140            else_block: outer_else,
10141            ..
10142        } = single_stmt(&ast).kind
10143        else {
10144            panic!("expected outer If");
10145        };
10146        assert!(outer_else.is_none(), "else must bind to the inner if");
10147        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10148            panic!("expected wrapped Block");
10149        };
10150        let crate::StmtKind::If {
10151            else_block: Some(_),
10152            ..
10153        } = ast.try_stmt(body[0]).unwrap().kind
10154        else {
10155            panic!("inner If should own the else");
10156        };
10157    }
10158
10159    #[test]
10160    fn parse_while_braceless_body() {
10161        let (ast, diags) = parse_str("while (a) x();");
10162        assert!(diags.is_empty(), "{diags:?}");
10163        let crate::StmtKind::While { body, .. } = single_stmt(&ast).kind else {
10164            panic!("expected While");
10165        };
10166        assert!(matches!(
10167            ast.try_stmt(body).unwrap().kind,
10168            crate::StmtKind::Block(ref v) if v.len() == 1
10169        ));
10170    }
10171
10172    #[test]
10173    fn parse_loop_with_empty_body() {
10174        for src in [
10175            "while (a());",
10176            "for (const x of xs);",
10177            "for (let i = 0; i < 3; i++);",
10178            "do ; while (a());",
10179        ] {
10180            let (ast, diags) = parse_str(src);
10181            assert!(diags.is_empty(), "{src}: {diags:?}");
10182            let body = match single_stmt(&ast).kind {
10183                crate::StmtKind::While { body, .. }
10184                | crate::StmtKind::DoWhile { body, .. }
10185                | crate::StmtKind::For { body, .. }
10186                | crate::StmtKind::ForOf { body, .. } => body,
10187                ref other => panic!("{src}: expected a loop, got {other:?}"),
10188            };
10189            assert!(
10190                matches!(ast.try_stmt(body).unwrap().kind, crate::StmtKind::Block(ref v) if v.is_empty()),
10191                "{src}"
10192            );
10193        }
10194    }
10195
10196    #[test]
10197    fn parse_if_with_empty_body_is_an_error() {
10198        let (_ast, diags) = parse_str("if (a);");
10199        assert_eq!(diags[0].message, "`if` has an empty body");
10200    }
10201
10202    #[test]
10203    fn parse_empty_branch_body_reports_once() {
10204        let (_ast, diags) = parse_str("if (a) ; else b();");
10205        assert_eq!(diags.len(), 1, "{diags:?}");
10206        assert_eq!(diags[0].message, "`if` has an empty body");
10207        let (_ast, diags) = parse_str("if (a) b(); else ;");
10208        assert_eq!(diags.len(), 1, "{diags:?}");
10209        assert_eq!(diags[0].message, "`else` has an empty body");
10210        // One diagnostic per stray `;`, and the `else if` chain still parses.
10211        let (_ast, diags) = parse_str("if (a) ; else if (b) ; else ;");
10212        let messages: Vec<_> = diags.iter().map(|d| d.message.as_str()).collect();
10213        assert_eq!(
10214            messages,
10215            [
10216                "`if` has an empty body",
10217                "`if` has an empty body",
10218                "`else` has an empty body"
10219            ]
10220        );
10221    }
10222
10223    #[test]
10224    fn parse_for_of_braceless_body() {
10225        let (ast, diags) = parse_str("for (const x of xs) f(x);");
10226        assert!(diags.is_empty(), "{diags:?}");
10227        let crate::StmtKind::ForOf { body, .. } = single_stmt(&ast).kind else {
10228            panic!("expected ForOf");
10229        };
10230        assert!(matches!(
10231            ast.try_stmt(body).unwrap().kind,
10232            crate::StmtKind::Block(ref v) if v.len() == 1
10233        ));
10234    }
10235
10236    #[test]
10237    fn parse_nested_if() {
10238        let (ast, diags) = parse_str("if (a) { if (b) { } }");
10239        assert!(diags.is_empty());
10240        let outer = single_stmt(&ast);
10241        let crate::StmtKind::If { then_block, .. } = outer.kind else {
10242            panic!("expected outer If");
10243        };
10244        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10245            panic!("expected Block")
10246        };
10247        assert_eq!(body.len(), 1);
10248        assert!(matches!(
10249            ast.try_stmt(body[0]).unwrap().kind,
10250            crate::StmtKind::If { .. }
10251        ));
10252    }
10253
10254    #[test]
10255    fn if_missing_open_paren_diagnoses() {
10256        let (ast, diags) = parse_str("if a { }");
10257        assert!(!diags.is_empty());
10258        assert_eq!(diags[0].message, "expected `(`");
10259        assert!(ast.top_level.is_empty());
10260    }
10261
10262    #[test]
10263    fn if_missing_close_paren_diagnoses() {
10264        let (ast, diags) = parse_str("if (a { }");
10265        assert!(!diags.is_empty());
10266        assert_eq!(diags[0].message, "expected `)`");
10267        assert!(ast.top_level.is_empty());
10268    }
10269
10270    #[test]
10271    fn if_braceless_body_parses() {
10272        let (ast, diags) = parse_str("if (a) x;");
10273        assert!(diags.is_empty(), "{diags:?}");
10274        let crate::StmtKind::If { then_block, .. } = single_stmt(&ast).kind else {
10275            panic!("expected If");
10276        };
10277        assert!(matches!(
10278            ast.try_stmt(then_block).unwrap().kind,
10279            crate::StmtKind::Block(ref v) if v.len() == 1
10280        ));
10281    }
10282
10283    #[test]
10284    fn snapshot_else_if_chain() {
10285        let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10286        assert!(diags.is_empty());
10287        insta::assert_debug_snapshot!(ast);
10288    }
10289
10290    #[test]
10291    fn parse_while_simple() {
10292        let (ast, diags) = parse_str("while (a) { }");
10293        assert!(diags.is_empty());
10294        let stmt = single_stmt(&ast);
10295        match stmt.kind {
10296            crate::StmtKind::While { condition, body } => {
10297                assert!(matches!(
10298                    ast.try_expr(condition).unwrap().kind,
10299                    crate::ExprKind::Identifier(_)
10300                ));
10301                assert!(matches!(
10302                    ast.try_stmt(body).unwrap().kind,
10303                    crate::StmtKind::Block(ref v) if v.is_empty()
10304                ));
10305            }
10306            _ => panic!("expected While"),
10307        }
10308    }
10309
10310    #[test]
10311    fn parse_while_with_body() {
10312        let (ast, diags) = parse_str("while (a) { x; y; }");
10313        assert!(diags.is_empty());
10314        let stmt = single_stmt(&ast);
10315        let crate::StmtKind::While { body, .. } = stmt.kind else {
10316            panic!("expected While");
10317        };
10318        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10319            panic!("expected Block")
10320        };
10321        assert_eq!(stmts.len(), 2);
10322    }
10323
10324    #[test]
10325    fn while_missing_paren_diagnoses() {
10326        let (ast, diags) = parse_str("while a { }");
10327        assert!(!diags.is_empty());
10328        assert_eq!(diags[0].message, "expected `(`");
10329        assert!(ast.top_level.is_empty());
10330    }
10331
10332    #[test]
10333    fn parse_for_all_slots() {
10334        let (ast, diags) = parse_str("for (let i = 0; i < 10; i = i + 1) { x; }");
10335        assert!(diags.is_empty(), "{diags:?}");
10336        let stmt = single_stmt(&ast);
10337        let crate::StmtKind::For {
10338            init,
10339            condition,
10340            update,
10341            body,
10342        } = &stmt.kind
10343        else {
10344            panic!("expected For");
10345        };
10346        assert!(!init.is_empty(), "init missing");
10347        assert!(condition.is_some(), "condition missing");
10348        assert!(update.is_some(), "update missing");
10349        assert!(matches!(
10350            ast.try_stmt(*body).unwrap().kind,
10351            crate::StmtKind::Block(_)
10352        ));
10353    }
10354
10355    #[test]
10356    fn parse_for_empty_slots() {
10357        let (ast, diags) = parse_str("for (;;) { break; }");
10358        assert!(diags.is_empty(), "{diags:?}");
10359        let stmt = single_stmt(&ast);
10360        let crate::StmtKind::For {
10361            init,
10362            condition,
10363            update,
10364            ..
10365        } = &stmt.kind
10366        else {
10367            panic!("expected For");
10368        };
10369        assert!(init.is_empty() && condition.is_none() && update.is_none());
10370    }
10371
10372    #[test]
10373    fn parse_for_of_const_binding() {
10374        let (ast, diags) = parse_str("for (const x of arr) { y; }");
10375        assert!(diags.is_empty(), "{diags:?}");
10376        let stmt = single_stmt(&ast);
10377        let crate::StmtKind::ForOf {
10378            binding_kind,
10379            ref name,
10380            ref ty,
10381            ..
10382        } = stmt.kind
10383        else {
10384            panic!("expected ForOf");
10385        };
10386        assert!(matches!(binding_kind, crate::BindingKind::Const));
10387        assert_eq!(name.name, "x");
10388        assert!(ty.is_none());
10389    }
10390
10391    #[test]
10392    fn parse_for_of_let_with_annotation() {
10393        let (ast, diags) = parse_str("for (let x: number of arr) { y; }");
10394        assert!(diags.is_empty(), "{diags:?}");
10395        let stmt = single_stmt(&ast);
10396        let crate::StmtKind::ForOf {
10397            binding_kind,
10398            ref ty,
10399            ..
10400        } = stmt.kind
10401        else {
10402            panic!("expected ForOf");
10403        };
10404        assert!(matches!(binding_kind, crate::BindingKind::Let));
10405        assert!(ty.is_some());
10406    }
10407
10408    #[test]
10409    fn parse_do_while_simple() {
10410        let (ast, diags) = parse_str("do { x; } while (a);");
10411        assert!(diags.is_empty(), "{diags:?}");
10412        let stmt = single_stmt(&ast);
10413        assert!(matches!(stmt.kind, crate::StmtKind::DoWhile { .. }));
10414    }
10415
10416    #[test]
10417    fn parse_break_and_continue() {
10418        let (ast, diags) = parse_str("while (a) { break; continue; }");
10419        assert!(diags.is_empty(), "{diags:?}");
10420        let stmt = single_stmt(&ast);
10421        let crate::StmtKind::While { body, .. } = stmt.kind else {
10422            panic!("expected While");
10423        };
10424        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10425            panic!("expected Block");
10426        };
10427        assert!(matches!(
10428            ast.try_stmt(stmts[0]).unwrap().kind,
10429            crate::StmtKind::Break
10430        ));
10431        assert!(matches!(
10432            ast.try_stmt(stmts[1]).unwrap().kind,
10433            crate::StmtKind::Continue
10434        ));
10435    }
10436
10437    #[test]
10438    fn parse_bare_return_in_function() {
10439        let (ast, diags) = parse_str("function f(): void { return; }");
10440        assert!(diags.is_empty());
10441        let f = single_stmt(&ast);
10442        let crate::StmtKind::Function { body, .. } = f.kind else {
10443            panic!("expected Function");
10444        };
10445        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10446            panic!("expected Block")
10447        };
10448        assert_eq!(stmts.len(), 1);
10449        match ast.try_stmt(stmts[0]).unwrap().kind {
10450            crate::StmtKind::Return(None) => {}
10451            _ => panic!("expected Return(None)"),
10452        }
10453    }
10454
10455    #[test]
10456    fn parse_return_with_value() {
10457        let (ast, diags) = parse_str("function f(): number { return 1; }");
10458        assert!(diags.is_empty());
10459        let f = single_stmt(&ast);
10460        let crate::StmtKind::Function { body, .. } = f.kind else {
10461            panic!("expected Function");
10462        };
10463        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10464            panic!("expected Block")
10465        };
10466        match ast.try_stmt(stmts[0]).unwrap().kind {
10467            crate::StmtKind::Return(Some(value)) => {
10468                assert_eq!(
10469                    ast.try_expr(value).unwrap().kind,
10470                    crate::ExprKind::Number(1.0)
10471                );
10472            }
10473            _ => panic!("expected Return(Some)"),
10474        }
10475    }
10476
10477    #[test]
10478    fn parse_return_with_expression() {
10479        let (ast, diags) = parse_str("function f(): number { return 1 + 2; }");
10480        assert!(diags.is_empty());
10481        let f = single_stmt(&ast);
10482        let crate::StmtKind::Function { body, .. } = f.kind else {
10483            panic!("expected Function");
10484        };
10485        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10486            panic!("expected Block")
10487        };
10488        match ast.try_stmt(stmts[0]).unwrap().kind {
10489            crate::StmtKind::Return(Some(value)) => {
10490                let (op, ..) = binary(ast.try_expr(value).unwrap());
10491                assert_eq!(op, crate::BinOp::Add);
10492            }
10493            _ => panic!("expected Return(Some(Binary))"),
10494        }
10495    }
10496
10497    #[test]
10498    fn return_missing_semicolon_diagnoses() {
10499        let (_, diags) = parse_str("function f(): number { return x x; }");
10500        assert!(!diags.is_empty());
10501        assert_eq!(diags[0].message, "expected `;` after return");
10502    }
10503
10504    #[test]
10505    fn parse_bare_block() {
10506        let (ast, diags) = parse_str("{ x; y; }");
10507        assert!(diags.is_empty());
10508        let stmt = single_stmt(&ast);
10509        let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10510            panic!("expected Block");
10511        };
10512        assert_eq!(stmts.len(), 2);
10513    }
10514
10515    #[test]
10516    fn parse_empty_bare_block() {
10517        let (ast, diags) = parse_str("{ }");
10518        assert!(diags.is_empty());
10519        let stmt = single_stmt(&ast);
10520        let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10521            panic!("expected Block");
10522        };
10523        assert!(stmts.is_empty());
10524    }
10525
10526    #[test]
10527    fn parse_nested_bare_blocks() {
10528        let (ast, diags) = parse_str("{ { } }");
10529        assert!(diags.is_empty());
10530        let outer = single_stmt(&ast);
10531        let crate::StmtKind::Block(ref stmts) = outer.kind else {
10532            panic!("expected outer Block");
10533        };
10534        assert_eq!(stmts.len(), 1);
10535        assert!(matches!(
10536            ast.try_stmt(stmts[0]).unwrap().kind,
10537            crate::StmtKind::Block(ref v) if v.is_empty()
10538        ));
10539    }
10540
10541    #[test]
10542    fn snapshot_function_with_if_and_return() {
10543        let source = "function isPositive(n: number): boolean {\n  if (n > 0) {\n    return true;\n  } else {\n    return false;\n  }\n}";
10544        let (ast, diags) = parse_str(source);
10545        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10546        insta::assert_debug_snapshot!(ast);
10547    }
10548
10549    fn object_literal(e: &crate::Expr) -> &[crate::ObjectLiteralMember] {
10550        match e.kind {
10551            crate::ExprKind::ObjectLiteral { ref members } => members.as_slice(),
10552            _ => panic!("expected ObjectLiteral, got {:?}", e.kind),
10553        }
10554    }
10555
10556    fn object_literal_field(m: &crate::ObjectLiteralMember) -> &crate::ObjectLiteralField {
10557        match m {
10558            crate::ObjectLiteralMember::Field(f) => f,
10559            crate::ObjectLiteralMember::Spread { .. }
10560            | crate::ObjectLiteralMember::Computed { .. } => {
10561                panic!("expected Field member, got Spread")
10562            }
10563        }
10564    }
10565
10566    fn array_literal(e: &crate::Expr) -> &[crate::ArrayLiteralElement] {
10567        match e.kind {
10568            crate::ExprKind::ArrayLiteral { ref elements } => elements.as_slice(),
10569            _ => panic!("expected ArrayLiteral, got {:?}", e.kind),
10570        }
10571    }
10572
10573    fn array_literal_value(el: &crate::ArrayLiteralElement) -> crate::ExprId {
10574        match el {
10575            crate::ArrayLiteralElement::Value(id) => *id,
10576            crate::ArrayLiteralElement::Spread { .. } => {
10577                panic!("expected Value element, got Spread")
10578            }
10579        }
10580    }
10581
10582    #[test]
10583    fn parse_empty_object_literal() {
10584        let (ast, diags) = parse_str("let x = {};");
10585        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10586        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10587            panic!("expected Let");
10588        };
10589        assert!(object_literal(ast.try_expr(value).unwrap()).is_empty());
10590    }
10591
10592    #[test]
10593    fn parse_object_literal_simple() {
10594        let (ast, diags) = parse_str("let p = { x: 1, y: 2 };");
10595        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10596        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10597            panic!("expected Let");
10598        };
10599        let members = object_literal(ast.try_expr(value).unwrap());
10600        assert_eq!(members.len(), 2);
10601        let f0 = object_literal_field(&members[0]);
10602        let f1 = object_literal_field(&members[1]);
10603        assert_eq!(f0.name.name, "x");
10604        assert_eq!(f1.name.name, "y");
10605        assert_eq!(
10606            ast.try_expr(f0.value).unwrap().kind,
10607            crate::ExprKind::Number(1.0)
10608        );
10609        assert_eq!(
10610            ast.try_expr(f1.value).unwrap().kind,
10611            crate::ExprKind::Number(2.0)
10612        );
10613    }
10614
10615    #[test]
10616    fn parse_object_literal_keyword_keys() {
10617        let (ast, diags) = parse_str("let p = { type: 1, default: 2, null: 3, true: 4 };");
10618        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10619        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10620            panic!("expected Let");
10621        };
10622        let names: Vec<&str> = object_literal(ast.try_expr(value).unwrap())
10623            .iter()
10624            .map(object_literal_field)
10625            .map(|f| f.name.name.as_str())
10626            .collect();
10627        assert_eq!(names, vec!["type", "default", "null", "true"]);
10628    }
10629
10630    #[test]
10631    fn parse_object_literal_string_key() {
10632        let (ast, diags) = parse_str(r#"let p = { "hello": 1 };"#);
10633        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10634        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10635            panic!("expected Let");
10636        };
10637        let members = object_literal(ast.try_expr(value).unwrap());
10638        assert_eq!(members.len(), 1);
10639        let f0 = object_literal_field(&members[0]);
10640        assert_eq!(f0.name.name, "hello");
10641    }
10642
10643    #[test]
10644    fn parse_object_literal_string_key_decodes_escape() {
10645        let (ast, diags) = parse_str(r#"let p = { "content\u002dtype": 1 };"#);
10646        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10647        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10648            panic!("expected Let");
10649        };
10650        let members = object_literal(ast.try_expr(value).unwrap());
10651        assert_eq!(members.len(), 1);
10652        let f0 = object_literal_field(&members[0]);
10653        assert_eq!(f0.name.name, "content-type");
10654    }
10655
10656    #[test]
10657    fn parse_object_literal_shorthand() {
10658        let (ast, diags) = parse_str("let p = { x, y: 2 };");
10659        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10660        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10661            panic!("expected Let");
10662        };
10663        let members = object_literal(ast.try_expr(value).unwrap());
10664        assert_eq!(members.len(), 2);
10665        let f0 = object_literal_field(&members[0]);
10666        assert_eq!(f0.name.name, "x");
10667        // `{ x }` desugars to `{ x: x }` — value is an identifier reference.
10668        assert_eq!(
10669            ast.try_expr(f0.value).unwrap().kind,
10670            crate::ExprKind::Identifier(crate::Ident {
10671                name: "x".into(),
10672                span: f0.name.span,
10673            })
10674        );
10675        let f1 = object_literal_field(&members[1]);
10676        assert_eq!(f1.name.name, "y");
10677        assert_eq!(
10678            ast.try_expr(f1.value).unwrap().kind,
10679            crate::ExprKind::Number(2.0)
10680        );
10681    }
10682
10683    #[test]
10684    fn parse_object_literal_string_key_shorthand_rejected() {
10685        let (_ast, diags) = parse_str(r#"let p = { "x" };"#);
10686        assert!(
10687            diags
10688                .iter()
10689                .any(|d| d.message.contains("expected `:` after field name")),
10690            "string-key shorthand should be rejected: {diags:?}"
10691        );
10692    }
10693
10694    #[test]
10695    fn parse_object_literal_trailing_comma() {
10696        let (ast, diags) = parse_str("let p = { x: 1, y: 2, };");
10697        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10698        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10699            panic!("expected Let");
10700        };
10701        assert_eq!(object_literal(ast.try_expr(value).unwrap()).len(), 2);
10702    }
10703
10704    #[test]
10705    fn parse_object_literal_duplicate_key_diagnoses() {
10706        let (_, diags) = parse_str("let p = { x: 1, x: 2 };");
10707        assert_eq!(diags.len(), 1);
10708        assert_eq!(diags[0].message, "duplicate field `x` in object literal");
10709    }
10710
10711    #[test]
10712    fn parse_empty_array_literal() {
10713        let (ast, diags) = parse_str("let xs = [];");
10714        assert!(diags.is_empty());
10715        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10716            panic!("expected Let");
10717        };
10718        assert!(array_literal(ast.try_expr(value).unwrap()).is_empty());
10719    }
10720
10721    #[test]
10722    fn parse_array_literal_simple() {
10723        let (ast, diags) = parse_str("let xs = [1, 2, 3];");
10724        assert!(diags.is_empty());
10725        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10726            panic!("expected Let");
10727        };
10728        let elements = array_literal(ast.try_expr(value).unwrap());
10729        assert_eq!(elements.len(), 3);
10730        assert_eq!(
10731            ast.try_expr(array_literal_value(&elements[0]))
10732                .unwrap()
10733                .kind,
10734            crate::ExprKind::Number(1.0)
10735        );
10736        assert_eq!(
10737            ast.try_expr(array_literal_value(&elements[2]))
10738                .unwrap()
10739                .kind,
10740            crate::ExprKind::Number(3.0)
10741        );
10742    }
10743
10744    #[test]
10745    fn parse_array_literal_trailing_comma() {
10746        let (ast, diags) = parse_str("let xs = [1, 2,];");
10747        assert!(diags.is_empty());
10748        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10749            panic!("expected Let");
10750        };
10751        assert_eq!(array_literal(ast.try_expr(value).unwrap()).len(), 2);
10752    }
10753
10754    #[test]
10755    fn parse_nested_array_in_object() {
10756        let (ast, diags) = parse_str("let p = { items: [1, 2] };");
10757        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10758        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10759            panic!("expected Let");
10760        };
10761        let members = object_literal(ast.try_expr(value).unwrap());
10762        let f0 = object_literal_field(&members[0]);
10763        assert_eq!(f0.name.name, "items");
10764        let inner = array_literal(ast.try_expr(f0.value).unwrap());
10765        assert_eq!(inner.len(), 2);
10766    }
10767
10768    #[test]
10769    fn parse_nested_object_in_array() {
10770        let (ast, diags) = parse_str("let xs = [{ x: 1 }, { x: 2 }];");
10771        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10772        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10773            panic!("expected Let");
10774        };
10775        let elements = array_literal(ast.try_expr(value).unwrap());
10776        assert_eq!(elements.len(), 2);
10777        assert_eq!(
10778            object_literal(ast.try_expr(array_literal_value(&elements[0])).unwrap()).len(),
10779            1
10780        );
10781        assert_eq!(
10782            object_literal(ast.try_expr(array_literal_value(&elements[1])).unwrap()).len(),
10783            1
10784        );
10785    }
10786
10787    #[test]
10788    fn brace_at_statement_start_is_block_not_object() {
10789        let (_, diags) = parse_str("{ x: 1 }");
10790        assert!(
10791            !diags.is_empty(),
10792            "expected a diagnostic for `x:` inside block"
10793        );
10794    }
10795
10796    fn field_access(e: &crate::Expr) -> (crate::ExprId, &str) {
10797        match e.kind {
10798            crate::ExprKind::FieldAccess { receiver, ref name } => (receiver, name.name.as_str()),
10799            _ => panic!("expected FieldAccess, got {:?}", e.kind),
10800        }
10801    }
10802
10803    fn index_access(e: &crate::Expr) -> (crate::ExprId, crate::ExprId) {
10804        match e.kind {
10805            crate::ExprKind::IndexAccess { receiver, index } => (receiver, index),
10806            _ => panic!("expected IndexAccess, got {:?}", e.kind),
10807        }
10808    }
10809
10810    #[test]
10811    fn parse_field_access_simple() {
10812        let (ast, diags) = parse_str("a.b;");
10813        assert!(diags.is_empty());
10814        let outer = expr_of_single_stmt(&ast);
10815        let (receiver, name) = field_access(outer);
10816        assert!(matches!(
10817            ast.try_expr(receiver).unwrap().kind,
10818            crate::ExprKind::Identifier(_)
10819        ));
10820        assert_eq!(name, "b");
10821    }
10822
10823    #[test]
10824    fn parse_field_access_keyword_name() {
10825        let (ast, diags) = parse_str("status.type.default.null;");
10826        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10827        let outer = expr_of_single_stmt(&ast);
10828        let (receiver, name) = field_access(outer);
10829        assert_eq!(name, "null");
10830        let (receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10831        assert_eq!(name, "default");
10832        let (_receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10833        assert_eq!(name, "type");
10834    }
10835
10836    #[test]
10837    fn parse_field_access_chain_left_assoc() {
10838        let (ast, diags) = parse_str("a.b.c;");
10839        assert!(diags.is_empty());
10840        let outer = expr_of_single_stmt(&ast);
10841        let (mid_id, c) = field_access(outer);
10842        assert_eq!(c, "c");
10843        let (a_id, b) = field_access(ast.try_expr(mid_id).unwrap());
10844        assert_eq!(b, "b");
10845        assert!(matches!(
10846            ast.try_expr(a_id).unwrap().kind,
10847            crate::ExprKind::Identifier(_)
10848        ));
10849    }
10850
10851    #[test]
10852    fn parse_index_access_simple() {
10853        let (ast, diags) = parse_str("a[0];");
10854        assert!(diags.is_empty());
10855        let outer = expr_of_single_stmt(&ast);
10856        let (receiver, idx) = index_access(outer);
10857        assert!(matches!(
10858            ast.try_expr(receiver).unwrap().kind,
10859            crate::ExprKind::Identifier(_)
10860        ));
10861        assert_eq!(
10862            ast.try_expr(idx).unwrap().kind,
10863            crate::ExprKind::Number(0.0)
10864        );
10865    }
10866
10867    #[test]
10868    fn parse_index_access_chain() {
10869        let (ast, diags) = parse_str("a[0][1];");
10870        assert!(diags.is_empty());
10871        let outer = expr_of_single_stmt(&ast);
10872        let (mid_id, one) = index_access(outer);
10873        assert_eq!(
10874            ast.try_expr(one).unwrap().kind,
10875            crate::ExprKind::Number(1.0)
10876        );
10877        let (a_id, zero) = index_access(ast.try_expr(mid_id).unwrap());
10878        assert_eq!(
10879            ast.try_expr(zero).unwrap().kind,
10880            crate::ExprKind::Number(0.0)
10881        );
10882        assert!(matches!(
10883            ast.try_expr(a_id).unwrap().kind,
10884            crate::ExprKind::Identifier(_)
10885        ));
10886    }
10887
10888    #[test]
10889    fn parse_mixed_access_and_call_chain() {
10890        let (ast, diags) = parse_str("a.b[c].d();");
10891        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10892        let outer = expr_of_single_stmt(&ast);
10893        let (callee, args) = call(outer);
10894        assert!(args.is_empty());
10895        let (idx_id, d_name) = field_access(ast.try_expr(callee).unwrap());
10896        assert_eq!(d_name, "d");
10897        let (b_id, c_idx) = index_access(ast.try_expr(idx_id).unwrap());
10898        assert!(matches!(
10899            ast.try_expr(c_idx).unwrap().kind,
10900            crate::ExprKind::Identifier(_)
10901        ));
10902        let (a_id, b_name) = field_access(ast.try_expr(b_id).unwrap());
10903        assert_eq!(b_name, "b");
10904        assert!(matches!(
10905            ast.try_expr(a_id).unwrap().kind,
10906            crate::ExprKind::Identifier(_)
10907        ));
10908    }
10909
10910    #[test]
10911    fn parse_call_then_field() {
10912        let (ast, diags) = parse_str("f().x;");
10913        assert!(diags.is_empty());
10914        let outer = expr_of_single_stmt(&ast);
10915        let (call_id, x_name) = field_access(outer);
10916        assert_eq!(x_name, "x");
10917        let (_callee, args) = call(ast.try_expr(call_id).unwrap());
10918        assert!(args.is_empty());
10919    }
10920
10921    #[test]
10922    fn parse_index_with_complex_expression() {
10923        let (ast, diags) = parse_str("a[i + 1];");
10924        assert!(diags.is_empty());
10925        let outer = expr_of_single_stmt(&ast);
10926        let (_, idx) = index_access(outer);
10927        let (op, ..) = binary(ast.try_expr(idx).unwrap());
10928        assert_eq!(op, crate::BinOp::Add);
10929    }
10930
10931    #[test]
10932    fn field_access_missing_name_diagnoses() {
10933        let (_, diags) = parse_str("a.;");
10934        assert!(!diags.is_empty());
10935        assert_eq!(diags[0].message, "expected field name after `.`");
10936    }
10937
10938    #[test]
10939    fn parse_postfix_increment() {
10940        let (ast, diags) = parse_str("x++;");
10941        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10942        let expr = expr_of_single_stmt(&ast);
10943        let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10944            panic!("expected PostfixUnary, got {:?}", expr.kind);
10945        };
10946        assert_eq!(op, crate::PostfixOp::Inc);
10947        assert!(matches!(
10948            ast.try_expr(operand).unwrap().kind,
10949            crate::ExprKind::Identifier(_)
10950        ));
10951        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
10952    }
10953
10954    #[test]
10955    fn parse_postfix_decrement() {
10956        let (ast, diags) = parse_str("x--;");
10957        assert!(diags.is_empty());
10958        let expr = expr_of_single_stmt(&ast);
10959        let crate::ExprKind::PostfixUnary { op, .. } = expr.kind else {
10960            panic!("expected PostfixUnary");
10961        };
10962        assert_eq!(op, crate::PostfixOp::Dec);
10963    }
10964
10965    #[test]
10966    fn parse_postfix_non_null_assertion() {
10967        let (ast, diags) = parse_str("x!;");
10968        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10969        let expr = expr_of_single_stmt(&ast);
10970        let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10971            panic!("expected PostfixUnary, got {:?}", expr.kind);
10972        };
10973        assert_eq!(op, crate::PostfixOp::NonNullAssert);
10974        assert!(matches!(
10975            ast.try_expr(operand).unwrap().kind,
10976            crate::ExprKind::Identifier(_)
10977        ));
10978        assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
10979    }
10980
10981    #[test]
10982    fn parse_non_null_assertion_continues_chain() {
10983        let (ast, diags) = parse_str("x!.y;");
10984        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10985        let expr = expr_of_single_stmt(&ast);
10986        let crate::ExprKind::FieldAccess { receiver, name } = &expr.kind else {
10987            panic!("expected FieldAccess, got {:?}", expr.kind);
10988        };
10989        assert_eq!(name.name, "y");
10990        assert!(matches!(
10991            ast.try_expr(*receiver).unwrap().kind,
10992            crate::ExprKind::PostfixUnary {
10993                op: crate::PostfixOp::NonNullAssert,
10994                ..
10995            }
10996        ));
10997    }
10998
10999    #[test]
11000    fn parse_postfix_on_field_access() {
11001        let (ast, diags) = parse_str("o.f++;");
11002        assert!(diags.is_empty());
11003        let expr = expr_of_single_stmt(&ast);
11004        let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
11005            panic!("expected PostfixUnary");
11006        };
11007        assert!(matches!(
11008            ast.try_expr(operand).unwrap().kind,
11009            crate::ExprKind::FieldAccess { .. }
11010        ));
11011    }
11012
11013    #[test]
11014    fn parse_postfix_on_index_access() {
11015        let (ast, diags) = parse_str("xs[0]++;");
11016        assert!(diags.is_empty());
11017        let expr = expr_of_single_stmt(&ast);
11018        let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
11019            panic!("expected PostfixUnary");
11020        };
11021        assert!(matches!(
11022            ast.try_expr(operand).unwrap().kind,
11023            crate::ExprKind::IndexAccess { .. }
11024        ));
11025    }
11026
11027    #[test]
11028    fn parse_postfix_terminates_chain() {
11029        let (_, diags) = parse_str("x++.y;");
11030        assert!(!diags.is_empty());
11031        assert_eq!(diags[0].message, "expected `;` after expression");
11032    }
11033
11034    #[test]
11035    fn parse_postfix_in_binary_continues() {
11036        let (ast, diags) = parse_str("let y = x++ + 1;");
11037        assert!(diags.is_empty(), "unexpected: {diags:?}");
11038        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11039            panic!("expected Let");
11040        };
11041        let (op, lhs, _) = binary(ast.try_expr(value).unwrap());
11042        assert_eq!(op, crate::BinOp::Add);
11043        assert!(matches!(
11044            ast.try_expr(lhs).unwrap().kind,
11045            crate::ExprKind::PostfixUnary { .. }
11046        ));
11047    }
11048
11049    #[test]
11050    fn parse_index_assignment() {
11051        let (ast, diags) = parse_str("xs[0] = 1;");
11052        assert!(diags.is_empty(), "unexpected: {diags:?}");
11053        let stmt = single_stmt(&ast);
11054        let crate::StmtKind::AssignIndex {
11055            receiver,
11056            index,
11057            value,
11058        } = stmt.kind
11059        else {
11060            panic!("expected AssignIndex, got {:?}", stmt.kind);
11061        };
11062        assert!(matches!(
11063            ast.try_expr(receiver).unwrap().kind,
11064            crate::ExprKind::Identifier(_)
11065        ));
11066        assert_eq!(
11067            ast.try_expr(index).unwrap().kind,
11068            crate::ExprKind::Number(0.0)
11069        );
11070        assert_eq!(
11071            ast.try_expr(value).unwrap().kind,
11072            crate::ExprKind::Number(1.0)
11073        );
11074    }
11075
11076    #[test]
11077    fn parse_for_update_postfix() {
11078        let (ast, diags) = parse_str("function main(): void { for (let i = 0; i < 3; i++) {} }");
11079        assert!(diags.is_empty(), "unexpected: {diags:?}");
11080        assert_eq!(ast.top_level.len(), 1);
11081    }
11082
11083    #[test]
11084    fn parse_for_update_index_assignment() {
11085        let (ast, diags) =
11086            parse_str("function main(): void { for (let i = 0; i < 3; xs[i] = i) {} }");
11087        assert!(diags.is_empty(), "unexpected: {diags:?}");
11088        assert_eq!(ast.top_level.len(), 1);
11089    }
11090
11091    #[test]
11092    fn index_access_missing_close_bracket_diagnoses() {
11093        let (_, diags) = parse_str("a[1;");
11094        assert!(!diags.is_empty());
11095        assert_eq!(diags[0].message, "expected `]`");
11096    }
11097
11098    #[test]
11099    fn field_access_inside_object_literal_value_does_not_steal_dot() {
11100        let (ast, diags) = parse_str("let x = { a: 1 }.foo;");
11101        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11102        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11103            panic!("expected Let");
11104        };
11105        let (recv_id, name) = field_access(ast.try_expr(value).unwrap());
11106        assert_eq!(name, "foo");
11107        assert_eq!(object_literal(ast.try_expr(recv_id).unwrap()).len(), 1);
11108    }
11109
11110    fn type_of_let(stmt: &crate::Stmt) -> &crate::TypeAnnotation {
11111        match stmt.kind {
11112            crate::StmtKind::Let { ref ty, .. } => ty.as_ref().expect("expected type annotation"),
11113            _ => panic!("expected Let"),
11114        }
11115    }
11116
11117    #[test]
11118    fn parse_array_type_annotation() {
11119        let (ast, diags) = parse_str("let xs: number[] = null;");
11120        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11121        let ty = type_of_let(single_stmt(&ast));
11122        match ty.kind {
11123            crate::TypeAnnotationKind::Array(ref inner) => {
11124                assert!(matches!(inner.kind, crate::TypeAnnotationKind::Name { .. }));
11125            }
11126            _ => panic!("expected Array, got {:?}", ty.kind),
11127        }
11128    }
11129
11130    #[test]
11131    fn parse_nested_array_type_annotation() {
11132        let (ast, diags) = parse_str("let xs: number[][] = null;");
11133        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11134        let ty = type_of_let(single_stmt(&ast));
11135        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11136            panic!("expected outer Array");
11137        };
11138        let crate::TypeAnnotationKind::Array(ref inner2) = inner.kind else {
11139            panic!("expected inner Array");
11140        };
11141        assert!(matches!(
11142            inner2.kind,
11143            crate::TypeAnnotationKind::Name { .. }
11144        ));
11145    }
11146
11147    #[test]
11148    fn parse_object_type_annotation_simple() {
11149        let (ast, diags) = parse_str("let p: { x: number; y: number } = null;");
11150        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11151        let ty = type_of_let(single_stmt(&ast));
11152        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11153            panic!("expected Object, got {:?}", ty.kind);
11154        };
11155        assert_eq!(fields.len(), 2);
11156        assert_eq!(fields[0].name.name, "x");
11157        assert_eq!(fields[1].name.name, "y");
11158    }
11159
11160    #[test]
11161    fn readonly_is_rejected_on_methods_accessors_and_method_signatures() {
11162        let source = "class K {\n\
11163             readonly m(): number { return 1; }\n\
11164             public readonly n<T>(): number { return 1; }\n\
11165             readonly get g(): number { return 1; }\n\
11166             readonly p: number = 1;\n\
11167             }\n\
11168             type T = { readonly r(a: number): boolean; readonly f: () => void };\n\
11169             interface I { readonly s(): void; readonly q: number; }\n";
11170        let (_, diags) = parse_str(source);
11171        let lines: Vec<usize> = diags
11172            .iter()
11173            .map(|d| {
11174                assert_eq!(
11175                    d.message,
11176                    "`readonly` can only modify a property or index signature"
11177                );
11178                source[..d.span.start as usize].matches('\n').count() + 1
11179            })
11180            .collect();
11181        assert_eq!(lines, vec![2, 3, 4, 7, 8]);
11182    }
11183
11184    #[test]
11185    fn parse_object_type_method_members() {
11186        // `m(): T` is the same member as `m: () => T`, so it parses to a function-typed
11187        // field — including through the `?` modifier.
11188        let (ast, diags) =
11189            parse_str("let p: { m(): number; opt?(): string; r(a: number): boolean } = null;");
11190        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11191        let ty = type_of_let(single_stmt(&ast));
11192        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11193            panic!("expected Object, got {:?}", ty.kind);
11194        };
11195        assert_eq!(fields.len(), 3);
11196        for field in fields {
11197            assert!(
11198                matches!(field.ty.kind, crate::TypeAnnotationKind::Function { .. }),
11199                "member `{}` should be function-typed, got {:?}",
11200                field.name.name,
11201                field.ty.kind,
11202            );
11203        }
11204        assert!(fields[1].optional, "`opt?()` is an optional member");
11205        let crate::TypeAnnotationKind::Function { ref params, .. } = fields[2].ty.kind else {
11206            unreachable!("checked above");
11207        };
11208        assert_eq!(params.len(), 1);
11209        assert_eq!(params[0].name.name, "a");
11210    }
11211
11212    #[test]
11213    fn parse_object_type_annotation_with_commas() {
11214        let (ast, diags) = parse_str("let p: { x: number, y: number } = null;");
11215        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11216        let ty = type_of_let(single_stmt(&ast));
11217        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11218            panic!("expected Object");
11219        };
11220        assert_eq!(fields.len(), 2);
11221    }
11222
11223    #[test]
11224    fn parse_object_type_annotation_empty() {
11225        let (ast, diags) = parse_str("let p: {} = null;");
11226        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11227        let ty = type_of_let(single_stmt(&ast));
11228        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11229            panic!("expected Object");
11230        };
11231        assert!(fields.is_empty());
11232    }
11233
11234    #[test]
11235    fn parse_object_type_with_optional_field() {
11236        let (ast, diags) = parse_str("let p: { id: number; nick?: string } = null;");
11237        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11238        let ty = type_of_let(single_stmt(&ast));
11239        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11240            panic!("expected Object");
11241        };
11242        assert_eq!(fields.len(), 2);
11243        assert_eq!(fields[0].name.name, "id");
11244        assert!(!fields[0].optional);
11245        assert_eq!(fields[1].name.name, "nick");
11246        assert!(fields[1].optional);
11247    }
11248
11249    #[test]
11250    fn parse_object_type_with_readonly_fields() {
11251        let (ast, diags) = parse_str(
11252            "let issue: { readonly id: string; readonly title?: string; body: string } = null;",
11253        );
11254        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11255        let ty = type_of_let(single_stmt(&ast));
11256        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11257            panic!("expected Object");
11258        };
11259        let fields: Vec<(&str, bool, bool)> = fields
11260            .iter()
11261            .map(|field| (field.name.name.as_str(), field.optional, field.readonly))
11262            .collect();
11263        assert_eq!(
11264            fields,
11265            vec![
11266                ("id", false, true),
11267                ("title", true, true),
11268                ("body", false, false),
11269            ]
11270        );
11271    }
11272
11273    #[test]
11274    fn parse_object_type_mixed_optional_and_required() {
11275        let (ast, diags) = parse_str("let p: { a?: number; b: string; c?: boolean } = null;");
11276        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11277        let ty = type_of_let(single_stmt(&ast));
11278        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11279            panic!("expected Object");
11280        };
11281        let opt: Vec<(&str, bool)> = fields
11282            .iter()
11283            .map(|f| (f.name.name.as_str(), f.optional))
11284            .collect();
11285        assert_eq!(opt, vec![("a", true), ("b", false), ("c", true)]);
11286    }
11287
11288    #[test]
11289    fn parse_object_type_keyword_fields() {
11290        let (ast, diags) =
11291            parse_str("let p: { type: string; default?: number; null: boolean } = null;");
11292        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11293        let ty = type_of_let(single_stmt(&ast));
11294        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11295            panic!("expected Object");
11296        };
11297        let opt: Vec<(&str, bool)> = fields
11298            .iter()
11299            .map(|f| (f.name.name.as_str(), f.optional))
11300            .collect();
11301        assert_eq!(
11302            opt,
11303            vec![("type", false), ("default", true), ("null", false)]
11304        );
11305    }
11306
11307    #[test]
11308    fn parse_object_type_quoted_fields() {
11309        let (ast, diags) =
11310            parse_str(r#"let p: { "content-type": string; "x\u002drequest-id"?: string } = null;"#);
11311        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11312        let ty = type_of_let(single_stmt(&ast));
11313        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11314            panic!("expected Object");
11315        };
11316        let opt: Vec<(&str, bool)> = fields
11317            .iter()
11318            .map(|f| (f.name.name.as_str(), f.optional))
11319            .collect();
11320        assert_eq!(opt, vec![("content-type", false), ("x-request-id", true)]);
11321    }
11322
11323    #[test]
11324    fn parse_interface_with_optional_property() {
11325        let (ast, diags) = parse_str("interface User { id: number; deletedAt?: string; }");
11326        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11327        let stmt = single_stmt(&ast);
11328        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11329            panic!("expected interface decl");
11330        };
11331        let opt: Vec<(&str, bool)> = members
11332            .iter()
11333            .map(|m| match m {
11334                crate::InterfaceMember::Property { name, optional, .. } => {
11335                    (name.name.as_str(), *optional)
11336                }
11337                _ => panic!("expected property"),
11338            })
11339            .collect();
11340        assert_eq!(opt, vec![("id", false), ("deletedAt", true)]);
11341    }
11342
11343    #[test]
11344    fn parse_interface_with_readonly_properties() {
11345        let (ast, diags) = parse_str(
11346            "interface Issue { readonly id: string; readonly title?: string; body: string; }",
11347        );
11348        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11349        let stmt = single_stmt(&ast);
11350        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11351            panic!("expected interface decl");
11352        };
11353        let opt: Vec<(&str, bool, bool)> = members
11354            .iter()
11355            .map(|member| match member {
11356                crate::InterfaceMember::Property {
11357                    name,
11358                    optional,
11359                    readonly,
11360                    ..
11361                } => (name.name.as_str(), *optional, *readonly),
11362                _ => panic!("expected property"),
11363            })
11364            .collect();
11365        assert_eq!(
11366            opt,
11367            vec![
11368                ("id", false, true),
11369                ("title", true, true),
11370                ("body", false, false),
11371            ]
11372        );
11373    }
11374
11375    #[test]
11376    fn parse_readonly_property_name_without_modifier() {
11377        let (ast, diags) = parse_str(
11378            "interface Meta { readonly: boolean; }\nlet meta: { readonly: boolean } = null;",
11379        );
11380        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11381        let crate::StmtKind::InterfaceDecl { ref members, .. } =
11382            ast.try_stmt(ast.top_level[0]).unwrap().kind
11383        else {
11384            panic!("expected interface decl");
11385        };
11386        let crate::InterfaceMember::Property {
11387            ref name, readonly, ..
11388        } = members[0]
11389        else {
11390            panic!("expected property");
11391        };
11392        assert_eq!(name.name, "readonly");
11393        assert!(
11394            !readonly,
11395            "`readonly` here is the property name, not a modifier"
11396        );
11397
11398        let ty = type_of_let(ast.try_stmt(ast.top_level[1]).unwrap());
11399        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11400            panic!("expected Object");
11401        };
11402        assert_eq!(fields[0].name.name, "readonly");
11403        assert!(!fields[0].readonly);
11404    }
11405
11406    #[test]
11407    fn parse_interface_keyword_members() {
11408        let (ast, diags) =
11409            parse_str("interface IssueStatus { type: string; default(): string; null: boolean; }");
11410        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11411        let stmt = single_stmt(&ast);
11412        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11413            panic!("expected interface decl");
11414        };
11415        let names: Vec<&str> = members
11416            .iter()
11417            .map(|m| match m {
11418                crate::InterfaceMember::Method { name, .. }
11419                | crate::InterfaceMember::Property { name, .. } => name.name.as_str(),
11420                crate::InterfaceMember::IndexSignature(_) => panic!("unexpected index signature"),
11421            })
11422            .collect();
11423        assert_eq!(names, vec!["type", "default", "null"]);
11424    }
11425
11426    #[test]
11427    fn parse_interface_quoted_property_members() {
11428        let (ast, diags) = parse_str(
11429            r#"interface Headers { "content-type": string; "x\u002drequest-id"?: string; }"#,
11430        );
11431        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11432        let stmt = single_stmt(&ast);
11433        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11434            panic!("expected interface decl");
11435        };
11436        let names: Vec<(&str, bool)> = members
11437            .iter()
11438            .map(|m| match m {
11439                crate::InterfaceMember::Property { name, optional, .. } => {
11440                    (name.name.as_str(), *optional)
11441                }
11442                _ => panic!("expected property"),
11443            })
11444            .collect();
11445        assert_eq!(names, vec![("content-type", false), ("x-request-id", true)]);
11446    }
11447
11448    #[test]
11449    fn parse_param_optional_marker() {
11450        let (ast, diags) = parse_str("function f(x?: number): void {}");
11451        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11452        let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
11453            panic!("expected function")
11454        };
11455        assert!(params[0].optional);
11456        assert!(params[0].default.is_none());
11457    }
11458
11459    #[test]
11460    fn parse_interface_optional_method() {
11461        let (ast, diags) = parse_str("interface F { foo?(x?: number): void; }");
11462        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11463        let crate::StmtKind::InterfaceDecl { ref members, .. } = single_stmt(&ast).kind else {
11464            panic!("expected interface")
11465        };
11466        let crate::InterfaceMember::Property { optional, ty, .. } = &members[0] else {
11467            panic!("expected callable property")
11468        };
11469        assert!(*optional);
11470        let crate::TypeAnnotationKind::Function { params, .. } = &ty.kind else {
11471            panic!("expected function type")
11472        };
11473        assert!(params[0].optional);
11474    }
11475
11476    #[test]
11477    fn parse_interface_with_call_signature() {
11478        let (ast, diags) = parse_str("interface F { (s: string): number; }");
11479        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11480        let stmt = single_stmt(&ast);
11481        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11482            panic!("expected interface decl");
11483        };
11484        assert_eq!(members.len(), 1);
11485        match &members[0] {
11486            crate::InterfaceMember::Method { name, params, .. } => {
11487                assert_eq!(name.name, "@call");
11488                assert_eq!(params.len(), 1);
11489                assert_eq!(params[0].name.name, "s");
11490            }
11491            other => panic!("expected method (call signature), got {other:?}"),
11492        }
11493    }
11494
11495    #[test]
11496    fn parse_interface_mixed_call_signature_and_method() {
11497        let (ast, diags) = parse_str("interface F { greet(): string; (x: number): number; }");
11498        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11499        let stmt = single_stmt(&ast);
11500        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11501            panic!("expected interface decl");
11502        };
11503        let names: Vec<&str> = members
11504            .iter()
11505            .map(|m| match m {
11506                crate::InterfaceMember::Method { name, .. } => name.name.as_str(),
11507                _ => "<property>",
11508            })
11509            .collect();
11510        assert_eq!(names, vec!["greet", "@call"]);
11511    }
11512
11513    #[test]
11514    fn parse_interface_rejects_generic_call_signature() {
11515        let (_ast, diags) = parse_str("interface F { <T>(x: T): T; }");
11516        assert!(
11517            diags
11518                .iter()
11519                .any(|d| d.message.contains("generic call signatures")),
11520            "expected generic-call-signature diagnostic, got {diags:?}"
11521        );
11522    }
11523
11524    #[test]
11525    fn parse_object_type_with_array_field() {
11526        let (ast, diags) = parse_str("let p: { xs: number[] } = null;");
11527        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11528        let ty = type_of_let(single_stmt(&ast));
11529        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11530            panic!("expected Object");
11531        };
11532        assert_eq!(fields[0].name.name, "xs");
11533        assert!(matches!(
11534            fields[0].ty.kind,
11535            crate::TypeAnnotationKind::Array(_)
11536        ));
11537    }
11538
11539    #[test]
11540    fn parse_array_of_objects() {
11541        let (ast, diags) = parse_str("let xs: { x: number }[] = null;");
11542        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11543        let ty = type_of_let(single_stmt(&ast));
11544        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11545            panic!("expected Array");
11546        };
11547        assert!(matches!(
11548            inner.kind,
11549            crate::TypeAnnotationKind::Object { .. }
11550        ));
11551    }
11552
11553    #[test]
11554    fn parse_tuple_type_annotation() {
11555        let (ast, diags) = parse_str("let p: [string, number] = null;");
11556        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11557        let ty = type_of_let(single_stmt(&ast));
11558        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11559            panic!("expected Tuple, got {:?}", ty.kind);
11560        };
11561        assert_eq!(elems.len(), 2);
11562        assert!(matches!(
11563            elems[0].kind,
11564            crate::TypeAnnotationKind::Name { .. }
11565        ));
11566        assert!(matches!(
11567            elems[1].kind,
11568            crate::TypeAnnotationKind::Name { .. }
11569        ));
11570    }
11571
11572    #[test]
11573    fn parse_singleton_tuple_type() {
11574        let (ast, diags) = parse_str("let p: [number] = null;");
11575        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11576        let ty = type_of_let(single_stmt(&ast));
11577        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11578            panic!("expected Tuple");
11579        };
11580        assert_eq!(elems.len(), 1);
11581    }
11582
11583    #[test]
11584    fn parse_nested_tuple_type() {
11585        let (ast, diags) = parse_str("let p: [[number, string], boolean] = null;");
11586        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11587        let ty = type_of_let(single_stmt(&ast));
11588        let crate::TypeAnnotationKind::Tuple(ref outer) = ty.kind else {
11589            panic!("expected outer Tuple");
11590        };
11591        assert_eq!(outer.len(), 2);
11592        let crate::TypeAnnotationKind::Tuple(ref inner) = outer[0].kind else {
11593            panic!("expected inner Tuple");
11594        };
11595        assert_eq!(inner.len(), 2);
11596    }
11597
11598    #[test]
11599    fn parse_array_of_tuple_type() {
11600        let (ast, diags) = parse_str("let xs: [string, number][] = null;");
11601        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11602        let ty = type_of_let(single_stmt(&ast));
11603        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11604            panic!("expected outer Array, got {:?}", ty.kind);
11605        };
11606        assert!(matches!(inner.kind, crate::TypeAnnotationKind::Tuple(_)));
11607    }
11608
11609    #[test]
11610    fn parse_trailing_comma_tuple_type() {
11611        let (ast, diags) = parse_str("let p: [number, string,] = null;");
11612        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11613        let ty = type_of_let(single_stmt(&ast));
11614        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11615            panic!("expected Tuple");
11616        };
11617        assert_eq!(elems.len(), 2);
11618    }
11619
11620    #[test]
11621    fn parse_empty_tuple_type_rejected() {
11622        let (_ast, diags) = parse_str("let p: [] = null;");
11623        assert!(
11624            diags.iter().any(|d| d
11625                .message
11626                .contains("tuple types must have at least one element")),
11627            "expected empty-tuple diagnostic, got: {diags:?}"
11628        );
11629    }
11630
11631    #[test]
11632    fn parse_labeled_tuple_drops_labels() {
11633        let (ast, diags) = parse_str("let p: [first: number, string] = null;");
11634        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11635        let ty = type_of_let(single_stmt(&ast));
11636        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11637            panic!("expected Tuple, got {:?}", ty.kind);
11638        };
11639        assert_eq!(elems.len(), 2);
11640        assert!(
11641            elems
11642                .iter()
11643                .all(|e| matches!(e.kind, crate::TypeAnnotationKind::Name { .. }))
11644        );
11645    }
11646
11647    #[test]
11648    fn parse_labeled_optional_tuple_element() {
11649        let (ast, diags) = parse_str("let p: [first: number, second?: string] = null;");
11650        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11651        let ty = type_of_let(single_stmt(&ast));
11652        let crate::TypeAnnotationKind::Tuple(elements) = &ty.kind else {
11653            panic!("expected tuple")
11654        };
11655        assert!(matches!(
11656            elements[1].kind,
11657            crate::TypeAnnotationKind::Optional(_)
11658        ));
11659    }
11660
11661    #[test]
11662    fn parse_readonly_binds_to_the_postfix_type() {
11663        let (ast, diags) = parse_str("let p: readonly number[][] | null = null;");
11664        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11665        let ty = type_of_let(single_stmt(&ast));
11666        let crate::TypeAnnotationKind::Union(ref members) = ty.kind else {
11667            panic!("expected Union, got {:?}", ty.kind);
11668        };
11669        let crate::TypeAnnotationKind::Readonly(ref operand) = members[0].kind else {
11670            panic!("expected Readonly, got {:?}", members[0].kind);
11671        };
11672        let crate::TypeAnnotationKind::Array(ref inner) = operand.kind else {
11673            panic!("expected Array, got {:?}", operand.kind);
11674        };
11675        assert!(matches!(inner.kind, crate::TypeAnnotationKind::Array(_)));
11676    }
11677
11678    #[test]
11679    fn parse_readonly_as_a_type_name_is_not_the_operator() {
11680        let (ast, diags) = parse_str("let p: readonly = null;");
11681        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11682        let ty = type_of_let(single_stmt(&ast));
11683        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11684    }
11685
11686    #[test]
11687    fn parse_generic_one_arg() {
11688        let (ast, diags) = parse_str("let x: Foo<number> = null;");
11689        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11690        let ty = type_of_let(single_stmt(&ast));
11691        let crate::TypeAnnotationKind::Name { ref name, ref args } = ty.kind else {
11692            panic!("expected Name, got {:?}", ty.kind);
11693        };
11694        assert_eq!(name.span, crate::Span::new(F, 7, 10).unwrap());
11695        assert_eq!(ty.span, crate::Span::new(F, 7, 18).unwrap());
11696        assert_eq!(args.len(), 1);
11697        assert!(matches!(
11698            args[0].kind,
11699            crate::TypeAnnotationKind::Name { .. }
11700        ));
11701    }
11702
11703    #[test]
11704    fn parse_generic_two_args() {
11705        let (ast, diags) = parse_str("let m: Map<string, number> = null;");
11706        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11707        let ty = type_of_let(single_stmt(&ast));
11708        let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11709            panic!("expected Name");
11710        };
11711        assert_eq!(args.len(), 2);
11712    }
11713
11714    #[test]
11715    fn parse_generic_nested() {
11716        let (ast, diags) = parse_str("let b: Box<Box<T>> = null;");
11717        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11718        let ty = type_of_let(single_stmt(&ast));
11719        let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11720            panic!("expected outer Name");
11721        };
11722        assert_eq!(args.len(), 1);
11723        let crate::TypeAnnotationKind::Name {
11724            args: ref inner, ..
11725        } = args[0].kind
11726        else {
11727            panic!("expected inner Name");
11728        };
11729        assert_eq!(inner.len(), 1);
11730    }
11731
11732    #[test]
11733    fn parse_generic_array_postfix() {
11734        let (ast, diags) = parse_str("let xs: Foo<number>[] = null;");
11735        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11736        let ty = type_of_let(single_stmt(&ast));
11737        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11738            panic!("expected Array");
11739        };
11740        assert!(
11741            matches!(inner.kind, crate::TypeAnnotationKind::Name { ref args, .. } if args.len() == 1)
11742        );
11743    }
11744
11745    #[test]
11746    fn parse_generic_empty_diagnoses() {
11747        let (_ast, diags) = parse_str("let x: Foo<> = null;");
11748        assert!(
11749            diags
11750                .iter()
11751                .any(|d| d.message.contains("empty generic argument list")),
11752            "expected empty-generic-args diagnostic, got: {diags:?}"
11753        );
11754    }
11755
11756    #[test]
11757    fn parse_generic_trailing_comma_ok() {
11758        let (_ast, diags) = parse_str("let x: Foo<number,> = null;");
11759        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11760    }
11761
11762    #[test]
11763    fn parse_generic_missing_close_diagnoses() {
11764        let (_ast, diags) = parse_str("let x: Foo<number = null;");
11765        assert!(
11766            diags
11767                .iter()
11768                .any(|d| d.message.contains("expected `,` or `>`")),
11769            "expected unterminated-generic diagnostic, got: {diags:?}"
11770        );
11771    }
11772
11773    #[test]
11774    fn parse_qualified_two_segments() {
11775        let (ast, diags) = parse_str("let x: Foo.Bar = null;");
11776        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11777        let ty = type_of_let(single_stmt(&ast));
11778        let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11779            panic!("expected Qualified, got {:?}", ty.kind);
11780        };
11781        assert_eq!(path.len(), 2);
11782        // `Foo` at 7..10, `Bar` at 11..14.
11783        assert_eq!(path[0].span, crate::Span::new(F, 7, 10).unwrap());
11784        assert_eq!(path[1].span, crate::Span::new(F, 11, 14).unwrap());
11785        assert!(args.is_empty());
11786        assert_eq!(ty.span, crate::Span::new(F, 7, 14).unwrap());
11787    }
11788
11789    #[test]
11790    fn parse_qualified_three_segments() {
11791        let (ast, diags) = parse_str("let x: Foo.Bar.Baz = null;");
11792        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11793        let ty = type_of_let(single_stmt(&ast));
11794        let crate::TypeAnnotationKind::Qualified { ref path, .. } = ty.kind else {
11795            panic!("expected Qualified, got {:?}", ty.kind);
11796        };
11797        assert_eq!(path.len(), 3);
11798    }
11799
11800    #[test]
11801    fn parse_qualified_with_generic_args() {
11802        let (ast, diags) = parse_str("let x: Foo.Box<number> = null;");
11803        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11804        let ty = type_of_let(single_stmt(&ast));
11805        let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11806            panic!("expected Qualified, got {:?}", ty.kind);
11807        };
11808        assert_eq!(path.len(), 2);
11809        assert_eq!(args.len(), 1);
11810        assert!(matches!(
11811            args[0].kind,
11812            crate::TypeAnnotationKind::Name { .. }
11813        ));
11814    }
11815
11816    #[test]
11817    fn parse_qualified_with_array_postfix() {
11818        let (ast, diags) = parse_str("let xs: Foo.Bar[] = null;");
11819        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11820        let ty = type_of_let(single_stmt(&ast));
11821        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11822            panic!("expected Array, got {:?}", ty.kind);
11823        };
11824        assert!(matches!(
11825            inner.kind,
11826            crate::TypeAnnotationKind::Qualified { .. }
11827        ));
11828    }
11829
11830    #[test]
11831    fn parse_qualified_trailing_dot_diagnoses() {
11832        let (_ast, diags) = parse_str("let x: Foo. = null;");
11833        assert!(
11834            diags.iter().any(|d| d
11835                .message
11836                .contains("expected identifier after `.` in type name")),
11837            "expected dotted-name diagnostic, got: {diags:?}"
11838        );
11839    }
11840
11841    #[test]
11842    fn parse_qualified_void_root_rejected_dot() {
11843        let (ast, diags) = parse_str("function f(): void.Bar { }");
11844        assert!(!diags.is_empty(), "expected a diagnostic for `void.Bar`");
11845        let _ = ast;
11846    }
11847
11848    fn union_members(ty: &crate::TypeAnnotation) -> &[crate::TypeAnnotation] {
11849        match &ty.kind {
11850            crate::TypeAnnotationKind::Union(members) => members,
11851            other => panic!("expected Union, got {other:?}"),
11852        }
11853    }
11854
11855    #[test]
11856    fn parse_union_two_members() {
11857        let (ast, diags) = parse_str("let x: number | string = null;");
11858        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11859        let ty = type_of_let(single_stmt(&ast));
11860        let members = union_members(ty);
11861        assert_eq!(members.len(), 2);
11862        assert!(matches!(
11863            members[0].kind,
11864            crate::TypeAnnotationKind::Name { .. }
11865        ));
11866        assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); // `number`
11867        assert!(matches!(
11868            members[1].kind,
11869            crate::TypeAnnotationKind::Name { .. }
11870        ));
11871        assert_eq!(members[1].span, crate::Span::new(F, 16, 22).unwrap()); // `string`
11872        assert_eq!(ty.span, crate::Span::new(F, 7, 22).unwrap());
11873    }
11874
11875    #[test]
11876    fn parse_union_three_members() {
11877        let (ast, diags) = parse_str("let x: A | B | C = null;");
11878        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11879        let ty = type_of_let(single_stmt(&ast));
11880        let members = union_members(ty);
11881        assert_eq!(members.len(), 3);
11882        for m in members {
11883            assert!(matches!(m.kind, crate::TypeAnnotationKind::Name { .. }));
11884        }
11885    }
11886
11887    #[test]
11888    fn parse_union_lower_than_array() {
11889        let (ast, diags) = parse_str("let x: number[] | string = null;");
11890        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11891        let ty = type_of_let(single_stmt(&ast));
11892        let members = union_members(ty);
11893        assert_eq!(members.len(), 2);
11894        assert!(
11895            matches!(members[0].kind, crate::TypeAnnotationKind::Array(_)),
11896            "expected first member to be Array, got {:?}",
11897            members[0].kind
11898        );
11899        assert!(matches!(
11900            members[1].kind,
11901            crate::TypeAnnotationKind::Name { .. }
11902        ));
11903    }
11904
11905    #[test]
11906    fn parse_union_with_null_member() {
11907        let (ast, diags) = parse_str("let x: number | null = null;");
11908        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11909        let ty = type_of_let(single_stmt(&ast));
11910        let members = union_members(ty);
11911        assert_eq!(members.len(), 2);
11912        assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); // `number`
11913        assert!(matches!(
11914            members[1].kind,
11915            crate::TypeAnnotationKind::Name { .. }
11916        ));
11917        assert_eq!(members[1].span, crate::Span::new(F, 16, 20).unwrap()); // `null`
11918    }
11919
11920    #[test]
11921    fn parse_union_in_function_return_type() {
11922        let (ast, diags) = parse_str("function f(): number | string { }");
11923        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11924        let stmt = single_stmt(&ast);
11925        match stmt.kind {
11926            crate::StmtKind::Function {
11927                ref return_type, ..
11928            } => {
11929                let return_type = return_type.as_ref().expect("plain return type");
11930                let members = union_members(return_type);
11931                assert_eq!(members.len(), 2);
11932            }
11933            _ => panic!("expected Function"),
11934        }
11935    }
11936
11937    #[test]
11938    fn parse_union_in_function_param_type() {
11939        let (ast, diags) = parse_str("function f(x: number | string): void { }");
11940        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11941        let stmt = single_stmt(&ast);
11942        match stmt.kind {
11943            crate::StmtKind::Function { ref params, .. } => {
11944                let p_ty = params[0].ty.as_ref().expect("param type");
11945                let members = union_members(p_ty);
11946                assert_eq!(members.len(), 2);
11947            }
11948            _ => panic!("expected Function"),
11949        }
11950    }
11951
11952    #[test]
11953    fn parse_union_in_object_field_type() {
11954        let (ast, diags) = parse_str("let p: { x: number | string } = null;");
11955        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11956        let ty = type_of_let(single_stmt(&ast));
11957        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11958            panic!("expected Object, got {:?}", ty.kind);
11959        };
11960        assert_eq!(fields.len(), 1);
11961        let members = union_members(&fields[0].ty);
11962        assert_eq!(members.len(), 2);
11963    }
11964
11965    #[test]
11966    fn parse_union_leading_pipe_is_forgiven() {
11967        let (ast, diags) = parse_str("let x: | number | string = null;");
11968        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11969        let ty = type_of_let(single_stmt(&ast));
11970        let members = union_members(ty);
11971        assert_eq!(members.len(), 2);
11972    }
11973
11974    #[test]
11975    fn parse_union_leading_pipe_single_member_unwraps() {
11976        let (ast, diags) = parse_str("let x: | number = null;");
11977        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11978        let ty = type_of_let(single_stmt(&ast));
11979        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11980    }
11981
11982    #[test]
11983    fn parse_union_trailing_pipe_diagnoses() {
11984        let (_, diags) = parse_str("let x: number | = null;");
11985        assert!(!diags.is_empty());
11986        assert_eq!(diags[0].message, "expected type");
11987    }
11988
11989    #[test]
11990    fn snapshot_union_type_mix() {
11991        let (ast, diags) = parse_str("let xs: number[] | string | null = null;");
11992        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11993        insta::assert_debug_snapshot!(ast);
11994    }
11995
11996    #[test]
11997    fn parse_object_type_duplicate_field_diagnoses() {
11998        let (_, diags) = parse_str("let p: { readonly x: number; x: string } = null;");
11999        assert!(!diags.is_empty());
12000        assert!(
12001            diags
12002                .iter()
12003                .any(|d| d.message == "duplicate field `x` in object type"),
12004            "expected duplicate-field diagnostic, got: {diags:?}"
12005        );
12006    }
12007
12008    #[test]
12009    fn snapshot_object_and_array_literals() {
12010        let (ast, diags) = parse_str(r#"let mix = [{ a: 1, b: "hi" }, {}];"#);
12011        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12012        insta::assert_debug_snapshot!(ast);
12013    }
12014
12015    #[test]
12016    fn snapshot_function_with_while() {
12017        let source = "function loop(n: number): void {\n  while (n > 0) {\n    log(n);\n  }\n}";
12018        let (ast, diags) = parse_str(source);
12019        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12020        insta::assert_debug_snapshot!(ast);
12021    }
12022
12023    fn parse_arrow_const(
12024        source: &str,
12025    ) -> (
12026        Ast,
12027        Vec<crate::ParamDecl>,
12028        Option<crate::TypeAnnotation>,
12029        crate::ArrowBody,
12030    ) {
12031        let (ast, diags) = parse_str(source);
12032        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12033        assert_eq!(ast.top_level.len(), 1);
12034        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12035        let value = match &stmt.kind {
12036            StmtKind::Const { value, .. } => *value,
12037            _ => panic!("expected const declaration"),
12038        };
12039        let value = match ast.try_expr(value).unwrap().kind {
12040            ExprKind::FunctionExpression { function, .. } => function,
12041            _ => value,
12042        };
12043        let expr = ast.try_expr(value).unwrap().clone();
12044        let (params, return_type, body) = match expr.kind {
12045            ExprKind::Arrow {
12046                params,
12047                return_type,
12048                body,
12049                ..
12050            } => (params, return_type, body),
12051            other => panic!("expected Arrow, got {other:?}"),
12052        };
12053        (ast, params, return_type, body)
12054    }
12055
12056    #[test]
12057    fn parses_anonymous_function_expression_as_arrow() {
12058        let (ast, params, return_type, body) =
12059            parse_arrow_const("const f = function (x: number): number { return x + 1; };");
12060        assert_eq!(params.len(), 1);
12061        assert_eq!(params[0].name.name, "x");
12062        assert!(return_type.is_some());
12063        assert!(matches!(body, crate::ArrowBody::Block(_)));
12064        let _ = ast;
12065    }
12066
12067    #[test]
12068    fn parses_function_expression_with_no_params() {
12069        let (_ast, params, _return_type, body) =
12070            parse_arrow_const("const f = function (): void {};");
12071        assert!(params.is_empty());
12072        assert!(matches!(body, crate::ArrowBody::Block(_)));
12073    }
12074
12075    /// The named wrapper preserves its body signature.
12076    #[test]
12077    fn parses_named_function_expression_signature() {
12078        let (_ast, params, _return_type, _body) =
12079            parse_arrow_const("const f = function named(x: number): number { return x + 1; };");
12080        assert_eq!(params.len(), 1);
12081    }
12082
12083    /// Self references are resolved in the function expression scope.
12084    #[test]
12085    fn accepts_a_named_function_expression_that_calls_itself() {
12086        let (_ast, diags) =
12087            parse_str("const f = function bar(x: number): number { return bar(x); };");
12088        assert!(diags.is_empty(), "{diags:?}");
12089    }
12090
12091    /// A member name after `.` is never a reference to the function itself.
12092    #[test]
12093    fn accepts_a_named_function_expression_using_the_name_as_a_property() {
12094        let (_ast, diags) = parse_str("const f = function bar(o: O): number { return o.bar; };");
12095        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12096    }
12097
12098    /// Nor is a key before `:`.
12099    #[test]
12100    fn accepts_a_named_function_expression_using_the_name_as_an_object_key() {
12101        let (_ast, diags) = parse_str(
12102            "const f = function bar(x: number): number { const o = { bar: 1 }; return o.bar + x; };",
12103        );
12104        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12105    }
12106
12107    /// A string that happens to spell the name is not a reference.
12108    #[test]
12109    fn accepts_a_named_function_expression_with_its_name_in_a_string() {
12110        let (_ast, diags) =
12111            parse_str("const f = function bar(x: number): string { return \"bar\"; };");
12112        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12113    }
12114
12115    /// A self reference in a template substitution remains a normal expression.
12116    #[test]
12117    fn accepts_a_self_call_inside_a_template_substitution() {
12118        let (_ast, diags) =
12119            parse_str("const f = function bar(x: number): string { return `${bar(0)}`; };");
12120        assert!(diags.is_empty(), "{diags:?}");
12121    }
12122
12123    #[test]
12124    fn rejects_a_generic_function_expression() {
12125        let (_ast, diags) = parse_str("const f = function <T>(x: T): T { return x; };");
12126        assert!(
12127            diags
12128                .iter()
12129                .any(|d| d.message.contains("generic function expressions")),
12130            "generic function expression should be rejected: {diags:?}"
12131        );
12132    }
12133
12134    #[test]
12135    fn accepts_this_inside_a_function_expression_in_a_class_method() {
12136        let (_ast, diags) = parse_str(
12137            "class C { x: number = 1; m(): number { \
12138             const f = function (this: { x: number }): number { return this.x; }; return f(); } }",
12139        );
12140        assert!(diags.is_empty(), "{diags:?}");
12141    }
12142
12143    #[test]
12144    fn retains_this_boundary_inside_method_shorthand() {
12145        let (ast, diags) = parse_str(
12146            "class C { x: number = 1; m(): number { \
12147             const o = { x: 2, g(): number { return this.x; } }; return o.g(); } }",
12148        );
12149        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12150        assert!((0..ast.exprs_len()).any(|index| matches!(
12151            ast.try_expr(crate::ExprId(index as u32)).unwrap().kind,
12152            ExprKind::ThisOutsideReceiver
12153        )));
12154    }
12155
12156    #[test]
12157    fn this_still_parses_in_an_ordinary_class_method() {
12158        let (_ast, diags) = parse_str("class C { x: number = 1; m(): number { return this.x; } }");
12159        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12160    }
12161
12162    /// `function` is a valid property name; the expression form must not shadow that.
12163    #[test]
12164    fn function_keyword_still_parses_as_an_object_key() {
12165        let (_ast, diags) = parse_str("let o = { function: 1 };");
12166        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12167    }
12168
12169    fn object_literal_member_values(source: &str) -> (Ast, Vec<crate::ExprId>) {
12170        let (ast, diags) = parse_str(source);
12171        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12172        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12173        let value = match &stmt.kind {
12174            StmtKind::Const { value, .. } => *value,
12175            other => panic!("expected a const declaration, got {other:?}"),
12176        };
12177        let members = match &ast.try_expr(value).unwrap().kind {
12178            ExprKind::ObjectLiteral { members } => members
12179                .iter()
12180                .map(crate::ObjectLiteralMember::value)
12181                .collect::<Vec<_>>(),
12182            other => panic!("expected ObjectLiteral, got {other:?}"),
12183        };
12184        (ast, members)
12185    }
12186
12187    #[test]
12188    fn parses_object_literal_method_shorthand_as_arrow() {
12189        let (ast, members) =
12190            object_literal_member_values("const o = { m(x: number): number { return x; } };");
12191        assert_eq!(members.len(), 1);
12192        match &ast.try_expr(members[0]).unwrap().kind {
12193            ExprKind::Arrow { params, body, .. } => {
12194                assert_eq!(params.len(), 1);
12195                assert_eq!(params[0].name.name, "x");
12196                assert!(matches!(body, crate::ArrowBody::Block(_)));
12197            }
12198            other => panic!("expected Arrow, got {other:?}"),
12199        }
12200    }
12201
12202    /// A keyword is a valid method name, just as it is a valid property name.
12203    #[test]
12204    fn parses_method_shorthand_with_a_keyword_name() {
12205        let (ast, members) =
12206            object_literal_member_values("const o = { if(x: number): number { return x; } };");
12207        assert!(matches!(
12208            ast.try_expr(members[0]).unwrap().kind,
12209            ExprKind::Arrow { .. }
12210        ));
12211    }
12212
12213    #[test]
12214    fn parses_method_shorthand_alongside_plain_and_shorthand_properties() {
12215        let (ast, members) =
12216            object_literal_member_values("const o = { a: 1, m(): void {}, b: 2 };");
12217        assert_eq!(members.len(), 3);
12218        assert!(matches!(
12219            ast.try_expr(members[1]).unwrap().kind,
12220            ExprKind::Arrow { .. }
12221        ));
12222    }
12223
12224    #[test]
12225    fn parses_bare_ident_arrow_expression_body() {
12226        let (_ast, params, return_type, body) = parse_arrow_const("const f = x => x + 1;");
12227        assert_eq!(params.len(), 1);
12228        assert_eq!(params[0].name.name, "x");
12229        assert!(params[0].ty.is_none());
12230        assert!(return_type.is_none());
12231        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12232    }
12233
12234    #[test]
12235    fn parses_zero_param_arrow() {
12236        let (_ast, params, return_type, body) = parse_arrow_const("const f = () => 0;");
12237        assert!(params.is_empty());
12238        assert!(return_type.is_none());
12239        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12240    }
12241
12242    #[test]
12243    fn parses_paren_unannotated_arrow() {
12244        let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x) => x;");
12245        assert_eq!(params.len(), 1);
12246        assert_eq!(params[0].name.name, "x");
12247        assert!(params[0].ty.is_none());
12248    }
12249
12250    #[test]
12251    fn parses_typed_param_arrow() {
12252        let (_ast, params, return_type, body) =
12253            parse_arrow_const("const f = (x: number) => x * 2;");
12254        assert_eq!(params.len(), 1);
12255        assert!(params[0].ty.is_some());
12256        assert!(return_type.is_none());
12257        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12258    }
12259
12260    #[test]
12261    fn parses_typed_param_and_return_arrow() {
12262        let (_ast, params, return_type, body) =
12263            parse_arrow_const("const f = (x: number): number => x * 2;");
12264        assert_eq!(params.len(), 1);
12265        assert!(params[0].ty.is_some());
12266        assert!(return_type.is_some());
12267        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12268    }
12269
12270    #[test]
12271    fn parses_multi_param_unannotated_arrow() {
12272        let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x, y) => x + y;");
12273        assert_eq!(params.len(), 2);
12274        assert_eq!(params[0].name.name, "x");
12275        assert_eq!(params[1].name.name, "y");
12276        assert!(params[0].ty.is_none());
12277        assert!(params[1].ty.is_none());
12278    }
12279
12280    #[test]
12281    fn parses_multi_param_typed_arrow() {
12282        let (_ast, params, _return_type, _body) =
12283            parse_arrow_const("const f = (x: number, y: number) => x + y;");
12284        assert_eq!(params.len(), 2);
12285        assert!(params[0].ty.is_some());
12286        assert!(params[1].ty.is_some());
12287    }
12288
12289    #[test]
12290    fn parses_block_body_arrow() {
12291        let (_ast, params, _return_type, body) =
12292            parse_arrow_const("const f = (x: number) => { return x; };");
12293        assert_eq!(params.len(), 1);
12294        assert!(matches!(body, crate::ArrowBody::Block(_)));
12295    }
12296
12297    #[test]
12298    fn rejects_typed_bare_ident_arrow() {
12299        let (_ast, diags) = parse_str("const f = x: number => x;");
12300        assert!(
12301            !diags.is_empty(),
12302            "expected diagnostics for `x: number => …`",
12303        );
12304    }
12305
12306    #[test]
12307    fn arrow_inside_call_argument_parses() {
12308        let (ast, diags) = parse_str("f((x: number) => x + 1, 2);");
12309        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12310        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12311        let call = match &stmt.kind {
12312            StmtKind::Expr(eid) => ast.try_expr(*eid).unwrap(),
12313            _ => panic!("expected expression statement"),
12314        };
12315        let args = match &call.kind {
12316            ExprKind::Call { args, .. } => args.clone(),
12317            other => panic!("expected Call, got {other:?}"),
12318        };
12319        assert_eq!(args.len(), 2);
12320        assert!(matches!(
12321            ast.try_expr(args[0]).unwrap().kind,
12322            ExprKind::Arrow { .. }
12323        ));
12324    }
12325
12326    #[test]
12327    fn function_decl_param_still_requires_annotation() {
12328        let (_ast, diags) = parse_str("function f(x): number { return x; }");
12329        assert!(
12330            diags.iter().any(|d| d.message.contains("type annotation")),
12331            "expected `parameter requires a type annotation` diagnostic, got {diags:?}",
12332        );
12333    }
12334
12335    #[test]
12336    fn paren_expression_still_parses_when_no_arrow() {
12337        let (ast, diags) = parse_str("const a = (1 + 2);");
12338        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12339        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12340        let value = match &stmt.kind {
12341            StmtKind::Const { value, .. } => *value,
12342            _ => panic!("expected const declaration"),
12343        };
12344        assert!(matches!(
12345            ast.try_expr(value).unwrap().kind,
12346            ExprKind::Paren(_)
12347        ));
12348    }
12349
12350    #[test]
12351    fn parse_named_import_single() {
12352        let (ast, diags) = parse_str("import { v4 } from \"submilli:uuid\";");
12353        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12354        assert_eq!(ast.top_level.len(), 1);
12355        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12356        let (module, kind) = match &stmt.kind {
12357            StmtKind::Import { module, kind, .. } => (module, kind),
12358            other => panic!("expected Import, got {other:?}"),
12359        };
12360        assert_eq!(module, "submilli:uuid");
12361        let specs = match kind {
12362            ImportKind::Named(specs) => specs,
12363            other => panic!("expected Named, got {other:?}"),
12364        };
12365        assert_eq!(specs.len(), 1);
12366        assert_eq!(specs[0].imported_name.name, "v4");
12367        assert_eq!(specs[0].local_name.name, "v4");
12368    }
12369
12370    #[test]
12371    fn parse_named_import_multi_and_alias() {
12372        let (ast, diags) =
12373            parse_str("import { v4, v7 as makeId, validate } from \"submilli:uuid\";");
12374        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12375        let specs = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12376            StmtKind::Import {
12377                kind: ImportKind::Named(s),
12378                ..
12379            } => s.clone(),
12380            other => panic!("expected named import, got {other:?}"),
12381        };
12382        assert_eq!(specs.len(), 3);
12383        assert_eq!(specs[0].imported_name.name, "v4");
12384        assert_eq!(specs[0].local_name.name, "v4");
12385        assert_eq!(specs[1].imported_name.name, "v7");
12386        assert_eq!(specs[1].local_name.name, "makeId");
12387        assert_eq!(specs[2].imported_name.name, "validate");
12388        assert_eq!(specs[2].local_name.name, "validate");
12389    }
12390
12391    #[test]
12392    fn parse_namespace_import() {
12393        let (ast, diags) = parse_str("import uuid from \"submilli:uuid\";");
12394        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12395        let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12396            StmtKind::Import { kind, .. } => kind.clone(),
12397            other => panic!("expected import, got {other:?}"),
12398        };
12399        let local = match kind {
12400            ImportKind::Namespace { local_name } => local_name,
12401            other => panic!("expected Namespace, got {other:?}"),
12402        };
12403        assert_eq!(local.name, "uuid");
12404    }
12405
12406    #[test]
12407    fn parse_wildcard_namespace_import_synonym() {
12408        let (ast, diags) = parse_str("import * as uuid from \"submilli:uuid\";");
12409        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12410        let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12411            StmtKind::Import { kind, .. } => kind.clone(),
12412            other => panic!("expected import, got {other:?}"),
12413        };
12414        let local = match kind {
12415            ImportKind::Namespace { local_name } => local_name,
12416            other => panic!("expected Namespace, got {other:?}"),
12417        };
12418        assert_eq!(local.name, "uuid");
12419    }
12420
12421    #[test]
12422    fn parse_wildcard_namespace_import_without_as_rejected() {
12423        let (_ast, diags) = parse_str("import * from \"submilli:uuid\";");
12424        assert!(
12425            diags
12426                .iter()
12427                .any(|d| d.message.contains("expected `as <name>`")),
12428            "expected missing-as diagnostic, got: {diags:?}",
12429        );
12430    }
12431
12432    #[test]
12433    fn parse_combined_default_and_named_rejected() {
12434        let (_ast, diags) = parse_str("import uuid, { v4 } from \"submilli:uuid\";");
12435        assert!(
12436            diags
12437                .iter()
12438                .any(|d| d.message.contains("combining default and named")),
12439            "expected combined-import diagnostic, got: {diags:?}",
12440        );
12441    }
12442
12443    #[test]
12444    fn parse_side_effect_import_rejected() {
12445        let (_ast, diags) = parse_str("import \"submilli:uuid\";");
12446        assert!(
12447            diags
12448                .iter()
12449                .any(|d| d.message.contains("side-effect-only imports")),
12450            "expected side-effect-import diagnostic, got: {diags:?}",
12451        );
12452    }
12453
12454    #[test]
12455    fn parse_empty_specifier_list_rejected() {
12456        let (_ast, diags) = parse_str("import {} from \"submilli:uuid\";");
12457        assert!(
12458            diags
12459                .iter()
12460                .any(|d| d.message.contains("empty import specifier list")),
12461            "expected empty-specifier diagnostic, got: {diags:?}",
12462        );
12463    }
12464
12465    #[test]
12466    fn parse_import_inside_function_body_rejected() {
12467        let (_ast, diags) =
12468            parse_str("function main(): void { import { v4 } from \"submilli:uuid\"; }");
12469        assert!(
12470            diags
12471                .iter()
12472                .any(|d| d.message.contains("must appear at the top of the file")),
12473            "expected top-level diagnostic, got: {diags:?}",
12474        );
12475    }
12476
12477    #[test]
12478    fn parse_missing_from_rejected() {
12479        let (_ast, diags) = parse_str("import { v4 } \"submilli:uuid\";");
12480        assert!(
12481            diags.iter().any(|d| d.message.contains("expected `from`")),
12482            "expected missing-from diagnostic, got: {diags:?}",
12483        );
12484    }
12485
12486    #[test]
12487    fn parse_missing_module_specifier_rejected() {
12488        let (_ast, diags) = parse_str("import { v4 } from;");
12489        assert!(
12490            diags
12491                .iter()
12492                .any(|d| d.message.contains("expected module specifier")),
12493            "expected missing-module diagnostic, got: {diags:?}",
12494        );
12495    }
12496
12497    #[test]
12498    fn parse_export_function_marks_exported() {
12499        // `export function main() {}` parses to a single function declaration in
12500        // `top_level`, plus an `exported_decls` entry pointing at it.
12501        let (ast, diags) = parse_str("export function main(): void {}");
12502        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12503        let stmt = single_stmt(&ast);
12504        assert!(
12505            matches!(stmt.kind, crate::StmtKind::Function { .. }),
12506            "expected Function decl, got {:?}",
12507            stmt.kind,
12508        );
12509        assert_eq!(ast.exported_decls.len(), 1, "one exported decl");
12510        assert_eq!(ast.exported_decls[0].stmt, ast.top_level[0]);
12511    }
12512
12513    #[test]
12514    fn parse_export_const_type_interface_enum_are_marked() {
12515        for src in [
12516            "export const X: number = 1;",
12517            "export type Id = string;",
12518            "export interface P { x: number; }",
12519            "export enum E { A, B }",
12520        ] {
12521            let (ast, diags) = parse_str(src);
12522            assert!(diags.is_empty(), "unexpected diags for {src:?}: {diags:?}");
12523            assert_eq!(ast.top_level.len(), 1, "one decl for {src:?}");
12524            assert_eq!(ast.exported_decls.len(), 1, "one exported decl for {src:?}");
12525        }
12526    }
12527
12528    #[test]
12529    fn parse_export_default_is_rejected_with_focused_message() {
12530        let (_ast, diags) = parse_str("export default function main(): void {}");
12531        assert!(
12532            diags
12533                .iter()
12534                .any(|d| d.message.contains("`export default` is not supported")),
12535            "expected focused export-default diagnostic, got: {diags:?}",
12536        );
12537        assert!(
12538            !diags.iter().any(|d| d.message.contains("expected `;`")),
12539            "the spurious missing-semicolon error must be gone: {diags:?}",
12540        );
12541    }
12542
12543    #[test]
12544    fn parse_export_list_parses_as_export_from() {
12545        // Form 2 (`export { x };`, no `from`) now parses cleanly — the
12546        // single-file gating happens in the typechecker, not the parser.
12547        let (ast, diags) = parse_str("function main(): void {}\nexport { main };");
12548        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12549        assert_eq!(ast.top_level.len(), 2, "function + export-from");
12550        let export = ast.try_stmt(ast.top_level[1]).unwrap();
12551        let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12552            panic!("expected ExportFrom, got {:?}", export.kind);
12553        };
12554        assert!(source.is_none(), "bare `export {{ x }}` has no source");
12555        assert_eq!(specs.len(), 1);
12556        assert_eq!(specs[0].imported_name.name, "main");
12557        assert_eq!(specs[0].local_name.name, "main");
12558    }
12559
12560    #[test]
12561    fn parse_export_from_with_source_and_alias_parses() {
12562        let (ast, diags) = parse_str("export { foo, bar as baz } from \"./util\";");
12563        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12564        let export = ast.try_stmt(ast.top_level[0]).unwrap();
12565        let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12566            panic!("expected ExportFrom, got {:?}", export.kind);
12567        };
12568        let Some((module, _)) = source else {
12569            panic!("expected a `from` source");
12570        };
12571        assert_eq!(module, "./util");
12572        assert_eq!(specs.len(), 2);
12573        assert_eq!(specs[1].imported_name.name, "bar");
12574        assert_eq!(specs[1].local_name.name, "baz");
12575    }
12576
12577    #[test]
12578    fn parse_export_inside_function_body_rejected() {
12579        let (_ast, diags) = parse_str("function main(): void { export const X: number = 1; }");
12580        assert!(
12581            diags
12582                .iter()
12583                .any(|d| d.message.contains("must appear at the top of the file")),
12584            "expected top-level diagnostic, got: {diags:?}",
12585        );
12586    }
12587
12588    #[test]
12589    fn parse_throw_with_expression() {
12590        let (ast, diags) = parse_str(r#"throw new Error("oops");"#);
12591        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12592        let stmt = single_stmt(&ast);
12593        let crate::StmtKind::Throw { value } = stmt.kind else {
12594            panic!("expected Throw, got {:?}", stmt.kind);
12595        };
12596        assert!(matches!(
12597            ast.try_expr(value).unwrap().kind,
12598            crate::ExprKind::New { .. }
12599        ));
12600    }
12601
12602    #[test]
12603    fn parse_new_with_dotted_callee() {
12604        let (ast, diags) = parse_str("new Temporal.Duration({});");
12605        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12606        let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12607            panic!("expected expression statement");
12608        };
12609        let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12610            panic!("expected New");
12611        };
12612        let crate::ExprKind::FieldAccess { name, .. } = &ast.try_expr(*callee).unwrap().kind else {
12613            panic!("expected FieldAccess callee");
12614        };
12615        assert_eq!(name.name, "Duration");
12616    }
12617
12618    #[test]
12619    fn parse_new_with_three_level_dotted_callee() {
12620        let (ast, diags) = parse_str("new Foo.Bar.Baz();");
12621        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12622        let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12623            panic!("expected expression statement");
12624        };
12625        let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12626            panic!("expected New");
12627        };
12628        let crate::ExprKind::FieldAccess { receiver, name } = &ast.try_expr(*callee).unwrap().kind
12629        else {
12630            panic!("expected outer FieldAccess");
12631        };
12632        assert_eq!(name.name, "Baz");
12633        let crate::ExprKind::FieldAccess { name: mid_name, .. } =
12634            &ast.try_expr(*receiver).unwrap().kind
12635        else {
12636            panic!("expected inner FieldAccess");
12637        };
12638        assert_eq!(mid_name.name, "Bar");
12639    }
12640
12641    #[test]
12642    fn parse_new_with_trailing_dot_diagnoses() {
12643        let (_ast, diags) = parse_str("new Foo.;");
12644        assert!(
12645            diags.iter().any(|d| d
12646                .message
12647                .contains("expected identifier after `.` in constructor name")),
12648            "expected trailing-dot diagnostic, got: {diags:?}",
12649        );
12650    }
12651
12652    #[test]
12653    fn throw_without_expression_diagnoses() {
12654        let (_ast, diags) = parse_str("throw;");
12655        assert!(
12656            diags
12657                .iter()
12658                .any(|d| d.message == "expected expression after `throw`"),
12659            "expected bare-throw diagnostic, got: {diags:?}",
12660        );
12661    }
12662
12663    #[test]
12664    fn parse_try_catch() {
12665        let (ast, diags) = parse_str("try { } catch (e: Error) { }");
12666        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12667        let stmt = single_stmt(&ast);
12668        let crate::StmtKind::Try {
12669            body,
12670            ref catches,
12671            ref finally,
12672        } = stmt.kind
12673        else {
12674            panic!("expected Try, got {:?}", stmt.kind);
12675        };
12676        assert!(matches!(
12677            ast.try_stmt(body).unwrap().kind,
12678            crate::StmtKind::Block(ref v) if v.is_empty()
12679        ));
12680        let [clause] = catches.as_slice() else {
12681            panic!("expected one catch clause, got {catches:?}");
12682        };
12683        assert_eq!(clause.binding.name, "e");
12684        assert!(clause.ty.is_some());
12685        assert!(finally.is_none());
12686    }
12687
12688    #[test]
12689    fn parse_try_finally() {
12690        let (ast, diags) = parse_str("try { } finally { }");
12691        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12692        let stmt = single_stmt(&ast);
12693        let crate::StmtKind::Try {
12694            ref catches,
12695            ref finally,
12696            ..
12697        } = stmt.kind
12698        else {
12699            panic!("expected Try");
12700        };
12701        assert!(catches.is_empty());
12702        let finally_id = finally.expect("finally block");
12703        assert!(matches!(
12704            ast.try_stmt(finally_id).unwrap().kind,
12705            crate::StmtKind::Block(_)
12706        ));
12707    }
12708
12709    #[test]
12710    fn parse_try_catch_finally() {
12711        let (ast, diags) = parse_str("try { } catch (e: Error) { } finally { }");
12712        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12713        let stmt = single_stmt(&ast);
12714        let crate::StmtKind::Try {
12715            ref catches,
12716            ref finally,
12717            ..
12718        } = stmt.kind
12719        else {
12720            panic!("expected Try");
12721        };
12722        assert_eq!(catches.len(), 1);
12723        assert!(finally.is_some());
12724    }
12725
12726    #[test]
12727    fn bare_try_diagnoses() {
12728        let (_ast, diags) = parse_str("try { }");
12729        assert!(
12730            diags
12731                .iter()
12732                .any(|d| d.message == "try requires at least one of `catch` or `finally`"),
12733            "expected bare-try diagnostic, got: {diags:?}",
12734        );
12735    }
12736
12737    #[test]
12738    fn parse_untyped_catch() {
12739        let (ast, diags) = parse_str("try { } catch (e) { }");
12740        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12741        let stmt = single_stmt(&ast);
12742        let crate::StmtKind::Try {
12743            ref catches,
12744            ref finally,
12745            ..
12746        } = stmt.kind
12747        else {
12748            panic!("expected Try");
12749        };
12750        let [clause] = catches.as_slice() else {
12751            panic!("expected one catch clause, got {catches:?}");
12752        };
12753        assert_eq!(clause.binding.name, "e");
12754        assert!(clause.ty.is_none());
12755        assert!(finally.is_none());
12756    }
12757
12758    #[test]
12759    fn parse_multiple_catch_clauses() {
12760        let (ast, diags) = parse_str("try { } catch (e: HttpError) { } catch (f) { }");
12761        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12762        let stmt = single_stmt(&ast);
12763        let crate::StmtKind::Try {
12764            ref catches,
12765            ref finally,
12766            ..
12767        } = stmt.kind
12768        else {
12769            panic!("expected Try");
12770        };
12771        let [first, second] = catches.as_slice() else {
12772            panic!("expected two catch clauses, got {catches:?}");
12773        };
12774        assert_eq!(first.binding.name, "e");
12775        assert!(first.ty.is_some());
12776        assert_eq!(second.binding.name, "f");
12777        assert!(second.ty.is_none());
12778        assert!(finally.is_none());
12779    }
12780
12781    #[test]
12782    fn catch_after_finally_diagnoses() {
12783        let (_ast, diags) = parse_str("try { } finally { } catch (e: Error) { }");
12784        assert!(
12785            !diags.is_empty(),
12786            "expected diagnostic for catch-after-finally"
12787        );
12788    }
12789
12790    #[test]
12791    fn catch_missing_open_paren_diagnoses() {
12792        let (_ast, diags) = parse_str("try { } catch e: Error) { }");
12793        assert!(
12794            diags
12795                .iter()
12796                .any(|d| d.message == "expected `(` after `catch`"),
12797            "expected open-paren diagnostic, got: {diags:?}",
12798        );
12799    }
12800
12801    fn class_members(ast: &Ast) -> &[crate::ClassMember] {
12802        match &single_stmt(ast).kind {
12803            StmtKind::ClassDecl { members, .. } => members,
12804            other => panic!("expected ClassDecl, got {other:?}"),
12805        }
12806    }
12807
12808    #[test]
12809    fn parse_animal_class() {
12810        let src = r#"
12811class Animal {
12812  name: string;
12813  private sound: string;
12814  readonly species: string;
12815
12816  constructor(name: string, sound: string, species: string) {
12817    this.name = name;
12818    this.sound = sound;
12819    this.species = species;
12820  }
12821
12822  speak(): string {
12823    return this.name + " says " + this.sound;
12824  }
12825}
12826"#;
12827        let (ast, diags) = parse_str(src);
12828        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12829        let StmtKind::ClassDecl {
12830            name,
12831            generics,
12832            extends,
12833            implements,
12834            members,
12835            ..
12836        } = &single_stmt(&ast).kind
12837        else {
12838            panic!("expected ClassDecl");
12839        };
12840        assert_eq!(name.name, "Animal");
12841        assert!(generics.is_empty());
12842        assert!(extends.is_none());
12843        assert!(implements.is_empty());
12844        assert_eq!(members.len(), 5);
12845
12846        let crate::ClassMember::Field {
12847            name, modifiers, ..
12848        } = &members[0]
12849        else {
12850            panic!("member 0 should be a field");
12851        };
12852        assert_eq!(name.name, "name");
12853        assert_eq!(modifiers.visibility, crate::Visibility::Public);
12854        assert!(modifiers.readonly.is_none());
12855
12856        let crate::ClassMember::Field { modifiers, .. } = &members[1] else {
12857            panic!("member 1 should be a field");
12858        };
12859        assert_eq!(modifiers.visibility, crate::Visibility::Private);
12860
12861        let crate::ClassMember::Field { modifiers, .. } = &members[2] else {
12862            panic!("member 2 should be a field");
12863        };
12864        assert!(modifiers.readonly.is_some());
12865        assert_eq!(modifiers.visibility, crate::Visibility::Public);
12866
12867        assert!(matches!(
12868            &members[3],
12869            crate::ClassMember::Constructor { .. }
12870        ));
12871
12872        let crate::ClassMember::Method { name, .. } = &members[4] else {
12873            panic!("member 4 should be a method");
12874        };
12875        assert_eq!(name.name, "speak");
12876    }
12877
12878    #[test]
12879    fn parse_dog_class_extends_super_and_void_method() {
12880        let src = r#"
12881class Dog extends Animal {
12882  private tricks: string[];
12883
12884  constructor(name: string) {
12885    super(name, "woof", "canis familiaris");
12886    this.tricks = [];
12887  }
12888
12889  learn(trick: string): void {
12890    this.tricks.push(trick);
12891  }
12892}
12893"#;
12894        let (ast, diags) = parse_str(src);
12895        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12896        let StmtKind::ClassDecl {
12897            extends, members, ..
12898        } = &single_stmt(&ast).kind
12899        else {
12900            panic!("expected ClassDecl");
12901        };
12902        assert!(extends.is_some(), "Dog should extend Animal");
12903        assert_eq!(members.len(), 3);
12904        assert!(matches!(&members[0], crate::ClassMember::Field { .. }));
12905        assert!(matches!(
12906            &members[1],
12907            crate::ClassMember::Constructor { .. }
12908        ));
12909        assert!(matches!(&members[2], crate::ClassMember::Method { .. }));
12910    }
12911
12912    #[test]
12913    fn parse_class_implements_list() {
12914        let (ast, diags) = parse_str("class C implements I, J { run(): void {} }");
12915        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12916        let StmtKind::ClassDecl { implements, .. } = &single_stmt(&ast).kind else {
12917            panic!("expected ClassDecl");
12918        };
12919        assert_eq!(implements.len(), 2);
12920    }
12921
12922    #[test]
12923    fn parse_class_extends_and_implements() {
12924        let (ast, diags) = parse_str("class C extends B implements I { run(): void {} }");
12925        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12926        let StmtKind::ClassDecl {
12927            extends,
12928            implements,
12929            ..
12930        } = &single_stmt(&ast).kind
12931        else {
12932            panic!("expected ClassDecl");
12933        };
12934        assert!(extends.is_some());
12935        assert_eq!(implements.len(), 1);
12936    }
12937
12938    #[test]
12939    fn parse_class_generics_optional_field_and_initializer() {
12940        let (ast, diags) = parse_str("class Box<T> { value?: T; items: T[] = []; }");
12941        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12942        let StmtKind::ClassDecl {
12943            generics, members, ..
12944        } = &single_stmt(&ast).kind
12945        else {
12946            panic!("expected ClassDecl");
12947        };
12948        assert_eq!(generics.len(), 1);
12949        assert_eq!(generics[0].name, "T");
12950
12951        let crate::ClassMember::Field {
12952            optional,
12953            initializer,
12954            ..
12955        } = &members[0]
12956        else {
12957            panic!("member 0 should be a field");
12958        };
12959        assert!(*optional);
12960        assert!(initializer.is_none());
12961
12962        let crate::ClassMember::Field { initializer, .. } = &members[1] else {
12963            panic!("member 1 should be a field");
12964        };
12965        assert!(initializer.is_some());
12966    }
12967
12968    #[test]
12969    fn export_class_is_marked() {
12970        let (ast, diags) = parse_str("export class Foo {}");
12971        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12972        assert_eq!(ast.top_level.len(), 1);
12973        assert_eq!(ast.exported_decls.len(), 1);
12974        assert!(matches!(
12975            &single_stmt(&ast).kind,
12976            StmtKind::ClassDecl { .. }
12977        ));
12978    }
12979
12980    #[test]
12981    fn parameterless_index_signature_is_reported_from_its_member_start() {
12982        for (source, expected) in [
12983            ("interface P { readonly []: number }", "readonly []"),
12984            ("let p: { readonly []: number } = {};", "readonly []"),
12985            ("let q: { []: number } = {};", "[]"),
12986        ] {
12987            let (_ast, diags) = parse_str(source);
12988            let diag = diags
12989                .iter()
12990                .find(|d| d.message.contains("must declare exactly one parameter"))
12991                .unwrap_or_else(|| panic!("no diagnostic for {source}"));
12992            let reported = &source[diag.span.start as usize..diag.span.end as usize];
12993            assert_eq!(reported, expected, "{source}");
12994        }
12995    }
12996
12997    #[test]
12998    fn unterminated_object_type_expects_closing_brace() {
12999        let (_ast, diags) = parse_str("let p: { a: number\n");
13000        assert!(
13001            diags.iter().any(|d| d.message == "expected `}`"),
13002            "{diags:?}"
13003        );
13004    }
13005
13006    #[test]
13007    fn conflicting_visibility_modifiers_diagnose() {
13008        let (_ast, diags) = parse_str("class C { public private x: number; }");
13009        assert!(
13010            diags
13011                .iter()
13012                .any(|d| d.message.contains("at most one visibility modifier")),
13013            "got: {diags:?}",
13014        );
13015    }
13016
13017    #[test]
13018    fn duplicate_readonly_modifier_diagnoses() {
13019        let (_ast, diags) = parse_str("class C { readonly readonly x: number; }");
13020        assert!(
13021            diags
13022                .iter()
13023                .any(|d| d.message.contains("duplicate `readonly`")),
13024            "got: {diags:?}",
13025        );
13026    }
13027
13028    #[test]
13029    fn field_named_like_a_modifier_is_allowed() {
13030        let (ast, diags) = parse_str("class C { private: number; }");
13031        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13032        let crate::ClassMember::Field {
13033            name, modifiers, ..
13034        } = &class_members(&ast)[0]
13035        else {
13036            panic!("expected a field");
13037        };
13038        assert_eq!(name.name, "private");
13039        assert_eq!(modifiers.visibility, crate::Visibility::Public);
13040    }
13041
13042    #[test]
13043    fn method_named_get_is_allowed() {
13044        let (ast, diags) = parse_str("class C { get(): number { return 1; } }");
13045        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13046        let crate::ClassMember::Method { name, .. } = &class_members(&ast)[0] else {
13047            panic!("expected a method");
13048        };
13049        assert_eq!(name.name, "get");
13050    }
13051
13052    #[test]
13053    fn static_method_parses() {
13054        let (ast, diags) = parse_str("class C { static f(): number { return 1; } }");
13055        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13056        let crate::ClassMember::Method {
13057            name, modifiers, ..
13058        } = &class_members(&ast)[0]
13059        else {
13060            panic!("expected a method");
13061        };
13062        assert_eq!(name.name, "f");
13063        assert!(modifiers.static_span.is_some());
13064    }
13065
13066    #[test]
13067    fn static_readonly_field_parses() {
13068        let (ast, diags) = parse_str("class C { static readonly x: number = 1; }");
13069        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13070        let crate::ClassMember::Field {
13071            name,
13072            modifiers,
13073            initializer,
13074            ..
13075        } = &class_members(&ast)[0]
13076        else {
13077            panic!("expected a field");
13078        };
13079        assert_eq!(name.name, "x");
13080        assert!(modifiers.static_span.is_some());
13081        assert!(modifiers.readonly.is_some());
13082        assert!(initializer.is_some());
13083    }
13084
13085    #[test]
13086    fn private_static_method_parses() {
13087        let (ast, diags) = parse_str("class C { private static f(): void {} }");
13088        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13089        let crate::ClassMember::Method { modifiers, .. } = &class_members(&ast)[0] else {
13090            panic!("expected a method");
13091        };
13092        assert_eq!(modifiers.visibility, crate::Visibility::Private);
13093        assert!(modifiers.static_span.is_some());
13094    }
13095
13096    #[test]
13097    fn method_named_static_is_allowed() {
13098        let (ast, diags) = parse_str("class C { static(): number { return 1; } }");
13099        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13100        let crate::ClassMember::Method {
13101            name, modifiers, ..
13102        } = &class_members(&ast)[0]
13103        else {
13104            panic!("expected a method");
13105        };
13106        assert_eq!(name.name, "static");
13107        assert!(modifiers.static_span.is_none());
13108    }
13109
13110    #[test]
13111    fn visibility_after_static_diagnoses() {
13112        let (_ast, diags) = parse_str("class C { static private f(): void {} }");
13113        assert!(
13114            diags
13115                .iter()
13116                .any(|d| d.message.contains("`private` must come before `static`")),
13117            "got: {diags:?}",
13118        );
13119    }
13120
13121    #[test]
13122    fn duplicate_static_modifier_diagnoses() {
13123        let (_ast, diags) = parse_str("class C { static static f(): void {} }");
13124        assert!(
13125            diags
13126                .iter()
13127                .any(|d| d.message.contains("duplicate `static` modifier")),
13128            "got: {diags:?}",
13129        );
13130    }
13131
13132    #[test]
13133    fn static_accessor_is_rejected() {
13134        let (_ast, diags) = parse_str("class C { static get x(): number { return 1; } }");
13135        assert!(
13136            diags
13137                .iter()
13138                .any(|d| d.message.contains("static accessors are not supported")),
13139            "got: {diags:?}",
13140        );
13141    }
13142
13143    #[test]
13144    fn static_constructor_is_rejected() {
13145        let (_ast, diags) = parse_str("class C { static constructor() {} }");
13146        assert!(
13147            diags
13148                .iter()
13149                .any(|d| d.message.contains("a constructor cannot be `static`")),
13150            "got: {diags:?}",
13151        );
13152    }
13153
13154    #[test]
13155    fn optional_static_field_is_rejected() {
13156        let (_ast, diags) = parse_str("class C { static x?: number; }");
13157        assert!(
13158            diags
13159                .iter()
13160                .any(|d| d.message.contains("a static field cannot be optional")),
13161            "got: {diags:?}",
13162        );
13163    }
13164
13165    #[test]
13166    fn async_function_names_the_synchronous_rewrite() {
13167        let (_ast, diags) = parse_str(
13168            "async function main(): Promise<string> {\n  const x = await f();\n  return x;\n}",
13169        );
13170        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13171        assert_eq!(
13172            messages,
13173            [
13174                "`async` functions are not supported",
13175                "`await` is not supported"
13176            ],
13177            "{diags:?}"
13178        );
13179        assert_eq!(
13180            diags[0].help,
13181            [
13182                "calls return their value directly: remove `async`, write the return type `T` \
13183              instead of `Promise<T>`, and drop `await`"
13184            ]
13185        );
13186        assert_eq!(
13187            diags[1].help,
13188            ["calls return their value directly: remove `await`"]
13189        );
13190    }
13191
13192    #[test]
13193    fn async_is_rejected_on_every_function_form() {
13194        for source in [
13195            "export async function f(): void {}",
13196            "const f = async (): void => {};",
13197            "const f = async x => x;",
13198            "const f = async function (): void {};",
13199            "class C { async run(): void {} }",
13200            "class C { static async run(): void {} }",
13201        ] {
13202            let (_ast, diags) = parse_str(source);
13203            assert_eq!(
13204                diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13205                ["`async` functions are not supported"],
13206                "{source}: {diags:?}"
13207            );
13208        }
13209    }
13210
13211    #[test]
13212    fn await_before_a_template_is_reported() {
13213        for source in ["const a = await `hi`;", "const a = await `a${1}b`;"] {
13214            let (_ast, diags) = parse_str(source);
13215            let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13216            assert_eq!(
13217                messages,
13218                ["`await` is not supported"],
13219                "{source}: {diags:?}"
13220            );
13221        }
13222    }
13223
13224    #[test]
13225    fn async_and_await_stay_ordinary_names() {
13226        for source in [
13227            "const async = 1; const y = async + 1;",
13228            "function async(n: number): number { return n; } async(1);",
13229            "const await = 2; const z = await * 2;",
13230            "const await = (n: number): number => n; const a = await(1);",
13231            "const await = [1, 2]; const b = await[0];",
13232            "const async = { k: 1 }; const c = \"k\" in async; const d = async in ({ o: 1 });",
13233            "const async: unknown = 1; const n = async as (number);",
13234            "const await: unknown = 1; const m = await as number;",
13235            "const await = 1; const t = `${await}`;",
13236            "class C { async(): void {} async: number = 1; }",
13237        ] {
13238            let (_ast, diags) = parse_str(source);
13239            assert!(diags.is_empty(), "{source}: {diags:?}");
13240        }
13241    }
13242
13243    #[test]
13244    fn an_async_object_literal_method_names_the_rewrite_once() {
13245        assert_eq!(
13246            crate::typechecker::rules::test_util::compile_messages(
13247                "function main(): string {\n  const o = { async run(): string { return \"a\"; } };\n  return o.run();\n}\n"
13248            ),
13249            ["`async` functions are not supported"]
13250        );
13251        let (_ast, diags) = parse_str("const o = { async run(): string { return \"a\"; } };");
13252        assert_eq!(
13253            diags[0].help,
13254            [
13255                "calls return their value directly: remove `async`, write the return type `T` \
13256              instead of `Promise<T>`, and drop `await`"
13257            ]
13258        );
13259    }
13260
13261    #[test]
13262    fn await_in_an_async_object_literal_method_is_reported_too() {
13263        let (_ast, diags) =
13264            parse_str("const o = { async run(): string { const x = await f(); return x; } };");
13265        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13266        assert_eq!(
13267            messages,
13268            [
13269                "`async` functions are not supported",
13270                "`await` is not supported"
13271            ],
13272            "{diags:?}"
13273        );
13274    }
13275
13276    #[test]
13277    fn every_async_member_form_reports_async() {
13278        for source in [
13279            "const o = { async *run(): void {} };",
13280            "class C { async *run(): void {} }",
13281            "const o = { async [k](): void {} };",
13282            "const o = { async in(): void {} };",
13283            "class C { async as(): void {} }",
13284        ] {
13285            let (_ast, diags) = parse_str(source);
13286            assert_eq!(
13287                diags[0].message, "`async` functions are not supported",
13288                "{source}: {diags:?}"
13289            );
13290        }
13291    }
13292
13293    #[test]
13294    fn async_string_key_methods_report_only_async() {
13295        for source in [
13296            "const o = { async \"s\"(): string { return \"a\"; } };",
13297            "class C { async \"s\"(): string { return \"a\"; } }",
13298        ] {
13299            let (_ast, diags) = parse_str(source);
13300            assert_eq!(
13301                diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13302                ["`async` functions are not supported"],
13303                "{source}: {diags:?}"
13304            );
13305        }
13306    }
13307
13308    #[test]
13309    fn async_before_a_line_break_is_a_member_of_its_own() {
13310        for source in [
13311            "class C {\n  async\n  run(): void {}\n}",
13312            "const o = { async\n run(): void {} };",
13313        ] {
13314            let (_ast, diags) = parse_str(source);
13315            assert!(
13316                diags
13317                    .iter()
13318                    .all(|d| d.message != "`async` functions are not supported"),
13319                "{source}: {diags:?}"
13320            );
13321        }
13322    }
13323
13324    #[test]
13325    fn async_stays_an_object_literal_key() {
13326        let messages = crate::typechecker::rules::test_util::compile_messages(
13327            "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",
13328        );
13329        assert!(messages.is_empty(), "{messages:?}");
13330    }
13331
13332    #[test]
13333    fn for_await_names_the_rewrite_once() {
13334        assert_eq!(
13335            crate::typechecker::rules::test_util::compile_messages(
13336                "import * as fs from \"submilli:fs\";\nfunction main(): string {\n  for await (const e of fs.list(\".\", false)) { }\n  return \"x\";\n}\n"
13337            ),
13338            ["`for await` is not supported"]
13339        );
13340        let (ast, diags) = parse_str("for await (const e of [1]) { }");
13341        assert_eq!(
13342            diags[0].help,
13343            ["calls return their values directly: remove `await` and write `for (const e of …)`"]
13344        );
13345        assert_eq!(ast.top_level.len(), 1, "the loop is still read");
13346    }
13347
13348    #[test]
13349    fn a_broken_switch_leaves_later_declarations_at_the_top_level() {
13350        assert_eq!(
13351            crate::typechecker::rules::test_util::compile_messages(
13352                "function g(): void { switch (1) { case 1 } }\nclass C {}\nfunction main() { return \"x\"; }\n"
13353            ),
13354            [
13355                "expected `:` after `case` label",
13356                "expected expression",
13357                "expected `:` and return type"
13358            ]
13359        );
13360    }
13361
13362    #[test]
13363    fn every_switch_arm_error_restores_the_nesting() {
13364        for switch in [
13365            "switch (1) { case 1 }",
13366            "switch (1) { case : break; }",
13367            "switch (1) { default: break; default: break; }",
13368            "switch (1) { default }",
13369            "switch (1) { x; }",
13370            "switch (1) { case 1: }",
13371        ] {
13372            let source = format!("function g(): void {{ {switch} }}\nclass C {{}}\n");
13373            let (_ast, diags) = parse_str(&source);
13374            assert!(
13375                diags.iter().all(|d| !d.message.contains("top level")),
13376                "{switch}: {diags:?}"
13377            );
13378        }
13379    }
13380
13381    #[test]
13382    fn protected_member_is_rejected() {
13383        let (_ast, diags) = parse_str("class C { protected x: number; }");
13384        assert!(
13385            diags
13386                .iter()
13387                .any(|d| d.message.contains("`protected` is not supported")),
13388            "got: {diags:?}",
13389        );
13390    }
13391
13392    #[test]
13393    fn abstract_member_is_rejected() {
13394        let (_ast, diags) = parse_str("class C { abstract run(): void; }");
13395        assert!(
13396            diags
13397                .iter()
13398                .any(|d| d.message.contains("abstract classes are not supported")),
13399            "got: {diags:?}",
13400        );
13401    }
13402
13403    #[test]
13404    fn getter_parses_as_accessor() {
13405        let (ast, diags) = parse_str("class C { get value(): number { return 1; } }");
13406        assert!(diags.is_empty(), "got: {diags:?}");
13407        let crate::ClassMember::Accessor { kind, name, .. } = &class_members(&ast)[0] else {
13408            panic!("expected an accessor member");
13409        };
13410        assert_eq!(*kind, crate::AccessorKind::Get);
13411        assert_eq!(name.name, "value");
13412    }
13413
13414    #[test]
13415    fn getter_without_return_type_is_rejected() {
13416        let (ast, diags) = parse_str("class C { get value() { return 1; } }");
13417        assert_eq!(diags.len(), 1, "got: {diags:?}");
13418        assert_eq!(diags[0].message, "expected `:` and return type");
13419        assert_eq!(diags[0].help, vec!["get name(): T { … }".to_string()]);
13420        assert!(
13421            matches!(
13422                &class_members(&ast)[0],
13423                crate::ClassMember::Accessor {
13424                    return_type: None,
13425                    ..
13426                }
13427            ),
13428            "the getter is still recovered as a member"
13429        );
13430    }
13431
13432    #[test]
13433    fn setter_parses_as_accessor() {
13434        let (ast, diags) = parse_str("class C { set value(v: number) {} }");
13435        assert!(diags.is_empty(), "got: {diags:?}");
13436        let crate::ClassMember::Accessor { kind, param, .. } = &class_members(&ast)[0] else {
13437            panic!("expected an accessor member");
13438        };
13439        assert_eq!(*kind, crate::AccessorKind::Set);
13440        assert!(param.is_some(), "setter should have a parameter");
13441    }
13442
13443    #[test]
13444    fn param_property_modifier_outside_constructor_is_rejected() {
13445        let (_ast, diags) = parse_str("function f(public x: number): void {}");
13446        assert!(
13447            diags.iter().any(|d| d
13448                .message
13449                .contains("parameter properties are only allowed in a constructor")),
13450            "got: {diags:?}",
13451        );
13452    }
13453
13454    #[test]
13455    fn constructor_return_type_is_rejected() {
13456        let (_ast, diags) = parse_str("class C { constructor(): void {} }");
13457        assert!(
13458            diags.iter().any(|d| d
13459                .message
13460                .contains("constructor cannot declare a return type")),
13461            "got: {diags:?}",
13462        );
13463    }
13464
13465    #[test]
13466    fn field_initializer_without_type_is_rejected() {
13467        let (_ast, diags) = parse_str("class C { x = 1; }");
13468        assert!(
13469            diags
13470                .iter()
13471                .any(|d| d.message.contains("class fields require a type annotation")),
13472            "got: {diags:?}",
13473        );
13474    }
13475
13476    #[test]
13477    fn this_outside_method_is_left_to_typechecker() {
13478        let (_ast, diags) = parse_str("function f(): string { return this.name; }");
13479        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13480    }
13481
13482    #[test]
13483    fn super_outside_method_is_rejected() {
13484        let (_ast, diags) = parse_str("function f(): void { super(1); }");
13485        assert!(
13486            diags.iter().any(|d| d
13487                .message
13488                .contains("`super` is only valid inside a class method or constructor body")),
13489            "got: {diags:?}",
13490        );
13491    }
13492
13493    #[test]
13494    fn this_inside_method_is_accepted_by_parser() {
13495        let (_ast, diags) = parse_str("class C { x: number; read(): number { return this.x; } }");
13496        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13497    }
13498    #[test]
13499    fn parse_void_expression_preserves_operand_and_precedence() {
13500        let (ast, diags) = parse_str("void effect() === undefined;");
13501        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13502        let expression = expr_of_single_stmt(&ast);
13503        let crate::ExprKind::Binary { lhs, rhs, .. } = expression.kind else {
13504            panic!("expected equality")
13505        };
13506        let crate::ExprKind::Void { operand } = ast.try_expr(lhs).unwrap().kind else {
13507            panic!("expected void")
13508        };
13509        assert!(matches!(
13510            ast.try_expr(operand).unwrap().kind,
13511            crate::ExprKind::Call { .. }
13512        ));
13513        assert!(matches!(
13514            ast.try_expr(rhs).unwrap().kind,
13515            crate::ExprKind::Identifier(_)
13516        ));
13517    }
13518
13519    #[test]
13520    fn parse_void_newline_and_object_operands() {
13521        for source in [
13522            "void\n0;",
13523            "void { value: 1 };",
13524            "void\n{ value: 1 };",
13525            "void /x/;",
13526            "void void 0;",
13527        ] {
13528            let (_, diags) = parse_str(source);
13529            assert!(diags.is_empty(), "{source}: {diags:?}");
13530        }
13531        let (_, diags) = parse_str("void; 0;");
13532        assert!(
13533            !diags.is_empty(),
13534            "an explicit semicolon cannot split void from its operand"
13535        );
13536    }
13537
13538    #[test]
13539    fn parse_optional_parameters_and_defaults_in_callable_forms() {
13540        for source in [
13541            "function f(x = 1): number { return x; }",
13542            "function f(x: number = 1, y: number): number { return y; }",
13543            "function f({ x } = { x: 1 }): number { return x; }",
13544            "const f = (x?: number) => x;",
13545            "const f = (x: number = effect()) => x;",
13546            "const f = function(x: number = effect()) { return x; };",
13547            "const f = { m(x: number = effect()): number { return x; } };",
13548            "const f = ([x = 1]: number[] = values) => x;",
13549            "let f: (x?: number, ...rest: string[]) => void = value;",
13550            "let f: { method?(x?: number): void } = value;",
13551            "function f(x: number = 1, y?: number): void {}",
13552        ] {
13553            let (_, diags) = parse_str(source);
13554            assert!(diags.is_empty(), "{source}: {diags:?}");
13555        }
13556    }
13557
13558    #[test]
13559    fn parse_invalid_optional_parameters() {
13560        for source in [
13561            "function f(x?: number, y: number): void {}",
13562            "const f = (x?: number, y: number) => y;",
13563            "let f: (x?: number, y: number) => void = value;",
13564            "function f(...x?: number[]): void {}",
13565            "function f(x?: number = 1): void {}",
13566            "const f = (x?: number = 1) => x;",
13567            "function f({x}?: T): void {}",
13568        ] {
13569            let (_, diags) = parse_str(source);
13570            assert!(!diags.is_empty(), "expected diagnostic for {source}");
13571        }
13572    }
13573
13574    #[test]
13575    fn parse_optional_tuple_forms_and_order() {
13576        for source in [
13577            "let x: [number, string?] = value;",
13578            "let x: [x?: number, y?: string] = value;",
13579            "let x: readonly [(number | null)?, string?] = value;",
13580        ] {
13581            let (_, diags) = parse_str(source);
13582            assert!(diags.is_empty(), "{source}: {diags:?}");
13583        }
13584        for source in [
13585            "let x: [number?, string] = value;",
13586            "let x: [x: number?] = value;",
13587        ] {
13588            let (_, diags) = parse_str(source);
13589            assert!(!diags.is_empty(), "expected diagnostic for {source}");
13590        }
13591    }
13592
13593    #[test]
13594    fn parse_array_pattern_defaults_preserve_holes() {
13595        let (ast, diags) = parse_str("const [, a = effect(), b] = values;");
13596        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13597        let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
13598            panic!("expected pattern")
13599        };
13600        let crate::Binding::Array {
13601            elems, defaults, ..
13602        } = binding
13603        else {
13604            panic!("expected array")
13605        };
13606        assert_eq!(elems.len(), 3);
13607        assert_eq!(defaults.len(), elems.len());
13608        assert!(elems[0].is_none());
13609        assert!(defaults[0].is_none());
13610        assert!(defaults[1].is_some());
13611        assert!(defaults[2].is_none());
13612    }
13613
13614    #[test]
13615    fn parse_optional_class_method_forms() {
13616        let (ast, diags) =
13617            parse_str("class C { declared?(x?: number): void; implemented?(): void\n{} }");
13618        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13619        let members = class_members(&ast);
13620        assert!(matches!(
13621            &members[0],
13622            crate::ClassMember::Field {
13623                optional: true,
13624                initializer: None,
13625                ty: crate::TypeAnnotation {
13626                    kind: crate::TypeAnnotationKind::Function { .. },
13627                    ..
13628                },
13629                ..
13630            }
13631        ));
13632        assert!(matches!(
13633            &members[1],
13634            crate::ClassMember::Method { optional: true, .. }
13635        ));
13636    }
13637    #[test]
13638    fn defaults_retain_their_callable_receiver_scope() {
13639        for source in [
13640            "class C { value: number = 1; method(value: number = this.value): number { return value; } }",
13641            "class C { constructor(value: number = this.value) {} value: number = 1; }",
13642            "const f = function(this: { value: number }, value: number = this.value): number { return value; };",
13643        ] {
13644            let (ast, diagnostics) = parse_str(source);
13645            assert!(diagnostics.is_empty(), "{source}: {diagnostics:?}");
13646            assert!(
13647                ast.source_expressions()
13648                    .iter()
13649                    .any(|expr| matches!(expr.kind, ExprKind::This))
13650            );
13651            assert!(
13652                !ast.source_expressions()
13653                    .iter()
13654                    .any(|expr| matches!(expr.kind, ExprKind::ThisOutsideReceiver))
13655            );
13656        }
13657    }
13658}