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        eof,
80        fatal: None,
81    };
82    p.parse_program();
83    if p.fatal.is_none() {
84        p.reject_use_strict_with_non_simple_params();
85    }
86    if let Some(span) = p.recursion_limit_span {
87        p.fatal = Some(CompilerFailure::Limit {
88            stage: CompilerStage::Parse,
89            span: Some(span),
90            message: "parser recursion limit exceeded".into(),
91            help: vec!["simplify nested syntax or split it into separate declarations".into()],
92        });
93    }
94    if let Some(fatal) = p.fatal {
95        return Err(CompileError {
96            diagnostics: p.diagnostics,
97            fatal: Some(fatal),
98        });
99    }
100    p.ast
101        .validate_source(source, file)
102        .map_err(|error| CompileError {
103            diagnostics: p.diagnostics.clone(),
104            fatal: Some(error.into_compiler_failure(CompilerStage::Parse)),
105        })?;
106    crate::tree_height::check_syntax(&p.ast).map_err(|fatal| CompileError {
107        diagnostics: p.diagnostics.clone(),
108        fatal: Some(fatal),
109    })?;
110    Ok((p.ast, p.diagnostics))
111}
112
113fn validate_token_stream(source: &str, tokens: &[Token], file: FileId) -> Result<(), &'static str> {
114    if !matches!(tokens.last().map(|token| &token.kind), Some(TokenKind::Eof)) {
115        return Err("token stream must end with EOF");
116    }
117    let mut previous = 0;
118    for (index, token) in tokens.iter().enumerate() {
119        let span = token.span;
120        if span.file != file
121            || span.start < previous
122            || span.start > span.end
123            || source.get(span.start as usize..span.end as usize).is_none()
124        {
125            return Err("token span is outside its source or out of order");
126        }
127        if matches!(token.kind, TokenKind::Eof) && index != tokens.len() - 1 {
128            return Err("tokens follow EOF");
129        }
130        if let Some(doc) = &token.leading_doc {
131            let span = doc.span;
132            if span.file != file
133                || span.start > span.end
134                || span.end > token.span.start
135                || source.get(span.start as usize..span.end as usize) != Some(doc.text.as_str())
136                || !doc.text.starts_with("/**")
137                || !doc.text.ends_with("*/")
138            {
139                return Err("documentation metadata does not match its source");
140            }
141        }
142        previous = span.start;
143    }
144    Ok(())
145}
146
147pub(crate) struct Parser<'a> {
148    source: &'a str,
149    tokens: Vec<Token>,
150    file: FileId,
151    pos: usize,
152    ast: Ast,
153    diagnostics: Vec<Diagnostic>,
154    /// Zero at top level; used to reject `import` inside nested blocks.
155    block_depth: u32,
156    /// Nonzero while parsing a class method or constructor body; gates `this`/`super`.
157    class_member_body_depth: u32,
158    function_expression_body_depth: u32,
159    recursion_depth: usize,
160    recursion_limit_span: Option<Span>,
161    /// Set by a class member that reported its error after reading its whole
162    /// body (a method missing its return type): the class loop takes it and
163    /// continues at the next member instead of abandoning the class.
164    resume_at_next_member: bool,
165    /// End offset of the type in the most recent `x as T`. A statement ending there
166    /// may end at a line break, since the type stops at one (see
167    /// [`Parser::at_statement_end`]).
168    last_as_type_end: Option<u32>,
169    eof: Token,
170    fatal: Option<CompilerFailure>,
171}
172
173impl<'a> Parser<'a> {
174    fn span(&self, start: u32, end: u32) -> Span {
175        Span {
176            file: self.file,
177            start,
178            end,
179        }
180    }
181
182    fn type_name(&mut self, span: Span) -> Option<Ident> {
183        match span.text(self.source, self.file) {
184            Ok(name) => Some(Ident {
185                name: name.to_string(),
186                span,
187            }),
188            Err(error) => {
189                self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
190                None
191            }
192        }
193    }
194
195    fn parse_program(&mut self) {
196        while !self.is_at_eof() {
197            if self.fatal.is_some() || self.error_count() >= MAX_ERRORS {
198                break;
199            }
200            if matches!(self.peek().kind, TokenKind::Semicolon) {
201                self.advance();
202                continue;
203            }
204            let pos_before = self.pos;
205            if let Some(id) = self.parse_statement() {
206                self.ast.top_level.push(id);
207            } else {
208                if self.fatal.is_some() {
209                    break;
210                }
211                // A failed statement that moved and stopped at the start of a
212                // top-level item leaves that item to be parsed, for example after a
213                // class whose last method body took its `}`. Recovering would skip
214                // `class`, `type`, `interface`, `enum` or `import`.
215                if self.pos != pos_before && self.peek_starts_top_level_item() {
216                    continue;
217                }
218                self.recover();
219                if self.pos != pos_before && matches!(self.peek().kind, TokenKind::RightBrace) {
220                    self.advance();
221                    continue;
222                }
223                // Forward-progress guarantee: advance if recover didn't move.
224                if self.pos == pos_before && !self.is_at_eof() {
225                    self.advance();
226                }
227            }
228        }
229    }
230
231    fn parse_statement(&mut self) -> Option<StmtId> {
232        self.with_recursion_limit(Self::parse_statement_inner)
233    }
234
235    fn parse_statement_inner(&mut self) -> Option<StmtId> {
236        self.reject_async_modifier();
237        match self.peek().kind {
238            TokenKind::Let => self.parse_let_or_const(false),
239            TokenKind::Const => self.parse_let_or_const(true),
240            TokenKind::Function => self.parse_function_decl(),
241            TokenKind::Class => self.parse_class_decl(),
242            TokenKind::Interface => self.parse_interface_decl(),
243            TokenKind::Enum => self.parse_enum_decl(),
244            TokenKind::Identifier if self.at_type_alias_head() => self.parse_type_alias_decl(),
245            TokenKind::Export => self.parse_export(),
246            TokenKind::Import => self.parse_import_decl(),
247            TokenKind::If => self.parse_if(),
248            TokenKind::While => self.parse_while(),
249            TokenKind::Do => self.parse_do_while(),
250            TokenKind::For => self.parse_for(),
251            TokenKind::Switch => self.parse_switch(),
252            TokenKind::Break => self.parse_break(),
253            TokenKind::Continue => self.parse_continue(),
254            TokenKind::Return => self.parse_return(),
255            TokenKind::Throw => self.parse_throw(),
256            TokenKind::Try => self.parse_try(),
257            TokenKind::LeftBrace => self.parse_block(),
258            _ => self.parse_expression_statement(),
259        }
260    }
261
262    fn parse_expression_statement(&mut self) -> Option<StmtId> {
263        let expr_id = self.parse_expression()?;
264        let expr_span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
265        if !self.at_statement_end() {
266            let what = if matches!(
267                parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.kind,
268                ExprKind::Assign { .. }
269            ) {
270                "assignment"
271            } else {
272                "expression"
273            };
274            self.error_at_peek(format!("expected `;` after {what}"));
275            return None;
276        }
277        let end = self.finish_statement();
278        let span = self.span(expr_span.start, end);
279        let kind = self.statement_kind_for(expr_id)?;
280        parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
281    }
282
283    /// An assignment in statement position becomes an assignment statement;
284    /// only one used as a value stays an `ExprKind::Assign`.
285    fn statement_kind_for(&mut self, expr_id: ExprId) -> Option<StmtKind> {
286        let ExprKind::Assign {
287            target,
288            op,
289            op_span,
290            value,
291        } = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?
292            .kind
293            .clone()
294        else {
295            return Some(StmtKind::Expr(expr_id));
296        };
297        Some(
298            match (
299                parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?
300                    .kind
301                    .clone(),
302                op,
303            ) {
304                (ExprKind::Identifier(target), None) => StmtKind::Assign { target, value },
305                (ExprKind::Identifier(target), Some(op)) => StmtKind::CompoundAssign {
306                    target,
307                    op,
308                    op_span,
309                    value,
310                },
311                (ExprKind::FieldAccess { receiver, name }, None) => StmtKind::AssignField {
312                    receiver,
313                    field_name: name,
314                    value,
315                },
316                (ExprKind::FieldAccess { receiver, name }, Some(op)) => {
317                    StmtKind::CompoundAssignField {
318                        receiver,
319                        field_name: name,
320                        op,
321                        op_span,
322                        value,
323                    }
324                }
325                (ExprKind::IndexAccess { receiver, index }, None) => StmtKind::AssignIndex {
326                    receiver,
327                    index,
328                    value,
329                },
330                (ExprKind::IndexAccess { receiver, index }, Some(op)) => {
331                    StmtKind::CompoundAssignIndex {
332                        receiver,
333                        index,
334                        op,
335                        op_span,
336                        value,
337                    }
338                }
339                _ => return self.invariant_failure("invalid assignment target"),
340            },
341        )
342    }
343
344    fn parse_let_or_const(&mut self, is_const: bool) -> Option<StmtId> {
345        let doc = self.take_leading_doc();
346        let kw = self.advance();
347
348        let binding = match self.peek().kind {
349            TokenKind::LeftBrace | TokenKind::LeftBracket => Some(self.parse_binding()?),
350            _ => None,
351        };
352
353        let name = if binding.is_none() {
354            let name_tok =
355                self.expect_binding_identifier("expected identifier after `let`/`const`")?;
356            Some(self.ident_from_token(&name_tok))
357        } else {
358            None
359        };
360
361        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
362            self.advance();
363            Some(self.parse_type_annotation()?)
364        } else {
365            None
366        };
367
368        let implicit = !is_const
369            && ty.is_some()
370            && !matches!(self.peek().kind, TokenKind::Equals)
371            && matches!(self.peek().kind, TokenKind::Semicolon);
372        let value = if let (true, Some(name)) = (implicit, &name) {
373            // `let x: T;` starts as `undefined`; the typechecker requires `T` to allow it.
374            let value = parse_arena_result(
375                self.ast.try_push_expr(Expr {
376                    kind: ExprKind::Identifier(Ident {
377                        name: "undefined".to_string(),
378                        span: name.span,
379                    }),
380                    span: name.span,
381                }),
382                &mut self.fatal,
383            )?;
384            self.ast.implicit_initializers.insert(value);
385            self.ast.synthetic_undefined.insert(value);
386            value
387        } else {
388            if !matches!(self.peek().kind, TokenKind::Equals) {
389                let (msg, help) = if is_const {
390                    (
391                        "`const` declaration requires an initializer",
392                        vec!["const x: T = expr;".to_string()],
393                    )
394                } else {
395                    (
396                        "`let` declaration requires an initializer",
397                        vec![
398                            "let x: T = expr;".to_string(),
399                            "or declare one that starts empty: `let x: T | undefined;`".to_string(),
400                        ],
401                    )
402                };
403                self.error_at_peek_with_help(msg, help);
404                return None;
405            }
406            self.advance();
407            self.parse_expression()?
408        };
409
410        if !self.at_statement_end() {
411            if matches!(self.peek().kind, TokenKind::Comma) {
412                let keyword = if is_const { "const" } else { "let" };
413                let first = name.as_ref().map_or("a", |name| name.name.as_str());
414                let next = self.peek_at(1);
415                let second = if matches!(next.kind, TokenKind::Identifier) {
416                    &self.source[next.span.start as usize..next.span.end as usize]
417                } else {
418                    "b"
419                };
420                let help = format!(
421                    "declare one variable per statement: `{keyword} {first} = …; {keyword} {second} = …;`; \
422                     in a `for (…)` header, declare `{second}` before the loop"
423                );
424                self.error_at_peek_with_help("one declaration declares one variable", vec![help]);
425            } else {
426                self.error_at_peek("expected `;` after declaration");
427            }
428            return None;
429        }
430        let end = self.finish_statement();
431
432        let span = self.span(kw.span.start, end);
433        let kind = match (binding, name) {
434            (Some(binding), _) => {
435                if is_const {
436                    StmtKind::ConstPattern {
437                        binding,
438                        ty,
439                        value,
440                        doc,
441                    }
442                } else {
443                    StmtKind::LetPattern {
444                        binding,
445                        ty,
446                        value,
447                        doc,
448                    }
449                }
450            }
451            (None, Some(name)) => {
452                if is_const {
453                    StmtKind::Const {
454                        name,
455                        ty,
456                        value,
457                        doc,
458                    }
459                } else {
460                    StmtKind::Let {
461                        name,
462                        ty,
463                        value,
464                        doc,
465                    }
466                }
467            }
468            (None, None) => return self.invariant_failure("either binding or name must be Some"),
469        };
470        parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
471    }
472
473    // One level only; nested patterns are rejected.
474    fn parse_binding(&mut self) -> Option<Binding> {
475        match self.peek().kind {
476            TokenKind::LeftBrace => self.parse_object_binding(),
477            TokenKind::LeftBracket => self.parse_array_binding(),
478            _ => {
479                self.error_at_peek("expected `{` or `[` to start a destructuring pattern");
480                None
481            }
482        }
483    }
484
485    fn parse_object_binding(&mut self) -> Option<Binding> {
486        let open = self.advance();
487        let mut fields: Vec<ObjectPatternField> = Vec::new();
488        let mut rest: Option<Ident> = None;
489
490        if matches!(self.peek().kind, TokenKind::RightBrace) {
491            self.error_at_peek("empty object destructuring pattern");
492            return None;
493        }
494
495        loop {
496            if matches!(self.peek().kind, TokenKind::DotDotDot) {
497                let dots = self.advance();
498                let name_tok = self.expect_binding_identifier("expected identifier after `...`")?;
499                let name = self.ident_from_token(&name_tok);
500                if !matches!(self.peek().kind, TokenKind::RightBrace) {
501                    self.error_at_peek_with_help(
502                        "rest element must be the last element in a destructuring pattern",
503                        vec!["move `...` after every other binding".to_string()],
504                    );
505                    return None;
506                }
507                rest = Some(Ident {
508                    name: name.name,
509                    span: self.span(dots.span.start, name.span.end),
510                });
511                break;
512            }
513
514            let source_tok = self.expect_property_name("expected field name in object pattern")?;
515            let source = self.ident_from_token(&source_tok);
516            let field_span_start = source.span.start;
517
518            let (local, field_end) = if matches!(self.peek().kind, TokenKind::Colon) {
519                self.advance();
520                match self.peek().kind {
521                    TokenKind::LeftBrace | TokenKind::LeftBracket => {
522                        self.error_at_peek_with_help(
523                                "nested destructuring is not supported",
524                                vec![
525                                    "destructure once and access nested fields explicitly: const { a } = obj; const inner = a.b;"
526                                        .to_string(),
527                                ],
528                            );
529                        return None;
530                    }
531                    _ => {
532                        let local_tok = self.expect_binding_identifier(
533                            "expected identifier after `:` in pattern",
534                        )?;
535                        let local = self.ident_from_token(&local_tok);
536                        let end = local.span.end;
537                        (local, end)
538                    }
539                }
540            } else {
541                if !matches!(source_tok.kind, TokenKind::Identifier) {
542                    self.error_at_peek("expected `:` after keyword field name in object pattern");
543                    return None;
544                }
545                self.reject_strict_mode_reserved_word(&source_tok);
546                self.reject_eval_or_arguments(source_tok.span);
547                let end = source.span.end;
548                (source.clone(), end)
549            };
550
551            let default = if matches!(self.peek().kind, TokenKind::Equals) {
552                self.advance();
553                Some(self.parse_expression()?)
554            } else {
555                None
556            };
557            let field_end = match default {
558                Some(value) => {
559                    parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?
560                        .span
561                        .end
562                }
563                None => field_end,
564            };
565
566            fields.push(ObjectPatternField {
567                source,
568                local,
569                default,
570                span: self.span(field_span_start, field_end),
571            });
572
573            match self.peek().kind {
574                TokenKind::Comma => {
575                    self.advance();
576                    if matches!(self.peek().kind, TokenKind::RightBrace) {
577                        break;
578                    }
579                }
580                TokenKind::RightBrace => break,
581                _ => {
582                    self.error_at_peek("expected `,` or `}` in object pattern");
583                    return None;
584                }
585            }
586        }
587
588        if !matches!(self.peek().kind, TokenKind::RightBrace) {
589            self.error_at_peek("expected `}` to close object pattern");
590            return None;
591        }
592        let close = self.advance();
593
594        Some(Binding::Object {
595            fields,
596            rest,
597            span: self.span(open.span.start, close.span.end),
598        })
599    }
600
601    fn parse_array_binding(&mut self) -> Option<Binding> {
602        let open = self.advance();
603        let mut elems: Vec<Option<Ident>> = Vec::new();
604        let mut defaults = Vec::new();
605        let mut rest: Option<Ident> = None;
606
607        if matches!(self.peek().kind, TokenKind::RightBracket) {
608            self.error_at_peek("empty array destructuring pattern");
609            return None;
610        }
611
612        loop {
613            match self.peek().kind {
614                TokenKind::Comma => {
615                    elems.push(None);
616                    defaults.push(None);
617                    self.advance();
618                    if matches!(self.peek().kind, TokenKind::RightBracket) {
619                        break;
620                    }
621                    continue;
622                }
623                TokenKind::RightBracket => break,
624                TokenKind::DotDotDot => {
625                    let dots = self.advance();
626                    let name_tok =
627                        self.expect_binding_identifier("expected identifier after `...`")?;
628                    let name = self.ident_from_token(&name_tok);
629                    if !matches!(self.peek().kind, TokenKind::RightBracket) {
630                        self.error_at_peek_with_help(
631                            "rest element must be the last element in a destructuring pattern",
632                            vec!["move `...` after every other binding".to_string()],
633                        );
634                        return None;
635                    }
636                    rest = Some(Ident {
637                        name: name.name,
638                        span: self.span(dots.span.start, name.span.end),
639                    });
640                    break;
641                }
642                TokenKind::LeftBrace | TokenKind::LeftBracket => {
643                    self.error_at_peek_with_help(
644                        "nested destructuring is not supported",
645                        vec![
646                            "destructure once and access nested elements explicitly: const [a] = arr; const inner = a[0];"
647                                .to_string(),
648                        ],
649                    );
650                    return None;
651                }
652                _ => {
653                    let name_tok =
654                        self.expect_binding_identifier("expected identifier in array pattern")?;
655                    let name = self.ident_from_token(&name_tok);
656
657                    let default = if matches!(self.peek().kind, TokenKind::Equals) {
658                        self.advance();
659                        Some(self.parse_expression()?)
660                    } else {
661                        None
662                    };
663                    defaults.push(default);
664                    elems.push(Some(name));
665
666                    match self.peek().kind {
667                        TokenKind::Comma => {
668                            self.advance();
669                            if matches!(self.peek().kind, TokenKind::RightBracket) {
670                                break;
671                            }
672                        }
673                        TokenKind::RightBracket => break,
674                        _ => {
675                            self.error_at_peek("expected `,` or `]` in array pattern");
676                            return None;
677                        }
678                    }
679                }
680            }
681        }
682
683        if !matches!(self.peek().kind, TokenKind::RightBracket) {
684            self.error_at_peek("expected `]` to close array pattern");
685            return None;
686        }
687        let close = self.advance();
688
689        Some(Binding::Array {
690            elems,
691            defaults,
692            rest,
693            span: self.span(open.span.start, close.span.end),
694        })
695    }
696
697    fn parse_function_decl(&mut self) -> Option<StmtId> {
698        let doc = self.take_leading_doc();
699        let kw = self.advance();
700
701        let name_tok = self.expect_binding_identifier("expected function name")?;
702        let name = self.ident_from_token(&name_tok);
703        let is_main = self.block_depth == 0 && name.name == "main";
704        let decl = self.parse_function_decl_after_name(doc, kw, name);
705        if decl.is_none() && is_main {
706            self.ast.unparsed_main = true;
707        }
708        decl
709    }
710
711    fn parse_function_decl_after_name(
712        &mut self,
713        doc: Option<crate::DocComment>,
714        kw: Token,
715        name: Ident,
716    ) -> Option<StmtId> {
717        let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
718            self.parse_generic_param_list()?
719        } else {
720            Vec::new()
721        };
722
723        if !matches!(self.peek().kind, TokenKind::LeftParen) {
724            self.error_at_peek("expected `(` after function name");
725            return None;
726        }
727        self.advance();
728
729        let params = self.parse_param_list(false)?;
730        self.advance(); // `)` — parse_param_list left us on it
731
732        if !matches!(self.peek().kind, TokenKind::Colon) {
733            self.error_at_peek_with_help(
734                "expected `:` and return type",
735                vec![format!(
736                    "write the return type: `function {}(…): T {{ … }}`, with `void` when it returns nothing",
737                    name.name
738                )],
739            );
740            // Read the body anyway, so its own errors are reported and recovery
741            // does not resume inside it and misread its closing `}`.
742            if matches!(self.peek().kind, TokenKind::LeftBrace) {
743                let _ = self.parse_outer_this_boundary(Self::parse_block);
744            }
745            return None;
746        }
747        self.advance();
748        let (return_type, type_predicate) =
749            self.parse_predicate_or_return_type(TypePos::Anywhere)?;
750
751        // A declaration nested in a method has no receiver of its own, and
752        // must not see the method's.
753        let body = self.parse_outer_this_boundary(Self::parse_block)?;
754        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
755            .span
756            .end;
757
758        parse_arena_result(
759            self.ast.try_push_stmt(Stmt {
760                kind: StmtKind::Function {
761                    name,
762                    generics,
763                    params,
764                    return_type,
765                    type_predicate,
766                    body,
767                    doc,
768                },
769                span: self.span(kw.span.start, body_end),
770            }),
771            &mut self.fatal,
772        )
773    }
774
775    fn parse_class_decl(&mut self) -> Option<StmtId> {
776        let doc = self.take_leading_doc();
777        let kw = self.advance();
778        // Classes are collected from the module's top level only; a nested one
779        // would otherwise be dropped unchecked.
780        if self.block_depth > 0 {
781            self.error_at_with_help(
782                kw.span,
783                "a `class` must be declared at the top level of the module",
784                vec!["move this `class` outside any block or function body".to_string()],
785            );
786        }
787        let name = self.parse_type_decl_name("a", "class")?;
788
789        let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
790            self.parse_generic_param_list()?
791        } else {
792            Vec::new()
793        };
794
795        let extends = if matches!(self.peek().kind, TokenKind::Extends) {
796            self.advance();
797            Some(self.parse_type_annotation()?)
798        } else {
799            None
800        };
801
802        let mut implements: Vec<crate::TypeAnnotation> = Vec::new();
803        if matches!(self.peek().kind, TokenKind::Implements) {
804            self.advance();
805            loop {
806                implements.push(self.parse_type_annotation()?);
807                if matches!(self.peek().kind, TokenKind::Comma) {
808                    self.advance();
809                    continue;
810                }
811                break;
812            }
813        }
814
815        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
816            self.error_at_peek("expected `{` after class header");
817            return None;
818        }
819        self.advance();
820
821        let mut members: Vec<crate::ClassMember> = Vec::new();
822        let mut member_failed = false;
823        // After a failed member, a body missing its own `}` may have taken the
824        // class's: a declaration or import there is the next top-level item, not a
825        // member, so the class stops and reports the missing `}`.
826        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
827            && !(member_failed && self.peek_starts_top_level_item())
828        {
829            // Stray `;` between members is accepted (TS allows it).
830            if matches!(self.peek().kind, TokenKind::Semicolon) {
831                self.advance();
832                continue;
833            }
834            match self.parse_class_member() {
835                Some(member) => members.push(member),
836                None if std::mem::take(&mut self.resume_at_next_member) => member_failed = true,
837                None => return None,
838            }
839        }
840
841        if !matches!(self.peek().kind, TokenKind::RightBrace) {
842            self.error_at_peek("expected `}` to close class");
843            return None;
844        }
845        let close = self.advance();
846        if member_failed {
847            return None;
848        }
849
850        parse_arena_result(
851            self.ast.try_push_stmt(Stmt {
852                kind: StmtKind::ClassDecl {
853                    name,
854                    generics,
855                    extends,
856                    implements,
857                    members,
858                    doc,
859                },
860                span: self.span(kw.span.start, close.span.end),
861            }),
862            &mut self.fatal,
863        )
864    }
865
866    fn parse_class_member(&mut self) -> Option<crate::ClassMember> {
867        let doc = self.take_leading_doc();
868        let member_start = self.peek().span.start;
869
870        // Excluded modifiers — reject before anything else, naming the fix.
871        if self.peek_identifier_text_is("protected") {
872            let tok = self.advance();
873            self.error_at_with_help(
874                tok.span,
875                "`protected` is not supported",
876                vec!["use `private` or `public`".to_string()],
877            );
878            return None;
879        }
880        if self.peek_identifier_text_is("abstract") {
881            let tok = self.advance();
882            self.error_at_with_help(
883                tok.span,
884                "abstract classes are not supported",
885                vec!["provide a concrete implementation instead of `abstract`".to_string()],
886            );
887            return None;
888        }
889
890        let modifiers = self.parse_class_modifiers();
891        self.reject_async_member_modifier();
892
893        // Getter/setter: `get`/`set` <name> ( … ). A member literally named `get`/`set`
894        // (followed directly by `(`) is an ordinary method and stays accepted.
895        for (kw, kind) in [
896            ("get", crate::AccessorKind::Get),
897            ("set", crate::AccessorKind::Set),
898        ] {
899            if self.peek_identifier_text_is(kw)
900                && is_property_name(&self.peek_at(1).kind)
901                && matches!(self.peek_at(2).kind, TokenKind::LeftParen)
902            {
903                if let Some(span) = modifiers.readonly {
904                    self.reject_readonly_modifier(span);
905                }
906                if let Some(span) = modifiers.static_span {
907                    self.error_at_with_help(
908                        span,
909                        "static accessors are not supported",
910                        vec!["use a static method: `static name(): T { … }`".to_string()],
911                    );
912                    return None;
913                }
914                return self.parse_accessor(member_start, modifiers, kind, doc);
915            }
916        }
917
918        // Constructor.
919        if self.peek_identifier_text_is("constructor")
920            && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
921        {
922            if let Some(span) = modifiers.static_span {
923                self.error_at_with_help(
924                    span,
925                    "a constructor cannot be `static`",
926                    vec![
927                        "remove `static` — the constructor already belongs to the class, not \
928                         instances"
929                            .to_string(),
930                    ],
931                );
932                return None;
933            }
934            if let Some(span) = modifiers.readonly {
935                self.error_at_with_help(
936                    span,
937                    "a constructor cannot be `readonly`",
938                    vec!["remove `readonly`; it applies to fields".to_string()],
939                );
940                return None;
941            }
942            return self.parse_constructor(member_start, modifiers.visibility, doc);
943        }
944
945        let name = self.expect_property_ident("expected class member name")?;
946        let optional_span = if matches!(self.peek().kind, TokenKind::Question) {
947            Some(self.advance().span)
948        } else {
949            None
950        };
951        let optional = optional_span.is_some();
952
953        // Method.
954        if matches!(self.peek().kind, TokenKind::LessThan | TokenKind::LeftParen) {
955            if let Some(span) = modifiers.readonly {
956                self.reject_readonly_modifier(span);
957            }
958            let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
959                self.parse_generic_param_list()?
960            } else {
961                Vec::new()
962            };
963            if !matches!(self.peek().kind, TokenKind::LeftParen) {
964                self.error_at_peek("expected `(` to start the method parameter list");
965                return None;
966            }
967            self.advance();
968            let params = self.parse_class_member_params(false)?;
969            self.advance(); // `)` — parse_param_list left us on it
970            if !matches!(self.peek().kind, TokenKind::Colon) {
971                self.error_at_peek_with_help(
972                    "expected `:` and return type",
973                    vec![format!(
974                        "write the return type: `{}(…): T {{ … }}`, with `void` when it returns nothing",
975                        name.name
976                    )],
977                );
978                // As for a function: read the body so recovery resumes after it.
979                if matches!(self.peek().kind, TokenKind::LeftBrace)
980                    && self.parse_class_member_body().is_some()
981                {
982                    self.resume_at_next_member = true;
983                }
984                return None;
985            }
986            self.advance();
987            let return_type = self.parse_type_annotation()?;
988            if self.at_inserted_semicolon() && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
989            {
990                self.advance();
991            }
992            if optional
993                && matches!(
994                    self.peek().kind,
995                    TokenKind::Semicolon | TokenKind::RightBrace
996                )
997            {
998                if !generics.is_empty() {
999                    self.error_at(
1000                        name.span,
1001                        "generic optional method declarations are not supported",
1002                    );
1003                    return None;
1004                }
1005                let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1006                    self.advance().span.end
1007                } else {
1008                    return_type.span.end
1009                };
1010                let ty = self.method_declaration_type(
1011                    params,
1012                    return_type,
1013                    self.span(name.span.start, end),
1014                )?;
1015                return Some(crate::ClassMember::Field {
1016                    name,
1017                    modifiers,
1018                    optional: true,
1019                    method_signature: true,
1020                    ty,
1021                    initializer: None,
1022                    span: self.span(member_start, end),
1023                    doc,
1024                });
1025            }
1026            let body = self.parse_class_member_body()?;
1027            let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1028                .span
1029                .end;
1030            return Some(crate::ClassMember::Method {
1031                name,
1032                optional,
1033                modifiers,
1034                generics,
1035                params,
1036                return_type,
1037                body,
1038                span: self.span(member_start, body_end),
1039                doc,
1040            });
1041        }
1042
1043        // Field.
1044        if let Some(span) = optional_span
1045            && modifiers.static_span.is_some()
1046        {
1047            self.error_at_with_help(
1048                span,
1049                "a static field cannot be optional",
1050                vec!["a static field must be initialized; give it a value with `= …`".to_string()],
1051            );
1052            return None;
1053        }
1054        if !matches!(self.peek().kind, TokenKind::Colon) {
1055            if matches!(self.peek().kind, TokenKind::Equals) {
1056                self.error_at_peek_with_help(
1057                    "class fields require a type annotation",
1058                    vec!["add `: T` before the initializer, e.g. `name: string = …`".to_string()],
1059                );
1060            } else {
1061                self.error_at_peek("expected `:` and a type for the class field");
1062            }
1063            return None;
1064        }
1065        self.advance();
1066        let ty = self.parse_type_annotation()?;
1067        let initializer = if matches!(self.peek().kind, TokenKind::Equals) {
1068            self.advance();
1069            // A field initializer may read `this` (the typechecker binds it); gate
1070            // it like a member body so `this.x` parses.
1071            self.class_member_body_depth += 1;
1072            let init = self.parse_expression();
1073            self.class_member_body_depth -= 1;
1074            Some(init?)
1075        } else {
1076            None
1077        };
1078        if !matches!(self.peek().kind, TokenKind::Semicolon) {
1079            self.error_at_peek("expected `;` after class field");
1080            return None;
1081        }
1082        let semi = self.advance();
1083        Some(crate::ClassMember::Field {
1084            name,
1085            modifiers,
1086            optional,
1087            method_signature: false,
1088            ty,
1089            initializer,
1090            span: self.span(member_start, semi.span.end),
1091            doc,
1092        })
1093    }
1094
1095    fn parse_constructor(
1096        &mut self,
1097        member_start: u32,
1098        visibility: crate::Visibility,
1099        doc: Option<crate::DocComment>,
1100    ) -> Option<crate::ClassMember> {
1101        self.advance(); // `constructor`
1102        self.advance(); // `(` — guaranteed by the caller
1103        let params = self.parse_class_member_params(true)?;
1104        self.advance(); // `)` — parse_param_list left us on it
1105        if matches!(self.peek().kind, TokenKind::Colon) {
1106            let colon = self.advance();
1107            let _ = self.parse_type_annotation();
1108            self.error_at_with_help(
1109                colon.span,
1110                "a constructor cannot declare a return type",
1111                vec![
1112                    "drop the `: T` — a constructor always returns the class instance".to_string(),
1113                ],
1114            );
1115        }
1116        let body = self.parse_class_member_body()?;
1117        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1118            .span
1119            .end;
1120        Some(crate::ClassMember::Constructor {
1121            visibility,
1122            params,
1123            body,
1124            span: self.span(member_start, body_end),
1125            doc,
1126        })
1127    }
1128
1129    /// `get name(): T { … }` / `set name(v: T) { … }`. The caller has confirmed the
1130    /// `get`/`set` keyword, a property name, and a `(` follow.
1131    fn parse_accessor(
1132        &mut self,
1133        member_start: u32,
1134        modifiers: crate::ClassModifiers,
1135        kind: crate::AccessorKind,
1136        doc: Option<crate::DocComment>,
1137    ) -> Option<crate::ClassMember> {
1138        self.advance(); // `get` / `set`
1139        let name = self.expect_property_ident("expected accessor name")?;
1140        self.advance(); // `(` — guaranteed by the caller
1141        let mut params = self.parse_class_member_params(false)?;
1142        self.advance(); // `)`
1143        let param = match kind {
1144            crate::AccessorKind::Get => {
1145                if !params.is_empty() {
1146                    self.error_at(name.span, "a getter cannot declare parameters");
1147                }
1148                None
1149            }
1150            crate::AccessorKind::Set => {
1151                if params.len() != 1 {
1152                    self.error_at(name.span, "a setter must declare exactly one parameter");
1153                    return None;
1154                }
1155                let param = params.remove(0);
1156                // An assignment always passes the value, so a setter's parameter
1157                // can't be omitted or collect several.
1158                if param.rest {
1159                    self.error_at(
1160                        param.name.span,
1161                        "a setter's parameter can't be a rest parameter",
1162                    );
1163                } else if param.default.is_some() {
1164                    self.error_at(name.span, "a setter's parameter can't have a default value");
1165                } else if param.optional {
1166                    self.error_at(param.name.span, "a setter's parameter can't be optional");
1167                }
1168                Some(Box::new(param))
1169            }
1170        };
1171        let return_type = if matches!(self.peek().kind, TokenKind::Colon) {
1172            self.advance();
1173            Some(self.parse_type_annotation()?)
1174        } else {
1175            // A getter's type is its return type, which, as for a method, is
1176            // written rather than inferred. The body still parses, so the
1177            // rest of the class is checked.
1178            if kind == crate::AccessorKind::Get {
1179                self.error_at_peek_with_help(
1180                    "expected `:` and return type",
1181                    vec!["get name(): T { … }".to_string()],
1182                );
1183            }
1184            None
1185        };
1186        let body = self.parse_class_member_body()?;
1187        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1188            .span
1189            .end;
1190        Some(crate::ClassMember::Accessor {
1191            name,
1192            modifiers,
1193            kind,
1194            param,
1195            return_type,
1196            body,
1197            span: self.span(member_start, body_end),
1198            doc,
1199        })
1200    }
1201
1202    /// `public`/`private`/`static`/`readonly`. Duplicate or conflicting modifiers are
1203    /// diagnosed but parsing continues so the member shape is still recovered.
1204    /// Visibility must precede `static` and `readonly` (the TypeScript order);
1205    /// `readonly` is accepted on either side of `static` — no semantic difference
1206    /// under our subset.
1207    fn parse_class_modifiers(&mut self) -> crate::ClassModifiers {
1208        let mut visibility = crate::Visibility::Public;
1209        let mut visibility_span: Option<Span> = None;
1210        let mut readonly: Option<Span> = None;
1211        let mut static_span: Option<Span> = None;
1212        loop {
1213            if self.peek_word_is_class_modifier("public")
1214                || self.peek_word_is_class_modifier("private")
1215            {
1216                let (kind, word) = if self.peek_identifier_text_is("private") {
1217                    (crate::Visibility::Private, "private")
1218                } else {
1219                    (crate::Visibility::Public, "public")
1220                };
1221                let tok = self.advance();
1222                if visibility_span.is_some() {
1223                    self.error_at_with_help(
1224                        tok.span,
1225                        "a class member may have at most one visibility modifier",
1226                        vec!["keep a single `public` or `private`".to_string()],
1227                    );
1228                    continue;
1229                }
1230                // TypeScript reports one misplaced modifier per member; `static` is
1231                // named first when both precede.
1232                let preceding = static_span
1233                    .map(|_| "static")
1234                    .or(readonly.map(|_| "readonly"));
1235                if let Some(preceding) = preceding {
1236                    self.error_at_with_help(
1237                        tok.span,
1238                        format!("`{word}` must come before `{preceding}`"),
1239                        vec![format!("write `{word} {preceding} <name>`")],
1240                    );
1241                }
1242                visibility = kind;
1243                visibility_span = Some(tok.span);
1244                continue;
1245            }
1246            if self.peek_word_is_class_modifier("static") {
1247                let tok = self.advance();
1248                if static_span.is_some() {
1249                    self.error_at(tok.span, "duplicate `static` modifier");
1250                } else {
1251                    static_span = Some(tok.span);
1252                }
1253                continue;
1254            }
1255            if self.peek_word_is_class_modifier("readonly") {
1256                let tok = self.advance();
1257                if readonly.is_some() {
1258                    self.error_at(tok.span, "duplicate `readonly` modifier");
1259                } else {
1260                    readonly = Some(tok.span);
1261                }
1262                continue;
1263            }
1264            break;
1265        }
1266        crate::ClassModifiers {
1267            visibility,
1268            visibility_span,
1269            readonly,
1270            static_span,
1271        }
1272    }
1273
1274    /// A contextual modifier keyword counts as a modifier only when another member token
1275    /// (the real name, or a further modifier) follows — otherwise the word is the member
1276    /// name itself (e.g. a field named `private`). Mirrors `eat_readonly_property_modifier`.
1277    /// As in TypeScript, only `static` may be followed by a line break; in a class body
1278    /// ASI already ends the member there, but a constructor parameter list has no ASI.
1279    fn peek_word_is_class_modifier(&self, word: &str) -> bool {
1280        if !self.peek_identifier_text_is(word) {
1281            return false;
1282        }
1283        if word != "static" && self.line_break_after_peek() {
1284            return false;
1285        }
1286        let next = &self.peek_at(1).kind;
1287        is_property_name(next) || matches!(next, TokenKind::StringLiteral(_))
1288    }
1289
1290    fn parse_class_member_params(
1291        &mut self,
1292        allow_param_properties: bool,
1293    ) -> Option<Vec<ParamDecl>> {
1294        let saved = self.class_member_body_depth;
1295        self.class_member_body_depth = 1;
1296        let params = self.parse_param_list(allow_param_properties);
1297        self.class_member_body_depth = saved;
1298        params
1299    }
1300
1301    fn parse_class_member_body(&mut self) -> Option<StmtId> {
1302        self.class_member_body_depth += 1;
1303        let body = self.parse_block();
1304        self.class_member_body_depth -= 1;
1305        body
1306    }
1307
1308    fn parse_interface_decl(&mut self) -> Option<StmtId> {
1309        let doc = self.take_leading_doc();
1310        let kw = self.advance();
1311        let name = self.parse_type_decl_name("an", "interface")?;
1312
1313        let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1314            self.parse_generic_param_list()?
1315        } else {
1316            Vec::new()
1317        };
1318
1319        let mut extends = Vec::new();
1320        if matches!(self.peek().kind, TokenKind::Extends) {
1321            self.advance();
1322            loop {
1323                extends.push(self.parse_type_annotation()?);
1324                if !matches!(self.peek().kind, TokenKind::Comma) {
1325                    break;
1326                }
1327                self.advance();
1328            }
1329        }
1330        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1331            self.error_at_peek("expected `{` after interface name");
1332            return None;
1333        }
1334        self.advance();
1335
1336        let mut members: Vec<crate::InterfaceMember> = Vec::new();
1337        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
1338            && !self.peek_starts_top_level_item()
1339        {
1340            // Call signatures `(params): ret;` are stored under the sentinel name `@call`.
1341            // The `@` prefix is not a valid identifier start, so collisions with user methods
1342            // are impossible.
1343            let member_doc = self.take_leading_doc();
1344            if matches!(self.peek().kind, TokenKind::LeftParen) {
1345                let open = self.advance();
1346                let params = self.parse_param_list(false)?;
1347                self.advance();
1348                if !matches!(self.peek().kind, TokenKind::Colon) {
1349                    self.error_at_peek("expected `:` and return type");
1350                    return None;
1351                }
1352                self.advance();
1353                let return_type = self.parse_type_annotation()?;
1354                let end = self.finish_interface_member(return_type.span.end)?;
1355                let span = self.span(open.span.start, end);
1356                let sentinel = Ident {
1357                    name: "@call".to_string(),
1358                    span: open.span,
1359                };
1360                members.push(crate::InterfaceMember::Method {
1361                    name: sentinel,
1362                    optional: false,
1363                    generics: Vec::new(),
1364                    params,
1365                    return_type,
1366                    span,
1367                    doc: member_doc,
1368                });
1369                continue;
1370            }
1371            if matches!(self.peek().kind, TokenKind::LessThan) {
1372                self.error_at_peek("generic call signatures are not yet supported");
1373                return None;
1374            }
1375            let member_start = self.peek().span.start;
1376            let readonly = self.eat_readonly_property_modifier();
1377            if self.peek_is_parameterless_index_signature() {
1378                self.skip_parameterless_index_signature(member_start)?;
1379                self.finish_interface_member(self.prev_token_end())?;
1380                continue;
1381            }
1382            if matches!(self.peek().kind, TokenKind::LeftBracket) {
1383                let signature = self.parse_index_signature(readonly)?;
1384                self.finish_interface_member(signature.span.end)?;
1385                members.push(crate::InterfaceMember::IndexSignature(signature));
1386                continue;
1387            }
1388            let member_name = self.expect_property_ident("expected interface member name")?;
1389            let optional = matches!(self.peek().kind, TokenKind::Question);
1390            if optional {
1391                self.advance();
1392            }
1393            if matches!(self.peek().kind, TokenKind::Colon) {
1394                self.advance();
1395                let ty = self.parse_type_annotation()?;
1396                let end = self.finish_interface_member(ty.span.end)?;
1397                let span = self.span(member_name.span.start, end);
1398                members.push(crate::InterfaceMember::Property {
1399                    name: member_name,
1400                    ty,
1401                    optional,
1402                    readonly,
1403                    span,
1404                    doc: member_doc,
1405                });
1406                continue;
1407            }
1408            if readonly {
1409                self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
1410            }
1411            let m_generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1412                self.parse_generic_param_list()?
1413            } else {
1414                Vec::new()
1415            };
1416            if !matches!(self.peek().kind, TokenKind::LeftParen) {
1417                self.error_at_peek(
1418                    "expected `(` to start a method signature or `:` to start a property",
1419                );
1420                return None;
1421            }
1422            self.advance();
1423            let params = self.parse_param_list(false)?;
1424            self.advance();
1425            if !matches!(self.peek().kind, TokenKind::Colon) {
1426                self.error_at_peek("expected `:` and return type");
1427                return None;
1428            }
1429            self.advance();
1430            let return_type = self.parse_type_annotation()?;
1431            let end = self.finish_interface_member(return_type.span.end)?;
1432            let span = self.span(member_name.span.start, end);
1433            if optional {
1434                if !m_generics.is_empty() {
1435                    self.error_at(
1436                        member_name.span,
1437                        "generic optional method declarations are not supported",
1438                    );
1439                    return None;
1440                }
1441                let ty = self.method_declaration_type(params, return_type, span)?;
1442                members.push(crate::InterfaceMember::Property {
1443                    name: member_name,
1444                    ty,
1445                    optional: true,
1446                    readonly,
1447                    span,
1448                    doc: member_doc,
1449                });
1450            } else {
1451                members.push(crate::InterfaceMember::Method {
1452                    name: member_name,
1453                    optional,
1454                    generics: m_generics,
1455                    params,
1456                    return_type,
1457                    span,
1458                    doc: member_doc,
1459                });
1460            }
1461        }
1462
1463        if !matches!(self.peek().kind, TokenKind::RightBrace) {
1464            self.error_at_peek("expected `}` to close interface");
1465            return None;
1466        }
1467        let close = self.advance();
1468
1469        parse_arena_result(
1470            self.ast.try_push_stmt(Stmt {
1471                kind: StmtKind::InterfaceDecl {
1472                    name,
1473                    generics,
1474                    extends,
1475                    members,
1476                    doc,
1477                },
1478                span: self.span(kw.span.start, close.span.end),
1479            }),
1480            &mut self.fatal,
1481        )
1482    }
1483
1484    fn method_declaration_type(
1485        &mut self,
1486        params: Vec<ParamDecl>,
1487        return_type: TypeAnnotation,
1488        span: Span,
1489    ) -> Option<TypeAnnotation> {
1490        let mut fields = Vec::new();
1491        for param in params {
1492            if param.pattern.is_some() || param.default.is_some() {
1493                self.error_at(
1494                    param.name.span,
1495                    "a method declaration cannot contain a parameter pattern or default value",
1496                );
1497                return None;
1498            }
1499            let Some(ty) = param.ty else {
1500                return self.invariant_failure("method parameter annotation was not retained");
1501            };
1502            fields.push(TypeAnnotationField {
1503                name: param.name,
1504                ty,
1505                optional: param.optional,
1506                readonly: false,
1507                rest: param.rest,
1508                method: false,
1509            });
1510        }
1511        Some(TypeAnnotation {
1512            kind: TypeAnnotationKind::Function {
1513                params: fields,
1514                return_type: Box::new(return_type),
1515            },
1516            span,
1517        })
1518    }
1519
1520    fn finish_interface_member(&mut self, body_end: u32) -> Option<u32> {
1521        self.finish_type_member(body_end, "expected `;`, `,`, or `}` after interface member")
1522    }
1523
1524    /// Consume the separator after an interface or type-literal member and return
1525    /// where the member ends. `;` and `,` are interchangeable and the last member's
1526    /// is optional. A line break also ends a member, as in TypeScript: ASI inserts
1527    /// `;` there, but keeps some tokens, such as `[` and `(`, attached. `body_end` is
1528    /// where the member ends when it carries no separator of its own.
1529    fn finish_type_member(&mut self, body_end: u32, message: &str) -> Option<u32> {
1530        match self.peek().kind {
1531            TokenKind::Semicolon | TokenKind::Comma => Some(self.advance().span.end),
1532            TokenKind::RightBrace => Some(body_end),
1533            _ if self.line_break_before_peek() => Some(body_end),
1534            _ => {
1535                self.error_at_peek(message);
1536                None
1537            }
1538        }
1539    }
1540
1541    fn parse_type_alias_decl(&mut self) -> Option<StmtId> {
1542        let doc = self.take_leading_doc();
1543        let kw = self.advance();
1544        let name = self.parse_type_decl_name("a", "type alias")?;
1545
1546        let generics: Vec<Ident> = if matches!(self.peek().kind, TokenKind::LessThan) {
1547            self.parse_generic_param_list()?
1548        } else {
1549            Vec::new()
1550        };
1551
1552        if !matches!(self.peek().kind, TokenKind::Equals) {
1553            self.error_at_peek("expected `=` after type alias name");
1554            return None;
1555        }
1556        self.advance();
1557
1558        let ty = self.parse_type_annotation()?;
1559
1560        // ASI keeps a `[` on the next line attached, but the type already ended at
1561        // the line break (see `parse_type_array_inner`), so the break ends the alias.
1562        let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1563            self.advance().span.end
1564        } else if self.line_break_before_peek() {
1565            self.prev_token_end()
1566        } else {
1567            self.error_at_peek("expected `;` after type alias body");
1568            return None;
1569        };
1570
1571        parse_arena_result(
1572            self.ast.try_push_stmt(Stmt {
1573                kind: StmtKind::TypeAliasDecl {
1574                    name,
1575                    generics,
1576                    ty,
1577                    doc,
1578                },
1579                span: self.span(kw.span.start, end),
1580            }),
1581            &mut self.fatal,
1582        )
1583    }
1584
1585    // Parser enforces kind-coherence: an enum cannot mix numeric and string initializers.
1586    fn parse_enum_decl(&mut self) -> Option<StmtId> {
1587        #[derive(Copy, Clone)]
1588        enum Kind {
1589            Number,
1590            String,
1591        }
1592        let doc = self.take_leading_doc();
1593        let kw = self.advance();
1594        let name = self.parse_type_decl_name("an", "enum")?;
1595
1596        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1597            self.error_at_peek("expected `{` after enum name");
1598            return None;
1599        }
1600        self.advance();
1601
1602        let mut members: Vec<EnumMember> = Vec::new();
1603        let mut first_explicit: Option<(Kind, Span)> = None;
1604        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1605            // ASI inserts `;` after `,` on a newline — tolerate it so multi-line enums parse.
1606            if matches!(self.peek().kind, TokenKind::Semicolon) {
1607                self.advance();
1608                continue;
1609            }
1610            let member_doc = self.take_leading_doc();
1611            let member_name_tok = self.expect_identifier_name("expected enum member name")?;
1612            let member_name = self.ident_from_token(&member_name_tok);
1613
1614            let value = if matches!(self.peek().kind, TokenKind::Equals) {
1615                self.advance();
1616                let init = self.parse_enum_initializer()?;
1617                let (this_kind, this_span) = match &init {
1618                    EnumInitializer::Number { span, .. } => (Kind::Number, *span),
1619                    EnumInitializer::String { span, .. } => (Kind::String, *span),
1620                };
1621                match first_explicit {
1622                    None => first_explicit = Some((this_kind, this_span)),
1623                    Some((prev, _)) => {
1624                        let mismatched = matches!(
1625                            (prev, this_kind),
1626                            (Kind::Number, Kind::String) | (Kind::String, Kind::Number)
1627                        );
1628                        if mismatched {
1629                            self.error_at(
1630                                this_span,
1631                                "mixed numeric and string enum members are not allowed",
1632                            );
1633                        }
1634                    }
1635                }
1636                Some(init)
1637            } else {
1638                None
1639            };
1640
1641            let end_span = value
1642                .as_ref()
1643                .map_or(member_name.span, super::ast::EnumInitializer::span);
1644            let member_span = self.span(member_name.span.start, end_span.end);
1645            members.push(EnumMember {
1646                name: member_name,
1647                value,
1648                span: member_span,
1649                doc: member_doc,
1650            });
1651
1652            match self.peek().kind {
1653                // ASI supplies the `;` before a closing `}` and after a newline, so an
1654                // enum body sees one wherever a comma was omitted.
1655                TokenKind::Comma | TokenKind::Semicolon => {
1656                    self.advance();
1657                }
1658                TokenKind::RightBrace | TokenKind::Eof => {}
1659                _ => {
1660                    self.error_at_peek("expected `,` or `}` after enum member");
1661                    return None;
1662                }
1663            }
1664        }
1665
1666        if !matches!(self.peek().kind, TokenKind::RightBrace) {
1667            self.error_at_peek("expected `}` to close enum");
1668            return None;
1669        }
1670        let close = self.advance();
1671
1672        parse_arena_result(
1673            self.ast.try_push_stmt(Stmt {
1674                kind: StmtKind::EnumDecl { name, members, doc },
1675                span: self.span(kw.span.start, close.span.end),
1676            }),
1677            &mut self.fatal,
1678        )
1679    }
1680
1681    /// `export` on a top-level declaration. Two forms (multi-file.md §3):
1682    /// Form 1 marks a declaration (`export function …`) — the declaration is
1683    /// parsed normally and recorded in `exported_decls`; Form 2 is a re-export
1684    /// (`export { a, b as c } from "./util";` / `export { x };`) parsed into an
1685    /// `ExportFrom` node. `export` is top-level only; inside a function body it
1686    /// is a parse error. `export default` stays rejected.
1687    fn parse_export(&mut self) -> Option<StmtId> {
1688        let export_tok = self.advance();
1689        let export_span = export_tok.span;
1690
1691        let nested = self.block_depth > 0;
1692        if nested {
1693            self.error_at_with_help(
1694                export_span,
1695                "`export` statements must appear at the top of the file",
1696                vec!["move this `export` to the top level of the module".to_string()],
1697            );
1698            // Fall through to parse the declaration / list anyway so recovery
1699            // stays in sync, but return None below so it isn't recorded.
1700        }
1701
1702        // `export type { … }` — TS's type-only re-export, accepted as a no-op
1703        // synonym: exports carry both value and type spaces here.
1704        if self.peek_identifier_text_is("type")
1705            && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1706        {
1707            self.advance();
1708        }
1709
1710        self.reject_async_modifier();
1711        let declares = matches!(
1712            self.peek().kind,
1713            TokenKind::Function
1714                | TokenKind::Class
1715                | TokenKind::Const
1716                | TokenKind::Let
1717                | TokenKind::Interface
1718                | TokenKind::Enum
1719        ) || self.at_type_alias_head();
1720        if declares {
1721            // Carry any doc comment that attached to `export` onto the
1722            // declaration token so the declaration parser still sees it.
1723            if export_tok.leading_doc.is_some() && self.peek().leading_doc.is_none() {
1724                if let Some(token) = self.tokens.get_mut(self.pos) {
1725                    token.leading_doc = export_tok.leading_doc;
1726                } else {
1727                    return self.invariant_failure("export cursor is outside token stream");
1728                }
1729            }
1730            let id = self.parse_statement()?;
1731            if nested {
1732                return None;
1733            }
1734            self.ast.exported_decls.push(ExportedDecl {
1735                stmt: id,
1736                export_span,
1737            });
1738            return Some(id);
1739        }
1740
1741        match self.peek().kind {
1742            TokenKind::LeftBrace => {
1743                // A doc comment attaches to the `export` keyword, which we've
1744                // already consumed — carry it onto the re-export node.
1745                let doc = export_tok
1746                    .leading_doc
1747                    .as_ref()
1748                    .and_then(|raw| self.parse_doc(raw))
1749                    .or_else(|| self.take_leading_doc());
1750                self.parse_export_from(export_span, nested, doc)
1751            }
1752            TokenKind::Default => {
1753                self.error_at_peek_with_help(
1754                    "`export default` is not supported",
1755                    vec![
1756                        "default exports are out of scope; drop `default` — a named \
1757                         `export` marks the declaration visible to sibling modules"
1758                            .to_string(),
1759                    ],
1760                );
1761                None
1762            }
1763            _ => {
1764                self.error_at_peek_with_help(
1765                    "expected a declaration after `export`",
1766                    vec![
1767                        "`export` may precede `function`, `class`, `const`, `let`, `type`, \
1768                         `interface`, or `enum`, or take the form `export { … }`"
1769                            .to_string(),
1770                    ],
1771                );
1772                None
1773            }
1774        }
1775    }
1776
1777    /// Parse Form 2 re-export: `export { a, b as c } from "./util";` or the
1778    /// bare `export { x };`. The leading `export` has already been consumed.
1779    fn parse_export_from(
1780        &mut self,
1781        export_span: Span,
1782        nested: bool,
1783        doc: Option<crate::DocComment>,
1784    ) -> Option<StmtId> {
1785        let (specs, open_span) = self.parse_specifier_list(SpecifierList::Export)?;
1786        if specs.is_empty() {
1787            self.error_at_with_help(
1788                open_span,
1789                "empty export specifier list",
1790                vec!["name what you re-export: `export { name } from \"./util\";`".to_string()],
1791            );
1792            return None;
1793        }
1794
1795        let source = if self.peek_identifier_text_is("from") {
1796            self.advance();
1797            let module_tok = self.peek().clone();
1798            let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1799                s.clone()
1800            } else {
1801                self.error_at_peek("expected module specifier string after `from`");
1802                return None;
1803            };
1804            self.advance();
1805            Some((module, module_tok.span))
1806        } else {
1807            None
1808        };
1809
1810        if !matches!(self.peek().kind, TokenKind::Semicolon) {
1811            self.error_at_peek("expected `;` after export statement");
1812            return None;
1813        }
1814        let semi = self.advance();
1815
1816        if nested {
1817            return None;
1818        }
1819
1820        parse_arena_result(
1821            self.ast.try_push_stmt(Stmt {
1822                kind: StmtKind::ExportFrom { specs, source, doc },
1823                span: self.span(export_span.start, semi.span.end),
1824            }),
1825            &mut self.fatal,
1826        )
1827    }
1828
1829    fn parse_import_decl(&mut self) -> Option<StmtId> {
1830        let doc = self.take_leading_doc();
1831        let kw = self.advance();
1832        let kw_span = kw.span;
1833
1834        let nested = self.block_depth > 0;
1835        if nested {
1836            self.error_at_with_help(
1837                kw_span,
1838                "`import` statements must appear at the top of the file",
1839                vec!["move this `import` outside any block / function body".to_string()],
1840            );
1841            // Continue parsing to consume the whole import so recovery isn't confused.
1842        }
1843
1844        let kind = self.parse_import_kind()?;
1845
1846        if !self.peek_identifier_text_is("from") {
1847            self.error_at_peek("expected `from` after import specifier list");
1848            return None;
1849        }
1850        self.advance();
1851
1852        let module_tok = self.peek().clone();
1853        let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1854            s.clone()
1855        } else {
1856            self.error_at_peek("expected module specifier string after `from`");
1857            return None;
1858        };
1859        let module_span = module_tok.span;
1860        self.advance();
1861
1862        if !matches!(self.peek().kind, TokenKind::Semicolon) {
1863            self.error_at_peek("expected `;` after import statement");
1864            return None;
1865        }
1866        let semi = self.advance();
1867
1868        if nested {
1869            return None;
1870        }
1871
1872        parse_arena_result(
1873            self.ast.try_push_stmt(Stmt {
1874                kind: StmtKind::Import {
1875                    module,
1876                    module_span,
1877                    kind,
1878                    doc,
1879                },
1880                span: self.span(kw_span.start, semi.span.end),
1881            }),
1882            &mut self.fatal,
1883        )
1884    }
1885
1886    fn parse_import_kind(&mut self) -> Option<ImportKind> {
1887        // `import type …` — TS's type-only import, accepted as a no-op synonym:
1888        // imports carry both value and type spaces here. `import type from "m"`
1889        // stays a namespace import named `type`, so `type` is a modifier only
1890        // when what follows can't be a complete import clause without it.
1891        if self.peek_identifier_text_is("type")
1892            && match self.peek_at(1).kind {
1893                TokenKind::LeftBrace | TokenKind::Star => true,
1894                TokenKind::Identifier => {
1895                    !(self.peek_at_is_word(1, "from")
1896                        && matches!(self.peek_at(2).kind, TokenKind::StringLiteral(_)))
1897                }
1898                _ => false,
1899            }
1900        {
1901            self.advance();
1902        }
1903
1904        match self.peek().kind {
1905            TokenKind::LeftBrace => self.parse_named_imports(),
1906            TokenKind::Identifier => {
1907                let local_tok = self.advance();
1908                let local_name = self.ident_from_token(&local_tok);
1909                if matches!(self.peek().kind, TokenKind::Comma) {
1910                    self.error_at_peek_with_help(
1911                        "combining default and named imports is not supported",
1912                        vec!["use two separate `import` statements".to_string()],
1913                    );
1914                    return None;
1915                }
1916                Some(ImportKind::Namespace { local_name })
1917            }
1918            // `import * as ns from "pkg"` — forgiveness synonym for `import ns from "pkg"`.
1919            // Both are identical since we have no default exports.
1920            TokenKind::Star => {
1921                self.advance();
1922                if !self.peek_identifier_text_is("as") {
1923                    self.error_at_peek_with_help(
1924                        "expected `as <name>` after `import *`",
1925                        vec![
1926                            "name the namespace binding: \
1927                             `import * as ns from \"pkg\";` (or use the \
1928                             short form: `import ns from \"pkg\";`)"
1929                                .to_string(),
1930                        ],
1931                    );
1932                    return None;
1933                }
1934                self.advance();
1935                let local_tok = self.expect_identifier("expected namespace name after `as`")?;
1936                let local_name = self.ident_from_token(&local_tok);
1937                Some(ImportKind::Namespace { local_name })
1938            }
1939            TokenKind::StringLiteral(_) => {
1940                self.error_at_peek_with_help(
1941                    "side-effect-only imports are not supported",
1942                    vec![
1943                        "name what you import: `import { name } from \"pkg\";` \
1944                         or `import ns from \"pkg\";`"
1945                            .to_string(),
1946                    ],
1947                );
1948                None
1949            }
1950            _ => {
1951                self.error_at_peek("expected `{` or namespace name after `import`");
1952                None
1953            }
1954        }
1955    }
1956
1957    fn parse_named_imports(&mut self) -> Option<ImportKind> {
1958        let (specs, open_span) = self.parse_specifier_list(SpecifierList::Import)?;
1959        if specs.is_empty() {
1960            self.error_at_with_help(
1961                open_span,
1962                "empty import specifier list",
1963                vec!["name what you import: `import { name } from \"pkg\";`".to_string()],
1964            );
1965            return None;
1966        }
1967        Some(ImportKind::Named(specs))
1968    }
1969
1970    /// Parse a `{ a, b as c }` specifier list shared by `import` and re-`export`.
1971    /// `list` says whether the specifiers bind local names (an import) and names the
1972    /// construct in diagnostics. Returns the specifiers (possibly
1973    /// empty — callers reject empty with construct-specific help) and the span of
1974    /// the opening brace.
1975    fn parse_specifier_list(
1976        &mut self,
1977        list: SpecifierList,
1978    ) -> Option<(Vec<ImportSpecifier>, Span)> {
1979        let what = list.noun();
1980        let open = self.advance();
1981        let mut specs: Vec<ImportSpecifier> = Vec::new();
1982        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1983            // Inline type-only modifier (`{ type X }`), accepted as a no-op
1984            // synonym. `{ type as t }` instead names the binding `type`.
1985            if self.peek_identifier_text_is("type")
1986                && matches!(self.peek_at(1).kind, TokenKind::Identifier)
1987                && !self.peek_at_is_word(1, "as")
1988            {
1989                self.advance();
1990            }
1991            let imported_tok =
1992                self.expect_identifier_name(&format!("expected {what} specifier name"))?;
1993            let imported_name = self.ident_from_token(&imported_tok);
1994
1995            let binds_local = list == SpecifierList::Import;
1996            let local_name = if self.peek_identifier_text_is("as") {
1997                self.advance();
1998                let local_tok = self.expect_identifier_name("expected local name after `as`")?;
1999                if binds_local {
2000                    self.reject_strict_mode_reserved_word(&local_tok);
2001                }
2002                self.ident_from_token(&local_tok)
2003            } else {
2004                if binds_local {
2005                    self.reject_strict_mode_reserved_word(&imported_tok);
2006                }
2007                imported_name.clone()
2008            };
2009
2010            specs.push(ImportSpecifier {
2011                imported_name,
2012                local_name,
2013            });
2014
2015            match self.peek().kind {
2016                TokenKind::Comma => {
2017                    self.advance();
2018                }
2019                TokenKind::RightBrace | TokenKind::Eof => {}
2020                _ => {
2021                    self.error_at_peek(format!("expected `,` or `}}` after {what} specifier"));
2022                    return None;
2023                }
2024            }
2025        }
2026
2027        if !matches!(self.peek().kind, TokenKind::RightBrace) {
2028            self.error_at_peek(format!("expected `}}` to close {what} specifier list"));
2029            return None;
2030        }
2031        self.advance();
2032
2033        Some((specs, open.span))
2034    }
2035
2036    fn parse_enum_initializer(&mut self) -> Option<EnumInitializer> {
2037        let neg = if matches!(self.peek().kind, TokenKind::Minus) {
2038            Some(self.advance())
2039        } else {
2040            None
2041        };
2042        let tok = self.advance();
2043        match &tok.kind {
2044            TokenKind::NumberLiteral(n) => {
2045                let value = if neg.is_some() { -n } else { *n };
2046                // tsc constant-folds enum initializers, which turns `-0` into `0`.
2047                let value = if value == 0.0 { 0.0 } else { value };
2048                let start = neg.as_ref().map_or(tok.span.start, |t| t.span.start);
2049                Some(EnumInitializer::Number {
2050                    value,
2051                    span: self.span(start, tok.span.end),
2052                })
2053            }
2054            TokenKind::StringLiteral(s) => {
2055                if let Some(neg_tok) = &neg {
2056                    self.error_at(
2057                        self.span(neg_tok.span.start, tok.span.end),
2058                        "cannot negate a string enum initializer",
2059                    );
2060                    return None;
2061                }
2062                Some(EnumInitializer::String {
2063                    value: s.clone(),
2064                    span: tok.span,
2065                })
2066            }
2067            _ => {
2068                let span = neg.map_or(tok.span, |t| self.span(t.span.start, tok.span.end));
2069                self.error_at(
2070                    span,
2071                    "expected a number or string literal for enum initializer",
2072                );
2073                None
2074            }
2075        }
2076    }
2077
2078    /// Whether the token at `index` is `operator` (`keyof`, `readonly`) used as a
2079    /// prefix type operator.
2080    ///
2081    /// Both are contextual keywords — valid as ordinary identifiers in TypeScript and
2082    /// Submilli — so they are only operators when a type follows. A bare type name is
2083    /// never followed by another type name, so requiring one separates `keyof T` from
2084    /// a type literally named `keyof`.
2085    fn is_contextual_type_operator(&self, index: usize, operator: &str) -> bool {
2086        let Some(tok) = self.tokens.get(index) else {
2087            return false;
2088        };
2089        if !self.token_is_word(tok, operator) {
2090            return false;
2091        }
2092        matches!(
2093            self.tokens.get(index + 1).map(|t| &t.kind),
2094            Some(
2095                TokenKind::Identifier
2096                    | TokenKind::Void
2097                    | TokenKind::LeftBrace
2098                    | TokenKind::LeftParen
2099                    | TokenKind::LeftBracket
2100                    | TokenKind::StringLiteral(_)
2101                    | TokenKind::NumberLiteral(_)
2102                    | TokenKind::BooleanLiteral(_)
2103                    | TokenKind::NullLiteral
2104            )
2105        )
2106    }
2107
2108    fn parse_type_argument_list(&mut self) -> Option<(Vec<TypeAnnotation>, u32)> {
2109        let lt = self.advance();
2110        if matches!(self.peek().kind, TokenKind::GreaterThan) {
2111            self.error_at(lt.span, "empty generic argument list `<>` is not allowed");
2112            return None;
2113        }
2114        let mut args = Vec::new();
2115        loop {
2116            args.push(self.parse_type_annotation()?);
2117            match self.peek().kind {
2118                TokenKind::Comma => {
2119                    self.advance();
2120                    if matches!(self.peek().kind, TokenKind::GreaterThan) {
2121                        let gt = self.advance();
2122                        return Some((args, gt.span.end));
2123                    }
2124                }
2125                TokenKind::GreaterThan => {
2126                    let gt = self.advance();
2127                    return Some((args, gt.span.end));
2128                }
2129                _ => {
2130                    self.error_at(
2131                        lt.span,
2132                        "expected `,` or `>` to close generic argument list",
2133                    );
2134                    return None;
2135                }
2136            }
2137        }
2138    }
2139
2140    /// The name a `class`, `interface`, `type` or `enum` declares, `noun` naming
2141    /// which. A built-in type's name is reported, as in TypeScript: `class number {}`
2142    /// would read as the built-in in some positions and the class in others.
2143    fn parse_type_decl_name(&mut self, article: &str, noun: &str) -> Option<Ident> {
2144        let name_tok = self.expect_identifier(&format!("expected {noun} name"))?;
2145        let name = self.ident_from_token(&name_tok);
2146        if BUILT_IN_TYPE_NAMES.contains(&name.name.as_str()) {
2147            self.error_at_with_help(
2148                name.span,
2149                format!(
2150                    "`{}` is a built-in type and can't be used as {article} {noun} name",
2151                    name.name
2152                ),
2153                vec![format!("rename the {noun}")],
2154            );
2155        }
2156        Some(name)
2157    }
2158
2159    fn parse_generic_param_list(&mut self) -> Option<Vec<Ident>> {
2160        let lt = self.advance();
2161        if matches!(self.peek().kind, TokenKind::GreaterThan) {
2162            self.error_at(lt.span, "empty generic parameter list `<>` is not allowed");
2163            return None;
2164        }
2165        let mut generics = Vec::new();
2166        loop {
2167            let name_tok = self.expect_identifier("expected generic parameter name")?;
2168            let name = self.ident_from_token(&name_tok);
2169            // A duplicate is reported and dropped, as `tsc` drops it: the first
2170            // parameter keeps the name, and the type arguments count against the
2171            // parameters that remain.
2172            if generics.iter().any(|g: &Ident| g.name == name.name) {
2173                self.error_at_with_help(
2174                    name.span,
2175                    format!("duplicate type parameter `{}`", name.name),
2176                    vec!["give each type parameter its own name".to_string()],
2177                );
2178            } else {
2179                generics.push(name);
2180            }
2181            match self.peek().kind {
2182                TokenKind::Comma => {
2183                    self.advance();
2184                    if matches!(self.peek().kind, TokenKind::GreaterThan) {
2185                        self.advance();
2186                        return Some(generics);
2187                    }
2188                }
2189                TokenKind::GreaterThan => {
2190                    self.advance();
2191                    return Some(generics);
2192                }
2193                _ => {
2194                    self.error_at_peek("expected `,` or `>`");
2195                    return None;
2196                }
2197            }
2198        }
2199    }
2200
2201    fn parse_param_list(&mut self, allow_param_properties: bool) -> Option<Vec<ParamDecl>> {
2202        if matches!(self.peek().kind, TokenKind::RightParen) {
2203            return Some(Vec::new());
2204        }
2205        let mut params = Vec::new();
2206        loop {
2207            let param = self.parse_param(allow_param_properties)?;
2208            let is_rest = param.rest;
2209            params.push(param);
2210            match self.peek().kind {
2211                TokenKind::Comma => {
2212                    if is_rest {
2213                        let comma = self.advance();
2214                        self.error_at(comma.span, "rest parameter must be the last parameter");
2215                        return None;
2216                    }
2217                    self.advance();
2218                    if matches!(self.peek().kind, TokenKind::RightParen) {
2219                        break;
2220                    }
2221                }
2222                TokenKind::RightParen => break,
2223                _ => {
2224                    self.error_at_peek("expected `,` or `)`");
2225                    return None;
2226                }
2227            }
2228        }
2229        self.check_parameter_order(params.iter().map(ParamOrder::of_param));
2230        Some(params)
2231    }
2232
2233    fn parse_param(&mut self, allow_param_properties: bool) -> Option<ParamDecl> {
2234        // Parameter properties: `constructor(public x: T)` declares + assigns a
2235        // field. A modifier keyword counts only when a parameter name follows it
2236        // (so a param literally named `public`/`readonly` still parses).
2237        let modifiers = if self.peek_word_is_class_modifier("public")
2238            || self.peek_word_is_class_modifier("private")
2239            || self.peek_word_is_class_modifier("readonly")
2240        {
2241            let span = self.peek().span;
2242            let parsed = self.parse_class_modifiers();
2243            if allow_param_properties {
2244                Some(parsed)
2245            } else {
2246                self.error_at_with_help(
2247                    span,
2248                    "parameter properties are only allowed in a constructor",
2249                    vec![
2250                        "move the `public`/`private`/`readonly` modifier to a constructor parameter"
2251                            .to_string(),
2252                    ],
2253                );
2254                None
2255            }
2256        } else {
2257            None
2258        };
2259
2260        let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
2261            self.advance();
2262            true
2263        } else {
2264            false
2265        };
2266        if modifiers.is_some() && rest {
2267            self.error_at_peek("a parameter property cannot be a rest parameter");
2268            return None;
2269        }
2270
2271        // Placeholder `name` is the empty identifier at the pattern's span;
2272        // the pre-infer lowering pass replaces it with a fresh `#pattern_p_N`.
2273        let (name, pattern) = match self.peek().kind {
2274            TokenKind::LeftBrace | TokenKind::LeftBracket => {
2275                if rest {
2276                    self.error_at_peek("rest parameter cannot be destructured");
2277                    return None;
2278                }
2279                if modifiers.is_some() {
2280                    self.error_at_peek("a parameter property cannot be destructured");
2281                    return None;
2282                }
2283                let binding = self.parse_binding()?;
2284                let placeholder = Ident {
2285                    name: String::new(),
2286                    span: binding.span(),
2287                };
2288                (placeholder, Some(binding))
2289            }
2290            _ => {
2291                let name_tok = self.expect_binding_identifier(if rest {
2292                    "expected identifier after `...`"
2293                } else {
2294                    "expected parameter name"
2295                })?;
2296                let name = self.ident_from_token(&name_tok);
2297                (name, None)
2298            }
2299        };
2300
2301        let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
2302        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2303            self.advance();
2304            Some(self.parse_type_annotation()?)
2305        } else if !rest && matches!(self.peek().kind, TokenKind::Equals) {
2306            None
2307        } else {
2308            if rest {
2309                self.error_at_peek_with_help(
2310                    "rest parameter requires a type annotation",
2311                    vec!["function name(...args: T[]): R { … }".to_string()],
2312                );
2313            } else {
2314                self.error_at_peek_with_help(
2315                    "parameter requires a type annotation",
2316                    vec!["add a type (`x: T`) or a default initializer (`x = value`)".to_string()],
2317                );
2318            }
2319            return None;
2320        };
2321        let default = self.parse_parameter_default(rest, optional)?;
2322        Some(ParamDecl {
2323            name,
2324            ty,
2325            default,
2326            optional,
2327            pattern,
2328            rest,
2329            modifiers,
2330        })
2331    }
2332
2333    fn parse_parameter_optional(&mut self, rest: bool, pattern: bool) -> Option<bool> {
2334        if !matches!(self.peek().kind, TokenKind::Question) {
2335            return Some(false);
2336        }
2337        let question = self.advance();
2338        if rest || pattern {
2339            self.error_at(
2340                question.span,
2341                if rest {
2342                    "a rest parameter cannot be optional"
2343                } else {
2344                    "a destructured parameter cannot be optional; use a default value"
2345                },
2346            );
2347            return None;
2348        }
2349        Some(true)
2350    }
2351
2352    /// The `= value` after a parameter, if any. An optional parameter's default is
2353    /// reported but still parsed, so the rest of the list is checked too.
2354    fn parse_parameter_default(&mut self, rest: bool, optional: bool) -> Option<Option<ExprId>> {
2355        if !matches!(self.peek().kind, TokenKind::Equals) {
2356            return Some(None);
2357        }
2358        if rest {
2359            self.error_rest_parameter_default();
2360            return None;
2361        }
2362        if optional {
2363            self.error_at_peek_with_help(
2364                "an optional parameter cannot have a default value",
2365                vec!["drop the `?`: a parameter with a default is already optional".to_string()],
2366            );
2367        }
2368        self.advance();
2369        Some(Some(self.parse_expression()?))
2370    }
2371
2372    /// Rest's default is the empty array, so an explicit one is rejected.
2373    fn error_rest_parameter_default(&mut self) {
2374        self.error_at_peek_with_help(
2375            "rest parameter cannot have a default value",
2376            vec!["omit the `= …`; an unspecified rest defaults to `[]`".to_string()],
2377        );
2378    }
2379
2380    /// A caller cannot skip an optional argument and still pass a later one, so a
2381    /// required parameter after an optional one is reported. Only the first is, and
2382    /// parsing continues.
2383    fn check_parameter_order<'p>(&mut self, params: impl IntoIterator<Item = ParamOrder<'p>>) {
2384        let mut follows_optional = false;
2385        for param in params {
2386            if param.optional {
2387                follows_optional = true;
2388                continue;
2389            }
2390            if !follows_optional || !param.required {
2391                continue;
2392            }
2393            let label = match param.name {
2394                Some(name) => format!("parameter `{name}`"),
2395                None => "destructured parameter".to_string(),
2396            };
2397            self.error_at_with_help(
2398                param.span,
2399                format!("required {label} cannot follow an optional parameter"),
2400                vec!["make it optional too (`x?: T`), give it a default (`x: T = …`), or move it before the optional parameters".to_string()],
2401            );
2402            return;
2403        }
2404    }
2405
2406    /// Drop a `;` that ASI inserted directly before `next`, where the grammar requires
2407    /// `next` and so no statement can have ended. ASI is a token-stream pre-pass with no
2408    /// parser feedback, so it cannot see that a `}` ends a `do` body rather than a
2409    /// statement, or that the `{` after a signature's return annotation opens its body.
2410    fn eat_asi_semicolon_before(&mut self, next: TokenKind) {
2411        if matches!(self.peek().kind, TokenKind::Semicolon) && self.peek_at(1).kind == next {
2412            self.advance();
2413        }
2414    }
2415
2416    fn parse_block(&mut self) -> Option<StmtId> {
2417        self.eat_asi_semicolon_before(TokenKind::LeftBrace);
2418        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2419            self.error_at_peek("expected `{`");
2420            return None;
2421        }
2422        let open = self.advance();
2423        self.block_depth += 1;
2424
2425        let mut stmts = Vec::new();
2426        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2427            if matches!(self.peek().kind, TokenKind::Semicolon) {
2428                self.advance();
2429                continue;
2430            }
2431            let pos_before = self.pos;
2432            if let Some(id) = self.parse_statement() {
2433                stmts.push(id);
2434            } else {
2435                self.recover();
2436                if self.pos == pos_before && !self.is_at_eof() {
2437                    self.advance();
2438                }
2439            }
2440        }
2441
2442        self.block_depth -= 1;
2443        if !matches!(self.peek().kind, TokenKind::RightBrace) {
2444            self.error_at_peek("expected `}`");
2445            return None;
2446        }
2447        let close = self.advance();
2448
2449        let span = self.span(open.span.start, close.span.end);
2450        parse_arena_result(
2451            self.ast.try_push_stmt(Stmt {
2452                kind: StmtKind::Block(stmts),
2453                span,
2454            }),
2455            &mut self.fatal,
2456        )
2457    }
2458
2459    // Control-flow bodies accept either a braced block or a single statement;
2460    // `if (c) return x;` is identical to `if (c) { return x; }` under our type
2461    // system (forgiveness principle). Downstream passes assume a `Block`, so a
2462    // braceless statement is wrapped in one.
2463    fn parse_control_body(&mut self) -> Option<StmtId> {
2464        if matches!(self.peek().kind, TokenKind::LeftBrace) {
2465            return self.parse_block();
2466        }
2467        // A binding here would go out of scope as soon as it was made. TypeScript
2468        // or JavaScript rejects each of these, so it is reported and then parsed
2469        // as usual. Only a declaration is reported: a `let` followed by
2470        // anything else already fails to parse.
2471        let keyword = match self.peek().kind {
2472            TokenKind::Let => Some(("a", "let")),
2473            TokenKind::Const => Some(("a", "const")),
2474            TokenKind::Function => Some(("a", "function")),
2475            // A nested `class` is reported wherever it is: see `parse_class_decl`.
2476            TokenKind::Interface => Some(("an", "interface")),
2477            TokenKind::Enum => Some(("an", "enum")),
2478            _ if self.peek_identifier_text_is("type") => Some(("a", "type")),
2479            _ => None,
2480        };
2481        // After `let`/`const`, a bracket or brace starts a destructuring
2482        // pattern; after `type`, which is also an ordinary identifier, it is an
2483        // index or nothing a declaration can be.
2484        let declares = match self.peek_at(1).kind {
2485            TokenKind::Identifier => true,
2486            TokenKind::LeftBracket | TokenKind::LeftBrace => !matches!(keyword, Some((_, "type"))),
2487            _ => false,
2488        };
2489        if let Some((article, keyword)) = keyword
2490            && declares
2491        {
2492            self.error_at_peek_with_help(
2493                format!(
2494                    "{article} `{keyword}` declaration can't be the body of a statement without braces"
2495                ),
2496                vec![format!("wrap it in braces: `{{ {keyword} … }}`")],
2497            );
2498        }
2499        self.block_depth += 1;
2500        let stmt = self.parse_statement();
2501        self.block_depth -= 1;
2502        let stmt = stmt?;
2503        let span = parse_arena_result(self.ast.try_stmt(stmt), &mut self.fatal)?.span;
2504        parse_arena_result(
2505            self.ast.try_push_stmt(Stmt {
2506                kind: StmtKind::Block(vec![stmt]),
2507                span,
2508            }),
2509            &mut self.fatal,
2510        )
2511    }
2512
2513    /// A loop body may also be a lone `;`: `while (advance());` does all its work in
2514    /// the header. Branches do not accept it; see `parse_branch_body`.
2515    fn parse_loop_body(&mut self) -> Option<StmtId> {
2516        if !matches!(self.peek().kind, TokenKind::Semicolon) {
2517            return self.parse_control_body();
2518        }
2519        self.empty_body_at_semicolon()
2520    }
2521
2522    /// An `if` or `else` body. Unlike a loop, a branch cannot be a lone `;`: `if (c);`
2523    /// silently detaches the block meant to follow it. The `;` is reported, then
2524    /// consumed as an empty body so an `else` after it doesn't cascade into a second error.
2525    fn parse_branch_body(&mut self, keyword: &str) -> Option<StmtId> {
2526        if !matches!(self.peek().kind, TokenKind::Semicolon) {
2527            return self.parse_control_body();
2528        }
2529        self.error_at_peek_with_help(
2530            format!("`{keyword}` has an empty body"),
2531            vec![format!("remove the `;` after `{keyword}`")],
2532        );
2533        self.empty_body_at_semicolon()
2534    }
2535
2536    /// Consumes a lone `;` as an empty block spanning it.
2537    fn empty_body_at_semicolon(&mut self) -> Option<StmtId> {
2538        let semi = self.advance();
2539        parse_arena_result(
2540            self.ast.try_push_stmt(Stmt {
2541                kind: StmtKind::Block(vec![]),
2542                span: semi.span,
2543            }),
2544            &mut self.fatal,
2545        )
2546    }
2547
2548    fn parse_if(&mut self) -> Option<StmtId> {
2549        self.with_recursion_limit(Self::parse_if_inner)
2550    }
2551
2552    fn parse_if_inner(&mut self) -> Option<StmtId> {
2553        let kw = self.advance();
2554        let condition = self.parse_paren_condition()?;
2555        let then_block = self.parse_branch_body("if")?;
2556
2557        let else_block = if matches!(self.peek().kind, TokenKind::Else) {
2558            self.advance();
2559            if matches!(self.peek().kind, TokenKind::If) {
2560                Some(self.parse_if()?)
2561            } else {
2562                Some(self.parse_branch_body("else")?)
2563            }
2564        } else {
2565            None
2566        };
2567
2568        let end = match else_block {
2569            Some(id) => {
2570                parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
2571                    .span
2572                    .end
2573            }
2574            None => {
2575                parse_arena_result(self.ast.try_stmt(then_block), &mut self.fatal)?
2576                    .span
2577                    .end
2578            }
2579        };
2580
2581        parse_arena_result(
2582            self.ast.try_push_stmt(Stmt {
2583                kind: StmtKind::If {
2584                    condition,
2585                    then_block,
2586                    else_block,
2587                },
2588                span: self.span(kw.span.start, end),
2589            }),
2590            &mut self.fatal,
2591        )
2592    }
2593
2594    fn parse_while(&mut self) -> Option<StmtId> {
2595        let kw = self.advance();
2596        let condition = self.parse_paren_condition()?;
2597        let body = self.parse_loop_body()?;
2598        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2599            .span
2600            .end;
2601
2602        parse_arena_result(
2603            self.ast.try_push_stmt(Stmt {
2604                kind: StmtKind::While { condition, body },
2605                span: self.span(kw.span.start, body_end),
2606            }),
2607            &mut self.fatal,
2608        )
2609    }
2610
2611    // Disambiguates C-style for and for-of via non-consuming lookahead for `of`.
2612    fn parse_for(&mut self) -> Option<StmtId> {
2613        let kw = self.advance();
2614        // `for await (…)`: there are no promises to wait for, so name the
2615        // rewrite and read the loop as a plain `for`.
2616        if self.peek_identifier_text_is("await")
2617            && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
2618        {
2619            let tok = self.advance();
2620            self.error_at_with_help(
2621                tok.span,
2622                "`for await` is not supported",
2623                vec![
2624                    "calls return their values directly: remove `await` and write \
2625                     `for (const e of …)`"
2626                        .to_string(),
2627                ],
2628            );
2629        }
2630        if !matches!(self.peek().kind, TokenKind::LeftParen) {
2631            self.error_at_peek("expected `(` after `for`");
2632            return None;
2633        }
2634        self.advance();
2635
2636        if self.scan_for_of_shape() {
2637            self.parse_for_of_tail(kw)
2638        } else {
2639            self.parse_c_for_tail(kw)
2640        }
2641    }
2642
2643    // Counts bracket/angle depth to skip past optional type annotations.
2644    fn scan_for_of_shape(&self) -> bool {
2645        if !matches!(self.peek().kind, TokenKind::Let | TokenKind::Const) {
2646            return false;
2647        }
2648        // Object patterns are tentatively accepted here; `parse_for_of_tail` rejects them.
2649        let (start, mut depth) = match self.peek_at(1).kind {
2650            TokenKind::Identifier => match self.peek_at(2).kind {
2651                TokenKind::Identifier if self.peek_at_is_word(2, "of") => return true,
2652                TokenKind::Colon => (3, 0i32),
2653                _ => return false,
2654            },
2655            TokenKind::LeftBracket | TokenKind::LeftBrace => (2, 1i32),
2656            _ => return false,
2657        };
2658        let mut i = start;
2659        loop {
2660            match self.peek_at(i).kind {
2661                TokenKind::Eof => return false,
2662                TokenKind::LessThan
2663                | TokenKind::LeftParen
2664                | TokenKind::LeftBracket
2665                | TokenKind::LeftBrace => {
2666                    depth += 1;
2667                }
2668                TokenKind::GreaterThan
2669                | TokenKind::RightParen
2670                | TokenKind::RightBracket
2671                | TokenKind::RightBrace => {
2672                    if depth == 0 {
2673                        return false;
2674                    }
2675                    depth -= 1;
2676                }
2677                // A user type literally named `of` in the annotation would
2678                // misclassify here and error loudly downstream — acceptable.
2679                TokenKind::Identifier if depth == 0 && self.peek_at_is_word(i, "of") => {
2680                    return true;
2681                }
2682                TokenKind::Equals | TokenKind::Semicolon if depth == 0 => return false,
2683                _ => {}
2684            }
2685            i += 1;
2686        }
2687    }
2688
2689    // The trailing `;` of init-statement parsers doubles as the first `;` of the for header.
2690    fn parse_c_for_tail(&mut self, kw: Token) -> Option<StmtId> {
2691        let init = if matches!(self.peek().kind, TokenKind::Semicolon) {
2692            self.advance();
2693            None
2694        } else {
2695            let init_id = match self.peek().kind {
2696                TokenKind::Let => self.parse_let_or_const(false)?,
2697                TokenKind::Const => self.parse_let_or_const(true)?,
2698                _ => self.parse_expression_statement()?,
2699            };
2700            Some(init_id)
2701        };
2702        let for_stmt = self.finish_c_for(kw, init);
2703        if for_stmt.is_none()
2704            && let Some(init) = init
2705        {
2706            self.discard_detached_pattern(init);
2707        }
2708        for_stmt
2709    }
2710
2711    fn finish_c_for(&mut self, kw: Token, init: Option<StmtId>) -> Option<StmtId> {
2712        let condition = if matches!(self.peek().kind, TokenKind::Semicolon) {
2713            None
2714        } else {
2715            Some(self.parse_expression()?)
2716        };
2717        if !matches!(self.peek().kind, TokenKind::Semicolon) {
2718            self.error_at_peek("expected `;` after `for` condition");
2719            return None;
2720        }
2721        self.advance();
2722
2723        let update = if matches!(self.peek().kind, TokenKind::RightParen) {
2724            None
2725        } else {
2726            Some(self.parse_for_update_stmt()?)
2727        };
2728        if !matches!(self.peek().kind, TokenKind::RightParen) {
2729            self.error_at_peek("expected `)` after `for` update");
2730            return None;
2731        }
2732        self.advance();
2733
2734        let body = self.parse_loop_body()?;
2735        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2736            .span
2737            .end;
2738        parse_arena_result(
2739            self.ast.try_push_stmt(Stmt {
2740                kind: StmtKind::For {
2741                    init,
2742                    condition,
2743                    update,
2744                    body,
2745                },
2746                span: self.span(kw.span.start, body_end),
2747            }),
2748            &mut self.fatal,
2749        )
2750    }
2751
2752    /// A destructuring `for` initializer is lowered with its loop, so one whose
2753    /// loop failed to parse would otherwise reach inference unlowered.
2754    fn discard_detached_pattern(&mut self, init: StmtId) {
2755        let Some(stmt) = parse_arena_result(self.ast.try_stmt_mut(init), &mut self.fatal) else {
2756            return;
2757        };
2758        if matches!(
2759            stmt.kind,
2760            StmtKind::LetPattern { .. } | StmtKind::ConstPattern { .. }
2761        ) {
2762            stmt.kind = StmtKind::Block(Vec::new());
2763        }
2764    }
2765
2766    fn parse_for_update_stmt(&mut self) -> Option<StmtId> {
2767        let expr_id = self.parse_expression()?;
2768        let span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
2769        let kind = self.statement_kind_for(expr_id)?;
2770        parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
2771    }
2772
2773    fn parse_for_of_tail(&mut self, kw: Token) -> Option<StmtId> {
2774        let binding_tok = self.advance();
2775        let binding_kind = match binding_tok.kind {
2776            TokenKind::Let => crate::BindingKind::Let,
2777            TokenKind::Const => crate::BindingKind::Const,
2778            _ => return self.invariant_failure("scan_for_of_shape gated this"),
2779        };
2780        let (name, binding) = match self.peek().kind {
2781            TokenKind::LeftBracket => {
2782                let b = self.parse_binding()?;
2783                (None, Some(b))
2784            }
2785            TokenKind::LeftBrace => {
2786                let b = self.parse_binding()?;
2787                self.error_at(
2788                    b.span(),
2789                    "object destructuring is not supported in `for-of`; use array destructuring or unpack inside the loop body",
2790                );
2791                return None;
2792            }
2793            _ => {
2794                let name_tok = self.expect_binding_identifier(
2795                    "expected identifier after `let`/`const` in `for-of`",
2796                )?;
2797                (Some(self.ident_from_token(&name_tok)), None)
2798            }
2799        };
2800        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2801            self.advance();
2802            Some(self.parse_type_annotation()?)
2803        } else {
2804            None
2805        };
2806        if !self.peek_identifier_text_is("of") {
2807            self.error_at_peek("expected `of` in `for-of`");
2808            return None;
2809        }
2810        self.advance();
2811
2812        let iter = self.parse_expression()?;
2813        if !matches!(self.peek().kind, TokenKind::RightParen) {
2814            self.error_at_peek("expected `)` after `for-of` iterable");
2815            return None;
2816        }
2817        self.advance();
2818
2819        let body = self.parse_loop_body()?;
2820        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2821            .span
2822            .end;
2823        let kind = match (binding, name) {
2824            (Some(binding), _) => StmtKind::ForOfPattern {
2825                binding_kind,
2826                binding,
2827                ty,
2828                iter,
2829                body,
2830            },
2831            (None, Some(name)) => StmtKind::ForOf {
2832                binding_kind,
2833                name,
2834                ty,
2835                iter,
2836                body,
2837            },
2838            (None, None) => return self.invariant_failure("either binding or name must be Some"),
2839        };
2840        parse_arena_result(
2841            self.ast.try_push_stmt(Stmt {
2842                kind,
2843                span: self.span(kw.span.start, body_end),
2844            }),
2845            &mut self.fatal,
2846        )
2847    }
2848
2849    fn parse_do_while(&mut self) -> Option<StmtId> {
2850        let kw = self.advance();
2851        let body = self.parse_loop_body()?;
2852        self.eat_asi_semicolon_before(TokenKind::While);
2853        if !matches!(self.peek().kind, TokenKind::While) {
2854            self.error_at_peek("expected `while` after `do` body");
2855            return None;
2856        }
2857        self.advance();
2858        let condition = self.parse_paren_condition()?;
2859        // ECMAScript inserts the terminator after a do-while's `)` unconditionally, so the
2860        // `;` is optional here — ASI can't supply it, since that `)` closes a `while`
2861        // header everywhere else.
2862        let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
2863            self.advance().span.end
2864        } else {
2865            parse_arena_result(self.ast.try_expr(condition), &mut self.fatal)?
2866                .span
2867                .end
2868        };
2869        parse_arena_result(
2870            self.ast.try_push_stmt(Stmt {
2871                kind: StmtKind::DoWhile { body, condition },
2872                span: self.span(kw.span.start, end),
2873            }),
2874            &mut self.fatal,
2875        )
2876    }
2877
2878    // Multiple consecutive `case`/`default` labels share the next body.
2879    fn parse_switch(&mut self) -> Option<StmtId> {
2880        let kw = self.advance();
2881        if !matches!(self.peek().kind, TokenKind::LeftParen) {
2882            self.error_at_peek("expected `(` after `switch`");
2883            return None;
2884        }
2885        self.advance();
2886        let discriminant = self.parse_expression()?;
2887        if !matches!(self.peek().kind, TokenKind::RightParen) {
2888            self.error_at_peek("expected `)` after `switch` discriminant");
2889            return None;
2890        }
2891        self.advance();
2892
2893        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2894            self.error_at_peek("expected `{` to start `switch` body");
2895            return None;
2896        }
2897        let open = self.advance();
2898        self.block_depth += 1;
2899
2900        let mut cases: Vec<SwitchCase> = Vec::new();
2901        let mut default: Option<SwitchDefault> = None;
2902        let mut pending_values: Vec<ExprId> = Vec::new();
2903        let mut pending_label_start: Option<u32> = None;
2904        let mut pending_default: Option<Span> = None;
2905
2906        loop {
2907            match self.peek().kind {
2908                TokenKind::Eof => break,
2909                // ASI inserts `;` after `:` on a newline — treat it as a no-op here.
2910                TokenKind::Semicolon => {
2911                    self.advance();
2912                }
2913                TokenKind::RightBrace => {
2914                    // Case bodies are required (typechecker enforces break/return termination).
2915                    if pending_label_start.is_some() {
2916                        self.error_at_peek(
2917                            "expected case body before `}` — case labels must be followed by statements",
2918                        );
2919                        return None;
2920                    }
2921                    break;
2922                }
2923                TokenKind::Case => {
2924                    let case_kw = self.advance();
2925                    if pending_label_start.is_none() {
2926                        pending_label_start = Some(case_kw.span.start);
2927                    }
2928                    let value = self.parse_expression()?;
2929                    if !matches!(self.peek().kind, TokenKind::Colon) {
2930                        self.error_at_peek("expected `:` after `case` label");
2931                        return None;
2932                    }
2933                    self.advance();
2934                    pending_values.push(value);
2935                }
2936                TokenKind::Default => {
2937                    let kw_tok = self.advance();
2938                    if default.is_some() {
2939                        self.error_at(kw_tok.span, "duplicate `default` clause in `switch`");
2940                        return None;
2941                    }
2942                    if !matches!(self.peek().kind, TokenKind::Colon) {
2943                        self.error_at_peek("expected `:` after `default`");
2944                        return None;
2945                    }
2946                    self.advance();
2947                    if pending_label_start.is_none() {
2948                        pending_label_start = Some(kw_tok.span.start);
2949                    }
2950                    pending_default = Some(kw_tok.span);
2951                }
2952                _ => {
2953                    if pending_label_start.is_none() {
2954                        self.error_at_peek(
2955                            "expected `case` or `default` at the start of a `switch` body",
2956                        );
2957                        return None;
2958                    }
2959                    let Some(label_start) = pending_label_start else {
2960                        return self.invariant_failure("switch arm has no label");
2961                    };
2962                    let body = self.parse_switch_arm_body(label_start)?;
2963                    let arm_span =
2964                        parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
2965                    if !pending_values.is_empty() {
2966                        let first = parse_arena_result(
2967                            self.ast.try_expr(pending_values[0]),
2968                            &mut self.fatal,
2969                        )?
2970                        .span
2971                        .start;
2972                        let case_span = self.span(first, arm_span.end);
2973                        cases.push(SwitchCase {
2974                            values: std::mem::take(&mut pending_values),
2975                            body,
2976                            span: case_span,
2977                        });
2978                    }
2979                    if let Some(def_kw_span) = pending_default.take() {
2980                        default = Some(SwitchDefault {
2981                            body,
2982                            span: self.span(def_kw_span.start, arm_span.end),
2983                        });
2984                    }
2985                    pending_label_start = None;
2986                }
2987            }
2988        }
2989
2990        self.block_depth -= 1;
2991        if !matches!(self.peek().kind, TokenKind::RightBrace) {
2992            self.error_at_peek("expected `}` to close `switch` body");
2993            return None;
2994        }
2995        let close = self.advance();
2996
2997        let _ = open;
2998        parse_arena_result(
2999            self.ast.try_push_stmt(Stmt {
3000                kind: StmtKind::Switch {
3001                    discriminant,
3002                    cases,
3003                    default,
3004                },
3005                span: self.span(kw.span.start, close.span.end),
3006            }),
3007            &mut self.fatal,
3008        )
3009    }
3010
3011    // `body_start` is the label keyword's offset so diagnostics point at the label.
3012    fn parse_switch_arm_body(&mut self, body_start: u32) -> Option<StmtId> {
3013        let mut stmts: Vec<StmtId> = Vec::new();
3014        let mut end_offset = body_start;
3015        loop {
3016            if matches!(
3017                self.peek().kind,
3018                TokenKind::Case | TokenKind::Default | TokenKind::RightBrace | TokenKind::Eof
3019            ) {
3020                break;
3021            }
3022            if matches!(self.peek().kind, TokenKind::Semicolon) {
3023                end_offset = self.peek().span.end;
3024                self.advance();
3025                continue;
3026            }
3027            let pos_before = self.pos;
3028            if let Some(id) = self.parse_statement() {
3029                end_offset = parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
3030                    .span
3031                    .end;
3032                stmts.push(id);
3033            } else {
3034                self.recover();
3035                if self.pos == pos_before && !self.is_at_eof() {
3036                    self.advance();
3037                }
3038                // Recovery consumed tokens but produced no statement; advance the body
3039                // end past them so the arm span can't end before its own case value.
3040                end_offset = end_offset.max(self.prev_token_end());
3041            }
3042        }
3043        let span = self.span(body_start, end_offset);
3044        parse_arena_result(
3045            self.ast.try_push_stmt(Stmt {
3046                kind: StmtKind::Block(stmts),
3047                span,
3048            }),
3049            &mut self.fatal,
3050        )
3051    }
3052
3053    fn parse_break(&mut self) -> Option<StmtId> {
3054        let kw = self.advance();
3055        if !matches!(self.peek().kind, TokenKind::Semicolon) {
3056            self.error_at_peek("expected `;` after `break`");
3057            return None;
3058        }
3059        let semi = self.advance();
3060        parse_arena_result(
3061            self.ast.try_push_stmt(Stmt {
3062                kind: StmtKind::Break,
3063                span: self.span(kw.span.start, semi.span.end),
3064            }),
3065            &mut self.fatal,
3066        )
3067    }
3068
3069    fn parse_continue(&mut self) -> Option<StmtId> {
3070        let kw = self.advance();
3071        if !matches!(self.peek().kind, TokenKind::Semicolon) {
3072            self.error_at_peek("expected `;` after `continue`");
3073            return None;
3074        }
3075        let semi = self.advance();
3076        parse_arena_result(
3077            self.ast.try_push_stmt(Stmt {
3078                kind: StmtKind::Continue,
3079                span: self.span(kw.span.start, semi.span.end),
3080            }),
3081            &mut self.fatal,
3082        )
3083    }
3084
3085    fn parse_return(&mut self) -> Option<StmtId> {
3086        let kw = self.advance();
3087
3088        let value = if matches!(self.peek().kind, TokenKind::Semicolon) {
3089            None
3090        } else {
3091            Some(self.parse_expression()?)
3092        };
3093
3094        if !self.at_statement_end() {
3095            self.error_at_peek("expected `;` after return");
3096            return None;
3097        }
3098        let end = self.finish_statement();
3099
3100        parse_arena_result(
3101            self.ast.try_push_stmt(Stmt {
3102                kind: StmtKind::Return(value),
3103                span: self.span(kw.span.start, end),
3104            }),
3105            &mut self.fatal,
3106        )
3107    }
3108
3109    fn parse_throw(&mut self) -> Option<StmtId> {
3110        let kw = self.advance();
3111
3112        if matches!(self.peek().kind, TokenKind::Semicolon) {
3113            self.error_at_peek_with_help(
3114                "expected expression after `throw`",
3115                vec!["throw new Error(\"message\");".to_string()],
3116            );
3117            return None;
3118        }
3119
3120        let value = self.parse_expression()?;
3121
3122        if !self.at_statement_end() {
3123            self.error_at_peek("expected `;` after `throw`");
3124            return None;
3125        }
3126        let end = self.finish_statement();
3127
3128        parse_arena_result(
3129            self.ast.try_push_stmt(Stmt {
3130                kind: StmtKind::Throw { value },
3131                span: self.span(kw.span.start, end),
3132            }),
3133            &mut self.fatal,
3134        )
3135    }
3136
3137    fn parse_try(&mut self) -> Option<StmtId> {
3138        let kw = self.advance();
3139
3140        let body = self.parse_block()?;
3141
3142        let mut catches: Vec<CatchClause> = Vec::new();
3143        let mut finally: Option<StmtId> = None;
3144        let mut tail_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3145            .span
3146            .end;
3147
3148        loop {
3149            match self.peek().kind {
3150                TokenKind::Catch => {
3151                    if finally.is_some() {
3152                        self.error_at_peek("`catch` clause must appear before `finally`");
3153                        return None;
3154                    }
3155                    let clause = self.parse_catch_clause()?;
3156                    tail_end = clause.span.end;
3157                    catches.push(clause);
3158                }
3159                TokenKind::Finally => {
3160                    if finally.is_some() {
3161                        self.error_at_peek("duplicate `finally` clause");
3162                        return None;
3163                    }
3164                    self.advance();
3165                    let block = self.parse_block()?;
3166                    tail_end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
3167                        .span
3168                        .end;
3169                    finally = Some(block);
3170                }
3171                _ => break,
3172            }
3173        }
3174
3175        if catches.is_empty() && finally.is_none() {
3176            self.error_at(
3177                self.span(kw.span.start, tail_end),
3178                "try requires at least one of `catch` or `finally`",
3179            );
3180            return None;
3181        }
3182
3183        parse_arena_result(
3184            self.ast.try_push_stmt(Stmt {
3185                kind: StmtKind::Try {
3186                    body,
3187                    catches,
3188                    finally,
3189                },
3190                span: self.span(kw.span.start, tail_end),
3191            }),
3192            &mut self.fatal,
3193        )
3194    }
3195
3196    fn parse_catch_clause(&mut self) -> Option<CatchClause> {
3197        let kw = self.advance();
3198
3199        let (binding, ty) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
3200            // This binding is inaccessible to source code, including nested catches.
3201            (
3202                Ident {
3203                    name: "#catch".into(),
3204                    span: kw.span,
3205                },
3206                None,
3207            )
3208        } else {
3209            self.parse_catch_binding()?
3210        };
3211
3212        let body = self.parse_block()?;
3213        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3214            .span
3215            .end;
3216
3217        Some(CatchClause {
3218            binding,
3219            ty,
3220            body,
3221            span: self.span(kw.span.start, body_end),
3222        })
3223    }
3224
3225    fn parse_catch_binding(&mut self) -> Option<(Ident, Option<TypeAnnotation>)> {
3226        if !matches!(self.peek().kind, TokenKind::LeftParen) {
3227            self.error_at_peek("expected `(` after `catch`");
3228            return None;
3229        }
3230        self.advance();
3231        let name_tok = self.expect_binding_identifier("expected catch binding name")?;
3232        let binding = self.ident_from_token(&name_tok);
3233        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3234            self.advance();
3235            if self.reject_any_catch_annotation() {
3236                // The error is reported; parsing continues as an unannotated catch.
3237                None
3238            } else {
3239                Some(self.parse_type_annotation()?)
3240            }
3241        } else {
3242            None
3243        };
3244        if !matches!(self.peek().kind, TokenKind::RightParen) {
3245            self.error_at_peek("expected `)` after catch binding");
3246            return None;
3247        }
3248        self.advance();
3249        Some((binding, ty))
3250    }
3251
3252    /// Reports `catch (e: any)` and consumes the `any`, returning whether it did.
3253    /// Only exactly `any)` is handled; compound types keep the general diagnostic.
3254    fn reject_any_catch_annotation(&mut self) -> bool {
3255        let is_bare_any = self.peek_identifier_text_is("any")
3256            && matches!(self.peek_at(1).kind, TokenKind::RightParen);
3257        if !is_bare_any {
3258            return false;
3259        }
3260        let any_tok = self.advance();
3261        // The general `any` help suggests `unknown`, which a catch binding refuses.
3262        self.error_at_with_help(
3263            any_tok.span,
3264            "`any` is not supported",
3265            vec![
3266                "write `catch (e)` to catch every thrown error, or name an error class, \
3267                 as in `catch (e: RangeError)`, to catch only that type"
3268                    .to_string(),
3269            ],
3270        );
3271        true
3272    }
3273
3274    fn parse_paren_condition(&mut self) -> Option<ExprId> {
3275        if !matches!(self.peek().kind, TokenKind::LeftParen) {
3276            self.error_at_peek("expected `(`");
3277            return None;
3278        }
3279        self.advance();
3280        let expr = self.parse_expression()?;
3281        if !matches!(self.peek().kind, TokenKind::RightParen) {
3282            self.error_at_peek("expected `)`");
3283            return None;
3284        }
3285        self.advance();
3286        Some(expr)
3287    }
3288
3289    fn parse_predicate_or_return_type(
3290        &mut self,
3291        type_pos: TypePos,
3292    ) -> Option<(
3293        Option<TypeAnnotation>,
3294        Option<crate::TypePredicateAnnotation>,
3295    )> {
3296        if matches!(self.peek().kind, TokenKind::Identifier) && self.peek_at_is_word(1, "is") {
3297            let param_tok = self.advance();
3298            let param = self.ident_from_token(&param_tok);
3299            self.advance();
3300            let asserted = self.parse_type_annotation_in(type_pos)?;
3301            let span = self.span(param.span.start, asserted.span.end);
3302            return Some((
3303                None,
3304                Some(crate::TypePredicateAnnotation {
3305                    param,
3306                    asserted,
3307                    span,
3308                }),
3309            ));
3310        }
3311        Some((Some(self.parse_type_annotation_in(type_pos)?), None))
3312    }
3313
3314    fn parse_type_annotation(&mut self) -> Option<TypeAnnotation> {
3315        self.parse_type_annotation_in(TypePos::Anywhere)
3316    }
3317
3318    fn parse_type_annotation_in(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3319        // A leading `|` (prettier's multiline format) is accepted and discarded.
3320        let leading_pipe = matches!(self.peek().kind, TokenKind::Pipe);
3321        if leading_pipe {
3322            self.advance();
3323        }
3324        let first = self.parse_type_array(type_pos)?;
3325        if !matches!(self.peek().kind, TokenKind::Pipe) {
3326            return Some(first);
3327        }
3328        let start = first.span.start;
3329        let mut end = first.span.end;
3330        let mut members = vec![first];
3331        while matches!(self.peek().kind, TokenKind::Pipe) {
3332            self.advance();
3333            let member = self.parse_type_array(type_pos)?;
3334            end = member.span.end;
3335            members.push(member);
3336        }
3337        Some(TypeAnnotation {
3338            kind: TypeAnnotationKind::Union(members),
3339            span: self.span(start, end),
3340        })
3341    }
3342
3343    fn parse_type_array(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3344        self.with_recursion_limit(|parser| parser.parse_type_array_inner(type_pos))
3345    }
3346
3347    fn parse_type_array_inner(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3348        // `keyof` is contextual, not reserved: TypeScript allows it as an ordinary
3349        // identifier, and so does Submilli. It is an operator only when another type
3350        // follows it, which an identifier in type position never does.
3351        if self.is_contextual_type_operator(self.pos, "keyof") {
3352            let kw = self.advance();
3353            let operand = self.parse_type_array(type_pos)?;
3354            let span = self.span(kw.span.start, operand.span.end);
3355            return Some(TypeAnnotation {
3356                kind: TypeAnnotationKind::KeyOf(Box::new(operand)),
3357                span,
3358            });
3359        }
3360        if self.is_contextual_type_operator(self.pos, "readonly") {
3361            return self.parse_readonly_type(type_pos);
3362        }
3363
3364        let mut ty = match self.peek().kind {
3365            // `typeof x` names the type of a *value*. Unlike `keyof` this is already a
3366            // reserved word (the `typeof x === "string"` guard uses it), so it needs no
3367            // contextual check — in type position it is only ever the operator. Built
3368            // here rather than returned early so the `[]` suffix loop below still runs,
3369            // making `typeof a[]` an array of it, as TypeScript parses it too.
3370            TokenKind::Typeof => {
3371                let kw = self.advance();
3372                let first = self.expect_identifier("expected a value name after `typeof`")?;
3373                let mut path = vec![self.type_name(first.span)?];
3374                let mut end = first.span.end;
3375                while matches!(self.peek().kind, TokenKind::Dot) {
3376                    self.advance();
3377                    let seg = self.expect_identifier_name("expected a property name after `.`")?;
3378                    end = seg.span.end;
3379                    path.push(self.type_name(seg.span)?);
3380                }
3381                TypeAnnotation {
3382                    kind: TypeAnnotationKind::TypeOf { path },
3383                    span: self.span(kw.span.start, end),
3384                }
3385            }
3386            TokenKind::Identifier | TokenKind::Void => {
3387                // `void` never starts a qualified path.
3388                let leading_is_identifier = matches!(self.peek().kind, TokenKind::Identifier);
3389                let tok = self.advance();
3390                let first_span = tok.span;
3391                let mut path_end = first_span.end;
3392                let first_name = self.type_name(first_span)?;
3393                let mut path = vec![first_name.clone()];
3394                if leading_is_identifier {
3395                    while matches!(self.peek().kind, TokenKind::Dot) {
3396                        self.advance();
3397                        if !matches!(self.peek().kind, TokenKind::Identifier) {
3398                            self.error_at_peek("expected identifier after `.` in type name");
3399                            return None;
3400                        }
3401                        let seg = self.advance();
3402                        path_end = seg.span.end;
3403                        path.push(self.type_name(seg.span)?);
3404                    }
3405                }
3406                // Type arguments, like the `[]` suffix, must start on the type's line.
3407                let (args, end) = if matches!(self.peek().kind, TokenKind::LessThan)
3408                    && !self.line_break_before_peek()
3409                {
3410                    self.parse_type_argument_list()?
3411                } else {
3412                    (Vec::new(), path_end)
3413                };
3414                let outer_span = self.span(first_span.start, end);
3415                let kind = if path.len() == 1 {
3416                    if first_name.name == "any" {
3417                        self.error_at_with_help(
3418                            first_span,
3419                            "`any` is not supported",
3420                            vec![
3421                                "annotate a concrete type instead — an object shape \
3422                                 like `{ id: string }`, a named `interface`, or \
3423                                 `unknown` (a safe dynamic type you narrow before use)"
3424                                    .to_string(),
3425                            ],
3426                        );
3427                    }
3428                    TypeAnnotationKind::Name {
3429                        name: first_name,
3430                        args,
3431                    }
3432                } else {
3433                    TypeAnnotationKind::Qualified { path, args }
3434                };
3435                TypeAnnotation {
3436                    kind,
3437                    span: outer_span,
3438                }
3439            }
3440            // `null<T>` is meaningless — no type args after `null`.
3441            TokenKind::NullLiteral => {
3442                let tok = self.advance();
3443                TypeAnnotation {
3444                    kind: TypeAnnotationKind::Name {
3445                        name: self.type_name(tok.span)?,
3446                        args: Vec::new(),
3447                    },
3448                    span: tok.span,
3449                }
3450            }
3451            TokenKind::StringLiteral(_) => {
3452                let tok = self.advance();
3453                let span = tok.span;
3454                let TokenKind::StringLiteral(s) = tok.kind else {
3455                    // advance returns the token just matched, before moving the cursor.
3456                    unreachable!("advance preserves the matched StringLiteral variant");
3457                };
3458                TypeAnnotation {
3459                    kind: TypeAnnotationKind::StringLiteral(s),
3460                    span,
3461                }
3462            }
3463            TokenKind::NumberLiteral(_) => {
3464                let tok = self.advance();
3465                let span = tok.span;
3466                let TokenKind::NumberLiteral(v) = tok.kind else {
3467                    // advance returns the token just matched, before moving the cursor.
3468                    unreachable!("advance preserves the matched NumberLiteral variant");
3469                };
3470                // Canonicalize `-0.0` → `0.0` so literal type `0` matches both signs.
3471                let canonical = if v == 0.0 { 0.0 } else { v };
3472                TypeAnnotation {
3473                    kind: TypeAnnotationKind::NumberLiteral(crate::types::LiteralF64(canonical)),
3474                    span,
3475                }
3476            }
3477            TokenKind::BigIntLiteral(_) => {
3478                let tok = self.advance();
3479                let TokenKind::BigIntLiteral(digits) = tok.kind else {
3480                    // advance returns the token just matched, before moving the cursor.
3481                    unreachable!("advance preserves the matched BigIntLiteral variant");
3482                };
3483                TypeAnnotation {
3484                    kind: TypeAnnotationKind::BigIntLiteral(digits),
3485                    span: tok.span,
3486                }
3487            }
3488            TokenKind::Minus
3489                if matches!(
3490                    self.peek_at(1).kind,
3491                    TokenKind::NumberLiteral(_) | TokenKind::BigIntLiteral(_)
3492                ) =>
3493            {
3494                let minus = self.advance().span;
3495                let tok = self.advance();
3496                let span = self.span(minus.start, tok.span.end);
3497                let kind =
3498                    match tok.kind {
3499                        TokenKind::NumberLiteral(v) => TypeAnnotationKind::NumberLiteral(
3500                            crate::types::LiteralF64(if v == 0.0 { 0.0 } else { -v }),
3501                        ),
3502                        TokenKind::BigIntLiteral(digits) => TypeAnnotationKind::BigIntLiteral(
3503                            crate::types::negate_bigint_digits(&digits),
3504                        ),
3505                        // The guard admits only the two numeric literal tokens.
3506                        _ => unreachable!("guarded to a numeric literal after `-`"),
3507                    };
3508                TypeAnnotation { kind, span }
3509            }
3510            TokenKind::BooleanLiteral(value) => {
3511                let span = self.advance().span;
3512                TypeAnnotation {
3513                    kind: TypeAnnotationKind::BooleanLiteral(value),
3514                    span,
3515                }
3516            }
3517            TokenKind::LeftBrace => self.parse_object_type_annotation()?,
3518            TokenKind::LeftParen if self.paren_at_opens_function_type(self.pos, type_pos) => {
3519                self.parse_function_type_annotation()?
3520            }
3521            TokenKind::LeftParen => self.parse_grouped_or_function_type()?,
3522            // Leading `[` is unambiguously a tuple (postfix `[]` can only follow a base type).
3523            TokenKind::LeftBracket => self.parse_tuple_type_annotation()?,
3524            _ => {
3525                self.error_at_peek("expected type");
3526                return None;
3527            }
3528        };
3529        // Array suffixes nest boxed annotations without recursing, so each one
3530        // spends the recursive grammar budget to keep the chain's drop bounded.
3531        let mut array_suffixes = 0usize;
3532        // As in TypeScript, a line break ends the type before `[`: in an object type,
3533        // `a: B` ⏎ `[k: string]: V` is a field followed by an index signature.
3534        while matches!(self.peek().kind, TokenKind::LeftBracket) && !self.line_break_before_peek() {
3535            array_suffixes += 1;
3536            if self.recursion_depth.saturating_add(array_suffixes) >= MAX_PARSE_DEPTH {
3537                self.stop_at_recursion_limit();
3538                return None;
3539            }
3540            let open = self.advance();
3541            if !matches!(self.peek().kind, TokenKind::RightBracket) {
3542                self.error_at(open.span, "expected `]` to close array type");
3543                return None;
3544            }
3545            let close = self.advance();
3546            let span = self.span(ty.span.start, close.span.end);
3547            ty = TypeAnnotation {
3548                kind: TypeAnnotationKind::Array(Box::new(ty)),
3549                span,
3550            };
3551        }
3552        Some(ty)
3553    }
3554
3555    /// `readonly T[]` or `readonly [A, B]`. The operand binds like `keyof`'s, so
3556    /// `readonly T[][]` is a readonly array of mutable arrays and
3557    /// `readonly T[] | null` a union with a readonly member.
3558    fn parse_readonly_type(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3559        let kw = self.advance();
3560        let opens_group = matches!(self.peek().kind, TokenKind::LeftParen);
3561        let operand = self.parse_type_array(type_pos)?;
3562        // `readonly (T[])` is rejected as TypeScript rejects it: the operand must be
3563        // written as an array or tuple type, not grouped into one.
3564        let grouped = opens_group
3565            && matches!(
3566                self.pos
3567                    .checked_sub(1)
3568                    .and_then(|index| self.tokens.get(index))
3569                    .map(|token| &token.kind),
3570                Some(TokenKind::RightParen)
3571            );
3572        if grouped
3573            || !matches!(
3574                operand.kind,
3575                TypeAnnotationKind::Array(_) | TypeAnnotationKind::Tuple(_)
3576            )
3577        {
3578            self.error_at_with_help(
3579                kw.span,
3580                "`readonly` only applies to array and tuple types",
3581                vec![
3582                    "write `readonly T[]` or `readonly [A, B]`; to protect an object's \
3583                     properties, mark each one `readonly` instead"
3584                        .to_string(),
3585                ],
3586            );
3587            return Some(operand);
3588        }
3589        let span = self.span(kw.span.start, operand.span.end);
3590        Some(TypeAnnotation {
3591            kind: TypeAnnotationKind::Readonly(Box::new(operand)),
3592            span,
3593        })
3594    }
3595
3596    /// In type position `(` is ambiguous: a function type's parameter list, or a
3597    /// grouping paren the postfix `[]` / `|` then composes with. Decided by what the
3598    /// parens *contain*, not by what follows them — a trailing `=>` can belong to an
3599    /// enclosing arrow function's body (`(): (string | null) => null`), so looking
3600    /// past the `)` would read that return type as a parameter list. The exception is
3601    /// `(T)` / `(T, U)`, spelled identically either way, which only a trailing `=>`
3602    /// can break.
3603    ///
3604    /// Empty parens count as a parameter list: `()` groups nothing, so the
3605    /// function-type parse's "expected `=>`" is the message that names the fix.
3606    fn paren_at_opens_function_type(&self, index: usize, type_pos: TypePos) -> bool {
3607        let after = |offset: usize| self.tokens.get(index + offset).map(|t| &t.kind);
3608        match (after(1), after(2)) {
3609            // Only a parameter list is empty or starts with a rest element.
3610            (Some(TokenKind::RightParen | TokenKind::DotDotDot), _) => true,
3611            // A named, annotated parameter — no type starts this way.
3612            (Some(TokenKind::Identifier), Some(TokenKind::Colon | TokenKind::Question)) => true,
3613            // `(T)` and `(T, U)` are spelled exactly like a grouped type. v1
3614            // rejects bare parameter names, and a trailing `=>` is the only sign
3615            // the writer meant one — routing those to the function-type parse
3616            // keeps its "params require named annotations" message reachable in
3617            // declaration position (`type F = (T) => R`). In an arrow's *return*
3618            // annotation the trailing `=>` is the body's, so `(): (T) => x` loses
3619            // that message and falls back to the expression parse; dropping the
3620            // redundant parens is the fix there.
3621            (Some(TokenKind::Identifier), Some(TokenKind::Comma | TokenKind::RightParen)) => {
3622                type_pos == TypePos::Anywhere
3623                    && self.index_after_matching_paren(index).is_some_and(|i| {
3624                        matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow))
3625                    })
3626            }
3627            // Anything else can only begin a type, so the parens group.
3628            _ => false,
3629        }
3630    }
3631
3632    /// Parse a `(` the contents rule called a group. If that fails and a `=>` follows
3633    /// the matching `)`, the author was writing a function type after all — a typo in
3634    /// its parameter list is what made it look like a group — so re-parse that way to
3635    /// recover the diagnostic naming the parameter rule instead of a stray-`)` error.
3636    fn parse_grouped_or_function_type(&mut self) -> Option<TypeAnnotation> {
3637        let open = self.pos;
3638        let diagnostics_before = self.diagnostics.len();
3639        if let Some(ty) = self.parse_grouped_type_annotation() {
3640            return Some(ty);
3641        }
3642        let arrow_follows = self
3643            .index_after_matching_paren(open)
3644            .is_some_and(|i| matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)));
3645        if !arrow_follows {
3646            return None;
3647        }
3648        self.pos = open;
3649        self.diagnostics.truncate(diagnostics_before);
3650        self.parse_function_type_annotation()
3651    }
3652
3653    /// A parenthesized type is its inner type — the parens only group, so no node
3654    /// records them. The span covers them so a diagnostic points at what was written.
3655    fn parse_grouped_type_annotation(&mut self) -> Option<TypeAnnotation> {
3656        let open = self.advance();
3657        let inner = self.parse_type_annotation()?;
3658        if !matches!(self.peek().kind, TokenKind::RightParen) {
3659            self.error_at_peek("expected `)` to close a parenthesized type");
3660            return None;
3661        }
3662        let close = self.advance();
3663        Some(TypeAnnotation {
3664            span: self.span(open.span.start, close.span.end),
3665            ..inner
3666        })
3667    }
3668
3669    // Param names are required — bare `(T, U) => R` is rejected.
3670    fn parse_function_type_annotation(&mut self) -> Option<TypeAnnotation> {
3671        let open = self.advance();
3672        let params = self.parse_function_type_params()?;
3673        if !matches!(self.peek().kind, TokenKind::Arrow) {
3674            self.error_at_peek("expected `=>` in function-type annotation");
3675            return None;
3676        }
3677        self.advance();
3678        let return_type = self.parse_type_annotation()?;
3679        let end = return_type.span.end;
3680        Some(TypeAnnotation {
3681            kind: TypeAnnotationKind::Function {
3682                params,
3683                return_type: Box::new(return_type),
3684            },
3685            span: self.span(open.span.start, end),
3686        })
3687    }
3688
3689    /// The `(…)` of a function type, its `(` already consumed and its `)` consumed here.
3690    /// Shared with a type literal's method members, which spell the same parameter list
3691    /// before a `:` rather than a `=>`.
3692    fn parse_function_type_params(&mut self) -> Option<Vec<TypeAnnotationField>> {
3693        let mut params: Vec<TypeAnnotationField> = Vec::new();
3694        if !matches!(self.peek().kind, TokenKind::RightParen) {
3695            loop {
3696                let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
3697                    self.advance();
3698                    true
3699                } else {
3700                    false
3701                };
3702                let name_tok = self.expect_binding_identifier(if rest {
3703                    "expected identifier after `...` in function-type annotation"
3704                } else {
3705                    "expected parameter name in function-type annotation"
3706                })?;
3707                let name = self.ident_from_token(&name_tok);
3708                let optional = self.parse_parameter_optional(rest, false)?;
3709                if !matches!(self.peek().kind, TokenKind::Colon) {
3710                    if rest {
3711                        self.error_at_peek_with_help(
3712                            "rest parameter requires a type annotation",
3713                            vec!["(...args: T[]) => R".to_string()],
3714                        );
3715                    } else {
3716                        self.error_at_peek(
3717                            "expected `:` after parameter name (function-type params \
3718                             require named annotations in v1)",
3719                        );
3720                    }
3721                    return None;
3722                }
3723                self.advance();
3724                let ty = self.parse_type_annotation()?;
3725                if rest && !is_rest_array_annotation(&ty) {
3726                    self.error_at(ty.span, "rest parameter type must be an array");
3727                    return None;
3728                }
3729                if matches!(self.peek().kind, TokenKind::Equals) {
3730                    if rest {
3731                        self.error_rest_parameter_default();
3732                    } else {
3733                        self.error_at_peek(
3734                            "default values in function-type annotations are not yet supported",
3735                        );
3736                    }
3737                    return None;
3738                }
3739                params.push(TypeAnnotationField {
3740                    name,
3741                    ty,
3742                    optional,
3743                    readonly: false,
3744                    rest,
3745                    method: false,
3746                });
3747                match self.peek().kind {
3748                    TokenKind::Comma => {
3749                        if rest {
3750                            let comma = self.advance();
3751                            self.error_at(comma.span, "rest parameter must be the last parameter");
3752                            return None;
3753                        }
3754                        self.advance();
3755                        if matches!(self.peek().kind, TokenKind::RightParen) {
3756                            break;
3757                        }
3758                    }
3759                    TokenKind::RightParen => break,
3760                    _ => {
3761                        self.error_at_peek("expected `,` or `)`");
3762                        return None;
3763                    }
3764                }
3765            }
3766            self.check_parameter_order(params.iter().map(ParamOrder::of_field));
3767        }
3768        if !matches!(self.peek().kind, TokenKind::RightParen) {
3769            self.error_at_peek("expected `)`");
3770            return None;
3771        }
3772        self.advance();
3773        Some(params)
3774    }
3775
3776    fn parse_tuple_type_annotation(&mut self) -> Option<TypeAnnotation> {
3777        let open = self.advance();
3778        if matches!(self.peek().kind, TokenKind::RightBracket) {
3779            let close = self.peek().span;
3780            self.error_at(
3781                self.span(open.span.start, close.end),
3782                "tuple types must have at least one element",
3783            );
3784            return None;
3785        }
3786        let mut elements: Vec<TypeAnnotation> = Vec::new();
3787        loop {
3788            if matches!(self.peek().kind, TokenKind::DotDotDot) {
3789                self.error_at_with_help(
3790                    self.peek().span,
3791                    "rest elements in tuple types are not supported",
3792                    vec!["use an array type `T[]` for a list of varying length".to_string()],
3793                );
3794                return None;
3795            }
3796            // Labels may be mixed with unlabeled elements, as TypeScript allows
3797            // since 5.2.
3798            let label = self.skip_tuple_element_label();
3799            let mut ty = self.parse_type_annotation()?;
3800            let suffix_optional = matches!(self.peek().kind, TokenKind::Question);
3801            if suffix_optional && label != TupleElementLabel::None {
3802                self.error_at_peek_with_help(
3803                    "put the optional marker after the tuple element label",
3804                    vec!["write `name?: T` instead of `name: T?`".to_string()],
3805                );
3806                return None;
3807            }
3808            let optional = label == TupleElementLabel::Optional || suffix_optional;
3809            if optional {
3810                let end = if suffix_optional {
3811                    self.advance().span.end
3812                } else {
3813                    ty.span.end
3814                };
3815                let span = self.span(ty.span.start, end);
3816                ty = TypeAnnotation {
3817                    kind: TypeAnnotationKind::Optional(Box::new(ty)),
3818                    span,
3819                };
3820            } else if elements
3821                .iter()
3822                .any(|element| matches!(element.kind, TypeAnnotationKind::Optional(_)))
3823            {
3824                self.error_at(
3825                    ty.span,
3826                    "a required tuple element cannot follow an optional element",
3827                );
3828                return None;
3829            }
3830            elements.push(ty);
3831            match self.peek().kind {
3832                TokenKind::Comma => {
3833                    self.advance();
3834                    if matches!(self.peek().kind, TokenKind::RightBracket) {
3835                        break;
3836                    }
3837                }
3838                TokenKind::RightBracket => break,
3839                _ => {
3840                    self.error_at_peek("expected `,` or `]`");
3841                    return None;
3842                }
3843            }
3844        }
3845        if !matches!(self.peek().kind, TokenKind::RightBracket) {
3846            self.error_at_peek("expected `]`");
3847            return None;
3848        }
3849        let close = self.advance();
3850        Some(TypeAnnotation {
3851            kind: TypeAnnotationKind::Tuple(elements),
3852            span: self.span(open.span.start, close.span.end),
3853        })
3854    }
3855
3856    /// Labels document positions; retain only whether the label was optional.
3857    fn skip_tuple_element_label(&mut self) -> TupleElementLabel {
3858        if !is_property_name(&self.peek().kind) {
3859            return TupleElementLabel::None;
3860        }
3861        match self.peek_at(1).kind {
3862            TokenKind::Colon => {
3863                self.advance();
3864                self.advance();
3865                TupleElementLabel::Required
3866            }
3867            TokenKind::Question if matches!(self.peek_at(2).kind, TokenKind::Colon) => {
3868                self.advance();
3869                self.advance();
3870                self.advance();
3871                TupleElementLabel::Optional
3872            }
3873            _ => TupleElementLabel::None,
3874        }
3875    }
3876
3877    /// `[]` or `[]: V` — an index signature with no parameter, which TypeScript
3878    /// reads and rejects.
3879    fn peek_is_parameterless_index_signature(&self) -> bool {
3880        matches!(self.peek().kind, TokenKind::LeftBracket)
3881            && matches!(self.peek_at(1).kind, TokenKind::RightBracket)
3882    }
3883
3884    /// Diagnoses and skips a parameterless index signature (see
3885    /// `peek_is_parameterless_index_signature`) so the members after it still parse.
3886    /// `member_start` is where the member begins, at a `readonly` modifier if any.
3887    fn skip_parameterless_index_signature(&mut self, member_start: u32) -> Option<()> {
3888        self.advance();
3889        let close = self.advance();
3890        self.error_at_with_help(
3891            self.span(member_start, close.span.end),
3892            "an index signature must declare exactly one parameter",
3893            vec!["write `[key: string]: V`".to_string()],
3894        );
3895        if matches!(self.peek().kind, TokenKind::Colon) {
3896            self.advance();
3897            self.parse_type_annotation()?;
3898        }
3899        Some(())
3900    }
3901
3902    fn parse_index_signature(&mut self, readonly: bool) -> Option<crate::IndexSignatureAnnotation> {
3903        let open = self.advance();
3904        let parameter = self.expect_property_ident("expected index parameter name")?;
3905        self.reject_eval_or_arguments(parameter.span);
3906        if !matches!(self.peek().kind, TokenKind::Colon) {
3907            self.error_at_peek("expected `:` after index parameter name");
3908            return None;
3909        }
3910        self.advance();
3911        if !self.peek_identifier_text_is("string") {
3912            self.error_at_peek("index signatures require string keys; use `[key: string]: V`");
3913            return None;
3914        }
3915        self.advance();
3916        if !matches!(self.peek().kind, TokenKind::RightBracket) {
3917            self.error_at_peek("expected `]` after string index key type");
3918            return None;
3919        }
3920        self.advance();
3921        if !matches!(self.peek().kind, TokenKind::Colon) {
3922            self.error_at_peek("expected `:` before index value type");
3923            return None;
3924        }
3925        self.advance();
3926        let value = self.parse_type_annotation()?;
3927        Some(crate::IndexSignatureAnnotation {
3928            span: self.span(open.span.start, value.span.end),
3929            value,
3930            readonly,
3931        })
3932    }
3933
3934    fn parse_object_type_annotation(&mut self) -> Option<TypeAnnotation> {
3935        let open = self.advance();
3936        let mut fields: Vec<TypeAnnotationField> = Vec::new();
3937        let mut index = None;
3938        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
3939            && !self.peek_starts_top_level_item()
3940        {
3941            let member_start = self.peek().span.start;
3942            let readonly = self.eat_readonly_property_modifier();
3943            if self.peek_is_parameterless_index_signature() {
3944                self.skip_parameterless_index_signature(member_start)?;
3945            } else if self.peek_is_construct_signature() {
3946                self.reject_construct_signature()?;
3947            } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
3948                let signature = self.parse_index_signature(readonly)?;
3949                if index.is_some() {
3950                    self.error_at_with_help(
3951                        signature.span,
3952                        "duplicate string index signature",
3953                        vec![],
3954                    );
3955                }
3956                index = Some(Box::new(signature));
3957            } else {
3958                let field = self.parse_object_type_field(readonly, member_start)?;
3959                if let Some(existing) = fields.iter().find(|f| f.name.name == field.name.name) {
3960                    self.diagnostics.push(Diagnostic {
3961                        severity: Severity::Error,
3962                        span: field.name.span,
3963                        message: format!("duplicate field `{}` in object type", field.name.name),
3964                        help: vec![],
3965                        notes: vec![(existing.name.span, "first defined here".into())],
3966                    });
3967                }
3968                fields.push(field);
3969            }
3970            self.finish_type_member(self.prev_token_end(), "expected `;`, `,`, or `}`")?;
3971        }
3972        if !matches!(self.peek().kind, TokenKind::RightBrace) {
3973            self.error_at_peek("expected `}`");
3974            return None;
3975        }
3976        let close = self.advance();
3977        Some(TypeAnnotation {
3978            kind: TypeAnnotationKind::Object { index, fields },
3979            span: self.span(open.span.start, close.span.end),
3980        })
3981    }
3982
3983    /// A declaration or an `import` statement: what a top-level item starts with.
3984    /// `import(…)` and `import.meta` are expressions, not statements.
3985    fn peek_starts_top_level_item(&self) -> bool {
3986        let import_statement = matches!(self.peek().kind, TokenKind::Import)
3987            && matches!(
3988                self.peek_at(1).kind,
3989                TokenKind::LeftBrace
3990                    | TokenKind::Star
3991                    | TokenKind::Identifier
3992                    | TokenKind::StringLiteral(_)
3993            );
3994        import_statement || self.peek_starts_declaration()
3995    }
3996
3997    /// Whether the next tokens start a declaration (`function f`, `class C`, `type T`,
3998    /// …, optionally after `export`, `declare`, `async` or `abstract`) or an export
3999    /// statement (`export {`, `export *`, `export default`), which no type or class
4000    /// member can: a keyword is a member name only before `:`, `?` or `(`. A member
4001    /// list that reaches one was left unclosed, so it stops there and leaves the
4002    /// declaration to be parsed.
4003    fn peek_starts_declaration(&self) -> bool {
4004        if matches!(self.peek().kind, TokenKind::Export)
4005            && matches!(
4006                self.peek_at(1).kind,
4007                TokenKind::Default | TokenKind::LeftBrace | TokenKind::Star
4008            )
4009        {
4010            return true;
4011        }
4012        let mut offset = 0;
4013        while self.peek_at_is_declaration_modifier(offset) {
4014            offset += 1;
4015        }
4016        let declaration_keyword = matches!(
4017            self.peek_at(offset).kind,
4018            TokenKind::Function
4019                | TokenKind::Class
4020                | TokenKind::Let
4021                | TokenKind::Const
4022                | TokenKind::Enum
4023                | TokenKind::Interface
4024        ) || self.peek_at_is_word(offset, "type");
4025        declaration_keyword && matches!(self.peek_at(offset + 1).kind, TokenKind::Identifier)
4026    }
4027
4028    /// `export`, `declare`, `async` or `abstract` before a declaration keyword.
4029    fn peek_at_is_declaration_modifier(&self, offset: usize) -> bool {
4030        matches!(self.peek_at(offset).kind, TokenKind::Export)
4031            || ["declare", "async", "abstract"]
4032                .iter()
4033                .any(|word| self.peek_at_is_word(offset, word))
4034    }
4035
4036    /// `new (params): T` or `new <T>(params): T` inside a type literal: a construct
4037    /// signature, which TypeScript reads where it would otherwise see a method named
4038    /// `new`. A quoted `"new"()`, an optional `new?()` and a field `new: T` are
4039    /// ordinary members, as in TypeScript.
4040    fn peek_is_construct_signature(&self) -> bool {
4041        matches!(self.peek().kind, TokenKind::New)
4042            && matches!(
4043                self.peek_at(1).kind,
4044                TokenKind::LeftParen | TokenKind::LessThan
4045            )
4046    }
4047
4048    /// Only interfaces declare construct signatures, so one in a type literal (see
4049    /// `peek_is_construct_signature`) is diagnosed, then skipped rather than misread
4050    /// as a method named `new`.
4051    fn reject_construct_signature(&mut self) -> Option<()> {
4052        let keyword = self.advance();
4053        self.error_at_with_help(
4054            keyword.span,
4055            "construct signatures are not supported in object types",
4056            vec![
4057                "declare an `interface` with the `new (…)` signature, or pass a factory \
4058                 function such as `() => T`"
4059                    .to_string(),
4060            ],
4061        );
4062        if matches!(self.peek().kind, TokenKind::LessThan) {
4063            self.parse_generic_param_list()?;
4064        }
4065        if !matches!(self.peek().kind, TokenKind::LeftParen) {
4066            self.error_at_peek("expected `(` after construct signature type parameters");
4067            return None;
4068        }
4069        self.parse_object_type_method_signature(keyword.span.start)?;
4070        Some(())
4071    }
4072
4073    /// `name: T`, `name?: T` or the method signature `name(params): T`.
4074    fn parse_object_type_field(
4075        &mut self,
4076        readonly: bool,
4077        member_start: u32,
4078    ) -> Option<TypeAnnotationField> {
4079        let name = self.expect_property_ident("expected field name in object type")?;
4080        // `name?: T` — omittable at construction; reads widen to `T | undefined`.
4081        let optional = matches!(self.peek().kind, TokenKind::Question);
4082        if optional {
4083            self.advance();
4084        }
4085        let method = matches!(self.peek().kind, TokenKind::LeftParen);
4086        let ty = if method {
4087            if readonly {
4088                self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
4089            }
4090            self.parse_object_type_method_signature(name.span.start)?
4091        } else {
4092            if !matches!(self.peek().kind, TokenKind::Colon) {
4093                self.error_at_peek("expected `:` after field name");
4094                return None;
4095            }
4096            self.advance();
4097            self.parse_type_annotation()?
4098        };
4099        Some(TypeAnnotationField {
4100            name,
4101            ty,
4102            optional,
4103            readonly,
4104            rest: false,
4105            method,
4106        })
4107    }
4108
4109    /// `m(params): T` inside a type literal, spelled as the function-typed field
4110    /// `m: (params) => T` — the two are interchangeable in a structural shape, and
4111    /// interfaces already accept both spellings for the same member. `start` is the
4112    /// member name's offset, so the annotation's span covers the name too.
4113    fn parse_object_type_method_signature(&mut self, start: u32) -> Option<TypeAnnotation> {
4114        self.advance();
4115        let params = self.parse_function_type_params()?;
4116        if !matches!(self.peek().kind, TokenKind::Colon) {
4117            self.error_at_peek("expected `:` and return type after method parameters");
4118            return None;
4119        }
4120        self.advance();
4121        let return_type = self.parse_type_annotation()?;
4122        let end = return_type.span.end;
4123        Some(TypeAnnotation {
4124            kind: TypeAnnotationKind::Function {
4125                params,
4126                return_type: Box::new(return_type),
4127            },
4128            span: self.span(start, end),
4129        })
4130    }
4131
4132    /// An identifier that names a binding or a type, which rejects the words strict
4133    /// mode reserves (see [`STRICT_MODE_RESERVED_WORDS`]).
4134    fn expect_identifier(&mut self, message: &str) -> Option<Token> {
4135        let tok = self.expect_identifier_name(message)?;
4136        self.reject_strict_mode_reserved_word(&tok);
4137        Some(tok)
4138    }
4139
4140    /// The name a variable, parameter, function or `catch` binds. Strict mode also
4141    /// forbids `eval` and `arguments` there, though not as a class, enum or type
4142    /// name (TypeScript's TS1100 covers the same positions).
4143    fn expect_binding_identifier(&mut self, message: &str) -> Option<Token> {
4144        let tok = self.expect_identifier(message)?;
4145        self.reject_eval_or_arguments(tok.span);
4146        Some(tok)
4147    }
4148
4149    /// An identifier in a position where strict mode's reserved words are allowed: an
4150    /// enum member, a property, or the name an import specifier refers to.
4151    fn expect_identifier_name(&mut self, message: &str) -> Option<Token> {
4152        if matches!(self.peek().kind, TokenKind::Identifier) {
4153            return Some(self.advance());
4154        }
4155        if is_reserved_identifier_word(&self.peek().kind) {
4156            let tok = self.advance();
4157            let keyword = &self.source[tok.span.start as usize..tok.span.end as usize];
4158            self.error_at_with_help(
4159                tok.span,
4160                format!("`{keyword}` is a reserved keyword and can't be used as a name"),
4161                vec![format!(
4162                    "rename it, for example: `{}`",
4163                    reserved_keyword_rename_example(keyword)
4164                )],
4165            );
4166            return None;
4167        }
4168        self.error_at_peek(message.to_string());
4169        None
4170    }
4171
4172    /// Reports a binding named by a word strict mode reserves. Parsing continues
4173    /// with the name: nothing else about it is wrong.
4174    fn reject_strict_mode_reserved_word(&mut self, tok: &Token) {
4175        let word = &self.source[tok.span.start as usize..tok.span.end as usize];
4176        if !STRICT_MODE_RESERVED_WORDS.contains(&word) {
4177            return;
4178        }
4179        self.error_at_with_help(
4180            tok.span,
4181            format!("`{word}` is a reserved word in strict mode and can't be used as a name"),
4182            vec![format!(
4183                "rename it, for example: `{}`",
4184                reserved_keyword_rename_example(word)
4185            )],
4186        );
4187    }
4188
4189    /// Reports a binding named `eval` or `arguments`. Parsing continues with the
4190    /// name: nothing else about it is wrong.
4191    fn reject_eval_or_arguments(&mut self, span: Span) {
4192        let word = &self.source[span.start as usize..span.end as usize];
4193        if !matches!(word, "eval" | "arguments") {
4194            return;
4195        }
4196        self.error_at_with_help(
4197            span,
4198            format!("`{word}` can't name a binding in strict mode"),
4199            vec![format!(
4200                "rename it, for example: `{}`",
4201                reserved_keyword_rename_example(word)
4202            )],
4203        );
4204    }
4205
4206    fn ident_from_token(&self, tok: &Token) -> Ident {
4207        Ident {
4208            name: self.source[tok.span.start as usize..tok.span.end as usize].to_string(),
4209            span: tok.span,
4210        }
4211    }
4212
4213    fn property_ident_from_token(&self, tok: &Token) -> Ident {
4214        if let TokenKind::StringLiteral(s) = &tok.kind {
4215            Ident {
4216                name: s.clone(),
4217                span: tok.span,
4218            }
4219        } else {
4220            self.ident_from_token(tok)
4221        }
4222    }
4223
4224    fn expect_property_name(&mut self, message: &str) -> Option<Token> {
4225        if !is_property_name(&self.peek().kind) {
4226            self.error_at_peek(message.to_string());
4227            return None;
4228        }
4229        Some(self.advance())
4230    }
4231
4232    fn expect_property_ident(&mut self, message: &str) -> Option<Ident> {
4233        if !is_property_name(&self.peek().kind)
4234            && !matches!(self.peek().kind, TokenKind::StringLiteral(_))
4235        {
4236            self.error_at_peek(message.to_string());
4237            return None;
4238        }
4239        let tok = self.advance();
4240        Some(self.property_ident_from_token(&tok))
4241    }
4242
4243    /// The span of a `readonly` modifier that [`Parser::eat_readonly_property_modifier`]
4244    /// consumed at `member_start`.
4245    fn readonly_modifier_span(&self, member_start: u32) -> Span {
4246        self.span(member_start, member_start + "readonly".len() as u32)
4247    }
4248
4249    /// Reports `readonly` on a method, accessor or method signature, as TypeScript
4250    /// does (TS1024). Parsing continues: the modifier changes nothing else.
4251    fn reject_readonly_modifier(&mut self, span: Span) {
4252        self.error_at_with_help(
4253            span,
4254            "`readonly` can only modify a property or index signature",
4255            vec!["remove `readonly`".to_string()],
4256        );
4257    }
4258
4259    fn eat_readonly_property_modifier(&mut self) -> bool {
4260        if self.peek_identifier_text_is("readonly")
4261            && !self.line_break_after_peek()
4262            && (self.peek_starts_property_after_readonly()
4263                || matches!(self.peek_at(1).kind, TokenKind::LeftBracket))
4264        {
4265            self.advance();
4266            return true;
4267        }
4268        false
4269    }
4270
4271    fn peek_starts_property_after_readonly(&self) -> bool {
4272        let name = &self.peek_at(1).kind;
4273        let after_name = &self.peek_at(2).kind;
4274        let after_optional = &self.peek_at(3).kind;
4275        // A member starts its type with `:`, or its parameter list with `(` — otherwise
4276        // `readonly` is the member's own name.
4277        let starts_member_type =
4278            |kind: &TokenKind| matches!(kind, TokenKind::Colon | TokenKind::LeftParen);
4279        (is_property_name(name) || matches!(name, TokenKind::StringLiteral(_)))
4280            && (starts_member_type(after_name)
4281                || matches!(after_name, TokenKind::Question) && starts_member_type(after_optional))
4282    }
4283
4284    /// `as` and `satisfies`, which lex as identifiers but continue an expression.
4285    fn peek_at_is_binary_word(&self, offset: usize) -> bool {
4286        self.peek_at_is_word(offset, "as") || self.peek_at_is_word(offset, "satisfies")
4287    }
4288
4289    fn peek_identifier_text_is(&self, expected: &str) -> bool {
4290        self.token_is_word(self.peek(), expected)
4291    }
4292
4293    fn peek_at_is_word(&self, offset: usize, expected: &str) -> bool {
4294        self.token_is_word(self.peek_at(offset), expected)
4295    }
4296
4297    fn token_is_word(&self, tok: &Token, expected: &str) -> bool {
4298        matches!(tok.kind, TokenKind::Identifier)
4299            && &self.source[tok.span.start as usize..tok.span.end as usize] == expected
4300    }
4301
4302    /// `delete` is lexed as an identifier: `map.delete(k)`, a `delete()` method and
4303    /// `submilli:http`'s `delete(url)` function all use the name. So it is the
4304    /// operator only when an operand that can't be an argument list follows it, and
4305    /// `delete (o.x)` stays a call. `as` and `satisfies` are lexed as identifiers too:
4306    /// `delete as unknown` casts the function value, while `delete as.x` deletes from a
4307    /// binding named `as`.
4308    fn at_delete_operator(&self) -> bool {
4309        if !self.peek_identifier_text_is("delete") {
4310            return false;
4311        }
4312        if !matches!(
4313            self.peek_at(1).kind,
4314            TokenKind::Identifier | TokenKind::This
4315        ) {
4316            return false;
4317        }
4318        let next_is_cast_keyword =
4319            self.peek_at_is_word(1, "as") || self.peek_at_is_word(1, "satisfies");
4320        !next_is_cast_keyword || cannot_start_type(&self.peek_at(2).kind)
4321    }
4322
4323    /// `type X …` commits to a type-alias declaration only when followed by an
4324    /// identifier, mirroring TypeScript; `type = 5`, `type;`, `type(x)` stay
4325    /// expression statements over a binding named `type`.
4326    fn at_type_alias_head(&self) -> bool {
4327        self.peek_identifier_text_is("type")
4328            && matches!(self.peek_at(1).kind, TokenKind::Identifier)
4329    }
4330
4331    /// Assignment is the lowest-precedence expression and right-associative:
4332    /// `a = b = c` assigns `c` to both, and yields it.
4333    fn parse_expression(&mut self) -> Option<ExprId> {
4334        self.with_recursion_limit(Self::parse_expression_inner)
4335    }
4336
4337    fn parse_expression_inner(&mut self) -> Option<ExprId> {
4338        let written = self.parse_conditional()?;
4339        let shift_assignment = self.peek_shift().filter(|shift| shift.assignment);
4340        if !is_assign_lookahead(&self.peek().kind) && shift_assignment.is_none() {
4341            return Some(written);
4342        }
4343        let target_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4344        let target = self.assignment_target(written)?;
4345        let op_tok = self.advance();
4346        let mut op_span = op_tok.span;
4347        let op = if let Some(ShiftToken { op, tokens, .. }) = shift_assignment {
4348            for _ in 1..tokens {
4349                op_span.end = self.advance().span.end;
4350            }
4351            Some(op)
4352        } else {
4353            compound_op_for_token(&op_tok.kind)
4354        };
4355        let value = self.parse_expression()?;
4356        let value_span = parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
4357        parse_arena_result(
4358            self.ast.try_push_expr(Expr {
4359                kind: ExprKind::Assign {
4360                    target,
4361                    op,
4362                    op_span,
4363                    value,
4364                },
4365                span: self.span(target_span.start, value_span.end),
4366            }),
4367            &mut self.fatal,
4368        )
4369    }
4370
4371    /// The binding, field, or element `written` names, through any parentheses:
4372    /// `(a) = 1` assigns `a`, as in JavaScript.
4373    fn assignment_target(&mut self, written: ExprId) -> Option<ExprId> {
4374        let mut target = written;
4375        while let ExprKind::Paren(inner) =
4376            parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind
4377        {
4378            target = inner;
4379        }
4380        if matches!(
4381            parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind,
4382            ExprKind::Identifier(_) | ExprKind::FieldAccess { .. } | ExprKind::IndexAccess { .. }
4383        ) {
4384            return Some(target);
4385        }
4386        let error_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4387        self.error_at_with_help(
4388            error_span,
4389            "invalid assignment target",
4390            vec![
4391                "assign to a variable, a field (`o.f = …`), or an element (`a[i] = …`)".to_string(),
4392            ],
4393        );
4394        None
4395    }
4396
4397    fn parse_conditional(&mut self) -> Option<ExprId> {
4398        self.reject_async_modifier();
4399        if self.is_arrow_start() {
4400            return self.parse_arrow();
4401        }
4402        let cond = self.parse_binary(0)?;
4403        // Ternary sits below every binary op; right-associative.
4404        if !matches!(self.peek().kind, TokenKind::Question) {
4405            return Some(cond);
4406        }
4407        self.advance();
4408        let then_ = self.parse_expression()?;
4409        if !matches!(self.peek().kind, TokenKind::Colon) {
4410            self.error_at_peek_with_help(
4411                "expected `:` to complete ternary expression",
4412                vec!["const x = cond ? then : else;".to_string()],
4413            );
4414            return None;
4415        }
4416        self.advance();
4417        let else_ = self.parse_expression()?;
4418        let cond_span = parse_arena_result(self.ast.try_expr(cond), &mut self.fatal)?.span;
4419        let else_span = parse_arena_result(self.ast.try_expr(else_), &mut self.fatal)?.span;
4420        parse_arena_result(
4421            self.ast.try_push_expr(Expr {
4422                kind: ExprKind::Ternary { cond, then_, else_ },
4423                span: self.span(cond_span.start, else_span.end),
4424            }),
4425            &mut self.fatal,
4426        )
4427    }
4428
4429    fn is_arrow_start(&self) -> bool {
4430        match self.peek().kind {
4431            TokenKind::Identifier => matches!(self.peek_at(1).kind, TokenKind::Arrow),
4432            TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos),
4433            _ => false,
4434        }
4435    }
4436
4437    /// Reject an `async` that modifies a function, arrow or method, and keep a
4438    /// binding named `async` as it is.
4439    fn reject_async_modifier(&mut self) {
4440        if !self.peek_identifier_text_is("async") {
4441            return;
4442        }
4443        let modifies = match self.peek_at(1).kind {
4444            TokenKind::Function => true,
4445            // `async (x) => …`, not a call of a function named `async`.
4446            TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos + 1),
4447            // `async x => …`, or a method `async run(…)` / `async run<T>(…)`.
4448            // `async in (o)` and `async as (T)` are operators on a binding named
4449            // `async`.
4450            ref name
4451                if is_property_name(name)
4452                    && !matches!(name, TokenKind::In)
4453                    && !self.peek_at_is_binary_word(1) =>
4454            {
4455                matches!(
4456                    self.peek_at(2).kind,
4457                    TokenKind::Arrow | TokenKind::LeftParen | TokenKind::LessThan
4458                )
4459            }
4460            _ => false,
4461        };
4462        if modifies {
4463            self.report_async_modifier();
4464        }
4465    }
4466
4467    /// Reject an `async` that starts a class or object-literal method. A member's
4468    /// `async` is never a value an operator applies to, so `async *run`,
4469    /// `async in()` and `async "s"()` are methods too. After a line break,
4470    /// `async` is a member of its own, as in TypeScript.
4471    fn reject_async_member_modifier(&mut self) {
4472        if !self.peek_identifier_text_is("async") || self.line_break_after_peek() {
4473            return;
4474        }
4475        let modifies = match self.peek_at(1).kind {
4476            TokenKind::Star
4477            | TokenKind::LeftBracket
4478            | TokenKind::StringLiteral(_)
4479            | TokenKind::NumberLiteral(_) => true,
4480            ref name if is_property_name(name) => {
4481                matches!(
4482                    self.peek_at(2).kind,
4483                    TokenKind::LeftParen | TokenKind::LessThan
4484                )
4485            }
4486            _ => false,
4487        };
4488        if modifies {
4489            self.report_async_modifier();
4490        }
4491    }
4492
4493    /// There are no promises here, so name the rewrite, then consume `async`
4494    /// and parse on as if it were absent: the body's `await`s are reported in
4495    /// the same run.
4496    fn report_async_modifier(&mut self) {
4497        let tok = self.advance();
4498        self.error_at_with_help(
4499            tok.span,
4500            "`async` functions are not supported",
4501            vec![
4502                "calls return their value directly: remove `async`, write the return type \
4503                 `T` instead of `Promise<T>`, and drop `await`"
4504                    .to_string(),
4505            ],
4506        );
4507    }
4508
4509    /// `await` followed on the same line by an operand that cannot continue a
4510    /// binding named `await`: an identifier, a keyword, a literal or a template. `await(x)`
4511    /// and `await[i]` stay a call and an index; the typechecker names the fix if
4512    /// `await` then does not resolve. Calls are synchronous, so report it and
4513    /// parse on: the operand stays, and one run names every `await` in the program.
4514    fn reject_await_operator(&mut self) -> bool {
4515        if !self.peek_identifier_text_is("await") {
4516            return false;
4517        }
4518        let operand_starts = matches!(
4519            self.peek_at(1).kind,
4520            TokenKind::Identifier
4521                | TokenKind::This
4522                | TokenKind::Super
4523                | TokenKind::New
4524                | TokenKind::Function
4525                | TokenKind::StringLiteral(_)
4526                | TokenKind::NumberLiteral(_)
4527                | TokenKind::NullLiteral
4528                | TokenKind::BooleanLiteral(_)
4529                // A tagged template is not supported, so a template after `await`
4530                // can only be its operand.
4531                | TokenKind::TemplateHead(_)
4532                | TokenKind::TemplateNoSubstitution(_)
4533        ) && !self.peek_at_is_binary_word(1);
4534        if !operand_starts || self.line_break_after_peek() {
4535            return false;
4536        }
4537        let tok = self.advance();
4538        self.error_at_with_help(
4539            tok.span,
4540            "`await` is not supported",
4541            vec!["calls return their value directly: remove `await`".to_string()],
4542        );
4543        true
4544    }
4545
4546    /// Index just past the `)` matching the `(` at `index`; `None` when the parens
4547    /// never balance. Pure lookahead — emits no diagnostics.
4548    fn index_after_matching_paren(&self, index: usize) -> Option<usize> {
4549        if !matches!(
4550            self.tokens.get(index).map(|t| &t.kind),
4551            Some(TokenKind::LeftParen)
4552        ) {
4553            return None;
4554        }
4555        self.scan_past_balanced(
4556            index,
4557            |k| matches!(k, TokenKind::LeftParen),
4558            |k| matches!(k, TokenKind::RightParen),
4559        )
4560    }
4561
4562    fn find_arrow_after_matching_paren(&self, open: usize) -> bool {
4563        let Some(i) = self.index_after_matching_paren(open) else {
4564            return false;
4565        };
4566        let after_type = if matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Colon)) {
4567            match self.scan_past_type_annotation(i + 1, TypePos::ArrowReturn) {
4568                Some(p) => p,
4569                None => return false,
4570            }
4571        } else {
4572            i
4573        };
4574        matches!(
4575            self.tokens.get(after_type).map(|t| &t.kind),
4576            Some(TokenKind::Arrow),
4577        )
4578    }
4579
4580    // Pure lookahead used only by the arrow-disambiguator. No diagnostics.
4581    // Must accept every form `parse_type_annotation` accepts: a form missing
4582    // here makes the disambiguator reject a valid arrow, so the expression
4583    // parser reports a misleading `expected expression` at the return type.
4584    // `arrow_return_type_scanner_matches_type_grammar` pins the two together.
4585    fn scan_past_type_annotation(&self, start: usize, type_pos: TypePos) -> Option<usize> {
4586        self.scan_past_type_annotation_at_depth(start, type_pos, 0)
4587    }
4588
4589    fn scan_past_type_annotation_at_depth(
4590        &self,
4591        start: usize,
4592        type_pos: TypePos,
4593        depth: usize,
4594    ) -> Option<usize> {
4595        if depth >= MAX_PARSE_DEPTH {
4596            return None;
4597        }
4598        if matches!(
4599            self.tokens.get(start).map(|t| &t.kind),
4600            Some(TokenKind::Identifier)
4601        ) && self
4602            .tokens
4603            .get(start + 1)
4604            .is_some_and(|t| self.token_is_word(t, "is"))
4605        {
4606            return self.scan_past_type_annotation_at_depth(start + 2, type_pos, depth + 1);
4607        }
4608        // Leading `|` (prettier's multiline union format).
4609        let start = if matches!(
4610            self.tokens.get(start).map(|t| &t.kind),
4611            Some(TokenKind::Pipe)
4612        ) {
4613            start + 1
4614        } else {
4615            start
4616        };
4617        let mut i = self.scan_past_single_type_member(start, type_pos, depth + 1)?;
4618        while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Pipe)) {
4619            i = self.scan_past_single_type_member(i + 1, type_pos, depth + 1)?;
4620        }
4621        Some(i)
4622    }
4623
4624    fn scan_past_single_type_member(
4625        &self,
4626        start: usize,
4627        type_pos: TypePos,
4628        depth: usize,
4629    ) -> Option<usize> {
4630        if depth >= MAX_PARSE_DEPTH {
4631            return None;
4632        }
4633        if self.is_contextual_type_operator(start, "keyof")
4634            || self.is_contextual_type_operator(start, "readonly")
4635        {
4636            return self.scan_past_single_type_member(start + 1, type_pos, depth + 1);
4637        }
4638        let mut i = start;
4639        match self.tokens.get(i)?.kind {
4640            TokenKind::Identifier | TokenKind::Void => {
4641                i += 1;
4642                while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Dot)) {
4643                    if !matches!(
4644                        self.tokens.get(i + 1).map(|t| &t.kind),
4645                        Some(TokenKind::Identifier)
4646                    ) {
4647                        return None;
4648                    }
4649                    i += 2;
4650                }
4651                if matches!(
4652                    self.tokens.get(i).map(|t| &t.kind),
4653                    Some(TokenKind::LessThan)
4654                ) {
4655                    i = self.scan_past_balanced(
4656                        i,
4657                        |k| matches!(k, TokenKind::LessThan),
4658                        |k| matches!(k, TokenKind::GreaterThan),
4659                    )?;
4660                }
4661            }
4662            TokenKind::NullLiteral
4663            | TokenKind::StringLiteral(_)
4664            | TokenKind::NumberLiteral(_)
4665            | TokenKind::BooleanLiteral(_) => {
4666                i += 1;
4667            }
4668            TokenKind::LeftBrace => {
4669                i = self.scan_past_balanced(
4670                    i,
4671                    |k| matches!(k, TokenKind::LeftBrace),
4672                    |k| matches!(k, TokenKind::RightBrace),
4673                )?;
4674            }
4675            TokenKind::LeftBracket => {
4676                i = self.scan_past_balanced(
4677                    i,
4678                    |k| matches!(k, TokenKind::LeftBracket),
4679                    |k| matches!(k, TokenKind::RightBracket),
4680                )?;
4681            }
4682            // Same `(`-disambiguation `parse_type_array` uses, and it has to agree:
4683            // a function type carries its own `=>`, so everything after it is the
4684            // return type and the scan ends with that. Grouping parens instead fall
4685            // through to the postfix `[]` loop below.
4686            TokenKind::LeftParen => {
4687                let is_function_type = self.paren_at_opens_function_type(i, type_pos);
4688                i = self.scan_past_balanced(
4689                    i,
4690                    |k| matches!(k, TokenKind::LeftParen),
4691                    |k| matches!(k, TokenKind::RightParen),
4692                )?;
4693                if is_function_type {
4694                    if !matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)) {
4695                        return None;
4696                    }
4697                    return self.scan_past_type_annotation_at_depth(
4698                        i + 1,
4699                        TypePos::Anywhere,
4700                        depth + 1,
4701                    );
4702                }
4703            }
4704            _ => return None,
4705        }
4706        while matches!(
4707            self.tokens.get(i).map(|t| &t.kind),
4708            Some(TokenKind::LeftBracket)
4709        ) {
4710            i += 1;
4711            if !matches!(
4712                self.tokens.get(i).map(|t| &t.kind),
4713                Some(TokenKind::RightBracket)
4714            ) {
4715                return None;
4716            }
4717            i += 1;
4718        }
4719        Some(i)
4720    }
4721
4722    // `start` must index the opening token; returns the index just past the
4723    // matching close, or None if the tokens run out first.
4724    fn scan_past_balanced(
4725        &self,
4726        start: usize,
4727        is_open: fn(&TokenKind) -> bool,
4728        is_close: fn(&TokenKind) -> bool,
4729    ) -> Option<usize> {
4730        let mut depth: i32 = 1;
4731        let mut i = start + 1;
4732        while i < self.tokens.len() && depth > 0 {
4733            let token = self.tokens.get(i)?;
4734            let kind = &token.kind;
4735            if is_open(kind) {
4736                depth += 1;
4737            } else if is_close(kind) {
4738                depth -= 1;
4739            } else if matches!(kind, TokenKind::Eof) {
4740                return None;
4741            }
4742            i += 1;
4743        }
4744        if depth == 0 { Some(i) } else { None }
4745    }
4746
4747    fn parse_arrow(&mut self) -> Option<ExprId> {
4748        let start_span = self.peek().span;
4749
4750        let params = match self.peek().kind {
4751            TokenKind::Identifier => {
4752                let name_tok = self.advance();
4753                self.reject_eval_or_arguments(name_tok.span);
4754                let name = self.ident_from_token(&name_tok);
4755                vec![ParamDecl {
4756                    name,
4757                    ty: None,
4758                    default: None,
4759                    optional: false,
4760                    pattern: None,
4761                    rest: false,
4762                    modifiers: None,
4763                }]
4764            }
4765            TokenKind::LeftParen => {
4766                self.advance();
4767                let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4768                    Vec::new()
4769                } else {
4770                    self.parse_arrow_param_list()?
4771                };
4772                if !matches!(self.peek().kind, TokenKind::RightParen) {
4773                    self.error_at_peek("expected `)`");
4774                    return None;
4775                }
4776                self.advance();
4777                params
4778            }
4779            _ => return self.invariant_failure("is_arrow_start guarded the entry"),
4780        };
4781
4782        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4783            self.advance();
4784            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4785        } else {
4786            (None, None)
4787        };
4788
4789        if !matches!(self.peek().kind, TokenKind::Arrow) {
4790            self.error_at_peek("expected `=>` in arrow function");
4791            return None;
4792        }
4793        // Like TypeScript, the arrow still parses, so later errors keep their spans.
4794        if self.line_break_before_peek() {
4795            let arrow_span = self.peek().span;
4796            self.error_at(arrow_span, "a line break is not allowed before `=>`");
4797        }
4798        self.advance();
4799
4800        let (body, end_pos) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
4801            let block = self.parse_block()?;
4802            let span = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?.span;
4803            (ArrowBody::Block(block), span.end)
4804        } else {
4805            let expr = self.parse_expression()?;
4806            let span = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?.span;
4807            (ArrowBody::Expr(expr), span.end)
4808        };
4809
4810        parse_arena_result(
4811            self.ast.try_push_expr(Expr {
4812                kind: ExprKind::Arrow {
4813                    params,
4814                    return_type,
4815                    type_predicate,
4816                    body,
4817                },
4818                span: self.span(start_span.start, end_pos),
4819            }),
4820            &mut self.fatal,
4821        )
4822    }
4823
4824    /// Function expressions share parameter/body parsing with arrows, but retain
4825    /// their own receiver and optional recursive-name binding.
4826    fn parse_function_expression(&mut self) -> Option<ExprId> {
4827        let kw = self.advance();
4828
4829        if matches!(self.peek().kind, TokenKind::LessThan) {
4830            self.error_at_peek("generic function expressions are not supported");
4831            return None;
4832        }
4833
4834        let self_name = if matches!(self.peek().kind, TokenKind::Identifier) {
4835            let name_tok = self.advance();
4836            self.reject_eval_or_arguments(name_tok.span);
4837            Some(self.ident_from_token(&name_tok))
4838        } else {
4839            None
4840        };
4841
4842        if !matches!(self.peek().kind, TokenKind::LeftParen) {
4843            self.error_at_peek("expected `(` after `function`");
4844            return None;
4845        }
4846        self.advance();
4847
4848        let this_type = if matches!(self.peek().kind, TokenKind::This) {
4849            self.advance();
4850            if !matches!(self.peek().kind, TokenKind::Colon) {
4851                self.error_at_peek("expected a type annotation after `this`");
4852                return None;
4853            }
4854            self.advance();
4855            let ty = self.parse_type_annotation()?;
4856            if matches!(self.peek().kind, TokenKind::Comma) {
4857                self.advance();
4858            } else if !matches!(self.peek().kind, TokenKind::RightParen) {
4859                self.error_at_peek("expected `,` or `)` after the `this` parameter");
4860                return None;
4861            }
4862            Some(ty)
4863        } else {
4864            None
4865        };
4866        let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4867            Vec::new()
4868        } else {
4869            let saved_class = self.class_member_body_depth;
4870            let saved_function = self.function_expression_body_depth;
4871            self.class_member_body_depth = 0;
4872            self.function_expression_body_depth = 1;
4873            let params = self.parse_arrow_param_list();
4874            self.class_member_body_depth = saved_class;
4875            self.function_expression_body_depth = saved_function;
4876            params?
4877        };
4878        if !matches!(self.peek().kind, TokenKind::RightParen) {
4879            self.error_at_peek("expected `)`");
4880            return None;
4881        }
4882        self.advance();
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::LeftBrace) {
4892            self.error_at_peek("expected `{` to open the function body");
4893            return None;
4894        }
4895        let saved = self.class_member_body_depth;
4896        self.class_member_body_depth = 0;
4897        self.function_expression_body_depth += 1;
4898        let block = self.parse_block();
4899        self.function_expression_body_depth -= 1;
4900        self.class_member_body_depth = saved;
4901        let block = block?;
4902        let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
4903            .span
4904            .end;
4905
4906        let function = parse_arena_result(
4907            self.ast.try_push_expr(Expr {
4908                kind: ExprKind::Arrow {
4909                    params,
4910                    return_type,
4911                    type_predicate,
4912                    body: ArrowBody::Block(block),
4913                },
4914                span: self.span(kw.span.start, end),
4915            }),
4916            &mut self.fatal,
4917        )?;
4918        parse_arena_result(
4919            self.ast.try_push_expr(Expr {
4920                kind: ExprKind::FunctionExpression {
4921                    name: self_name,
4922                    this_type,
4923                    function,
4924                },
4925                span: self.span(kw.span.start, end),
4926            }),
4927            &mut self.fatal,
4928        )
4929    }
4930
4931    /// Shorthand methods still use arrow lowering. Reject their receiver syntax
4932    /// so an enclosing class or function receiver cannot leak into the body.
4933    fn parse_outer_this_boundary<T>(
4934        &mut self,
4935        parse: impl FnOnce(&mut Self) -> Option<T>,
4936    ) -> Option<T> {
4937        let saved = self.class_member_body_depth;
4938        self.class_member_body_depth = 0;
4939        let saved_function = self.function_expression_body_depth;
4940        self.function_expression_body_depth = 0;
4941        let parsed = parse(self);
4942        self.class_member_body_depth = saved;
4943        self.function_expression_body_depth = saved_function;
4944        parsed
4945    }
4946
4947    fn parse_arrow_param_list(&mut self) -> Option<Vec<ParamDecl>> {
4948        let mut params = Vec::new();
4949        loop {
4950            let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
4951                self.advance();
4952                true
4953            } else {
4954                false
4955            };
4956            // The lowering pass rewrites expression-body arrows to block form when any param
4957            // has a destructuring pattern.
4958            let (name, pattern) = match self.peek().kind {
4959                TokenKind::LeftBrace | TokenKind::LeftBracket => {
4960                    if rest {
4961                        self.error_at_peek("rest parameter cannot be destructured");
4962                        return None;
4963                    }
4964                    let binding = self.parse_binding()?;
4965                    let placeholder = Ident {
4966                        name: String::new(),
4967                        span: binding.span(),
4968                    };
4969                    (placeholder, Some(binding))
4970                }
4971                _ => {
4972                    let name_tok = self.expect_binding_identifier(if rest {
4973                        "expected identifier after `...`"
4974                    } else {
4975                        "expected parameter name"
4976                    })?;
4977                    let name = self.ident_from_token(&name_tok);
4978                    (name, None)
4979                }
4980            };
4981            let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
4982            let ty = if matches!(self.peek().kind, TokenKind::Colon) {
4983                self.advance();
4984                Some(self.parse_type_annotation()?)
4985            } else {
4986                // Rest without annotation is OK at parse time; the typechecker fills it
4987                // from a contextual function-type hint if available.
4988                None
4989            };
4990            let default = self.parse_parameter_default(rest, optional)?;
4991            params.push(ParamDecl {
4992                name,
4993                ty,
4994                default,
4995                optional,
4996                pattern,
4997                rest,
4998                modifiers: None,
4999            });
5000            match self.peek().kind {
5001                TokenKind::Comma => {
5002                    if rest {
5003                        let comma = self.advance();
5004                        self.error_at(comma.span, "rest parameter must be the last parameter");
5005                        return None;
5006                    }
5007                    self.advance();
5008                    if matches!(self.peek().kind, TokenKind::RightParen) {
5009                        break;
5010                    }
5011                }
5012                TokenKind::RightParen => break,
5013                _ => {
5014                    self.error_at_peek("expected `,` or `)`");
5015                    return None;
5016                }
5017            }
5018        }
5019        self.check_parameter_order(params.iter().map(ParamOrder::of_param));
5020        Some(params)
5021    }
5022
5023    fn parse_binary(&mut self, min_prec: u8) -> Option<ExprId> {
5024        self.with_recursion_limit(|parser| parser.parse_binary_inner(min_prec))
5025    }
5026
5027    /// Angle tokens stay separate for nested type arguments. Only adjacent
5028    /// source tokens form a shift; whitespace and comments cannot join them.
5029    fn peek_shift(&self) -> Option<ShiftToken> {
5030        let first = self.peek();
5031        let second = self.peek_at(1);
5032        if first.span.end != second.span.start {
5033            return None;
5034        }
5035        match (&first.kind, &second.kind) {
5036            (TokenKind::LessThan, TokenKind::LessThan) => {
5037                Some(ShiftToken::new(BinOp::Shl, 2, false))
5038            }
5039            (TokenKind::LessThan, TokenKind::LessEquals) => {
5040                Some(ShiftToken::new(BinOp::Shl, 2, true))
5041            }
5042            (TokenKind::GreaterThan, TokenKind::GreaterEquals) => {
5043                Some(ShiftToken::new(BinOp::Shr, 2, true))
5044            }
5045            (TokenKind::GreaterThan, TokenKind::GreaterThan) => {
5046                let third = self.peek_at(2);
5047                if second.span.end == third.span.start {
5048                    match third.kind {
5049                        TokenKind::GreaterThan => {
5050                            return Some(ShiftToken::new(BinOp::UnsignedShr, 3, false));
5051                        }
5052                        TokenKind::GreaterEquals => {
5053                            return Some(ShiftToken::new(BinOp::UnsignedShr, 3, true));
5054                        }
5055                        _ => {}
5056                    }
5057                }
5058                Some(ShiftToken::new(BinOp::Shr, 2, false))
5059            }
5060            _ => None,
5061        }
5062    }
5063
5064    fn peek_binary(&self) -> Option<BinaryToken> {
5065        if let Some(shift) = self.peek_shift() {
5066            return (!shift.assignment).then_some(BinaryToken {
5067                op: shift.op,
5068                precedence: SHIFT_PRECEDENCE,
5069                tokens: shift.tokens,
5070            });
5071        }
5072        peek_binop(&self.peek().kind).map(|(op, precedence)| BinaryToken {
5073            op,
5074            precedence,
5075            tokens: 1,
5076        })
5077    }
5078
5079    fn parse_binary_inner(&mut self, min_prec: u8) -> Option<ExprId> {
5080        let mut lhs = self.parse_cast()?;
5081        // Track the previous op to reject `a || b ?? c` / `a ?? b || c` mixes. Both
5082        // reach this loop: `??` parses its right operand above `&&` (see
5083        // `right_operand_min_prec`), so `||` / `&&` after it is left for this check.
5084        let mut last_op: Option<BinOp> = None;
5085        let mut reported_mixing = false;
5086        while let Some(BinaryToken {
5087            op,
5088            precedence: prec,
5089            tokens: count,
5090        }) = self.peek_binary()
5091        {
5092            if prec < min_prec {
5093                break;
5094            }
5095            if !reported_mixing
5096                && let Some(last) = last_op
5097                && mixed_logical(last, op)
5098            {
5099                reported_mixing = true;
5100                let op_span = self.peek().span;
5101                // Parsing continues as if the left side were grouped, so one error
5102                // inside an `if (...)` header doesn't cascade through the statement.
5103                // Later mixes in the same chain add nothing, so only the first is reported.
5104                self.error_at_with_help(
5105                    op_span,
5106                    "mixing `??` with `||` / `&&` requires parentheses",
5107                    vec![mixed_logical_help(last, op)],
5108                );
5109            }
5110            if op == BinOp::Pow
5111                && matches!(
5112                    parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.kind,
5113                    ExprKind::Unary { .. }
5114                        | ExprKind::Typeof { .. }
5115                        | ExprKind::Delete { .. }
5116                        | ExprKind::Void { .. }
5117                )
5118            {
5119                let error_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5120                self.error_at_with_help(
5121                    error_span,
5122                    "an unparenthesized unary expression cannot be the left operand of `**`",
5123                    vec!["choose the grouping explicitly: `(-x) ** 2` or `-(x ** 2)`".into()],
5124                );
5125                return None;
5126            }
5127            let loose = matches!(self.peek().kind, TokenKind::EqEq | TokenKind::BangEq);
5128            for _ in 0..count {
5129                self.advance();
5130            }
5131            let rhs = self.parse_binary(right_operand_min_prec(op, prec))?;
5132            let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5133            let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
5134            if loose {
5135                self.reject_loose_nullish_comparison(op, lhs, rhs)?;
5136            }
5137            lhs = parse_arena_result(
5138                self.ast.try_push_expr(Expr {
5139                    kind: ExprKind::Binary { op, lhs, rhs },
5140                    span: self.span(lhs_span.start, rhs_span.end),
5141                }),
5142                &mut self.fatal,
5143            )?;
5144            last_op = Some(op);
5145        }
5146        Some(lhs)
5147    }
5148
5149    // Higher than every binary op, lower than every unary prefix. Left-associative.
5150    fn parse_cast(&mut self) -> Option<ExprId> {
5151        let mut expr = self.parse_unary()?;
5152        while matches!(self.peek().kind, TokenKind::Instanceof)
5153            || self.peek_identifier_text_is("as")
5154        {
5155            let is_instanceof = matches!(self.peek().kind, TokenKind::Instanceof);
5156            self.advance();
5157            let ty = self.parse_type_annotation()?;
5158            let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5159                .span
5160                .start;
5161            let end = ty.span.end;
5162            let kind = if is_instanceof {
5163                ExprKind::InstanceOf { value: expr, ty }
5164            } else {
5165                self.last_as_type_end = Some(end);
5166                ExprKind::As { expr, ty }
5167            };
5168            expr = parse_arena_result(
5169                self.ast.try_push_expr(Expr {
5170                    kind,
5171                    span: self.span(start, end),
5172                }),
5173                &mut self.fatal,
5174            )?;
5175        }
5176        Some(expr)
5177    }
5178
5179    fn parse_unary(&mut self) -> Option<ExprId> {
5180        self.with_recursion_limit(Self::parse_unary_inner)
5181    }
5182
5183    fn parse_unary_inner(&mut self) -> Option<ExprId> {
5184        if self.reject_await_operator() {
5185            return self.parse_unary();
5186        }
5187        // `typeof` is parsed as a unary prefix so `typeof x === "T"` works without a new layer;
5188        // the typechecker rejects `Typeof` outside the recognized equality fold position.
5189        if matches!(self.peek().kind, TokenKind::Typeof) {
5190            return self.parse_prefix_expr(|operand| ExprKind::Typeof { operand });
5191        }
5192        if matches!(self.peek().kind, TokenKind::Void) {
5193            return self.parse_prefix_expr(|operand| ExprKind::Void { operand });
5194        }
5195        if self.at_delete_operator() {
5196            return self.parse_prefix_expr(|operand| ExprKind::Delete { operand });
5197        }
5198        if matches!(self.peek().kind, TokenKind::New) {
5199            let op_tok = self.advance();
5200            let mut callee = self.parse_atom()?;
5201            while matches!(self.peek().kind, TokenKind::Dot) {
5202                self.advance();
5203                if !matches!(self.peek().kind, TokenKind::Identifier) {
5204                    self.error_at_peek("expected identifier after `.` in constructor name");
5205                    return None;
5206                }
5207                let ident_tok = self.advance();
5208                let name = self.ident_from_token(&ident_tok);
5209                let receiver_span =
5210                    parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5211                let combined_span = self.span(receiver_span.start, ident_tok.span.end);
5212                callee = parse_arena_result(
5213                    self.ast.try_push_expr(Expr {
5214                        kind: ExprKind::FieldAccess {
5215                            receiver: callee,
5216                            name,
5217                        },
5218                        span: combined_span,
5219                    }),
5220                    &mut self.fatal,
5221                )?;
5222            }
5223            let type_args = if matches!(self.peek().kind, TokenKind::LessThan) {
5224                let saved_pos = self.pos;
5225                let saved_diag_len = self.diagnostics.len();
5226                if let Some(args) = self.try_parse_type_args_only() {
5227                    Some(args)
5228                } else {
5229                    self.pos = saved_pos;
5230                    self.diagnostics.truncate(saved_diag_len);
5231                    None
5232                }
5233            } else {
5234                None
5235            };
5236            if !matches!(self.peek().kind, TokenKind::LeftParen) {
5237                self.error_at_peek("expected `(` after constructor name in `new` expression");
5238                return None;
5239            }
5240            self.advance();
5241            let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5242                Vec::new()
5243            } else {
5244                self.parse_call_args()?
5245            };
5246            if !matches!(self.peek().kind, TokenKind::RightParen) {
5247                self.error_at_peek("expected `)`");
5248                return None;
5249            }
5250            let close = self.advance();
5251            let new_expr = parse_arena_result(
5252                self.ast.try_push_expr(Expr {
5253                    kind: ExprKind::New {
5254                        callee,
5255                        type_args,
5256                        args,
5257                    },
5258                    span: self.span(op_tok.span.start, close.span.end),
5259                }),
5260                &mut self.fatal,
5261            )?;
5262            // `new Foo()` is a postfix primary: `new Map().set(...)`, `new Map().size`,
5263            // `new Foo()[i]` all continue the chain off the constructor result.
5264            return self.parse_postfix_from(new_expr);
5265        }
5266        let op = match self.peek().kind {
5267            TokenKind::Bang => UnOp::Not,
5268            TokenKind::Tilde => UnOp::BitNot,
5269            TokenKind::Minus => UnOp::Neg,
5270            TokenKind::Plus => UnOp::Pos,
5271            _ => return self.parse_postfix(),
5272        };
5273        self.parse_prefix_expr(|operand| ExprKind::Unary { op, operand })
5274    }
5275
5276    /// Consume a prefix operator and its operand, spanning both.
5277    fn parse_prefix_expr(&mut self, kind: impl FnOnce(ExprId) -> ExprKind) -> Option<ExprId> {
5278        let op_tok = self.advance();
5279        // ASI cannot distinguish the `void` type from the expression operator.
5280        // In expression position a newline after the keyword still awaits an operand.
5281        if matches!(op_tok.kind, TokenKind::Void) && self.at_inserted_semicolon() {
5282            self.advance();
5283        }
5284        let operand = self.parse_unary()?;
5285        let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5286        parse_arena_result(
5287            self.ast.try_push_expr(Expr {
5288                kind: kind(operand),
5289                span: self.span(op_tok.span.start, operand_span.end),
5290            }),
5291            &mut self.fatal,
5292        )
5293    }
5294
5295    fn parse_postfix(&mut self) -> Option<ExprId> {
5296        let expr = self.parse_atom()?;
5297        self.parse_postfix_from(expr)
5298    }
5299
5300    fn parse_postfix_from(&mut self, mut expr: ExprId) -> Option<ExprId> {
5301        // OptionalChain is emitted once at end-of-loop so a speculative-`<` rewind
5302        // never strands a half-built chain. Everything before the first `?.` accumulates
5303        // in `expr` normally; from the first `?.` onward ops go into `parts`.
5304        let mut chain_parts: Option<(ExprId, Vec<crate::ChainPart>)> = None;
5305        loop {
5306            match self.peek().kind {
5307                TokenKind::LeftParen => {
5308                    if let Some((_, parts)) = &mut chain_parts {
5309                        let part = self.parse_chain_call_tail(/*optional=*/ false)?;
5310                        parts.push(part);
5311                    } else {
5312                        expr = self.parse_call_tail(expr, None)?;
5313                    }
5314                }
5315                TokenKind::Dot => {
5316                    if let Some((_, parts)) = &mut chain_parts {
5317                        let part = self.parse_chain_field_tail(/*optional=*/ false)?;
5318                        parts.push(part);
5319                    } else {
5320                        expr = self.parse_field_access_tail(expr)?;
5321                    }
5322                }
5323                TokenKind::LeftBracket => {
5324                    if let Some((_, parts)) = &mut chain_parts {
5325                        let part = self.parse_chain_index_tail(/*optional=*/ false)?;
5326                        parts.push(part);
5327                    } else {
5328                        expr = self.parse_index_access_tail(expr)?;
5329                    }
5330                }
5331                TokenKind::QuestionDot => {
5332                    if chain_parts.is_none() {
5333                        chain_parts = Some((expr, Vec::new()));
5334                    }
5335                    self.advance();
5336                    let part = match self.peek().kind {
5337                        TokenKind::LeftParen => self.parse_chain_call_tail(true)?,
5338                        TokenKind::LeftBracket => self.parse_chain_index_tail(true)?,
5339                        _ if is_property_name(&self.peek().kind) => {
5340                            self.parse_chain_field_tail(true)?
5341                        }
5342                        _ => {
5343                            self.error_at_peek(
5344                                "expected field name, `(` for call, or `[` for index after `?.`",
5345                            );
5346                            return None;
5347                        }
5348                    };
5349                    if let Some((_, parts)) = &mut chain_parts {
5350                        parts.push(part);
5351                    }
5352                }
5353                // `<` after a callable expression might be a generic call `f<T>(args)`.
5354                // Speculatively parse; on failure restore state and let `parse_binary`
5355                // treat `<` as a comparison.
5356                TokenKind::LessThan => {
5357                    if chain_parts.is_some() {
5358                        // `a?.<T>()` not yet supported — terminate the chain.
5359                        break;
5360                    }
5361                    let saved_pos = self.pos;
5362                    let saved_diag_len = self.diagnostics.len();
5363                    if let Some(call_id) = self.try_parse_generic_call_tail(expr) {
5364                        expr = call_id;
5365                    } else {
5366                        self.pos = saved_pos;
5367                        self.diagnostics.truncate(saved_diag_len);
5368                        break;
5369                    }
5370                }
5371                TokenKind::Bang => {
5372                    if let Some((_, parts)) = &mut chain_parts {
5373                        let tok = self.advance();
5374                        parts.push(crate::ChainPart::NonNull { span: tok.span });
5375                        continue;
5376                    }
5377                    let tok = self.advance();
5378                    let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5379                        .span
5380                        .start;
5381                    expr = parse_arena_result(
5382                        self.ast.try_push_expr(Expr {
5383                            kind: ExprKind::PostfixUnary {
5384                                op: PostfixOp::NonNullAssert,
5385                                operand: expr,
5386                            },
5387                            span: self.span(start, tok.span.end),
5388                        }),
5389                        &mut self.fatal,
5390                    )?;
5391                }
5392                // Postfix `++`/`--` terminates the chain; `a?.b++` is rejected.
5393                TokenKind::PlusPlus | TokenKind::MinusMinus => {
5394                    if chain_parts.is_some() {
5395                        break;
5396                    }
5397                    let tok = self.advance();
5398                    let op = if matches!(tok.kind, TokenKind::PlusPlus) {
5399                        PostfixOp::Inc
5400                    } else {
5401                        PostfixOp::Dec
5402                    };
5403                    let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5404                        .span
5405                        .start;
5406                    expr = parse_arena_result(
5407                        self.ast.try_push_expr(Expr {
5408                            kind: ExprKind::PostfixUnary { op, operand: expr },
5409                            span: self.span(start, tok.span.end),
5410                        }),
5411                        &mut self.fatal,
5412                    )?;
5413                    break;
5414                }
5415                _ => break,
5416            }
5417        }
5418        if let Some((base, parts)) = chain_parts {
5419            let base_span = parse_arena_result(self.ast.try_expr(base), &mut self.fatal)?.span;
5420            let end = parts.last().map_or(base_span.end, |p| p.span().end);
5421            Some(parse_arena_result(
5422                self.ast.try_push_expr(Expr {
5423                    kind: ExprKind::OptionalChain { base, parts },
5424                    span: self.span(base_span.start, end),
5425                }),
5426                &mut self.fatal,
5427            )?)
5428        } else {
5429            Some(expr)
5430        }
5431    }
5432
5433    // `.` is consumed here for the non-optional case; `?.` was consumed by the caller.
5434    fn parse_chain_field_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5435        let start_pos = self.peek().span.start;
5436        if !optional {
5437            self.advance();
5438        }
5439        let name_tok = if is_property_name(&self.peek().kind) {
5440            self.advance()
5441        } else {
5442            let what = if optional { "?." } else { "." };
5443            self.error_at_peek(format!("expected field name after `{what}`"));
5444            return None;
5445        };
5446        let name = self.ident_from_token(&name_tok);
5447        let end = name.span.end;
5448        Some(crate::ChainPart::Field {
5449            name,
5450            optional,
5451            span: self.span(start_pos, end),
5452        })
5453    }
5454
5455    fn parse_chain_index_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5456        let open = self.advance();
5457        let idx = self.parse_expression()?;
5458        if !matches!(self.peek().kind, TokenKind::RightBracket) {
5459            self.error_at_peek("expected `]`");
5460            return None;
5461        }
5462        let close = self.advance();
5463        Some(crate::ChainPart::Index {
5464            idx,
5465            optional,
5466            span: self.span(open.span.start, close.span.end),
5467        })
5468    }
5469
5470    fn parse_chain_call_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5471        let open = self.advance();
5472        let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5473            Vec::new()
5474        } else {
5475            self.parse_call_args()?
5476        };
5477        if !matches!(self.peek().kind, TokenKind::RightParen) {
5478            self.error_at_peek("expected `)`");
5479            return None;
5480        }
5481        let close = self.advance();
5482        Some(crate::ChainPart::Call {
5483            args,
5484            type_args: None,
5485            optional,
5486            span: self.span(open.span.start, close.span.end),
5487        })
5488    }
5489
5490    fn parse_field_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5491        self.advance();
5492        // Keywords are valid field names — `Foo.new(args)` is the canonical constructor call.
5493        let name_tok = if is_property_name(&self.peek().kind) {
5494            self.advance()
5495        } else {
5496            self.error_at_peek("expected field name after `.`");
5497            return None;
5498        };
5499        let name = self.ident_from_token(&name_tok);
5500        let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5501        parse_arena_result(
5502            self.ast.try_push_expr(Expr {
5503                kind: ExprKind::FieldAccess {
5504                    receiver,
5505                    name: name.clone(),
5506                },
5507                span: self.span(receiver_span.start, name.span.end),
5508            }),
5509            &mut self.fatal,
5510        )
5511    }
5512
5513    fn parse_index_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5514        self.advance();
5515        let index = self.parse_expression()?;
5516        if !matches!(self.peek().kind, TokenKind::RightBracket) {
5517            self.error_at_peek("expected `]`");
5518            return None;
5519        }
5520        let close = self.advance();
5521        let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5522        parse_arena_result(
5523            self.ast.try_push_expr(Expr {
5524                kind: ExprKind::IndexAccess { receiver, index },
5525                span: self.span(receiver_span.start, close.span.end),
5526            }),
5527            &mut self.fatal,
5528        )
5529    }
5530
5531    fn parse_call_tail(
5532        &mut self,
5533        callee: ExprId,
5534        type_args: Option<Vec<TypeAnnotation>>,
5535    ) -> Option<ExprId> {
5536        self.advance();
5537        let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5538            Vec::new()
5539        } else {
5540            self.parse_call_args()?
5541        };
5542        if !matches!(self.peek().kind, TokenKind::RightParen) {
5543            self.error_at_peek("expected `)`");
5544            return None;
5545        }
5546        let close = self.advance();
5547
5548        let callee_span = parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5549        parse_arena_result(
5550            self.ast.try_push_expr(Expr {
5551                kind: ExprKind::Call {
5552                    callee,
5553                    type_args,
5554                    args,
5555                },
5556                span: self.span(callee_span.start, close.span.end),
5557            }),
5558            &mut self.fatal,
5559        )
5560    }
5561
5562    // Caller restores position + diagnostics on `None` so `<` is re-interpreted as comparison.
5563    fn try_parse_generic_call_tail(&mut self, callee: ExprId) -> Option<ExprId> {
5564        let type_args = self.try_parse_type_args_only()?;
5565        if !matches!(self.peek().kind, TokenKind::LeftParen) {
5566            return None;
5567        }
5568        self.parse_call_tail(callee, Some(type_args))
5569    }
5570
5571    fn try_parse_type_args_only(&mut self) -> Option<Vec<TypeAnnotation>> {
5572        if !matches!(self.peek().kind, TokenKind::LessThan) {
5573            return self.invariant_failure("type argument parser expected `<`");
5574        }
5575        self.advance();
5576
5577        if matches!(self.peek().kind, TokenKind::GreaterThan) {
5578            return None;
5579        }
5580
5581        let mut type_args = Vec::new();
5582        loop {
5583            let ta = self.parse_type_annotation()?;
5584            type_args.push(ta);
5585            match self.peek().kind {
5586                TokenKind::Comma => {
5587                    self.advance();
5588                    if matches!(self.peek().kind, TokenKind::GreaterThan) {
5589                        self.advance();
5590                        break;
5591                    }
5592                }
5593                TokenKind::GreaterThan => {
5594                    self.advance();
5595                    break;
5596                }
5597                _ => return None,
5598            }
5599        }
5600        Some(type_args)
5601    }
5602
5603    fn parse_call_args(&mut self) -> Option<Vec<ExprId>> {
5604        let mut args = Vec::new();
5605        loop {
5606            let arg = self.parse_expression()?;
5607            args.push(arg);
5608            match self.peek().kind {
5609                TokenKind::Comma => {
5610                    self.advance();
5611                    if matches!(self.peek().kind, TokenKind::RightParen) {
5612                        return Some(args);
5613                    }
5614                }
5615                TokenKind::RightParen => return Some(args),
5616                _ => {
5617                    self.error_at_peek("expected `,` or `)`");
5618                    return None;
5619                }
5620            }
5621        }
5622    }
5623
5624    /// `<T>expr`, TypeScript's original cast syntax, lowered to the same
5625    /// [`ExprKind::As`] node `expr as T` produces — so it is checked and emitted
5626    /// identically, including the runtime `ref.test` our `as` performs.
5627    ///
5628    /// The operand is a unary expression, which is what binds `<T>-1` as a cast of
5629    /// `-1` rather than a cast of `1` that is then negated.
5630    fn parse_angle_cast(&mut self) -> Option<ExprId> {
5631        let open = self.advance();
5632        let ty = self.parse_type_annotation()?;
5633        if !matches!(self.peek().kind, TokenKind::GreaterThan) {
5634            self.error_at_peek("expected `>` to close a type assertion");
5635            return None;
5636        }
5637        self.advance();
5638        let expr = self.parse_unary()?;
5639        let end = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5640            .span
5641            .end;
5642        parse_arena_result(
5643            self.ast.try_push_expr(Expr {
5644                kind: ExprKind::As { expr, ty },
5645                span: self.span(open.span.start, end),
5646            }),
5647            &mut self.fatal,
5648        )
5649    }
5650
5651    fn parse_atom(&mut self) -> Option<ExprId> {
5652        match self.peek().kind {
5653            // `<T>expr` — the older spelling of `expr as T`, lowered to the same node.
5654            // Unambiguous only because this is an atom's first token: a `<` that means
5655            // comparison always has a left operand, and one that opens a generic
5656            // argument list always follows a callee, so neither reaches here.
5657            TokenKind::LessThan => return self.parse_angle_cast(),
5658            TokenKind::LeftParen => return self.parse_paren(),
5659            // `{` here is an object literal; statement-start `{` is dispatched to
5660            // `parse_block` before `parse_atom` runs, so the two stay disjoint.
5661            TokenKind::LeftBrace => return self.parse_object_literal(),
5662            TokenKind::LeftBracket => return self.parse_array_literal(),
5663            TokenKind::TemplateHead(_) => return self.parse_template_literal(),
5664            TokenKind::This => return self.parse_this_or_super(true),
5665            TokenKind::Super => return self.parse_this_or_super(false),
5666            TokenKind::Function => return self.parse_function_expression(),
5667            _ => {}
5668        }
5669        if !matches!(
5670            self.peek().kind,
5671            TokenKind::NumberLiteral(_)
5672                | TokenKind::BigIntLiteral(_)
5673                | TokenKind::StringLiteral(_)
5674                | TokenKind::TemplateNoSubstitution(_)
5675                | TokenKind::BooleanLiteral(_)
5676                | TokenKind::NullLiteral
5677                | TokenKind::Identifier
5678                | TokenKind::RegexLiteral { .. }
5679        ) {
5680            self.error_at_peek("expected expression");
5681            return None;
5682        }
5683        let tok = self.advance();
5684        let span = tok.span;
5685        let kind = match tok.kind {
5686            TokenKind::NumberLiteral(v) => ExprKind::Number(v),
5687            TokenKind::BigIntLiteral(s) => ExprKind::BigInt(s),
5688            TokenKind::StringLiteral(s) => ExprKind::String(s),
5689            TokenKind::TemplateNoSubstitution(s) => ExprKind::String(s),
5690            TokenKind::BooleanLiteral(b) => ExprKind::Boolean(b),
5691            TokenKind::NullLiteral => ExprKind::Null,
5692            TokenKind::Identifier => ExprKind::Identifier(Ident {
5693                name: self.source[span.start as usize..span.end as usize].to_string(),
5694                span,
5695            }),
5696            TokenKind::RegexLiteral { source, flags } => ExprKind::Regex { source, flags },
5697            // No cursor movement occurs between the accepted-kind check and advance.
5698            _ => unreachable!("advance preserves the accepted expression token"),
5699        };
5700        parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5701    }
5702
5703    /// Ordinary functions establish a `this` boundary and exclude an enclosing
5704    /// class's `super`; arrows inherit the current parsing context.
5705    fn parse_this_or_super(&mut self, is_this: bool) -> Option<ExprId> {
5706        let tok = self.advance();
5707        let span = tok.span;
5708        if !is_this && self.class_member_body_depth == 0 {
5709            self.error_at_with_help(
5710                span,
5711                "`super` is only valid inside a class method or constructor body",
5712                vec![
5713                    "call `super(...)` in a subclass constructor or `super.method(...)` \
5714                     in a subclass method"
5715                        .to_string(),
5716                ],
5717            );
5718        }
5719        let kind = if is_this
5720            && self.class_member_body_depth == 0
5721            && self.function_expression_body_depth == 0
5722        {
5723            ExprKind::ThisOutsideReceiver
5724        } else if is_this {
5725            ExprKind::This
5726        } else {
5727            ExprKind::Super
5728        };
5729        parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5730    }
5731
5732    // parse_atom dispatches immediately after matching TemplateHead.
5733    // The remaining template tokens are still parsed and diagnosed normally.
5734    fn parse_template_literal(&mut self) -> Option<ExprId> {
5735        let head_tok = self.advance();
5736        let TokenKind::TemplateHead(head) = head_tok.kind else {
5737            unreachable!("template dispatch requires TemplateHead");
5738        };
5739        let start = head_tok.span.start;
5740        let mut parts: Vec<String> = vec![head];
5741        let mut exprs: Vec<ExprId> = Vec::new();
5742        let mut substitution_spans: Vec<Span> = Vec::new();
5743        // The token before a substitution ends just past its `${`; the one after
5744        // starts at its `}`.
5745        let mut substitution_start = head_tok.span.end.saturating_sub(2);
5746        loop {
5747            let expr = self.parse_expression()?;
5748            exprs.push(expr);
5749            let closing = self.peek().span.start;
5750            let substitution_span = self.span(substitution_start, closing.saturating_add(1));
5751            match self.peek().kind.clone() {
5752                TokenKind::TemplateMiddle(s) => {
5753                    let tok = self.advance();
5754                    substitution_spans.push(substitution_span);
5755                    substitution_start = tok.span.end.saturating_sub(2);
5756                    parts.push(s);
5757                }
5758                TokenKind::TemplateTail(s) => {
5759                    let tok = self.advance();
5760                    substitution_spans.push(substitution_span);
5761                    parts.push(s);
5762                    return parse_arena_result(
5763                        self.ast.try_push_expr(Expr {
5764                            kind: ExprKind::TemplateLiteral {
5765                                parts,
5766                                exprs,
5767                                substitution_spans,
5768                            },
5769                            span: self.span(start, tok.span.end),
5770                        }),
5771                        &mut self.fatal,
5772                    );
5773                }
5774                _ => {
5775                    self.error_at_peek("expected `}` to close template interpolation");
5776                    return None;
5777                }
5778            }
5779        }
5780    }
5781
5782    fn parse_object_literal(&mut self) -> Option<ExprId> {
5783        let open = self.advance();
5784
5785        let mut members: Vec<ObjectLiteralMember> = Vec::new();
5786        if !matches!(self.peek().kind, TokenKind::RightBrace) {
5787            loop {
5788                if matches!(self.peek().kind, TokenKind::DotDotDot) {
5789                    let dots = self.advance();
5790                    let value = self.parse_expression()?;
5791                    let value_span =
5792                        parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5793                    members.push(ObjectLiteralMember::Spread {
5794                        value,
5795                        span: self.span(dots.span.start, value_span.end),
5796                    });
5797                } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
5798                    self.advance();
5799                    let key = self.parse_expression()?;
5800                    if !matches!(self.peek().kind, TokenKind::RightBracket) {
5801                        self.error_at_peek("expected `]` after computed property key");
5802                        return None;
5803                    }
5804                    self.advance();
5805                    if !matches!(self.peek().kind, TokenKind::Colon) {
5806                        self.error_at_peek("expected `:` after computed property key; computed methods are not supported");
5807                        return None;
5808                    }
5809                    self.advance();
5810                    let value = self.parse_expression()?;
5811                    members.push(ObjectLiteralMember::Computed { key, value });
5812                } else {
5813                    let field = self.parse_object_literal_field()?;
5814                    // Spread members are dynamic — only literal fields participate in
5815                    // the duplicate-key check.
5816                    if let Some(existing) = members.iter().find_map(|m| match m {
5817                        ObjectLiteralMember::Field(f) if f.name.name == field.name.name => Some(f),
5818                        _ => None,
5819                    }) {
5820                        self.diagnostics.push(Diagnostic {
5821                            severity: Severity::Error,
5822                            span: field.name.span,
5823                            message: format!(
5824                                "duplicate field `{}` in object literal",
5825                                field.name.name
5826                            ),
5827                            help: vec![],
5828                            notes: vec![(existing.name.span, "first defined here".into())],
5829                        });
5830                        // Keep parsing so the user sees all duplicates at once.
5831                    }
5832                    members.push(ObjectLiteralMember::Field(field));
5833                }
5834                if self.at_inserted_semicolon()
5835                    && matches!(self.peek_at(1).kind, TokenKind::RightBrace)
5836                {
5837                    self.advance();
5838                }
5839                match self.peek().kind {
5840                    TokenKind::Comma => {
5841                        self.advance();
5842                        if matches!(self.peek().kind, TokenKind::RightBrace) {
5843                            break;
5844                        }
5845                    }
5846                    TokenKind::RightBrace => break,
5847                    _ => {
5848                        self.error_at_peek("expected `,` or `}`");
5849                        return None;
5850                    }
5851                }
5852            }
5853        }
5854
5855        if !matches!(self.peek().kind, TokenKind::RightBrace) {
5856            self.error_at_peek("expected `}`");
5857            return None;
5858        }
5859        let close = self.advance();
5860
5861        parse_arena_result(
5862            self.ast.try_push_expr(Expr {
5863                kind: ExprKind::ObjectLiteral { members },
5864                span: self.span(open.span.start, close.span.end),
5865            }),
5866            &mut self.fatal,
5867        )
5868    }
5869
5870    fn parse_object_literal_field(&mut self) -> Option<ObjectLiteralField> {
5871        self.reject_async_member_modifier();
5872        let (name, shorthandable) = match self.peek().kind.clone() {
5873            TokenKind::Identifier => {
5874                let tok = self.advance();
5875                (self.ident_from_token(&tok), true)
5876            }
5877            kind if is_property_name(&kind) => {
5878                let tok = self.advance();
5879                (self.ident_from_token(&tok), false)
5880            }
5881            TokenKind::StringLiteral(_) => {
5882                let tok = self.advance();
5883                (self.property_ident_from_token(&tok), false)
5884            }
5885            _ => {
5886                self.error_at_peek("expected field name");
5887                return None;
5888            }
5889        };
5890
5891        // `{ m(x: T): R { … } }` is shorthand for `{ m: (x: T): R => { … } }`. Method
5892        // shorthand carries no `this` of its own here, for the same reason a function
5893        // expression does not, so the arrow is an exact lowering. Checked before the
5894        // `:` branch and independently of `shorthandable`, because a keyword or string
5895        // key can name a method too.
5896        if matches!(self.peek().kind, TokenKind::LeftParen) {
5897            let value = self.parse_method_shorthand_body(name.span)?;
5898            return Some(ObjectLiteralField { name, value });
5899        }
5900
5901        if !matches!(self.peek().kind, TokenKind::Colon) {
5902            // `{ x }` is shorthand for `{ x: x }`. Only identifier keys can use
5903            // it — a string key like `{ "x" }` has no binding to reference.
5904            if shorthandable {
5905                let value = parse_arena_result(
5906                    self.ast.try_push_expr(Expr {
5907                        kind: ExprKind::Identifier(name.clone()),
5908                        span: name.span,
5909                    }),
5910                    &mut self.fatal,
5911                )?;
5912                return Some(ObjectLiteralField { name, value });
5913            }
5914            self.error_at_peek("expected `:` after field name");
5915            return None;
5916        }
5917        self.advance();
5918
5919        let value = self.parse_expression()?;
5920        Some(ObjectLiteralField { name, value })
5921    }
5922
5923    /// The `(params): R { … }` tail of an object-literal method, lowered to an arrow.
5924    /// `name_span` is the key's span, so the arrow spans the whole member.
5925    fn parse_method_shorthand_body(&mut self, name_span: Span) -> Option<ExprId> {
5926        self.advance();
5927
5928        let params = if matches!(self.peek().kind, TokenKind::RightParen) {
5929            Vec::new()
5930        } else {
5931            self.parse_arrow_param_list()?
5932        };
5933        if !matches!(self.peek().kind, TokenKind::RightParen) {
5934            self.error_at_peek("expected `)`");
5935            return None;
5936        }
5937        self.advance();
5938
5939        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
5940            self.advance();
5941            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
5942        } else {
5943            (None, None)
5944        };
5945
5946        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
5947            self.error_at_peek("expected `{` to open the method body");
5948            return None;
5949        }
5950        let block = self.parse_outer_this_boundary(Self::parse_block)?;
5951        let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
5952            .span
5953            .end;
5954
5955        parse_arena_result(
5956            self.ast.try_push_expr(Expr {
5957                kind: ExprKind::Arrow {
5958                    params,
5959                    return_type,
5960                    type_predicate,
5961                    body: ArrowBody::Block(block),
5962                },
5963                span: self.span(name_span.start, end),
5964            }),
5965            &mut self.fatal,
5966        )
5967    }
5968
5969    fn parse_array_literal(&mut self) -> Option<ExprId> {
5970        let open = self.advance();
5971
5972        let mut elements: Vec<ArrayLiteralElement> = Vec::new();
5973        if !matches!(self.peek().kind, TokenKind::RightBracket) {
5974            loop {
5975                let element = if matches!(self.peek().kind, TokenKind::DotDotDot) {
5976                    let dots = self.advance();
5977                    let value = self.parse_expression()?;
5978                    let value_span =
5979                        parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5980                    ArrayLiteralElement::Spread {
5981                        value,
5982                        span: self.span(dots.span.start, value_span.end),
5983                    }
5984                } else {
5985                    ArrayLiteralElement::Value(self.parse_expression()?)
5986                };
5987                elements.push(element);
5988                match self.peek().kind {
5989                    TokenKind::Comma => {
5990                        self.advance();
5991                        if matches!(self.peek().kind, TokenKind::RightBracket) {
5992                            break;
5993                        }
5994                    }
5995                    TokenKind::RightBracket => break,
5996                    _ => {
5997                        self.error_at_peek("expected `,` or `]`");
5998                        return None;
5999                    }
6000                }
6001            }
6002        }
6003
6004        if !matches!(self.peek().kind, TokenKind::RightBracket) {
6005            self.error_at_peek("expected `]`");
6006            return None;
6007        }
6008        let close = self.advance();
6009
6010        parse_arena_result(
6011            self.ast.try_push_expr(Expr {
6012                kind: ExprKind::ArrayLiteral { elements },
6013                span: self.span(open.span.start, close.span.end),
6014            }),
6015            &mut self.fatal,
6016        )
6017    }
6018
6019    fn parse_paren(&mut self) -> Option<ExprId> {
6020        let open = self.advance();
6021        let inner = self.parse_expression()?;
6022        if !matches!(self.peek().kind, TokenKind::RightParen) {
6023            self.error_at_peek("expected `)`");
6024            return None;
6025        }
6026        let close = self.advance();
6027        let span = self.span(open.span.start, close.span.end);
6028        parse_arena_result(
6029            self.ast.try_push_expr(Expr {
6030                kind: ExprKind::Paren(inner),
6031                span,
6032            }),
6033            &mut self.fatal,
6034        )
6035    }
6036
6037    fn with_recursion_limit<T>(&mut self, parse: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
6038        if self.fatal.is_some() {
6039            return None;
6040        }
6041        if self.recursion_limit_span.is_some() || self.recursion_depth >= MAX_PARSE_DEPTH {
6042            self.stop_at_recursion_limit();
6043            return None;
6044        }
6045        self.recursion_depth += 1;
6046        let result = parse(self);
6047        self.recursion_depth -= 1;
6048        result
6049    }
6050
6051    fn stop_at_recursion_limit(&mut self) {
6052        self.recursion_limit_span.get_or_insert(self.peek().span);
6053        // Stop recovery and enclosing block loops as well as recursive calls.
6054        self.pos = self.tokens.len().saturating_sub(1);
6055    }
6056
6057    fn recover(&mut self) {
6058        while !self.is_at_eof() {
6059            match self.peek().kind {
6060                TokenKind::Semicolon => {
6061                    self.advance();
6062                    return;
6063                }
6064                TokenKind::RightBrace
6065                | TokenKind::Let
6066                | TokenKind::Const
6067                | TokenKind::Function
6068                | TokenKind::Export
6069                | TokenKind::If
6070                | TokenKind::While
6071                | TokenKind::Return
6072                | TokenKind::Try
6073                | TokenKind::Throw => return,
6074                _ => {
6075                    self.advance();
6076                }
6077            }
6078        }
6079    }
6080
6081    fn peek(&self) -> &Token {
6082        self.tokens.get(self.pos).unwrap_or(&self.eof)
6083    }
6084
6085    fn peek_at(&self, offset: usize) -> &Token {
6086        self.pos
6087            .checked_add(offset)
6088            .and_then(|index| self.tokens.get(index))
6089            .unwrap_or(&self.eof)
6090    }
6091
6092    /// ASI inserts its `;` as a zero-width token; a written `;` always has width.
6093    fn at_inserted_semicolon(&self) -> bool {
6094        let token = self.peek();
6095        matches!(token.kind, TokenKind::Semicolon) && token.span.start == token.span.end
6096    }
6097
6098    fn advance(&mut self) -> Token {
6099        let tok = self.peek().clone();
6100        if self.pos < self.tokens.len().saturating_sub(1) {
6101            self.pos += 1;
6102        }
6103        tok
6104    }
6105
6106    fn is_at_eof(&self) -> bool {
6107        self.fatal.is_some() || matches!(self.peek().kind, TokenKind::Eof)
6108    }
6109
6110    /// Whether a line break separates the next token from the one before it.
6111    fn line_break_before_peek(&self) -> bool {
6112        self.source_has_line_break(self.prev_token_end(), self.peek().span.start)
6113    }
6114
6115    /// Whether a line break separates the next token from the one after it.
6116    fn line_break_after_peek(&self) -> bool {
6117        self.source_has_line_break(self.peek().span.end, self.peek_at(1).span.start)
6118    }
6119
6120    /// The ASI pass drops newline tokens, so line breaks between tokens are read
6121    /// from the source.
6122    fn source_has_line_break(&self, start: u32, end: u32) -> bool {
6123        self.source
6124            .get(start as usize..end as usize)
6125            .is_some_and(|gap| gap.contains(['\n', '\r']))
6126    }
6127
6128    /// Whether the statement being parsed ends here: at a `;`, or at a line break
6129    /// right after an `as T`. ASI keeps a `[` or `(` on the next line attached, but
6130    /// the type already stopped at the break (see `parse_type_array_inner`), and no
6131    /// expression continues past a type, so TypeScript ends the statement there.
6132    fn at_statement_end(&self) -> bool {
6133        matches!(self.peek().kind, TokenKind::Semicolon)
6134            || (self.last_as_type_end == Some(self.prev_token_end())
6135                && self.line_break_before_peek())
6136    }
6137
6138    /// Consumes the `;` that [`Parser::at_statement_end`] found, if there is one, and
6139    /// returns the statement's end offset.
6140    fn finish_statement(&mut self) -> u32 {
6141        if matches!(self.peek().kind, TokenKind::Semicolon) {
6142            return self.advance().span.end;
6143        }
6144        self.prev_token_end()
6145    }
6146
6147    /// End offset of the most recently consumed token (the body of the file's first
6148    /// token if nothing has been consumed yet).
6149    fn prev_token_end(&self) -> u32 {
6150        self.tokens
6151            .get(self.pos.saturating_sub(1))
6152            .unwrap_or(&self.eof)
6153            .span
6154            .end
6155    }
6156
6157    fn take_leading_doc(&mut self) -> Option<crate::DocComment> {
6158        let parsed = self
6159            .peek()
6160            .leading_doc
6161            .as_ref()
6162            .map(crate::parse_doc_comment)
6163            .transpose();
6164        match parsed {
6165            Ok(doc) => doc,
6166            Err(error) => {
6167                self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6168                None
6169            }
6170        }
6171    }
6172
6173    fn parse_doc(&mut self, raw: &crate::RawDoc) -> Option<crate::DocComment> {
6174        match crate::parse_doc_comment(raw) {
6175            Ok(doc) => Some(doc),
6176            Err(error) => {
6177                self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6178                None
6179            }
6180        }
6181    }
6182
6183    /// JavaScript forbids a `"use strict"` directive in a function whose parameters
6184    /// aren't all plain names: one with a default, a pattern, or `...`. As TypeScript
6185    /// does, each such parameter and the directive are reported.
6186    fn reject_use_strict_with_non_simple_params(&mut self) {
6187        let mut functions: Vec<(Vec<Span>, StmtId)> = Vec::new();
6188        let non_simple = |param: &ParamDecl| {
6189            (param.default.is_some() || param.pattern.is_some() || param.rest).then(|| {
6190                param
6191                    .pattern
6192                    .as_ref()
6193                    .map_or(param.name.span, Binding::span)
6194            })
6195        };
6196        for expr in self.ast.source_expressions() {
6197            if let ExprKind::Arrow {
6198                params,
6199                body: ArrowBody::Block(body),
6200                ..
6201            } = &expr.kind
6202            {
6203                functions.push((params.iter().filter_map(non_simple).collect(), *body));
6204            }
6205        }
6206        for stmt in self.ast.source_statements() {
6207            match &stmt.kind {
6208                StmtKind::Function { params, body, .. } => {
6209                    functions.push((params.iter().filter_map(non_simple).collect(), *body));
6210                }
6211                StmtKind::ClassDecl { members, .. } => {
6212                    for member in members {
6213                        match member {
6214                            ClassMember::Method { params, body, .. }
6215                            | ClassMember::Constructor { params, body, .. } => {
6216                                functions
6217                                    .push((params.iter().filter_map(non_simple).collect(), *body));
6218                            }
6219                            ClassMember::Accessor { param, body, .. } => {
6220                                let params = param.as_deref().and_then(non_simple);
6221                                functions.push((params.into_iter().collect(), *body));
6222                            }
6223                            _ => {}
6224                        }
6225                    }
6226                }
6227                _ => {}
6228            }
6229        }
6230        for (params, body) in functions {
6231            if params.is_empty() {
6232                continue;
6233            }
6234            let Some(directive) = self.use_strict_directive(body) else {
6235                continue;
6236            };
6237            for span in params {
6238                self.error_at(
6239                    span,
6240                    "this parameter is not allowed with a `\"use strict\"` directive",
6241                );
6242            }
6243            self.error_at_with_help(
6244                directive,
6245                "a `\"use strict\"` directive can't be used with a parameter list that has defaults, patterns, or a rest parameter",
6246                vec!["remove the directive; Submilli code is always strict".into()],
6247            );
6248        }
6249    }
6250
6251    /// The span of a `"use strict"` directive in the prologue of a function body:
6252    /// the string-literal statements it starts with. As in TypeScript, the string's
6253    /// value decides, so an escape spelling it counts too; a template doesn't.
6254    fn use_strict_directive(&self, body: StmtId) -> Option<Span> {
6255        let StmtKind::Block(stmts) = &self.ast.try_stmt(body).ok()?.kind else {
6256            return None;
6257        };
6258        for stmt in stmts {
6259            let stmt = self.ast.try_stmt(*stmt).ok()?;
6260            let StmtKind::Expr(expr) = stmt.kind else {
6261                return None;
6262            };
6263            let expr = self.ast.try_expr(expr).ok()?;
6264            let ExprKind::String(value) = &expr.kind else {
6265                return None;
6266            };
6267            if self.source.as_bytes().get(expr.span.start as usize) == Some(&b'`') {
6268                return None;
6269            }
6270            if value == "use strict" {
6271                return Some(stmt.span);
6272            }
6273        }
6274        None
6275    }
6276
6277    fn error_at(&mut self, span: Span, message: impl Into<String>) {
6278        self.diagnostics.push(Diagnostic {
6279            severity: Severity::Error,
6280            span,
6281            message: message.into(),
6282            help: vec![],
6283            notes: vec![],
6284        });
6285    }
6286
6287    /// `==` is `===` here, so `x == null` would silently miss `undefined` — the
6288    /// one case where TypeScript's loose equality differs in a way code relies on.
6289    fn reject_loose_nullish_comparison(
6290        &mut self,
6291        op: BinOp,
6292        lhs: ExprId,
6293        rhs: ExprId,
6294    ) -> Option<()> {
6295        let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
6296        let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
6297        let (nullish, operand, nullish_first) =
6298            match (self.nullish_operand(lhs)?, self.nullish_operand(rhs)?) {
6299                (Some(left), Some(right)) => {
6300                    self.reject_nullish_against_nullish(op, lhs_span, rhs_span, [left, right]);
6301                    return Some(());
6302                }
6303                (None, Some(nullish)) => (nullish, lhs, false),
6304                (Some(nullish), None) => (nullish, rhs, true),
6305                (None, None) => return Some(()),
6306            };
6307        let comparison = LooseComparison {
6308            nullish,
6309            nullish_first,
6310            negated: op == BinOp::NotEq,
6311        };
6312        let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
6313        let x = self.single_line_source(operand_span).unwrap_or("x");
6314        let strict = self.strict_suggestion(&comparison, x);
6315        let either = self.either_suggestion(&comparison, operand, x)?;
6316        let literal = comparison.nullish.literal.text();
6317        // Quote the comparison as written, so `null == z` stays in that order.
6318        let written = self
6319            .single_line_source(self.span(lhs_span.start, rhs_span.end))
6320            .map_or_else(
6321                || {
6322                    let written_op = if comparison.negated { "!=" } else { "==" };
6323                    comparison.ordered(x, written_op, literal)
6324                },
6325                str::to_string,
6326            );
6327        self.error_at_with_help(
6328            self.span(lhs_span.start, rhs_span.end),
6329            format!(
6330                "`{written}` does not also match `{}`: `==` compares like `===`",
6331                comparison.nullish.literal.other().text()
6332            ),
6333            vec![
6334                either,
6335                format!("to test for `{literal}` alone, write `{strict}`"),
6336            ],
6337        );
6338        Some(())
6339    }
6340
6341    /// Both sides are always nullish. TypeScript's `==` treats `null` and
6342    /// `undefined` as equal, so the comparison has one value, which the help
6343    /// names; a `void` operand with effects still runs, as a statement.
6344    fn reject_nullish_against_nullish(
6345        &mut self,
6346        op: BinOp,
6347        lhs: Span,
6348        rhs: Span,
6349        sides: [NullishOperand; 2],
6350    ) {
6351        let span = self.span(lhs.start, rhs.end);
6352        // A comparison written over several lines can't be quoted on one, so
6353        // the message names it without the source.
6354        let subject = self.single_line_source(span).map_or_else(
6355            || "this comparison".to_string(),
6356            |written| format!("`{written}`"),
6357        );
6358        let value = if op == BinOp::NotEq { "false" } else { "true" };
6359        let effects: Vec<String> = sides
6360            .iter()
6361            .filter_map(|side| side.effect)
6362            .map(|effect| {
6363                self.single_line_source(effect.operand).map_or_else(
6364                    || "the `void` operand's expression".to_string(),
6365                    |statement| format!("`{statement};`"),
6366                )
6367            })
6368            .collect();
6369        let help = if effects.is_empty() {
6370            format!("in TypeScript this is always `{value}`; write `{value}`")
6371        } else {
6372            format!(
6373                "in TypeScript this is always `{value}`; run {} {}, then use `{value}`",
6374                effects.join(" and "),
6375                if effects.len() > 1 {
6376                    "each as its own statement"
6377                } else {
6378                    "as its own statement"
6379                }
6380            )
6381        };
6382        self.error_at_with_help(
6383            span,
6384            format!("{subject} compares two nullish values: `==` compares like `===`"),
6385            vec![help],
6386        );
6387    }
6388
6389    /// The strict comparison against the written nullish value. A `void`
6390    /// operand with effects stays as written, so the operand runs once.
6391    fn strict_suggestion(&self, comparison: &LooseComparison, x: &str) -> String {
6392        let eq = comparison.strict_op();
6393        match comparison.nullish.effect {
6394            Some(effect) => {
6395                let void = self.single_line_source(effect.void).unwrap_or("void expr");
6396                comparison.ordered(x, eq, void)
6397            }
6398            None => format!("{x} {eq} {}", comparison.nullish.literal.text()),
6399        }
6400    }
6401
6402    /// "To test for either" names the operand twice, so one with effects is
6403    /// bound to a `const` first, and a `void` operand's expression runs as a
6404    /// statement of its own.
6405    fn either_suggestion(
6406        &mut self,
6407        comparison: &LooseComparison,
6408        operand: ExprId,
6409        x: &str,
6410    ) -> Option<String> {
6411        let mut steps = Vec::new();
6412        let tested = if self.repeats_without_effect(operand)? {
6413            x
6414        } else {
6415            steps.push(format!("bind `{x}` to a `const` (`const value = {x};`)"));
6416            "value"
6417        };
6418        if let Some(effect) = comparison.nullish.effect {
6419            let statement = self.single_line_source(effect.operand).unwrap_or("expr");
6420            let step = format!("run `{statement};` as its own statement");
6421            if comparison.nullish_first {
6422                steps.insert(0, step);
6423            } else {
6424                steps.push(step);
6425            }
6426        }
6427        let eq = comparison.strict_op();
6428        let joiner = if comparison.negated { "&&" } else { "||" };
6429        let both = format!("{tested} {eq} null {joiner} {tested} {eq} undefined");
6430        Some(if steps.is_empty() {
6431            format!("to test for either, write `{both}`")
6432        } else {
6433            format!(
6434                "to test for either, first {}, then write `{both}`",
6435                steps.join(", then ")
6436            )
6437        })
6438    }
6439
6440    /// Whether naming `id` twice evaluates it twice with no effect: a literal,
6441    /// or a reference path.
6442    fn repeats_without_effect(&mut self, id: ExprId) -> Option<bool> {
6443        let unwrapped = self.unparenthesized(id)?;
6444        let expr = parse_arena_result(self.ast.try_expr(unwrapped), &mut self.fatal)?;
6445        if is_literal(&expr.kind) {
6446            return Some(true);
6447        }
6448        if self
6449            .nullish_operand(unwrapped)?
6450            .is_some_and(|nullish| nullish.effect.is_none())
6451        {
6452            return Some(true);
6453        }
6454        self.is_reference_path(id)
6455    }
6456
6457    /// `null`, `undefined` or `void e`, looking through parentheses.
6458    fn nullish_operand(&mut self, id: ExprId) -> Option<Option<NullishOperand>> {
6459        let id = self.unparenthesized(id)?;
6460        let expr = parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?;
6461        let span = expr.span;
6462        Some(match &expr.kind {
6463            ExprKind::Null => Some(NullishOperand {
6464                literal: Nullish::Null,
6465                effect: None,
6466            }),
6467            ExprKind::Identifier(ident) if ident.name == "undefined" => Some(NullishOperand {
6468                literal: Nullish::Undefined,
6469                effect: None,
6470            }),
6471            ExprKind::Void { operand } => {
6472                let operand_id = self.unparenthesized(*operand)?;
6473                let operand =
6474                    parse_arena_result(self.ast.try_expr(operand_id), &mut self.fatal)?.clone();
6475                // A literal or a reference path reads with no effect to keep.
6476                let effect_free =
6477                    is_literal(&operand.kind) || self.is_reference_path(operand_id)?;
6478                let effect = (!effect_free).then_some(VoidEffect {
6479                    void: span,
6480                    operand: operand.span,
6481                });
6482                Some(NullishOperand {
6483                    literal: Nullish::Undefined,
6484                    effect,
6485                })
6486            }
6487            _ => None,
6488        })
6489    }
6490
6491    /// An identifier, `this`, or a `.field` chain on one: naming it twice evaluates
6492    /// it twice with no effect.
6493    fn is_reference_path(&mut self, id: ExprId) -> Option<bool> {
6494        let mut id = id;
6495        loop {
6496            match &parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6497                ExprKind::Identifier(_) | ExprKind::This => return Some(true),
6498                ExprKind::FieldAccess { receiver, .. } => id = *receiver,
6499                ExprKind::Paren(inner) => id = *inner,
6500                _ => return Some(false),
6501            }
6502        }
6503    }
6504
6505    fn unparenthesized(&mut self, id: ExprId) -> Option<ExprId> {
6506        let mut id = id;
6507        loop {
6508            match parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6509                ExprKind::Paren(inner) => id = inner,
6510                _ => return Some(id),
6511            }
6512        }
6513    }
6514
6515    fn single_line_source(&self, span: Span) -> Option<&'a str> {
6516        self.source
6517            .get(span.start as usize..span.end as usize)
6518            .filter(|text| !text.contains('\n'))
6519    }
6520
6521    fn error_at_with_help(&mut self, span: Span, message: impl Into<String>, help: Vec<String>) {
6522        self.diagnostics.push(Diagnostic {
6523            severity: Severity::Error,
6524            span,
6525            message: message.into(),
6526            help,
6527            notes: vec![],
6528        });
6529    }
6530
6531    fn invariant_failure<T>(&mut self, message: &str) -> Option<T> {
6532        self.fatal.get_or_insert_with(|| CompilerFailure::Internal {
6533            stage: CompilerStage::Parse,
6534            span: None,
6535            message: message.into(),
6536        });
6537        self.pos = self.tokens.len();
6538        None
6539    }
6540
6541    fn error_at_peek(&mut self, message: impl Into<String>) {
6542        let span = self.peek().span;
6543        self.error_at(span, message);
6544    }
6545
6546    fn error_at_peek_with_help(&mut self, message: impl Into<String>, help: Vec<String>) {
6547        let span = self.peek().span;
6548        self.error_at_with_help(span, message, help);
6549    }
6550
6551    fn error_count(&self) -> usize {
6552        self.diagnostics
6553            .iter()
6554            .filter(|d| d.severity == Severity::Error)
6555            .count()
6556    }
6557}
6558
6559pub(crate) fn is_property_name(kind: &TokenKind) -> bool {
6560    matches!(
6561        kind,
6562        TokenKind::Identifier
6563            | TokenKind::BooleanLiteral(_)
6564            | TokenKind::NullLiteral
6565            | TokenKind::Let
6566            | TokenKind::Const
6567            | TokenKind::Function
6568            | TokenKind::If
6569            | TokenKind::Else
6570            | TokenKind::While
6571            | TokenKind::Do
6572            | TokenKind::For
6573            | TokenKind::Break
6574            | TokenKind::Continue
6575            | TokenKind::Return
6576            | TokenKind::Switch
6577            | TokenKind::Case
6578            | TokenKind::Default
6579            | TokenKind::Void
6580            | TokenKind::Interface
6581            | TokenKind::Enum
6582            | TokenKind::In
6583            | TokenKind::Typeof
6584            | TokenKind::Import
6585            | TokenKind::Export
6586            | TokenKind::New
6587            | TokenKind::Try
6588            | TokenKind::Catch
6589            | TokenKind::Finally
6590            | TokenKind::Throw
6591            | TokenKind::Class
6592            | TokenKind::Extends
6593            | TokenKind::Implements
6594            | TokenKind::Super
6595            | TokenKind::This
6596    )
6597}
6598
6599fn is_reserved_identifier_word(kind: &TokenKind) -> bool {
6600    matches!(
6601        kind,
6602        TokenKind::BooleanLiteral(_)
6603            | TokenKind::NullLiteral
6604            | TokenKind::Let
6605            | TokenKind::Const
6606            | TokenKind::Function
6607            | TokenKind::If
6608            | TokenKind::Else
6609            | TokenKind::While
6610            | TokenKind::Do
6611            | TokenKind::For
6612            | TokenKind::Break
6613            | TokenKind::Continue
6614            | TokenKind::Return
6615            | TokenKind::Switch
6616            | TokenKind::Case
6617            | TokenKind::Default
6618            | TokenKind::Export
6619            | TokenKind::Void
6620            | TokenKind::Interface
6621            | TokenKind::Enum
6622            | TokenKind::In
6623            | TokenKind::Typeof
6624            | TokenKind::Import
6625            | TokenKind::New
6626            | TokenKind::Try
6627            | TokenKind::Catch
6628            | TokenKind::Finally
6629            | TokenKind::Throw
6630            | TokenKind::Class
6631            | TokenKind::Extends
6632            | TokenKind::Implements
6633            | TokenKind::Super
6634            | TokenKind::This
6635    )
6636}
6637
6638/// Which statement a `{ … }` specifier list belongs to. An import binds local
6639/// names, which strict mode restricts; an export's are module export names,
6640/// which it doesn't.
6641#[derive(Clone, Copy, PartialEq, Eq)]
6642enum SpecifierList {
6643    Import,
6644    Export,
6645}
6646
6647impl SpecifierList {
6648    fn noun(self) -> &'static str {
6649        match self {
6650            SpecifierList::Import => "import",
6651            SpecifierList::Export => "export",
6652        }
6653    }
6654}
6655
6656/// Words strict mode reserves that Submilli otherwise lexes as identifiers: they
6657/// can't name a binding or a type, but stay valid as property names and modifiers.
6658/// (`implements`, `interface` and `let` are keywords already.)
6659const STRICT_MODE_RESERVED_WORDS: [&str; 6] = [
6660    "package",
6661    "private",
6662    "protected",
6663    "public",
6664    "static",
6665    "yield",
6666];
6667
6668fn reserved_keyword_rename_example(keyword: &str) -> &'static str {
6669    match keyword {
6670        "default" => "defaultValue",
6671        _ => "valueName",
6672    }
6673}
6674
6675fn mixed_logical(prev: BinOp, next: BinOp) -> bool {
6676    let prev_logical = matches!(prev, BinOp::Or | BinOp::And);
6677    let next_logical = matches!(next, BinOp::Or | BinOp::And);
6678    let prev_nullish = matches!(prev, BinOp::NullishCoalesce);
6679    let next_nullish = matches!(next, BinOp::NullishCoalesce);
6680    (prev_logical && next_nullish) || (prev_nullish && next_logical)
6681}
6682
6683/// Spells out both groupings in the order the operators were written. Called
6684/// only for pairs `mixed_logical` accepts, so the non-`??` operator is `||` or `&&`.
6685fn mixed_logical_help(prev: BinOp, next: BinOp) -> String {
6686    let symbol = |op: BinOp| if op == BinOp::And { "&&" } else { "||" };
6687    let (first, second) = if prev == BinOp::NullishCoalesce {
6688        ("??", symbol(next))
6689    } else {
6690        (symbol(prev), "??")
6691    };
6692    format!(
6693        "add parentheses to choose the grouping: `(a {first} b) {second} c` or `a {first} (b {second} c)`"
6694    )
6695}
6696
6697const LOGICAL_AND_PREC: u8 = 2;
6698
6699/// One side of a loose comparison that is always nullish.
6700#[derive(Clone, Copy)]
6701struct NullishOperand {
6702    literal: Nullish,
6703    /// Set for `void e` when `e` is not a literal and may have effects.
6704    effect: Option<VoidEffect>,
6705}
6706
6707#[derive(Clone, Copy, PartialEq, Eq)]
6708enum Nullish {
6709    Null,
6710    Undefined,
6711}
6712
6713impl Nullish {
6714    fn text(self) -> &'static str {
6715        match self {
6716            Nullish::Null => "null",
6717            Nullish::Undefined => "undefined",
6718        }
6719    }
6720
6721    /// The nullish value a loose comparison against `self` fails to match.
6722    fn other(self) -> Self {
6723        match self {
6724            Nullish::Null => Nullish::Undefined,
6725            Nullish::Undefined => Nullish::Null,
6726        }
6727    }
6728}
6729
6730/// A rejected `x == null`-style comparison, as written.
6731struct LooseComparison {
6732    nullish: NullishOperand,
6733    /// Whether the nullish side is written first, as in `null == x`.
6734    nullish_first: bool,
6735    /// `!=` rather than `==`.
6736    negated: bool,
6737}
6738
6739impl LooseComparison {
6740    fn strict_op(&self) -> &'static str {
6741        if self.negated { "!==" } else { "===" }
6742    }
6743
6744    /// `x op nullish`, in the order the comparison was written.
6745    fn ordered(&self, x: &str, op: &str, nullish: &str) -> String {
6746        if self.nullish_first {
6747            format!("{nullish} {op} {x}")
6748        } else {
6749            format!("{x} {op} {nullish}")
6750        }
6751    }
6752}
6753
6754#[derive(Clone, Copy)]
6755struct VoidEffect {
6756    /// The whole `void e`.
6757    void: Span,
6758    /// The `e`, without enclosing parentheses.
6759    operand: Span,
6760}
6761
6762fn is_literal(kind: &ExprKind) -> bool {
6763    matches!(
6764        kind,
6765        ExprKind::Number(_)
6766            | ExprKind::BigInt(_)
6767            | ExprKind::String(_)
6768            | ExprKind::Boolean(_)
6769            | ExprKind::Null
6770    )
6771}
6772
6773/// What `Parser::check_parameter_order` needs from either kind of parameter list.
6774struct ParamOrder<'p> {
6775    optional: bool,
6776    /// Neither optional, defaulted, nor rest.
6777    required: bool,
6778    /// `None` for a destructuring pattern.
6779    name: Option<&'p str>,
6780    span: Span,
6781}
6782
6783impl<'p> ParamOrder<'p> {
6784    fn of_param(param: &'p ParamDecl) -> Self {
6785        let (name, span) = match &param.pattern {
6786            Some(binding) => (None, binding.span()),
6787            None => (Some(param.name.name.as_str()), param.name.span),
6788        };
6789        Self {
6790            optional: param.optional,
6791            required: !param.is_omittable() && !param.rest,
6792            name,
6793            span,
6794        }
6795    }
6796
6797    fn of_field(field: &'p TypeAnnotationField) -> Self {
6798        Self {
6799            optional: field.optional,
6800            required: !field.optional && !field.rest,
6801            name: Some(field.name.name.as_str()),
6802            span: field.name.span,
6803        }
6804    }
6805}
6806
6807/// A binary operator at the cursor, which may span several tokens.
6808struct BinaryToken {
6809    op: BinOp,
6810    precedence: u8,
6811    tokens: usize,
6812}
6813
6814/// `<<`, `>>`, `>>>` or their compound assignments, read from adjacent angle tokens.
6815struct ShiftToken {
6816    op: BinOp,
6817    tokens: usize,
6818    /// The operator ends in `=`: `<<=`, `>>=`, `>>>=`.
6819    assignment: bool,
6820}
6821
6822impl ShiftToken {
6823    fn new(op: BinOp, tokens: usize, assignment: bool) -> Self {
6824        Self {
6825            op,
6826            tokens,
6827            assignment,
6828        }
6829    }
6830}
6831
6832/// Between relational (7) and additive (9) in the `peek_binop` table.
6833const SHIFT_PRECEDENCE: u8 = 8;
6834
6835#[derive(Clone, Copy, PartialEq)]
6836enum TupleElementLabel {
6837    None,
6838    /// `name: T`
6839    Required,
6840    /// `name?: T`
6841    Optional,
6842}
6843
6844fn peek_binop(kind: &TokenKind) -> Option<(BinOp, u8)> {
6845    Some(match kind {
6846        TokenKind::QuestionQuestion => (BinOp::NullishCoalesce, 0),
6847        TokenKind::PipePipe => (BinOp::Or, 1),
6848        TokenKind::AmpAmp => (BinOp::And, LOGICAL_AND_PREC),
6849        TokenKind::Pipe => (BinOp::BitOr, 3),
6850        TokenKind::Caret => (BinOp::BitXor, 4),
6851        TokenKind::Amp => (BinOp::BitAnd, 5),
6852        TokenKind::EqEqEq | TokenKind::EqEq => (BinOp::Eq, 6),
6853        TokenKind::BangEqEq | TokenKind::BangEq => (BinOp::NotEq, 6),
6854        TokenKind::LessThan => (BinOp::Lt, 7),
6855        TokenKind::GreaterThan => (BinOp::Gt, 7),
6856        TokenKind::LessEquals => (BinOp::Le, 7),
6857        TokenKind::GreaterEquals => (BinOp::Ge, 7),
6858        TokenKind::In => (BinOp::In, 7),
6859        // Shifts take `SHIFT_PRECEDENCE` (8); they span several angle tokens, so
6860        // `Parser::peek_shift` recognizes them rather than this table.
6861        TokenKind::Plus => (BinOp::Add, 9),
6862        TokenKind::Minus => (BinOp::Sub, 9),
6863        TokenKind::Star => (BinOp::Mul, 10),
6864        TokenKind::Slash => (BinOp::Div, 10),
6865        TokenKind::Percent => (BinOp::Rem, 10),
6866        // `**` recurses with `prec` not `prec + 1` — that's what makes it right-associative.
6867        TokenKind::StarStar => (BinOp::Pow, 11),
6868        _ => return None,
6869    })
6870}
6871
6872/// After `as` or `satisfies`, a token that can only extend or end an operand, never
6873/// begin a type. `[` counts, as TypeScript reads `delete as [0]` as an index, so a
6874/// tuple cast of a binding named `delete` needs parentheses.
6875fn cannot_start_type(kind: &TokenKind) -> bool {
6876    matches!(
6877        kind,
6878        TokenKind::Dot
6879            | TokenKind::QuestionDot
6880            | TokenKind::LeftBracket
6881            | TokenKind::Bang
6882            | TokenKind::Semicolon
6883            | TokenKind::RightParen
6884            | TokenKind::RightBrace
6885            | TokenKind::Comma
6886            | TokenKind::Eof
6887    )
6888}
6889
6890/// The lowest precedence the right operand of `op` may contain. `**` is
6891/// right-associative, so it recurses at its own level. An operand of `??` may
6892/// not be an unparenthesized `||` / `&&` expression, so it stops below them too.
6893fn right_operand_min_prec(op: BinOp, prec: u8) -> u8 {
6894    match op {
6895        BinOp::Pow => prec,
6896        BinOp::NullishCoalesce => LOGICAL_AND_PREC + 1,
6897        _ => prec + 1,
6898    }
6899}
6900
6901fn compound_op_for_token(kind: &TokenKind) -> Option<BinOp> {
6902    Some(match kind {
6903        TokenKind::PlusEquals => BinOp::Add,
6904        TokenKind::MinusEquals => BinOp::Sub,
6905        TokenKind::StarEquals => BinOp::Mul,
6906        TokenKind::SlashEquals => BinOp::Div,
6907        TokenKind::PercentEquals => BinOp::Rem,
6908        TokenKind::StarStarEquals => BinOp::Pow,
6909        TokenKind::AmpEquals => BinOp::BitAnd,
6910        TokenKind::PipeEquals => BinOp::BitOr,
6911        TokenKind::CaretEquals => BinOp::BitXor,
6912        _ => return None,
6913    })
6914}
6915
6916fn is_assign_lookahead(kind: &TokenKind) -> bool {
6917    matches!(kind, TokenKind::Equals) || compound_op_for_token(kind).is_some()
6918}
6919
6920/// Parser productions use `None` for recovery; arena errors also latch a fatal
6921/// cause so speculative parsing cannot turn them into ordinary syntax errors.
6922fn parse_arena_result<T>(
6923    result: Result<T, crate::arena::ArenaError>,
6924    fatal: &mut Option<CompilerFailure>,
6925) -> Option<T> {
6926    match result {
6927        Ok(value) => Some(value),
6928        Err(error) => {
6929            fatal.get_or_insert_with(|| error.into_compiler_failure(CompilerStage::Parse));
6930            None
6931        }
6932    }
6933}
6934
6935/// Whether a rest parameter's annotation names an array: `T[]`, or `readonly T[]`.
6936fn is_rest_array_annotation(ty: &crate::TypeAnnotation) -> bool {
6937    use crate::ast::TypeAnnotationKind;
6938    match &ty.kind {
6939        TypeAnnotationKind::Array(_) => true,
6940        TypeAnnotationKind::Readonly(inner) => matches!(inner.kind, TypeAnnotationKind::Array(_)),
6941        _ => false,
6942    }
6943}
6944
6945#[cfg(test)]
6946mod tests {
6947    use super::{MAX_ERRORS, MAX_PARSE_DEPTH, Parser, parse};
6948    use crate::source::Sources;
6949    use crate::{Asi, Ast, ExprKind, FileId, ImportKind, StmtKind, Token, TokenKind, diagnostics};
6950
6951    const F: FileId = FileId(0);
6952
6953    #[test]
6954    fn malformed_direct_token_streams_return_typed_failures() {
6955        use crate::compiler_error::CompilerFailure;
6956        for tokens in [
6957            Vec::new(),
6958            vec![Token::new(
6959                TokenKind::Identifier,
6960                crate::Span::new(F, 0, 2).unwrap(),
6961            )],
6962            vec![
6963                Token::new(
6964                    TokenKind::Identifier,
6965                    crate::Span {
6966                        file: F,
6967                        start: 1,
6968                        end: 2,
6969                    },
6970                ),
6971                Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
6972            ],
6973            vec![
6974                Token::new(TokenKind::Eof, crate::Span::at(F)),
6975                Token::new(TokenKind::Eof, crate::Span::at(F)),
6976            ],
6977            vec![
6978                Token::new(
6979                    TokenKind::Identifier,
6980                    crate::Span {
6981                        file: F,
6982                        start: 2,
6983                        end: 0,
6984                    },
6985                ),
6986                Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
6987            ],
6988            vec![Token::new(TokenKind::Eof, crate::Span::at(FileId(9)))],
6989        ] {
6990            let error = super::parse_checked("é", tokens, F).expect_err("invalid token metadata");
6991            assert!(matches!(
6992                error.fatal,
6993                Some(CompilerFailure::Internal { .. })
6994            ));
6995        }
6996    }
6997
6998    #[test]
6999    fn malformed_documentation_metadata_returns_a_fatal_error() {
7000        let source = "/** hello */ function main(): void {}";
7001        let valid = tokens_of(source);
7002        for doc in [
7003            crate::RawDoc {
7004                text: "/** hi */".into(),
7005                span: crate::Span {
7006                    file: F,
7007                    start: u32::MAX,
7008                    end: u32::MAX,
7009                },
7010            },
7011            crate::RawDoc {
7012                text: "/** different */".into(),
7013                span: crate::Span::new(F, 0, 12).unwrap(),
7014            },
7015            crate::RawDoc {
7016                text: "/** hello */".into(),
7017                span: crate::Span::new(FileId(9), 0, 12).unwrap(),
7018            },
7019        ] {
7020            let mut tokens = valid.clone();
7021            tokens[0].leading_doc = Some(doc);
7022            let error = super::parse_checked(source, tokens, F).expect_err("invalid doc metadata");
7023            assert!(error.to_string().contains("documentation metadata"));
7024        }
7025        assert!(super::parse_checked(source, valid, F).is_ok());
7026    }
7027
7028    #[test]
7029    fn invalid_parser_dispatch_survives_diagnostic_rollback() {
7030        let mut parser = parser_from_source("let x = 1;");
7031        assert!(parser.try_parse_type_args_only().is_none());
7032        parser.diagnostics.clear();
7033        assert!(parser.fatal.is_some());
7034        assert!(parser.parse_expression().is_none());
7035    }
7036
7037    #[test]
7038    fn invalid_arena_read_stops_parser_and_survives_diagnostic_rollback() {
7039        use crate::{
7040            ExprId,
7041            compiler_error::{CompilerFailure, CompilerStage},
7042        };
7043        let mut parser = parser_from_source("let x = 1;");
7044        assert!(parser.statement_kind_for(ExprId(u32::MAX)).is_none());
7045        assert!(matches!(
7046            parser.fatal,
7047            Some(CompilerFailure::Internal {
7048                stage: CompilerStage::Parse,
7049                span: None,
7050                ..
7051            })
7052        ));
7053        parser.diagnostics.clear();
7054        let position = parser.pos;
7055        assert!(parser.parse_statement().is_none());
7056        parser.recover();
7057        assert_eq!(parser.pos, position);
7058        assert!(parser.ast.expr_ids().unwrap().next().is_none());
7059    }
7060
7061    fn tokens_of(source: &str) -> Vec<Token> {
7062        let mut asi = Asi::new(source, crate::FileId(0));
7063        let mut tokens = Vec::new();
7064        loop {
7065            let tok = asi.next_token();
7066            let is_eof = matches!(tok.kind, TokenKind::Eof);
7067            tokens.push(tok);
7068            if is_eof {
7069                break;
7070            }
7071        }
7072        let diags = asi.into_diagnostics();
7073        assert!(diags.is_empty(), "unexpected lexer diagnostics: {diags:?}");
7074        tokens
7075    }
7076
7077    fn parse_str(source: &str) -> (Ast, Vec<crate::Diagnostic>) {
7078        parse(source, tokens_of(source), crate::FileId(0))
7079    }
7080
7081    fn parser_from_source(source: &str) -> Parser<'_> {
7082        Parser {
7083            source,
7084            tokens: tokens_of(source),
7085            file: F,
7086            pos: 0,
7087            ast: Ast::new(),
7088            diagnostics: Vec::new(),
7089            block_depth: 0,
7090            class_member_body_depth: 0,
7091            function_expression_body_depth: 0,
7092            recursion_depth: 0,
7093            recursion_limit_span: None,
7094            resume_at_next_member: false,
7095            last_as_type_end: None,
7096            eof: Token::new(
7097                TokenKind::Eof,
7098                crate::Span::new(F, source.len() as u32, source.len() as u32).unwrap(),
7099            ),
7100            fatal: None,
7101        }
7102    }
7103
7104    #[test]
7105    fn recursion_budget_accepts_boundary_and_restores_depth() {
7106        let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH - 1));
7107        let mut parser = parser_from_source(&source);
7108        assert!(parser.parse_unary().is_some());
7109        assert_eq!(parser.recursion_depth, 0);
7110        assert!(parser.recursion_limit_span.is_none());
7111
7112        let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH));
7113        let mut parser = parser_from_source(&source);
7114        assert!(parser.parse_unary().is_none());
7115        assert_eq!(parser.recursion_depth, 0);
7116        assert!(parser.recursion_limit_span.is_some());
7117    }
7118
7119    #[test]
7120    fn parse_empty_input() {
7121        let (ast, diags) = parse_str("");
7122        assert!(diags.is_empty());
7123        assert!(ast.top_level.is_empty());
7124    }
7125
7126    #[test]
7127    fn any_type_is_rejected() {
7128        // `any` is not supported — the parser rejects it in every type position
7129        // (annotation, cast target, array/generic element) and points to the fix.
7130        for src in [
7131            "function main(): void { const x: any = 1; }",
7132            "function main(): void { const x = 1 as any; }",
7133            "function main(): void { const x: any[] = []; }",
7134        ] {
7135            let (_ast, diags) = parse_str(src);
7136            assert!(
7137                diags
7138                    .iter()
7139                    .any(|d| d.message.contains("`any` is not supported")),
7140                "expected an `any` rejection for {src:?}, got: {diags:?}",
7141            );
7142            assert!(
7143                diags
7144                    .iter()
7145                    .any(|d| d.help.iter().any(|h| h.contains("`unknown`"))),
7146                "expected the fix-shape help for {src:?}, got: {diags:?}",
7147            );
7148        }
7149    }
7150
7151    #[test]
7152    fn any_catch_binding_help_suggests_catch_forms() {
7153        // `unknown` is not a valid catch binding type, so the general `any`
7154        // help would send the reader to a second error.
7155        let (_ast, diags) = parse_str("function main(): void { try { } catch (e: any) { } }");
7156        let any_diags: Vec<_> = diags
7157            .iter()
7158            .filter(|d| d.message.contains("`any` is not supported"))
7159            .collect();
7160        assert_eq!(any_diags.len(), 1, "got: {diags:?}");
7161        assert!(
7162            any_diags[0].help.iter().any(|h| h.contains("`catch (e)`")),
7163            "got: {diags:?}"
7164        );
7165        assert!(
7166            !any_diags[0].help.iter().any(|h| h.contains("`unknown`")),
7167            "got: {diags:?}"
7168        );
7169    }
7170
7171    #[test]
7172    fn arrow_return_type_scanner_matches_type_grammar() {
7173        // Every form the annotation grammar accepts must also be accepted by
7174        // the arrow-disambiguator's lookahead (scan_past_type_annotation):
7175        // a miss there turns a valid arrow into `expected expression`.
7176        // When adding a type form to parse_type_array, add a row here.
7177        for ty in [
7178            "number",
7179            "void",
7180            "null",
7181            "ns.Foo",
7182            "Map<string, number>",
7183            "ns.Foo<string>[]",
7184            "number[]",
7185            "[number, number]",
7186            "[number, [string, boolean]]",
7187            "[number, string][]",
7188            "{ a: number }",
7189            "{ a: number }[]",
7190            "\"on\" | \"off\"",
7191            "1 | 2",
7192            "Point | null",
7193            "| number | string",
7194            "(n: number) => number",
7195            "(n: number) => [number, number]",
7196            "readonly number[]",
7197            "readonly number[][]",
7198            "readonly [number, string]",
7199            "readonly number[] | null",
7200            "[first: number, second: string]",
7201            "[new: number, string]",
7202            "keyof { a: number }",
7203        ] {
7204            let annotation = format!("function f(): void {{ const a: {ty} = x; }}");
7205            let (_ast, diags) = parse_str(&annotation);
7206            assert!(
7207                diags.is_empty(),
7208                "annotation grammar rejected {ty:?}: {diags:?}",
7209            );
7210
7211            let arrow = format!("function f(): void {{ const g = (): {ty} => x; }}");
7212            let (_ast, diags) = parse_str(&arrow);
7213            assert!(
7214                diags.is_empty(),
7215                "arrow lookahead rejected return type {ty:?}: {diags:?}",
7216            );
7217        }
7218
7219        // Type-predicate returns ride the same scanner.
7220        let (_ast, diags) =
7221            parse_str("function f(): void { const g = (v: unknown): v is string => x; }");
7222        assert!(
7223            diags.is_empty(),
7224            "arrow lookahead rejected predicate return: {diags:?}",
7225        );
7226    }
7227
7228    #[test]
7229    fn bare_semicolons_are_silent_empty_statements() {
7230        let (ast, diags) = parse_str(";");
7231        assert!(diags.is_empty());
7232        assert!(ast.top_level.is_empty());
7233    }
7234
7235    #[test]
7236    fn parse_single_non_expression_token_errors() {
7237        let (ast, diags) = parse_str("}");
7238        assert_eq!(diags.len(), 1);
7239        assert_eq!(diags[0].message, "expected expression");
7240        assert!(ast.top_level.is_empty());
7241    }
7242
7243    #[test]
7244    fn recovery_across_many_bad_starts() {
7245        let (ast, diags) = parse_str("} } } x;");
7246        assert_eq!(diags.len(), 3);
7247        for d in &diags {
7248            assert_eq!(d.message, "expected expression");
7249        }
7250        assert_eq!(ast.top_level.len(), 1);
7251    }
7252
7253    #[test]
7254    fn recovery_consumes_semicolon() {
7255        let mut p = parser_from_source("foo ;");
7256        p.error_at_peek("test");
7257        p.recover();
7258        assert!(matches!(
7259            p.peek().kind,
7260            TokenKind::Semicolon | TokenKind::Eof
7261        ));
7262        assert!(matches!(p.peek().kind, TokenKind::Eof));
7263    }
7264
7265    #[test]
7266    fn recovery_stops_at_right_brace() {
7267        let mut p = parser_from_source("foo }");
7268        p.error_at_peek("test");
7269        p.recover();
7270        assert!(matches!(p.peek().kind, TokenKind::RightBrace));
7271    }
7272
7273    #[test]
7274    fn recovery_stops_at_let_keyword() {
7275        let mut p = parser_from_source("foo let x");
7276        p.error_at_peek("test");
7277        p.recover();
7278        assert!(matches!(p.peek().kind, TokenKind::Let));
7279    }
7280
7281    #[test]
7282    fn recovery_stops_at_function_keyword() {
7283        let mut p = parser_from_source("foo function bar");
7284        p.error_at_peek("test");
7285        p.recover();
7286        assert!(matches!(p.peek().kind, TokenKind::Function));
7287    }
7288
7289    #[test]
7290    fn error_cap_at_20() {
7291        let source = "} ".repeat(25);
7292        let (_, diags) = parse_str(&source);
7293        assert_eq!(diags.len(), MAX_ERRORS);
7294    }
7295
7296    #[test]
7297    fn driver_makes_progress_past_unmatched_brace() {
7298        let (ast, diags) = parse_str("} x;");
7299        assert_eq!(diags.len(), 1);
7300        assert_eq!(diags[0].message, "expected expression");
7301        assert_eq!(ast.top_level.len(), 1);
7302    }
7303
7304    #[test]
7305    fn snapshot_multi_error_render() {
7306        let source = "foo bar;\nbaz qux;\nhello world;";
7307        let (_, diags) = parse_str(source);
7308        let (sources, _) = Sources::single("script.subm", source).unwrap();
7309        let rendered: String = diags
7310            .iter()
7311            .map(|d| diagnostics::render(d, &sources))
7312            .collect::<Vec<_>>()
7313            .join("\n");
7314        insta::assert_snapshot!(rendered);
7315    }
7316
7317    fn single_stmt(ast: &Ast) -> &crate::Stmt {
7318        assert_eq!(ast.top_level.len(), 1);
7319        ast.try_stmt(ast.top_level[0]).unwrap()
7320    }
7321
7322    fn expr_of_single_stmt(ast: &Ast) -> &crate::Expr {
7323        let stmt = single_stmt(ast);
7324        match stmt.kind {
7325            crate::StmtKind::Expr(id) => ast.try_expr(id).unwrap(),
7326            _ => panic!("expected expression statement, got {:?}", stmt.kind),
7327        }
7328    }
7329
7330    #[test]
7331    fn parse_number_literal() {
7332        let (ast, diags) = parse_str("42;");
7333        assert!(diags.is_empty());
7334        let expr = expr_of_single_stmt(&ast);
7335        assert_eq!(expr.kind, crate::ExprKind::Number(42.0));
7336        assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
7337    }
7338
7339    #[test]
7340    fn parse_string_literal() {
7341        let (ast, diags) = parse_str(r#""s";"#);
7342        assert!(diags.is_empty());
7343        let expr = expr_of_single_stmt(&ast);
7344        assert_eq!(expr.kind, crate::ExprKind::String("s".to_string()));
7345        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7346    }
7347
7348    #[test]
7349    fn parse_boolean_true() {
7350        let (ast, diags) = parse_str("true;");
7351        assert!(diags.is_empty());
7352        let expr = expr_of_single_stmt(&ast);
7353        assert_eq!(expr.kind, crate::ExprKind::Boolean(true));
7354        assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7355    }
7356
7357    #[test]
7358    fn parse_boolean_false() {
7359        let (ast, diags) = parse_str("false;");
7360        assert!(diags.is_empty());
7361        let expr = expr_of_single_stmt(&ast);
7362        assert_eq!(expr.kind, crate::ExprKind::Boolean(false));
7363        assert_eq!(expr.span, crate::Span::new(F, 0, 5).unwrap());
7364    }
7365
7366    #[test]
7367    fn parse_null_literal() {
7368        let (ast, diags) = parse_str("null;");
7369        assert!(diags.is_empty());
7370        let expr = expr_of_single_stmt(&ast);
7371        assert_eq!(expr.kind, crate::ExprKind::Null);
7372        assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7373    }
7374
7375    #[test]
7376    fn parse_identifier() {
7377        let (ast, diags) = parse_str("x;");
7378        assert!(diags.is_empty());
7379        let expr = expr_of_single_stmt(&ast);
7380        assert!(matches!(expr.kind, crate::ExprKind::Identifier(_)));
7381        assert_eq!(expr.span, crate::Span::new(F, 0, 1).unwrap());
7382    }
7383
7384    #[test]
7385    fn reserved_keywords_remain_invalid_declaration_names() {
7386        let (_ast, diags) = parse_str("function default(): void {}");
7387        assert_eq!(diags.len(), 1, "{diags:#?}");
7388        assert_eq!(
7389            diags[0].message,
7390            "`default` is a reserved keyword and can't be used as a name"
7391        );
7392        assert!(
7393            diags[0]
7394                .help
7395                .iter()
7396                .any(|h| h == "rename it, for example: `defaultValue`"),
7397            "expected rename help, got: {diags:?}"
7398        );
7399    }
7400
7401    #[test]
7402    fn eval_and_arguments_cannot_name_bindings() {
7403        let bindings = [
7404            ("let arguments = 1;", "arguments"),
7405            ("const [eval] = [1];", "eval"),
7406            ("const { a: eval } = { a: 1 };", "eval"),
7407            ("const { eval } = { eval: 1 };", "eval"),
7408            ("const [...arguments] = [1];", "arguments"),
7409            ("function eval(): void {}", "eval"),
7410            ("function f(arguments: number): void {}", "arguments"),
7411            ("const f = arguments => arguments;", "arguments"),
7412            ("const f = (...eval: number[]) => 1;", "eval"),
7413            ("const f = function eval(): void {};", "eval"),
7414            ("for (const eval of [1]) {}", "eval"),
7415            ("try {} catch (arguments) {}", "arguments"),
7416            ("type F = (eval: number) => void;", "eval"),
7417            ("type G = (...arguments: number[]) => void;", "arguments"),
7418            ("type H = { [eval: string]: number };", "eval"),
7419        ];
7420        for (source, word) in bindings {
7421            let (_ast, diags) = parse_str(source);
7422            assert!(
7423                diags
7424                    .iter()
7425                    .any(|d| d.message == format!("`{word}` can't name a binding in strict mode")),
7426                "{source}: {diags:#?}"
7427            );
7428        }
7429    }
7430
7431    #[test]
7432    fn eval_and_arguments_still_name_types_and_properties() {
7433        for source in [
7434            "class eval {}",
7435            "interface arguments { eval: number }",
7436            "type eval = number;",
7437            "enum arguments { eval }",
7438            "const o = { eval: 1, arguments: 2 };",
7439            "import { eval } from \"./m\";",
7440        ] {
7441            let (_ast, diags) = parse_str(source);
7442            assert!(diags.is_empty(), "{source}: {diags:#?}");
7443        }
7444    }
7445
7446    #[test]
7447    fn reserved_keywords_remain_invalid_parameter_names() {
7448        let (_ast, diags) = parse_str("function f(default: string): void {}");
7449        assert_eq!(diags.len(), 1, "{diags:#?}");
7450        assert_eq!(
7451            diags[0].message,
7452            "`default` is a reserved keyword and can't be used as a name"
7453        );
7454        assert!(
7455            diags[0]
7456                .help
7457                .iter()
7458                .any(|h| h == "rename it, for example: `defaultValue`"),
7459            "expected rename help, got: {diags:?}"
7460        );
7461    }
7462
7463    #[test]
7464    fn contextual_keywords_are_valid_names() {
7465        for src in [
7466            "const type = \"x\";",
7467            "const from = 1;",
7468            "let of = 2;",
7469            "const as = 3;",
7470            "const is = 4;",
7471            "function f(from: string): void {}",
7472        ] {
7473            let (_ast, diags) = parse_str(src);
7474            assert!(
7475                diags.is_empty(),
7476                "unexpected diagnostics for {src:?}: {diags:#?}"
7477            );
7478        }
7479    }
7480
7481    #[test]
7482    fn type_alias_dispatch_table() {
7483        let (ast, diags) = parse_str("type X = number;");
7484        assert!(diags.is_empty(), "{diags:#?}");
7485        assert!(matches!(
7486            ast.try_stmt(ast.top_level[0]).unwrap().kind,
7487            crate::StmtKind::TypeAliasDecl { .. }
7488        ));
7489
7490        // A binding named `type` keeps expression/assignment statements working.
7491        for src in ["type = 5;", "type;", "type(1);", "type < 3;", "type.foo;"] {
7492            let (ast, diags) = parse_str(src);
7493            assert!(
7494                diags.is_empty(),
7495                "unexpected diagnostics for {src:?}: {diags:#?}"
7496            );
7497            assert!(
7498                !matches!(
7499                    ast.try_stmt(ast.top_level[0]).unwrap().kind,
7500                    crate::StmtKind::TypeAliasDecl { .. }
7501                ),
7502                "{src:?} must not parse as a type alias"
7503            );
7504        }
7505
7506        let (_ast, diags) = parse_str("type X;");
7507        assert!(
7508            diags
7509                .iter()
7510                .any(|d| d.message == "expected `=` after type alias name"),
7511            "bare `type X` should commit to the alias diagnostic, got: {diags:#?}"
7512        );
7513    }
7514
7515    #[test]
7516    fn export_type_alias_dispatch() {
7517        let (ast, diags) = parse_str("export type X = number;");
7518        assert!(diags.is_empty(), "{diags:#?}");
7519        assert_eq!(ast.exported_decls.len(), 1);
7520        assert!(matches!(
7521            ast.try_stmt(ast.exported_decls[0].stmt).unwrap().kind,
7522            crate::StmtKind::TypeAliasDecl { .. }
7523        ));
7524
7525        let (_ast, diags) = parse_str("export type = 5;");
7526        assert!(
7527            diags
7528                .iter()
7529                .any(|d| d.message == "expected a declaration after `export`"),
7530            "`export type = 5` must not become an exported assignment, got: {diags:#?}"
7531        );
7532    }
7533
7534    #[test]
7535    fn parse_paren_simple() {
7536        let (ast, diags) = parse_str("(x);");
7537        assert!(diags.is_empty());
7538        let expr = expr_of_single_stmt(&ast);
7539        let crate::ExprKind::Paren(inner_id) = expr.kind else {
7540            panic!("expected Paren, got {:?}", expr.kind);
7541        };
7542        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7543        let inner = ast.try_expr(inner_id).unwrap();
7544        assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7545        assert_eq!(inner.span, crate::Span::new(F, 1, 2).unwrap());
7546    }
7547
7548    #[test]
7549    fn parse_paren_nested() {
7550        let (ast, diags) = parse_str("((x));");
7551        assert!(diags.is_empty());
7552        let outer = expr_of_single_stmt(&ast);
7553        let crate::ExprKind::Paren(mid_id) = outer.kind else {
7554            panic!("expected outer Paren");
7555        };
7556        assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7557        let mid = ast.try_expr(mid_id).unwrap();
7558        let crate::ExprKind::Paren(inner_id) = mid.kind else {
7559            panic!("expected inner Paren");
7560        };
7561        assert_eq!(mid.span, crate::Span::new(F, 1, 4).unwrap());
7562        let inner = ast.try_expr(inner_id).unwrap();
7563        assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7564        assert_eq!(inner.span, crate::Span::new(F, 2, 3).unwrap());
7565    }
7566
7567    #[test]
7568    fn parse_sequence_of_atoms() {
7569        let (ast, diags) = parse_str(r#"42; "hi"; true; null; x; (x);"#);
7570        assert!(diags.is_empty());
7571        assert_eq!(ast.top_level.len(), 6);
7572        insta::assert_debug_snapshot!(ast);
7573    }
7574
7575    #[test]
7576    fn missing_semicolon_after_expression() {
7577        let (ast, diags) = parse_str("42 43");
7578        assert_eq!(diags.len(), 1);
7579        assert_eq!(diags[0].message, "expected `;` after expression");
7580        assert!(ast.top_level.is_empty());
7581    }
7582
7583    #[test]
7584    fn empty_paren_diagnoses() {
7585        let (ast, diags) = parse_str("();");
7586        assert_eq!(diags.len(), 1);
7587        assert_eq!(diags[0].message, "expected expression");
7588        assert!(ast.top_level.is_empty());
7589    }
7590
7591    #[test]
7592    fn unterminated_paren_diagnoses() {
7593        let (ast, diags) = parse_str("(x;");
7594        assert_eq!(diags.len(), 1);
7595        assert_eq!(diags[0].message, "expected `)`");
7596        assert!(ast.top_level.is_empty());
7597    }
7598
7599    fn binary(e: &crate::Expr) -> (crate::BinOp, crate::ExprId, crate::ExprId) {
7600        match e.kind {
7601            crate::ExprKind::Binary { op, lhs, rhs } => (op, lhs, rhs),
7602            _ => panic!("expected Binary, got {:?}", e.kind),
7603        }
7604    }
7605
7606    #[test]
7607    fn parse_binary_add() {
7608        let (ast, diags) = parse_str("a + b;");
7609        assert!(diags.is_empty());
7610        let outer = expr_of_single_stmt(&ast);
7611        let (op, lhs, rhs) = binary(outer);
7612        assert_eq!(op, crate::BinOp::Add);
7613        assert!(matches!(
7614            ast.try_expr(lhs).unwrap().kind,
7615            crate::ExprKind::Identifier(_)
7616        ));
7617        assert!(matches!(
7618            ast.try_expr(rhs).unwrap().kind,
7619            crate::ExprKind::Identifier(_)
7620        ));
7621        assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7622    }
7623
7624    #[test]
7625    fn bitwise_precedence_and_shift_adjacency() {
7626        let (ast, diags) = parse_str("a | b ^ c & d == e >> f + g;");
7627        assert!(diags.is_empty(), "{diags:?}");
7628        let (op, _, mut rhs) = binary(expr_of_single_stmt(&ast));
7629        assert_eq!(op, crate::BinOp::BitOr);
7630        for expected in [
7631            crate::BinOp::BitXor,
7632            crate::BinOp::BitAnd,
7633            crate::BinOp::Eq,
7634            crate::BinOp::Shr,
7635            crate::BinOp::Add,
7636        ] {
7637            let (op, _, next) = binary(ast.try_expr(rhs).unwrap());
7638            assert_eq!(op, expected);
7639            rhs = next;
7640        }
7641        for source in ["a > > b;", "a >/*gap*/> b;", "a < < b;"] {
7642            let (_, diags) = parse_str(source);
7643            assert!(!diags.is_empty(), "{source}");
7644        }
7645    }
7646
7647    #[test]
7648    fn bitwise_shift_assignment_span() {
7649        let (ast, diags) = parse_str("a >>>= b;");
7650        assert!(diags.is_empty(), "{diags:?}");
7651        let crate::StmtKind::CompoundAssign { op, op_span, .. } = single_stmt(&ast).kind else {
7652            panic!("expected assignment");
7653        };
7654        assert_eq!(op, crate::BinOp::UnsignedShr);
7655        assert_eq!(op_span, crate::Span::new(F, 2, 6).unwrap());
7656    }
7657
7658    #[test]
7659    fn precedence_mul_binds_tighter_than_add() {
7660        let (ast, diags) = parse_str("a + b * c;");
7661        assert!(diags.is_empty());
7662        let outer = expr_of_single_stmt(&ast);
7663        let (op, lhs, rhs) = binary(outer);
7664        assert_eq!(op, crate::BinOp::Add);
7665        assert!(matches!(
7666            ast.try_expr(lhs).unwrap().kind,
7667            crate::ExprKind::Identifier(_)
7668        ));
7669        let (rop, rlhs, rrhs) = binary(ast.try_expr(rhs).unwrap());
7670        assert_eq!(rop, crate::BinOp::Mul);
7671        assert!(matches!(
7672            ast.try_expr(rlhs).unwrap().kind,
7673            crate::ExprKind::Identifier(_)
7674        ));
7675        assert!(matches!(
7676            ast.try_expr(rrhs).unwrap().kind,
7677            crate::ExprKind::Identifier(_)
7678        ));
7679    }
7680
7681    #[test]
7682    fn precedence_mul_left_of_add() {
7683        let (ast, diags) = parse_str("a * b + c;");
7684        assert!(diags.is_empty());
7685        let outer = expr_of_single_stmt(&ast);
7686        let (op, lhs, rhs) = binary(outer);
7687        assert_eq!(op, crate::BinOp::Add);
7688        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7689        assert_eq!(lop, crate::BinOp::Mul);
7690        assert!(matches!(
7691            ast.try_expr(rhs).unwrap().kind,
7692            crate::ExprKind::Identifier(_)
7693        ));
7694    }
7695
7696    #[test]
7697    fn left_associative_same_precedence() {
7698        let (ast, diags) = parse_str("a - b - c;");
7699        assert!(diags.is_empty());
7700        let outer = expr_of_single_stmt(&ast);
7701        let (op, lhs, rhs) = binary(outer);
7702        assert_eq!(op, crate::BinOp::Sub);
7703        let (lop, llhs, lrhs) = binary(ast.try_expr(lhs).unwrap());
7704        assert_eq!(lop, crate::BinOp::Sub);
7705        assert!(matches!(
7706            ast.try_expr(llhs).unwrap().kind,
7707            crate::ExprKind::Identifier(_)
7708        ));
7709        assert!(matches!(
7710            ast.try_expr(lrhs).unwrap().kind,
7711            crate::ExprKind::Identifier(_)
7712        ));
7713        assert!(matches!(
7714            ast.try_expr(rhs).unwrap().kind,
7715            crate::ExprKind::Identifier(_)
7716        ));
7717    }
7718
7719    #[test]
7720    fn equality_inside_logical() {
7721        let (ast, diags) = parse_str("a === b && c === d;");
7722        assert!(diags.is_empty());
7723        let outer = expr_of_single_stmt(&ast);
7724        let (op, lhs, rhs) = binary(outer);
7725        assert_eq!(op, crate::BinOp::And);
7726        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7727        assert_eq!(lop, crate::BinOp::Eq);
7728        let (rop, ..) = binary(ast.try_expr(rhs).unwrap());
7729        assert_eq!(rop, crate::BinOp::Eq);
7730    }
7731
7732    #[test]
7733    fn double_equal_is_same_variant_as_triple() {
7734        let (ast, _) = parse_str("a == b;");
7735        let outer = expr_of_single_stmt(&ast);
7736        let (op, ..) = binary(outer);
7737        assert_eq!(op, crate::BinOp::Eq);
7738    }
7739
7740    #[test]
7741    fn bang_eq_is_not_eq() {
7742        let (ast, _) = parse_str("a != b;");
7743        let outer = expr_of_single_stmt(&ast);
7744        let (op, ..) = binary(outer);
7745        assert_eq!(op, crate::BinOp::NotEq);
7746    }
7747
7748    #[test]
7749    fn comparison_has_higher_prec_than_logical() {
7750        let (ast, _) = parse_str("a < b && c;");
7751        let outer = expr_of_single_stmt(&ast);
7752        let (op, lhs, _) = binary(outer);
7753        assert_eq!(op, crate::BinOp::And);
7754        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7755        assert_eq!(lop, crate::BinOp::Lt);
7756    }
7757
7758    #[test]
7759    fn full_precedence_chain_snapshot() {
7760        let (ast, diags) = parse_str("a || b && c === d < e + f * g;");
7761        assert!(diags.is_empty());
7762        insta::assert_debug_snapshot!(ast);
7763    }
7764
7765    #[test]
7766    fn paren_overrides_precedence() {
7767        let (ast, _) = parse_str("(a + b) * c;");
7768        let outer = expr_of_single_stmt(&ast);
7769        let (op, lhs, rhs) = binary(outer);
7770        assert_eq!(op, crate::BinOp::Mul);
7771        let crate::ExprKind::Paren(inner_id) = ast.try_expr(lhs).unwrap().kind else {
7772            panic!("expected Paren on lhs");
7773        };
7774        let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7775        assert_eq!(inner_op, crate::BinOp::Add);
7776        assert!(matches!(
7777            ast.try_expr(rhs).unwrap().kind,
7778            crate::ExprKind::Identifier(_)
7779        ));
7780    }
7781
7782    #[test]
7783    fn binary_missing_rhs_diagnoses() {
7784        let (ast, diags) = parse_str("a +;");
7785        assert_eq!(diags.len(), 1);
7786        assert_eq!(diags[0].message, "expected expression");
7787        assert!(ast.top_level.is_empty());
7788    }
7789
7790    fn unary(e: &crate::Expr) -> (crate::UnOp, crate::ExprId) {
7791        match e.kind {
7792            crate::ExprKind::Unary { op, operand } => (op, operand),
7793            _ => panic!("expected Unary, got {:?}", e.kind),
7794        }
7795    }
7796
7797    #[test]
7798    fn parse_unary_not() {
7799        let (ast, _) = parse_str("!x;");
7800        let outer = expr_of_single_stmt(&ast);
7801        let (op, operand) = unary(outer);
7802        assert_eq!(op, crate::UnOp::Not);
7803        assert!(matches!(
7804            ast.try_expr(operand).unwrap().kind,
7805            crate::ExprKind::Identifier(_)
7806        ));
7807        assert_eq!(outer.span, crate::Span::new(F, 0, 2).unwrap());
7808    }
7809
7810    #[test]
7811    fn parse_unary_neg() {
7812        let (ast, _) = parse_str("-x;");
7813        let (op, _) = unary(expr_of_single_stmt(&ast));
7814        assert_eq!(op, crate::UnOp::Neg);
7815    }
7816
7817    #[test]
7818    fn parse_unary_pos() {
7819        let (ast, _) = parse_str("+x;");
7820        let (op, _) = unary(expr_of_single_stmt(&ast));
7821        assert_eq!(op, crate::UnOp::Pos);
7822    }
7823
7824    #[test]
7825    fn repeated_unary_not() {
7826        let (ast, _) = parse_str("!!x;");
7827        let outer = expr_of_single_stmt(&ast);
7828        let (op, operand) = unary(outer);
7829        assert_eq!(op, crate::UnOp::Not);
7830        let (inner_op, inner_operand) = unary(ast.try_expr(operand).unwrap());
7831        assert_eq!(inner_op, crate::UnOp::Not);
7832        assert!(matches!(
7833            ast.try_expr(inner_operand).unwrap().kind,
7834            crate::ExprKind::Identifier(_)
7835        ));
7836    }
7837
7838    #[test]
7839    fn unary_binds_tighter_than_binary() {
7840        let (ast, _) = parse_str("-x + 1;");
7841        let outer = expr_of_single_stmt(&ast);
7842        let (op, lhs, rhs) = binary(outer);
7843        assert_eq!(op, crate::BinOp::Add);
7844        let (unop, _) = unary(ast.try_expr(lhs).unwrap());
7845        assert_eq!(unop, crate::UnOp::Neg);
7846        assert_eq!(
7847            ast.try_expr(rhs).unwrap().kind,
7848            crate::ExprKind::Number(1.0)
7849        );
7850    }
7851
7852    #[test]
7853    fn unary_over_paren() {
7854        let (ast, _) = parse_str("-(x + 1);");
7855        let outer = expr_of_single_stmt(&ast);
7856        let (op, operand) = unary(outer);
7857        assert_eq!(op, crate::UnOp::Neg);
7858        let crate::ExprKind::Paren(inner_id) = ast.try_expr(operand).unwrap().kind else {
7859            panic!("expected Paren");
7860        };
7861        let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7862        assert_eq!(inner_op, crate::BinOp::Add);
7863    }
7864
7865    #[test]
7866    fn parse_is_as_expression_rejected() {
7867        let (_, diags) = parse_str("x is number;");
7868        assert!(!diags.is_empty(), "expected parse error for `x is T`");
7869    }
7870
7871    fn as_cast(e: &crate::Expr) -> (crate::ExprId, &crate::TypeAnnotation) {
7872        match &e.kind {
7873            crate::ExprKind::As { expr, ty } => (*expr, ty),
7874            other => panic!("expected ExprKind::As, got {other:?}"),
7875        }
7876    }
7877
7878    #[test]
7879    fn parse_instanceof_class() {
7880        let (ast, diags) = parse_str("x instanceof Foo;");
7881        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7882        let outer = expr_of_single_stmt(&ast);
7883        let crate::ExprKind::InstanceOf { value, ty } = &outer.kind else {
7884            panic!("expected ExprKind::InstanceOf, got {:?}", outer.kind);
7885        };
7886        assert!(matches!(
7887            ast.try_expr(*value).unwrap().kind,
7888            crate::ExprKind::Identifier(_)
7889        ));
7890        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7891    }
7892
7893    #[test]
7894    fn parse_instanceof_binds_tighter_than_logical_and() {
7895        // `a instanceof B && c` parses as `(a instanceof B) && c`.
7896        let (ast, diags) = parse_str("a instanceof B && c;");
7897        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7898        let outer = expr_of_single_stmt(&ast);
7899        let (op, lhs, rhs) = binary(outer);
7900        assert_eq!(op, crate::BinOp::And);
7901        assert!(matches!(
7902            ast.try_expr(lhs).unwrap().kind,
7903            crate::ExprKind::InstanceOf { .. }
7904        ));
7905        assert!(matches!(
7906            ast.try_expr(rhs).unwrap().kind,
7907            crate::ExprKind::Identifier(_)
7908        ));
7909    }
7910
7911    /// `<T>x` produces the same node as `x as T`, so everything downstream of the
7912    /// parser sees one construct rather than two.
7913    #[test]
7914    fn parse_angle_cast_lowers_to_the_as_node() {
7915        let (ast, diags) = parse_str("<number>x;");
7916        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7917        let outer = expr_of_single_stmt(&ast);
7918        let (inner, ty) = as_cast(outer);
7919        assert!(matches!(
7920            ast.try_expr(inner).unwrap().kind,
7921            crate::ExprKind::Identifier(_)
7922        ));
7923        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7924    }
7925
7926    /// A `<` that means comparison always has a left operand, so it never reaches
7927    /// the cast branch. This is the regression the cast syntax could most easily
7928    /// have caused.
7929    #[test]
7930    fn a_less_than_comparison_is_not_read_as_a_cast() {
7931        let (ast, diags) = parse_str("a < b;");
7932        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7933        let e = expr_of_single_stmt(&ast);
7934        assert!(matches!(e.kind, crate::ExprKind::Binary { .. }));
7935    }
7936
7937    #[test]
7938    fn parse_typeof_in_type_position() {
7939        let (_ast, diags) = parse_str("let a: typeof b = 1;");
7940        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7941    }
7942
7943    #[test]
7944    fn parse_typeof_with_a_dotted_path() {
7945        let (_ast, diags) = parse_str("let a: typeof b.c.d = 1;");
7946        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7947    }
7948
7949    #[test]
7950    fn parse_as_primitive() {
7951        let (ast, diags) = parse_str("x as number;");
7952        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7953        let outer = expr_of_single_stmt(&ast);
7954        let (inner, ty) = as_cast(outer);
7955        assert!(matches!(
7956            ast.try_expr(inner).unwrap().kind,
7957            crate::ExprKind::Identifier(_)
7958        ));
7959        match &ty.kind {
7960            crate::TypeAnnotationKind::Name { args, .. } => {
7961                assert!(args.is_empty());
7962            }
7963            other => panic!("expected Name target, got {other:?}"),
7964        }
7965        assert_eq!(outer.span, crate::Span::new(F, 0, 11).unwrap());
7966    }
7967
7968    #[test]
7969    fn parse_as_object_literal_type() {
7970        let (ast, diags) = parse_str("x as { a: number };");
7971        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7972        let outer = expr_of_single_stmt(&ast);
7973        let (_, ty) = as_cast(outer);
7974        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Object { .. }));
7975    }
7976
7977    #[test]
7978    fn parse_as_array_short() {
7979        let (ast, diags) = parse_str("x as string[];");
7980        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7981        let outer = expr_of_single_stmt(&ast);
7982        let (_, ty) = as_cast(outer);
7983        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
7984    }
7985
7986    #[test]
7987    fn parse_as_array_generic() {
7988        let (ast, diags) = parse_str("x as T[];");
7989        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7990        let outer = expr_of_single_stmt(&ast);
7991        let (_, ty) = as_cast(outer);
7992        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
7993    }
7994
7995    #[test]
7996    fn parse_as_generic_instantiation() {
7997        let (ast, diags) = parse_str("x as Foo<Bar>;");
7998        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7999        let outer = expr_of_single_stmt(&ast);
8000        let (_, ty) = as_cast(outer);
8001        match &ty.kind {
8002            crate::TypeAnnotationKind::Name { args, .. } => {
8003                assert_eq!(args.len(), 1, "Foo<Bar> has one type arg");
8004            }
8005            other => panic!("expected Name with type args, got {other:?}"),
8006        }
8007    }
8008
8009    #[test]
8010    fn parse_as_precedence_higher_than_additive() {
8011        let (ast, diags) = parse_str("x as number + 1;");
8012        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8013        let outer = expr_of_single_stmt(&ast);
8014        let (op, lhs, rhs) = binary(outer);
8015        assert_eq!(op, crate::BinOp::Add);
8016        assert!(matches!(
8017            ast.try_expr(lhs).unwrap().kind,
8018            crate::ExprKind::As { .. }
8019        ));
8020        assert!(matches!(
8021            ast.try_expr(rhs).unwrap().kind,
8022            crate::ExprKind::Number(_)
8023        ));
8024    }
8025
8026    #[test]
8027    fn parse_as_precedence_lower_than_unary() {
8028        let (ast, diags) = parse_str("!x as boolean;");
8029        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8030        let outer = expr_of_single_stmt(&ast);
8031        let (inner, _) = as_cast(outer);
8032        assert!(matches!(
8033            ast.try_expr(inner).unwrap().kind,
8034            crate::ExprKind::Unary {
8035                op: crate::UnOp::Not,
8036                ..
8037            }
8038        ));
8039    }
8040
8041    #[test]
8042    fn parse_as_left_associative_chain() {
8043        let (ast, diags) = parse_str("x as A as B;");
8044        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8045        let outer = expr_of_single_stmt(&ast);
8046        let (inner, _outer_ty) = as_cast(outer);
8047        let (innermost, _inner_ty) = as_cast(ast.try_expr(inner).unwrap());
8048        assert!(matches!(
8049            ast.try_expr(innermost).unwrap().kind,
8050            crate::ExprKind::Identifier(_)
8051        ));
8052    }
8053
8054    #[test]
8055    fn parse_as_inside_call_arg() {
8056        let (ast, diags) = parse_str("f(x as T);");
8057        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8058        let outer = expr_of_single_stmt(&ast);
8059        match &outer.kind {
8060            crate::ExprKind::Call { args, .. } => {
8061                assert_eq!(args.len(), 1);
8062                assert!(matches!(
8063                    ast.try_expr(args[0]).unwrap().kind,
8064                    crate::ExprKind::As { .. }
8065                ));
8066            }
8067            other => panic!("expected Call, got {other:?}"),
8068        }
8069    }
8070
8071    #[test]
8072    fn parse_as_after_field_access() {
8073        let (ast, diags) = parse_str("obj.field as T;");
8074        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8075        let outer = expr_of_single_stmt(&ast);
8076        let (inner, _) = as_cast(outer);
8077        assert!(matches!(
8078            ast.try_expr(inner).unwrap().kind,
8079            crate::ExprKind::FieldAccess { .. }
8080        ));
8081    }
8082
8083    #[test]
8084    fn parse_type_guard_return_annotation_on_function() {
8085        let src = "function isCircle(s: Shape): s is Circle { return true; }";
8086        let (ast, diags) = parse_str(src);
8087        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8088        let stmt = single_stmt(&ast);
8089        match stmt.kind {
8090            crate::StmtKind::Function {
8091                ref return_type,
8092                ref type_predicate,
8093                ..
8094            } => {
8095                assert!(
8096                    return_type.is_none(),
8097                    "predicate form leaves return_type empty",
8098                );
8099                let tp = type_predicate.as_ref().expect("predicate parsed");
8100                assert_eq!(tp.param.name, "s");
8101                assert!(matches!(
8102                    tp.asserted.kind,
8103                    crate::TypeAnnotationKind::Name { .. }
8104                ));
8105                assert_eq!(tp.span, crate::Span::new(F, 29, 40).unwrap());
8106            }
8107            _ => panic!("expected Function"),
8108        }
8109    }
8110
8111    #[test]
8112    fn parse_type_guard_return_annotation_on_arrow() {
8113        let src = "const f = (s: Shape): s is Circle => true;";
8114        let (ast, diags) = parse_str(src);
8115        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8116        let value = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
8117            crate::StmtKind::Const { value, .. } => *value,
8118            _ => panic!("expected const"),
8119        };
8120        match &ast.try_expr(value).unwrap().kind {
8121            crate::ExprKind::Arrow {
8122                return_type,
8123                type_predicate,
8124                ..
8125            } => {
8126                assert!(return_type.is_none());
8127                let tp = type_predicate.as_ref().expect("predicate parsed");
8128                assert_eq!(tp.param.name, "s");
8129            }
8130            other => panic!("expected Arrow, got {other:?}"),
8131        }
8132    }
8133
8134    #[test]
8135    fn parse_plain_return_type_unaffected_by_predicate_parser() {
8136        let src = "function f(s: Shape): boolean { return true; }";
8137        let (ast, diags) = parse_str(src);
8138        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8139        let stmt = single_stmt(&ast);
8140        match stmt.kind {
8141            crate::StmtKind::Function {
8142                ref return_type,
8143                ref type_predicate,
8144                ..
8145            } => {
8146                assert!(return_type.is_some(), "plain form has return_type");
8147                assert!(type_predicate.is_none(), "plain form has no type_predicate",);
8148            }
8149            _ => panic!("expected Function"),
8150        }
8151    }
8152
8153    #[test]
8154    fn parse_type_guard_rejects_non_return_position() {
8155        let (_, diags) = parse_str("let x: y is Foo = null;");
8156        assert!(
8157            !diags.is_empty(),
8158            "expected parse error for `is` outside return-type position",
8159        );
8160    }
8161
8162    #[test]
8163    fn parse_typeof_prefix() {
8164        let (ast, diags) = parse_str("typeof x;");
8165        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8166        let outer = expr_of_single_stmt(&ast);
8167        let crate::ExprKind::Typeof { operand } = outer.kind else {
8168            panic!("expected Typeof, got {:?}", outer.kind);
8169        };
8170        assert!(matches!(
8171            ast.try_expr(operand).unwrap().kind,
8172            crate::ExprKind::Identifier(_)
8173        ));
8174        assert_eq!(outer.span, crate::Span::new(F, 0, 8).unwrap());
8175    }
8176
8177    #[test]
8178    fn parse_typeof_in_equality() {
8179        let (ast, diags) = parse_str(r#"typeof x === "number";"#);
8180        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8181        let outer = expr_of_single_stmt(&ast);
8182        let (op, lhs, rhs) = binary(outer);
8183        assert_eq!(op, crate::BinOp::Eq);
8184        assert!(matches!(
8185            ast.try_expr(lhs).unwrap().kind,
8186            crate::ExprKind::Typeof { .. }
8187        ));
8188        assert!(matches!(
8189            ast.try_expr(rhs).unwrap().kind,
8190            crate::ExprKind::String(ref s) if s == "number"
8191        ));
8192    }
8193
8194    #[test]
8195    fn parse_let_without_type() {
8196        let (ast, diags) = parse_str("let x = 1;");
8197        assert!(diags.is_empty());
8198        let stmt = single_stmt(&ast);
8199        assert_eq!(stmt.span, crate::Span::new(F, 0, 10).unwrap());
8200        match stmt.kind {
8201            crate::StmtKind::Let {
8202                ref name,
8203                ref ty,
8204                value,
8205                ..
8206            } => {
8207                assert_eq!(name.name, "x");
8208                assert_eq!(name.span, crate::Span::new(F, 4, 5).unwrap());
8209                assert!(ty.is_none());
8210                assert_eq!(
8211                    ast.try_expr(value).unwrap().kind,
8212                    crate::ExprKind::Number(1.0)
8213                );
8214            }
8215            _ => panic!("expected Let"),
8216        }
8217    }
8218
8219    #[test]
8220    fn parse_const_with_type_and_string() {
8221        let (ast, diags) = parse_str(r#"const y: string = "hi";"#);
8222        assert!(diags.is_empty());
8223        let stmt = single_stmt(&ast);
8224        match stmt.kind {
8225            crate::StmtKind::Const {
8226                ref name,
8227                ref ty,
8228                value,
8229                ..
8230            } => {
8231                assert_eq!(name.name, "y");
8232                assert_eq!(name.span, crate::Span::new(F, 6, 7).unwrap());
8233                let ty = ty.as_ref().expect("expected type annotation");
8234                assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8235                assert_eq!(ty.span, crate::Span::new(F, 9, 15).unwrap());
8236                assert_eq!(
8237                    ast.try_expr(value).unwrap().kind,
8238                    crate::ExprKind::String("hi".to_string())
8239                );
8240            }
8241            _ => panic!("expected Const"),
8242        }
8243    }
8244
8245    #[test]
8246    fn parse_let_with_type_and_binary_initializer() {
8247        let (ast, diags) = parse_str("let z: number = 1 + 2;");
8248        assert!(diags.is_empty());
8249        let stmt = single_stmt(&ast);
8250        match stmt.kind {
8251            crate::StmtKind::Let { ref ty, value, .. } => {
8252                assert!(ty.is_some());
8253                let (op, ..) = binary(ast.try_expr(value).unwrap());
8254                assert_eq!(op, crate::BinOp::Add);
8255            }
8256            _ => panic!("expected Let"),
8257        }
8258    }
8259
8260    #[test]
8261    fn void_type_annotation_is_accepted_syntactically() {
8262        let (ast, diags) = parse_str("let v: void = null;");
8263        assert!(diags.is_empty());
8264        let stmt = single_stmt(&ast);
8265        match stmt.kind {
8266            crate::StmtKind::Let { ref ty, .. } => {
8267                let ty = ty.as_ref().unwrap();
8268                assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8269                assert_eq!(ty.span, crate::Span::new(F, 7, 11).unwrap());
8270            }
8271            _ => panic!("expected Let"),
8272        }
8273    }
8274
8275    #[test]
8276    fn const_missing_initializer_diagnoses() {
8277        let (ast, diags) = parse_str("const x;");
8278        assert_eq!(diags.len(), 1);
8279        assert_eq!(
8280            diags[0].message,
8281            "`const` declaration requires an initializer"
8282        );
8283        assert!(ast.top_level.is_empty());
8284    }
8285
8286    #[test]
8287    fn parse_const_object_shorthand_pattern() {
8288        let (ast, diags) = parse_str("const { a, b } = obj;");
8289        assert!(diags.is_empty(), "unexpected: {diags:?}");
8290        let stmt = single_stmt(&ast);
8291        match stmt.kind {
8292            crate::StmtKind::ConstPattern {
8293                binding:
8294                    crate::Binding::Object {
8295                        ref fields,
8296                        ref rest,
8297                        ..
8298                    },
8299                ..
8300            } => {
8301                assert_eq!(fields.len(), 2);
8302                assert_eq!(fields[0].source.name, "a");
8303                assert_eq!(fields[0].local.name, "a");
8304                assert_eq!(fields[1].source.name, "b");
8305                assert_eq!(fields[1].local.name, "b");
8306                assert!(rest.is_none());
8307            }
8308            _ => panic!("expected ConstPattern Object, got {:?}", stmt.kind),
8309        }
8310    }
8311
8312    #[test]
8313    fn parse_const_object_pattern_keyword_source_key() {
8314        let (ast, diags) = parse_str("const { type: statusType, default: fallback } = obj;");
8315        assert!(diags.is_empty(), "unexpected: {diags:?}");
8316        let stmt = single_stmt(&ast);
8317        let crate::StmtKind::ConstPattern {
8318            binding: crate::Binding::Object { ref fields, .. },
8319            ..
8320        } = stmt.kind
8321        else {
8322            panic!("expected object const pattern");
8323        };
8324
8325        let names: Vec<(&str, &str)> = fields
8326            .iter()
8327            .map(|f| (f.source.name.as_str(), f.local.name.as_str()))
8328            .collect();
8329        assert_eq!(names, vec![("type", "statusType"), ("default", "fallback")]);
8330    }
8331
8332    #[test]
8333    fn parse_const_object_pattern_keyword_shorthand_rejected() {
8334        let (_ast, diags) = parse_str("const { default } = obj;");
8335        assert!(
8336            diags
8337                .iter()
8338                .any(|d| d.message == "expected `:` after keyword field name in object pattern"),
8339            "expected keyword-shorthand diagnostic, got: {diags:?}"
8340        );
8341    }
8342
8343    #[test]
8344    fn parse_const_object_renamed_pattern() {
8345        let (ast, diags) = parse_str("const { a: x, b: y } = obj;");
8346        assert!(diags.is_empty(), "unexpected: {diags:?}");
8347        match single_stmt(&ast).kind {
8348            crate::StmtKind::ConstPattern {
8349                binding: crate::Binding::Object { ref fields, .. },
8350                ..
8351            } => {
8352                assert_eq!(fields[0].source.name, "a");
8353                assert_eq!(fields[0].local.name, "x");
8354                assert_eq!(fields[1].source.name, "b");
8355                assert_eq!(fields[1].local.name, "y");
8356            }
8357            ref other => panic!("expected ConstPattern Object, got {other:?}"),
8358        }
8359    }
8360
8361    #[test]
8362    fn parse_const_object_with_rest() {
8363        let (ast, diags) = parse_str("const { a, ...rest } = obj;");
8364        assert!(diags.is_empty(), "unexpected: {diags:?}");
8365        match single_stmt(&ast).kind {
8366            crate::StmtKind::ConstPattern {
8367                binding:
8368                    crate::Binding::Object {
8369                        ref fields,
8370                        ref rest,
8371                        ..
8372                    },
8373                ..
8374            } => {
8375                assert_eq!(fields.len(), 1);
8376                assert_eq!(fields[0].source.name, "a");
8377                let r = rest.as_ref().expect("expected rest");
8378                assert_eq!(r.name, "rest");
8379            }
8380            ref other => panic!("expected ConstPattern Object, got {other:?}"),
8381        }
8382    }
8383
8384    #[test]
8385    fn parse_const_array_pattern() {
8386        let (ast, diags) = parse_str("const [x, y] = arr;");
8387        assert!(diags.is_empty(), "unexpected: {diags:?}");
8388        match single_stmt(&ast).kind {
8389            crate::StmtKind::ConstPattern {
8390                binding:
8391                    crate::Binding::Array {
8392                        ref elems,
8393                        ref rest,
8394                        ..
8395                    },
8396                ..
8397            } => {
8398                assert_eq!(elems.len(), 2);
8399                assert_eq!(elems[0].as_ref().unwrap().name, "x");
8400                assert_eq!(elems[1].as_ref().unwrap().name, "y");
8401                assert!(rest.is_none());
8402            }
8403            ref other => panic!("expected ConstPattern Array, got {other:?}"),
8404        }
8405    }
8406
8407    #[test]
8408    fn parse_const_array_with_hole_and_rest() {
8409        let (ast, diags) = parse_str("const [, x, ...rest] = arr;");
8410        assert!(diags.is_empty(), "unexpected: {diags:?}");
8411        match single_stmt(&ast).kind {
8412            crate::StmtKind::ConstPattern {
8413                binding:
8414                    crate::Binding::Array {
8415                        ref elems,
8416                        ref rest,
8417                        ..
8418                    },
8419                ..
8420            } => {
8421                assert_eq!(elems.len(), 2);
8422                assert!(elems[0].is_none(), "first slot should be a hole");
8423                assert_eq!(elems[1].as_ref().unwrap().name, "x");
8424                assert_eq!(rest.as_ref().unwrap().name, "rest");
8425            }
8426            ref other => panic!("expected ConstPattern Array, got {other:?}"),
8427        }
8428    }
8429
8430    #[test]
8431    fn parse_let_object_pattern() {
8432        let (ast, diags) = parse_str("let { a, b } = obj;");
8433        assert!(diags.is_empty(), "unexpected: {diags:?}");
8434        assert!(matches!(
8435            single_stmt(&ast).kind,
8436            crate::StmtKind::LetPattern {
8437                binding: crate::Binding::Object { .. },
8438                ..
8439            }
8440        ));
8441    }
8442
8443    #[test]
8444    fn parse_function_param_object_pattern() {
8445        let src = "function f({ a, b }: T): void { return; }";
8446        let (ast, diags) = parse_str(src);
8447        assert!(diags.is_empty(), "unexpected: {diags:?}");
8448        match single_stmt(&ast).kind {
8449            crate::StmtKind::Function { ref params, .. } => {
8450                assert_eq!(params.len(), 1);
8451                let p = &params[0];
8452                assert!(p.pattern.is_some(), "expected pattern param");
8453                assert_eq!(p.name.name, "", "pattern placeholder uses empty name");
8454                match p.pattern.as_ref().unwrap() {
8455                    crate::Binding::Object { fields, .. } => {
8456                        assert_eq!(fields.len(), 2);
8457                        assert_eq!(fields[0].source.name, "a");
8458                    }
8459                    other => panic!("expected Object binding, got {other:?}"),
8460                }
8461            }
8462            ref other => panic!("expected Function, got {other:?}"),
8463        }
8464    }
8465
8466    #[test]
8467    fn parse_arrow_param_array_pattern() {
8468        let src = "const g = ([x, y]: number[]): number => x + y;";
8469        let (_ast, diags) = parse_str(src);
8470        assert!(diags.is_empty(), "unexpected: {diags:?}");
8471    }
8472
8473    #[test]
8474    fn parse_destructure_nested_rejected() {
8475        let (_ast, diags) = parse_str("const { a: { b } } = obj;");
8476        assert!(
8477            diags
8478                .iter()
8479                .any(|d| d.message.contains("nested destructuring")),
8480            "expected nested-destructuring diagnostic, got: {diags:?}",
8481        );
8482    }
8483
8484    #[test]
8485    fn parse_destructure_array_nested_rejected() {
8486        let (_ast, diags) = parse_str("const [[a]] = arr;");
8487        assert!(
8488            diags
8489                .iter()
8490                .any(|d| d.message.contains("nested destructuring")),
8491            "expected nested-destructuring diagnostic, got: {diags:?}",
8492        );
8493    }
8494
8495    #[test]
8496    fn parse_destructure_pattern_defaults() {
8497        let (ast, diags) = parse_str("const { a = 1, b: c = 2 } = obj;");
8498        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8499        let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
8500            panic!("expected pattern")
8501        };
8502        let crate::Binding::Object { fields, .. } = binding else {
8503            panic!("expected object")
8504        };
8505        assert_eq!(fields.len(), 2);
8506        assert!(fields.iter().all(|field| field.default.is_some()));
8507        assert_eq!(fields[1].local.name, "c");
8508    }
8509
8510    #[test]
8511    fn parse_destructure_param_default() {
8512        let (ast, diags) = parse_str("function f({ a = 1 }: T = obj): void { return; }");
8513        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8514        let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
8515            panic!("expected function")
8516        };
8517        assert!(params[0].default.is_some());
8518        assert!(params[0].pattern.is_some());
8519    }
8520
8521    #[test]
8522    fn parse_destructure_rest_not_last_rejected() {
8523        let (_ast, diags) = parse_str("const { ...rest, a } = obj;");
8524        assert!(
8525            diags
8526                .iter()
8527                .any(|d| d.message.contains("rest element must be the last element")),
8528            "expected rest-not-last diagnostic, got: {diags:?}",
8529        );
8530    }
8531
8532    #[test]
8533    fn parse_destructure_empty_object_rejected() {
8534        let (_ast, diags) = parse_str("const { } = obj;");
8535        assert!(
8536            diags
8537                .iter()
8538                .any(|d| d.message.contains("empty object destructuring pattern")),
8539            "expected empty-pattern diagnostic, got: {diags:?}",
8540        );
8541    }
8542
8543    #[test]
8544    fn parse_ternary_simple() {
8545        let (ast, diags) = parse_str("const x: number = a ? 1 : 2;");
8546        assert!(diags.is_empty(), "unexpected: {diags:?}");
8547        let stmt = single_stmt(&ast);
8548        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8549            panic!("expected Const, got {:?}", stmt.kind);
8550        };
8551        assert!(matches!(
8552            ast.try_expr(value).unwrap().kind,
8553            crate::ExprKind::Ternary { .. }
8554        ));
8555    }
8556
8557    #[test]
8558    fn parse_ternary_nested_right_assoc() {
8559        let (ast, diags) = parse_str("const x: number = a ? 1 : b ? 2 : 3;");
8560        assert!(diags.is_empty(), "unexpected: {diags:?}");
8561        let stmt = single_stmt(&ast);
8562        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8563            panic!("expected Const");
8564        };
8565        let crate::ExprKind::Ternary { else_, .. } = ast.try_expr(value).unwrap().kind else {
8566            panic!("expected outer Ternary");
8567        };
8568        assert!(matches!(
8569            ast.try_expr(else_).unwrap().kind,
8570            crate::ExprKind::Ternary { .. }
8571        ));
8572    }
8573
8574    #[test]
8575    fn parse_ternary_missing_colon_diagnoses() {
8576        let (_, diags) = parse_str("const x: number = a ? 1;");
8577        assert!(
8578            diags
8579                .iter()
8580                .any(|d| d.message.contains("expected `:` to complete ternary")),
8581            "expected colon diagnostic, got: {diags:?}",
8582        );
8583    }
8584
8585    #[test]
8586    fn parse_nullish_coalesce_binop() {
8587        let (ast, diags) = parse_str("const x: number = a ?? 1;");
8588        assert!(diags.is_empty(), "unexpected: {diags:?}");
8589        let stmt = single_stmt(&ast);
8590        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8591            panic!("expected Const");
8592        };
8593        match ast.try_expr(value).unwrap().kind {
8594            crate::ExprKind::Binary { op, .. } => {
8595                assert_eq!(op, crate::BinOp::NullishCoalesce);
8596            }
8597            ref other => panic!("expected Binary NullishCoalesce, got {other:?}"),
8598        }
8599    }
8600
8601    #[test]
8602    fn parse_nullish_left_associative() {
8603        let (ast, diags) = parse_str("const x: number = a ?? b ?? c;");
8604        assert!(diags.is_empty(), "unexpected: {diags:?}");
8605        let stmt = single_stmt(&ast);
8606        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8607            panic!("expected Const");
8608        };
8609        let crate::ExprKind::Binary { lhs, .. } = ast.try_expr(value).unwrap().kind else {
8610            panic!("expected outer Binary");
8611        };
8612        match ast.try_expr(lhs).unwrap().kind {
8613            crate::ExprKind::Binary { op, .. } => {
8614                assert_eq!(op, crate::BinOp::NullishCoalesce);
8615            }
8616            ref other => panic!("expected inner Binary, got {other:?}"),
8617        }
8618    }
8619
8620    #[test]
8621    fn parse_pow_is_right_associative() {
8622        let (ast, diags) = parse_str("const x: number = 2 ** 3 ** 2;");
8623        assert!(diags.is_empty(), "unexpected: {diags:?}");
8624        let stmt = single_stmt(&ast);
8625        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8626            panic!("expected Const");
8627        };
8628        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8629            panic!("expected outer Binary");
8630        };
8631        assert_eq!(op, crate::BinOp::Pow);
8632        match ast.try_expr(rhs).unwrap().kind {
8633            crate::ExprKind::Binary { op: inner_op, .. } => {
8634                assert_eq!(inner_op, crate::BinOp::Pow);
8635            }
8636            ref other => panic!("expected inner Pow Binary on RHS, got {other:?}"),
8637        }
8638    }
8639
8640    #[test]
8641    fn parse_pow_higher_than_multiply() {
8642        let (ast, diags) = parse_str("const x: number = 2 * 3 ** 2;");
8643        assert!(diags.is_empty(), "unexpected: {diags:?}");
8644        let stmt = single_stmt(&ast);
8645        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8646            panic!("expected Const");
8647        };
8648        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8649            panic!("expected outer Binary");
8650        };
8651        assert_eq!(op, crate::BinOp::Mul);
8652        match ast.try_expr(rhs).unwrap().kind {
8653            crate::ExprKind::Binary { op: inner_op, .. } => {
8654                assert_eq!(inner_op, crate::BinOp::Pow);
8655            }
8656            ref other => panic!("expected Pow on the RHS of *, got {other:?}"),
8657        }
8658    }
8659
8660    #[test]
8661    fn parse_compound_assign_ident() {
8662        let (ast, diags) = parse_str("function main(): void { let x: number = 0; x += 5; }");
8663        assert!(diags.is_empty(), "unexpected: {diags:?}");
8664        let func = ast.top_level.iter().find_map(|sid| {
8665            if let crate::StmtKind::Function { body, .. } = &ast.try_stmt(*sid).unwrap().kind {
8666                Some(*body)
8667            } else {
8668                None
8669            }
8670        });
8671        let crate::StmtKind::Block(stmts) = &ast.try_stmt(func.expect("main")).unwrap().kind else {
8672            panic!("expected function body to be a block");
8673        };
8674        let target = stmts
8675            .iter()
8676            .find_map(|sid| match &ast.try_stmt(*sid).unwrap().kind {
8677                crate::StmtKind::CompoundAssign { target, op, .. } => {
8678                    Some((target.name.clone(), *op))
8679                }
8680                _ => None,
8681            });
8682        let (name, op) = target.expect("expected a CompoundAssign in the body");
8683        assert_eq!(name, "x");
8684        assert_eq!(op, crate::BinOp::Add);
8685    }
8686
8687    #[test]
8688    fn parse_nullish_mixed_with_or_rejected() {
8689        let (_, diags) = parse_str("const x: number = a || b ?? c;");
8690        assert!(
8691            diags
8692                .iter()
8693                .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8694            "expected mixing diagnostic, got: {diags:?}",
8695        );
8696    }
8697
8698    #[test]
8699    fn parse_nullish_mixed_with_and_rejected() {
8700        let (_, diags) = parse_str("const x: number = a && b ?? c;");
8701        assert!(
8702            diags
8703                .iter()
8704                .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8705            "expected mixing diagnostic, got: {diags:?}",
8706        );
8707    }
8708
8709    #[test]
8710    fn parse_nullish_followed_by_logical_rejected() {
8711        for (source, help) in [
8712            (
8713                "const x: number = a ?? b || c;",
8714                "`(a ?? b) || c` or `a ?? (b || c)`",
8715            ),
8716            (
8717                "const x: number = a ?? b && c;",
8718                "`(a ?? b) && c` or `a ?? (b && c)`",
8719            ),
8720            (
8721                "const x: number = a ?? b ?? c || d;",
8722                "`(a ?? b) || c` or `a ?? (b || c)`",
8723            ),
8724            // One error per chain, however many mixes it has.
8725            (
8726                "const x: number = a || b ?? c || d;",
8727                "`(a || b) ?? c` or `a || (b ?? c)`",
8728            ),
8729        ] {
8730            let (_, diags) = parse_str(source);
8731            assert_eq!(diags.len(), 1, "diags for {source:?}: {diags:?}");
8732            assert!(
8733                diags[0].message.contains("mixing `??` with `||` / `&&`"),
8734                "message for {source:?}: {diags:?}",
8735            );
8736            assert!(
8737                diags[0].help.iter().any(|h| h.contains(help)),
8738                "help for {source:?}: {diags:?}",
8739            );
8740        }
8741    }
8742
8743    #[test]
8744    fn parse_nullish_operand_keeps_tighter_operators() {
8745        // `a ?? (b == c)`: only `||` and `&&` are excluded from a `??` operand.
8746        let (ast, diags) = parse_str("const x: boolean = a ?? b == c;");
8747        assert!(diags.is_empty(), "unexpected: {diags:?}");
8748        let stmt = single_stmt(&ast);
8749        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8750            panic!("expected Const");
8751        };
8752        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8753            panic!("expected outer Binary");
8754        };
8755        assert_eq!(op, crate::BinOp::NullishCoalesce);
8756        assert!(matches!(
8757            ast.try_expr(rhs).unwrap().kind,
8758            crate::ExprKind::Binary {
8759                op: crate::BinOp::Eq,
8760                ..
8761            }
8762        ));
8763    }
8764
8765    #[test]
8766    fn parse_nullish_with_parens_ok() {
8767        let (_, diags) = parse_str("const x: number = (a || b) ?? c;");
8768        assert!(diags.is_empty(), "unexpected: {diags:?}");
8769    }
8770
8771    #[test]
8772    fn parse_optional_field_chain() {
8773        let (ast, diags) = parse_str("const x: number = a?.b;");
8774        assert!(diags.is_empty(), "unexpected: {diags:?}");
8775        let stmt = single_stmt(&ast);
8776        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8777            panic!("expected Const");
8778        };
8779        match ast.try_expr(value).unwrap().kind {
8780            crate::ExprKind::OptionalChain { ref parts, .. } => {
8781                assert_eq!(parts.len(), 1);
8782                match &parts[0] {
8783                    crate::ChainPart::Field { name, optional, .. } => {
8784                        assert_eq!(name.name, "b");
8785                        assert!(*optional);
8786                    }
8787                    other => panic!("expected Field, got {other:?}"),
8788                }
8789            }
8790            ref other => panic!("expected OptionalChain, got {other:?}"),
8791        }
8792    }
8793
8794    #[test]
8795    fn parse_optional_chain_continues_after_first_dot() {
8796        let (ast, diags) = parse_str("const x: number = a?.b.c;");
8797        assert!(diags.is_empty(), "unexpected: {diags:?}");
8798        let stmt = single_stmt(&ast);
8799        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8800            panic!("expected Const");
8801        };
8802        match ast.try_expr(value).unwrap().kind {
8803            crate::ExprKind::OptionalChain { ref parts, .. } => {
8804                assert_eq!(parts.len(), 2);
8805                if let crate::ChainPart::Field { optional, .. } = &parts[0] {
8806                    assert!(*optional);
8807                }
8808                if let crate::ChainPart::Field { optional, .. } = &parts[1] {
8809                    assert!(!*optional);
8810                }
8811            }
8812            ref other => panic!("expected OptionalChain, got {other:?}"),
8813        }
8814    }
8815
8816    #[test]
8817    fn parse_optional_chain_multiple_short_circuits() {
8818        let (ast, diags) = parse_str("const x: number = a?.b?.c;");
8819        assert!(diags.is_empty(), "unexpected: {diags:?}");
8820        let stmt = single_stmt(&ast);
8821        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8822            panic!("expected Const");
8823        };
8824        let crate::ExprKind::OptionalChain { parts, .. } = &ast.try_expr(value).unwrap().kind
8825        else {
8826            panic!("expected OptionalChain");
8827        };
8828        assert_eq!(parts.len(), 2);
8829        for p in parts {
8830            if let crate::ChainPart::Field { optional, .. } = p {
8831                assert!(*optional, "both parts should be optional");
8832            }
8833        }
8834    }
8835
8836    #[test]
8837    fn parse_optional_call_and_index() {
8838        let (_, diags) = parse_str("const x: number = f?.();");
8839        assert!(diags.is_empty(), "unexpected: {diags:?}");
8840        let (_, diags) = parse_str("const y: number = arr?.[0];");
8841        assert!(diags.is_empty(), "unexpected: {diags:?}");
8842    }
8843
8844    #[test]
8845    fn parse_optional_chain_terminates_at_questionmark_for_ternary() {
8846        let (ast, diags) = parse_str("const x: number = a?.b ? c : d;");
8847        assert!(diags.is_empty(), "unexpected: {diags:?}");
8848        let stmt = single_stmt(&ast);
8849        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8850            panic!("expected Const");
8851        };
8852        let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8853            panic!(
8854                "expected top-level Ternary, got {:?}",
8855                ast.try_expr(value).unwrap().kind
8856            );
8857        };
8858        assert!(matches!(
8859            ast.try_expr(cond).unwrap().kind,
8860            crate::ExprKind::OptionalChain { .. }
8861        ));
8862    }
8863
8864    #[test]
8865    fn parse_nullish_below_ternary() {
8866        let (ast, diags) = parse_str("const x: number = a ?? b ? c : d;");
8867        assert!(diags.is_empty(), "unexpected: {diags:?}");
8868        let stmt = single_stmt(&ast);
8869        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8870            panic!("expected Const");
8871        };
8872        let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8873            panic!("expected top-level Ternary");
8874        };
8875        match ast.try_expr(cond).unwrap().kind {
8876            crate::ExprKind::Binary { op, .. } => {
8877                assert_eq!(op, crate::BinOp::NullishCoalesce);
8878            }
8879            ref other => panic!("expected Binary ??, got {other:?}"),
8880        }
8881    }
8882
8883    #[test]
8884    fn let_missing_initializer_diagnoses() {
8885        let (ast, diags) = parse_str("let x;");
8886        assert_eq!(diags.len(), 1);
8887        assert_eq!(
8888            diags[0].message,
8889            "`let` declaration requires an initializer"
8890        );
8891        assert!(ast.top_level.is_empty());
8892    }
8893
8894    #[test]
8895    fn missing_identifier_after_let_diagnoses() {
8896        let (ast, diags) = parse_str("let = 1;");
8897        assert_eq!(diags.len(), 1);
8898        assert_eq!(diags[0].message, "expected identifier after `let`/`const`");
8899        assert!(ast.top_level.is_empty());
8900    }
8901
8902    #[test]
8903    fn missing_type_after_colon_diagnoses() {
8904        let (ast, diags) = parse_str("let x: = 1;");
8905        assert_eq!(diags.len(), 1);
8906        assert_eq!(diags[0].message, "expected type");
8907        assert!(ast.top_level.is_empty());
8908    }
8909
8910    #[test]
8911    fn missing_semicolon_after_declaration_diagnoses() {
8912        let (ast, diags) = parse_str("let x = 1 y;");
8913        assert_eq!(diags.len(), 1);
8914        assert_eq!(diags[0].message, "expected `;` after declaration");
8915        assert!(ast.top_level.is_empty());
8916    }
8917
8918    #[test]
8919    fn expression_statements_still_parse() {
8920        let (ast, diags) = parse_str("42;");
8921        assert!(diags.is_empty());
8922        assert_eq!(ast.top_level.len(), 1);
8923    }
8924
8925    #[test]
8926    fn snapshot_mixed_declarations() {
8927        let (ast, diags) =
8928            parse_str(r#"let x = 1; const y: string = "hi"; let z: number = 1 + 2;"#);
8929        assert!(diags.is_empty());
8930        assert_eq!(ast.top_level.len(), 3);
8931        insta::assert_debug_snapshot!(ast);
8932    }
8933
8934    #[test]
8935    fn parse_function_typical() {
8936        let (ast, diags) = parse_str("function f(a: number, b: string): boolean { }");
8937        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8938        let stmt = single_stmt(&ast);
8939        match stmt.kind {
8940            crate::StmtKind::Function {
8941                ref name,
8942                ref generics,
8943                ref params,
8944                ref return_type,
8945                body,
8946                ..
8947            } => {
8948                assert_eq!(name.name, "f");
8949                assert_eq!(name.span, crate::Span::new(F, 9, 10).unwrap());
8950                assert!(generics.is_empty());
8951                assert_eq!(params.len(), 2);
8952                assert_eq!(params[0].name.name, "a");
8953                assert_eq!(params[0].name.span, crate::Span::new(F, 11, 12).unwrap());
8954                let p0_ty = params[0]
8955                    .ty
8956                    .as_ref()
8957                    .expect("function-decl param requires annotation");
8958                assert!(matches!(p0_ty.kind, crate::TypeAnnotationKind::Name { .. }));
8959                assert_eq!(p0_ty.span, crate::Span::new(F, 14, 20).unwrap()); // `number`
8960                assert_eq!(params[1].name.name, "b");
8961                assert_eq!(params[1].name.span, crate::Span::new(F, 22, 23).unwrap());
8962                let p1_ty = params[1]
8963                    .ty
8964                    .as_ref()
8965                    .expect("function-decl param requires annotation");
8966                assert_eq!(p1_ty.span, crate::Span::new(F, 25, 31).unwrap()); // `string`
8967                let return_type = return_type.as_ref().expect("plain return type");
8968                assert!(matches!(
8969                    return_type.kind,
8970                    crate::TypeAnnotationKind::Name { .. }
8971                ));
8972                assert_eq!(return_type.span, crate::Span::new(F, 34, 41).unwrap()); // `boolean`
8973                let block = ast.try_stmt(body).unwrap();
8974                assert!(matches!(block.kind, crate::StmtKind::Block(ref v) if v.is_empty()));
8975            }
8976            _ => panic!("expected Function"),
8977        }
8978    }
8979
8980    #[test]
8981    fn parse_function_with_doc_comment() {
8982        let (ast, diags) = parse_str(
8983            "/**\n * Sum two numbers.\n * @param a First.\n * @param b Second.\n */\nfunction add(a: number, b: number): number { return a + b; }",
8984        );
8985        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8986        let stmt = single_stmt(&ast);
8987        match &stmt.kind {
8988            crate::StmtKind::Function { doc, .. } => {
8989                let d = doc.as_ref().expect("doc attached");
8990                assert_eq!(d.summary, "Sum two numbers.");
8991                assert_eq!(d.params.len(), 2);
8992                assert_eq!(d.params[0].name, "a");
8993                assert_eq!(d.params[1].name, "b");
8994            }
8995            _ => panic!("expected Function"),
8996        }
8997    }
8998
8999    #[test]
9000    fn parse_interface_member_docs() {
9001        let (ast, diags) = parse_str(
9002            "interface Foo {\n  /** First method. */ first(): void;\n  /** A field. */ field: number;\n}",
9003        );
9004        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9005        let stmt = single_stmt(&ast);
9006        match &stmt.kind {
9007            crate::StmtKind::InterfaceDecl { members, .. } => {
9008                assert_eq!(members.len(), 2);
9009                match &members[0] {
9010                    crate::InterfaceMember::Method { doc, .. } => {
9011                        assert_eq!(doc.as_ref().unwrap().summary, "First method.");
9012                    }
9013                    _ => panic!("expected Method first"),
9014                }
9015                match &members[1] {
9016                    crate::InterfaceMember::Property { doc, .. } => {
9017                        assert_eq!(doc.as_ref().unwrap().summary, "A field.");
9018                    }
9019                    _ => panic!("expected Property second"),
9020                }
9021            }
9022            _ => panic!("expected InterfaceDecl"),
9023        }
9024    }
9025
9026    #[test]
9027    fn parse_numeric_enum_all_implicit() {
9028        let (ast, diags) = parse_str("enum Direction { Up, Down, Left, Right }");
9029        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9030        let stmt = single_stmt(&ast);
9031        match &stmt.kind {
9032            crate::StmtKind::EnumDecl { name, members, doc } => {
9033                assert_eq!(name.name, "Direction");
9034                assert!(doc.is_none());
9035                assert_eq!(members.len(), 4);
9036                for (m, expected) in members.iter().zip(["Up", "Down", "Left", "Right"]) {
9037                    assert_eq!(m.name.name, expected);
9038                    assert!(m.value.is_none(), "expected no initializer on {expected}");
9039                }
9040            }
9041            _ => panic!("expected EnumDecl"),
9042        }
9043    }
9044
9045    #[test]
9046    fn parse_string_enum_all_explicit() {
9047        let (ast, diags) =
9048            parse_str("enum Status { Active = \"active\", Inactive = \"inactive\" }");
9049        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9050        let stmt = single_stmt(&ast);
9051        match &stmt.kind {
9052            crate::StmtKind::EnumDecl { name, members, .. } => {
9053                assert_eq!(name.name, "Status");
9054                assert_eq!(members.len(), 2);
9055                match &members[0].value {
9056                    Some(crate::EnumInitializer::String { value, .. }) => {
9057                        assert_eq!(value, "active");
9058                    }
9059                    other => panic!("expected String init, got {other:?}"),
9060                }
9061                match &members[1].value {
9062                    Some(crate::EnumInitializer::String { value, .. }) => {
9063                        assert_eq!(value, "inactive");
9064                    }
9065                    other => panic!("expected String init, got {other:?}"),
9066                }
9067            }
9068            _ => panic!("expected EnumDecl"),
9069        }
9070    }
9071
9072    #[test]
9073    fn parse_numeric_enum_with_negative_init() {
9074        let (ast, diags) = parse_str("enum D { Up = 1, Down = -1 }");
9075        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9076        let stmt = single_stmt(&ast);
9077        match &stmt.kind {
9078            crate::StmtKind::EnumDecl { members, .. } => {
9079                assert_eq!(members.len(), 2);
9080                match &members[0].value {
9081                    Some(crate::EnumInitializer::Number { value, .. }) => {
9082                        assert_eq!(*value, 1.0);
9083                    }
9084                    other => panic!("expected Number init, got {other:?}"),
9085                }
9086                match &members[1].value {
9087                    Some(crate::EnumInitializer::Number { value, .. }) => {
9088                        assert_eq!(*value, -1.0);
9089                    }
9090                    other => panic!("expected Number init, got {other:?}"),
9091                }
9092            }
9093            _ => panic!("expected EnumDecl"),
9094        }
9095    }
9096
9097    #[test]
9098    fn parse_enum_mixed_kinds_is_parse_error() {
9099        let (_ast, diags) = parse_str("enum Mix { A = 1, B = \"two\" }");
9100        assert!(
9101            diags
9102                .iter()
9103                .any(|d| d.message.contains("mixed numeric and string")),
9104            "expected mixed-kind diagnostic, got: {diags:?}"
9105        );
9106    }
9107
9108    #[test]
9109    fn parse_enum_member_docs() {
9110        let (ast, diags) = parse_str(
9111            "enum D {\n  /** The first one. */ A = 1,\n  /** The second one. */ B = 2,\n}",
9112        );
9113        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9114        let stmt = single_stmt(&ast);
9115        match &stmt.kind {
9116            crate::StmtKind::EnumDecl { members, .. } => {
9117                assert_eq!(members.len(), 2);
9118                assert_eq!(
9119                    members[0].doc.as_ref().expect("first doc").summary,
9120                    "The first one."
9121                );
9122                assert_eq!(
9123                    members[1].doc.as_ref().expect("second doc").summary,
9124                    "The second one."
9125                );
9126            }
9127            _ => panic!("expected EnumDecl"),
9128        }
9129    }
9130
9131    #[test]
9132    fn parse_enum_trailing_comma_ok() {
9133        let (ast, diags) = parse_str("enum D { A, B, }");
9134        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9135        let stmt = single_stmt(&ast);
9136        match &stmt.kind {
9137            crate::StmtKind::EnumDecl { members, .. } => assert_eq!(members.len(), 2),
9138            _ => panic!("expected EnumDecl"),
9139        }
9140    }
9141
9142    #[test]
9143    fn parse_function_empty_params() {
9144        let (ast, diags) = parse_str("function noop(): void { }");
9145        assert!(diags.is_empty());
9146        let stmt = single_stmt(&ast);
9147        match stmt.kind {
9148            crate::StmtKind::Function { ref params, .. } => assert!(params.is_empty()),
9149            _ => panic!("expected Function"),
9150        }
9151    }
9152
9153    #[test]
9154    fn parse_generic_function_single_param() {
9155        let (ast, diags) = parse_str("function identity<T>(x: T): T { return x; }");
9156        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9157        let stmt = single_stmt(&ast);
9158        match stmt.kind {
9159            crate::StmtKind::Function {
9160                ref name,
9161                ref generics,
9162                ref params,
9163                ..
9164            } => {
9165                assert_eq!(name.name, "identity");
9166                assert_eq!(generics.len(), 1);
9167                assert_eq!(generics[0].name, "T");
9168                assert_eq!(params.len(), 1);
9169                assert_eq!(params[0].name.name, "x");
9170            }
9171            _ => panic!("expected Function"),
9172        }
9173    }
9174
9175    #[test]
9176    fn parse_generic_function_multiple_params() {
9177        let (ast, diags) = parse_str("function pair<T, U>(a: T, b: U): T { return a; }");
9178        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9179        let stmt = single_stmt(&ast);
9180        match stmt.kind {
9181            crate::StmtKind::Function { ref generics, .. } => {
9182                assert_eq!(generics.len(), 2);
9183                assert_eq!(generics[0].name, "T");
9184                assert_eq!(generics[1].name, "U");
9185            }
9186            _ => panic!("expected Function"),
9187        }
9188    }
9189
9190    #[test]
9191    fn parse_non_generic_function_has_empty_generics() {
9192        let (ast, diags) = parse_str("function noop(): void { }");
9193        assert!(diags.is_empty());
9194        let stmt = single_stmt(&ast);
9195        match stmt.kind {
9196            crate::StmtKind::Function { ref generics, .. } => assert!(generics.is_empty()),
9197            _ => panic!("expected Function"),
9198        }
9199    }
9200
9201    #[test]
9202    fn parse_empty_generic_list_diagnoses() {
9203        let (_ast, diags) = parse_str("function f<>(): void { }");
9204        assert!(
9205            diags
9206                .iter()
9207                .any(|d| d.message.contains("empty generic parameter list")),
9208            "expected empty-generic-list diagnostic, got: {diags:?}",
9209        );
9210    }
9211
9212    #[test]
9213    fn parse_trailing_comma_in_generic_list_ok() {
9214        let (ast, diags) = parse_str("function f<T,>(): void { }");
9215        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9216        match single_stmt(&ast).kind {
9217            crate::StmtKind::Function { ref generics, .. } => assert_eq!(generics.len(), 1),
9218            _ => panic!("expected Function"),
9219        }
9220    }
9221
9222    #[test]
9223    fn parse_generic_call_single_type_arg() {
9224        let (ast, diags) = parse_str("identity<number>(42);");
9225        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9226        let stmt = single_stmt(&ast);
9227        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9228            panic!("expected Expr stmt");
9229        };
9230        match &ast.try_expr(call_id).unwrap().kind {
9231            crate::ExprKind::Call {
9232                type_args, args, ..
9233            } => {
9234                let ta = type_args.as_ref().expect("type_args set for generic call");
9235                assert_eq!(ta.len(), 1);
9236                assert_eq!(args.len(), 1);
9237            }
9238            other => panic!("expected Call, got {other:?}"),
9239        }
9240    }
9241
9242    #[test]
9243    fn parse_generic_call_trailing_comma_ok() {
9244        let (ast, diags) = parse_str("identity<number,>(42);");
9245        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9246        let stmt = single_stmt(&ast);
9247        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9248            panic!("expected Expr stmt");
9249        };
9250        match &ast.try_expr(call_id).unwrap().kind {
9251            crate::ExprKind::Call {
9252                type_args, args, ..
9253            } => {
9254                assert_eq!(type_args.as_ref().expect("type_args set").len(), 1);
9255                assert_eq!(args.len(), 1);
9256            }
9257            other => panic!("expected Call, got {other:?}"),
9258        }
9259    }
9260
9261    #[test]
9262    fn parse_generic_call_multi_type_args() {
9263        let (ast, diags) = parse_str("pair<number, string>(1, \"x\");");
9264        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9265        let stmt = single_stmt(&ast);
9266        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9267            panic!("expected Expr stmt");
9268        };
9269        match &ast.try_expr(call_id).unwrap().kind {
9270            crate::ExprKind::Call { type_args, .. } => {
9271                let ta = type_args.as_ref().expect("type_args set for generic call");
9272                assert_eq!(ta.len(), 2);
9273            }
9274            other => panic!("expected Call, got {other:?}"),
9275        }
9276    }
9277
9278    #[test]
9279    fn parse_comparison_chain_unaffected_by_generic_lookahead() {
9280        let (ast, diags) = parse_str("let z = a < b > c;");
9281        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9282        assert!(
9283            !ast_contains_call_with_type_args(&ast),
9284            "should not have parsed any generic call",
9285        );
9286    }
9287
9288    #[test]
9289    fn parse_lt_with_paren_rhs_is_comparison_not_generic() {
9290        let (ast, diags) = parse_str("let z = a < (b);");
9291        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9292        assert!(!ast_contains_call_with_type_args(&ast));
9293    }
9294
9295    #[test]
9296    fn parse_generic_call_no_args() {
9297        let (ast, diags) = parse_str("none<number>();");
9298        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9299        let stmt = single_stmt(&ast);
9300        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9301            panic!("expected Expr stmt");
9302        };
9303        match &ast.try_expr(call_id).unwrap().kind {
9304            crate::ExprKind::Call {
9305                type_args, args, ..
9306            } => {
9307                assert_eq!(type_args.as_ref().unwrap().len(), 1);
9308                assert!(args.is_empty());
9309            }
9310            other => panic!("expected Call, got {other:?}"),
9311        }
9312    }
9313
9314    fn ast_contains_call_with_type_args(ast: &crate::Ast) -> bool {
9315        for id in 0..ast.top_level.len() {
9316            let stmt = ast.try_stmt(ast.top_level[id]).unwrap();
9317            if walk_stmt_for_typed_call(ast, stmt) {
9318                return true;
9319            }
9320        }
9321        false
9322    }
9323
9324    fn walk_stmt_for_typed_call(ast: &crate::Ast, stmt: &crate::Stmt) -> bool {
9325        match &stmt.kind {
9326            crate::StmtKind::Let { value, .. } | crate::StmtKind::Const { value, .. } => {
9327                walk_expr_for_typed_call(ast, *value)
9328            }
9329            crate::StmtKind::Expr(e) => walk_expr_for_typed_call(ast, *e),
9330            crate::StmtKind::Block(stmts) => stmts
9331                .iter()
9332                .any(|sid| walk_stmt_for_typed_call(ast, ast.try_stmt(*sid).unwrap())),
9333            _ => false,
9334        }
9335    }
9336
9337    fn walk_expr_for_typed_call(ast: &crate::Ast, id: crate::ExprId) -> bool {
9338        match &ast.try_expr(id).unwrap().kind {
9339            crate::ExprKind::Call {
9340                type_args: Some(_), ..
9341            } => true,
9342            crate::ExprKind::Call { callee, args, .. } => {
9343                walk_expr_for_typed_call(ast, *callee)
9344                    || args.iter().any(|a| walk_expr_for_typed_call(ast, *a))
9345            }
9346            crate::ExprKind::Binary { lhs, rhs, .. } => {
9347                walk_expr_for_typed_call(ast, *lhs) || walk_expr_for_typed_call(ast, *rhs)
9348            }
9349            crate::ExprKind::Unary { operand, .. } => walk_expr_for_typed_call(ast, *operand),
9350            crate::ExprKind::Paren(e) => walk_expr_for_typed_call(ast, *e),
9351            crate::ExprKind::FieldAccess { receiver, .. } => {
9352                walk_expr_for_typed_call(ast, *receiver)
9353            }
9354            crate::ExprKind::IndexAccess { receiver, index } => {
9355                walk_expr_for_typed_call(ast, *receiver) || walk_expr_for_typed_call(ast, *index)
9356            }
9357            _ => false,
9358        }
9359    }
9360
9361    #[test]
9362    fn parse_function_body_with_statements() {
9363        let (ast, diags) = parse_str("function g(x: number): number { let y = x; y; }");
9364        assert!(diags.is_empty());
9365        let stmt = single_stmt(&ast);
9366        match stmt.kind {
9367            crate::StmtKind::Function { body, .. } => {
9368                let block = ast.try_stmt(body).unwrap();
9369                match block.kind {
9370                    crate::StmtKind::Block(ref v) => assert_eq!(v.len(), 2),
9371                    _ => panic!("expected Block"),
9372                }
9373            }
9374            _ => panic!("expected Function"),
9375        }
9376    }
9377
9378    #[test]
9379    fn function_missing_return_type_diagnoses() {
9380        let (ast, diags) = parse_str("function f() { }");
9381        assert!(!diags.is_empty());
9382        assert_eq!(diags[0].message, "expected `:` and return type");
9383        assert!(ast.top_level.is_empty());
9384    }
9385
9386    #[test]
9387    fn method_missing_return_type_still_reads_the_body() {
9388        let (_ast, diags) = parse_str(
9389            "class Box {\n  async run() {\n    const x = await f();\n    return x;\n  }\n  ok(): void {}\n}",
9390        );
9391        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9392        assert_eq!(
9393            messages,
9394            [
9395                "`async` functions are not supported",
9396                "expected `:` and return type",
9397                "`await` is not supported"
9398            ],
9399            "{diags:?}"
9400        );
9401        assert_eq!(
9402            diags[1].help,
9403            ["write the return type: `run(…): T { … }`, with `void` when it returns nothing"]
9404        );
9405    }
9406
9407    #[test]
9408    fn an_unclosed_method_body_leaves_the_next_declaration_alone() {
9409        let (ast, diags) =
9410            parse_str("class Box {\n  a() { return 1;\n}\nfunction main(): number { return 0; }");
9411        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9412        assert_eq!(
9413            messages,
9414            [
9415                "expected `:` and return type",
9416                "expected `}` to close class"
9417            ],
9418            "{diags:?}"
9419        );
9420        assert_eq!(ast.top_level.len(), 1, "`main` is still declared");
9421    }
9422
9423    #[test]
9424    fn every_declaration_after_an_unclosed_method_body_is_kept() {
9425        for next in [
9426            "class B { ok(): number { return 2; } }",
9427            "interface I { a: number; }",
9428            "enum E { A }",
9429            "type X = number;",
9430            "import * as fs from \"submilli:fs\";",
9431            "async function g(): number { return 1; }",
9432        ] {
9433            let source = format!(
9434                "class A {{\n  bad() {{\n    return 1;\n}}\n{next}\nfunction main(): void {{}}"
9435            );
9436            let (ast, diags) = parse_str(&source);
9437            let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9438            let expected_async = next.starts_with("async");
9439            let mut expected = vec![
9440                "expected `:` and return type",
9441                "expected `}` to close class",
9442            ];
9443            if expected_async {
9444                expected.push("`async` functions are not supported");
9445            }
9446            assert_eq!(messages, expected, "{next}: {diags:?}");
9447            assert_eq!(
9448                ast.top_level.len(),
9449                2,
9450                "{next} and `main` are kept: {diags:?}"
9451            );
9452        }
9453    }
9454
9455    #[test]
9456    fn a_failed_statement_leaves_the_next_declaration_to_be_parsed() {
9457        for (source, kept) in [
9458            ("let x = 1 +\nimport { exists } from \"submilli:fs\";", 1),
9459            ("const y =\nclass C {}\nfunction main(): void {}", 2),
9460            (
9461                "interface I {\n  a: number;\nimport * as fs from \"submilli:fs\";",
9462                1,
9463            ),
9464        ] {
9465            let (ast, diags) = parse_str(source);
9466            assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9467            assert_eq!(ast.top_level.len(), kept, "{source}: {diags:?}");
9468        }
9469    }
9470
9471    #[test]
9472    fn a_dynamic_import_after_a_failed_statement_reports_once() {
9473        for source in [
9474            "const m = import(\"submilli:fs\");\nclass C {}",
9475            "const d = import.meta.dirname;\nclass C {}",
9476        ] {
9477            let (_ast, diags) = parse_str(source);
9478            assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9479        }
9480    }
9481
9482    #[test]
9483    fn members_named_like_declarations_after_a_bad_method_stay_members() {
9484        let (_ast, diags) = parse_str(
9485            "class A {\n  bad() {}\n  type: number = 1;\n  function(): void {}\n  declare(): void {}\n  async(): void {}\n}",
9486        );
9487        assert_eq!(diags.len(), 1, "{diags:?}");
9488    }
9489
9490    #[test]
9491    fn every_member_after_a_bad_method_is_still_read() {
9492        let (_ast, diags) = parse_str(
9493            "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}",
9494        );
9495        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9496        assert_eq!(messages, ["expected `:` and return type"; 3], "{diags:?}");
9497    }
9498
9499    #[test]
9500    fn a_member_failing_after_a_type_literal_reports_once() {
9501        let (_ast, diags) = parse_str("class A {\n  x: { a: number } 5\n}");
9502        assert_eq!(diags.len(), 1, "{diags:?}");
9503    }
9504
9505    #[test]
9506    fn multiple_declarators_name_the_split() {
9507        let (_ast, diags) = parse_str("let a = 0, b = 0;");
9508        assert_eq!(diags.len(), 1, "{diags:?}");
9509        assert_eq!(diags[0].message, "one declaration declares one variable");
9510        assert_eq!(
9511            diags[0].help,
9512            [
9513                "declare one variable per statement: `let a = …; let b = …;`; in a `for (…)` header, declare `b` before the loop"
9514            ]
9515        );
9516        let (_ast, diags) = parse_str("for (let i = 0, j = 10; i < j; i++) {}");
9517        assert_eq!(
9518            diags[0].message, "one declaration declares one variable",
9519            "{diags:?}"
9520        );
9521        assert!(
9522            diags[0].help[0].ends_with("declare `j` before the loop"),
9523            "{diags:?}"
9524        );
9525    }
9526
9527    #[test]
9528    fn missing_return_type_still_reads_the_body() {
9529        let (_ast, diags) = parse_str(
9530            "async function main() {\n  const x = await f();\n  if (x) { return 1; }\n  return 2;\n}\nfunction g(): void {}",
9531        );
9532        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9533        assert_eq!(
9534            messages,
9535            [
9536                "`async` functions are not supported",
9537                "expected `:` and return type",
9538                "`await` is not supported"
9539            ],
9540            "{diags:?}"
9541        );
9542        assert_eq!(
9543            diags[1].help,
9544            [
9545                "write the return type: `function main(…): T { … }`, with `void` when it returns nothing"
9546            ]
9547        );
9548    }
9549
9550    #[test]
9551    fn function_missing_param_type_diagnoses() {
9552        let (ast, diags) = parse_str("function f(a): void { }");
9553        assert!(!diags.is_empty());
9554        assert_eq!(diags[0].message, "parameter requires a type annotation");
9555        assert!(ast.top_level.is_empty());
9556    }
9557
9558    #[test]
9559    fn function_missing_name_diagnoses() {
9560        let (ast, diags) = parse_str("function (x: number): void { }");
9561        assert!(!diags.is_empty());
9562        assert_eq!(diags[0].message, "expected function name");
9563        assert!(ast.top_level.is_empty());
9564    }
9565
9566    #[test]
9567    fn function_trailing_param_comma_ok() {
9568        let (ast, diags) = parse_str("function f(a: number,): void { }");
9569        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9570        match single_stmt(&ast).kind {
9571            crate::StmtKind::Function { ref params, .. } => assert_eq!(params.len(), 1),
9572            _ => panic!("expected Function"),
9573        }
9574    }
9575
9576    #[test]
9577    fn function_missing_open_brace_diagnoses() {
9578        let (ast, diags) = parse_str("function f(): void");
9579        assert_eq!(diags.len(), 1);
9580        assert_eq!(diags[0].message, "expected `{`");
9581        assert!(ast.top_level.is_empty());
9582    }
9583
9584    #[test]
9585    fn snapshot_function_declaration() {
9586        let (ast, diags) =
9587            parse_str("function add(a: number, b: number): number { let sum = a + b; sum; }");
9588        assert!(diags.is_empty());
9589        insta::assert_debug_snapshot!(ast);
9590    }
9591
9592    #[test]
9593    fn rest_param_basic_parses() {
9594        let (_, diags) = parse_str("function sum(...nums: number[]): number { return 0; }");
9595        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9596    }
9597
9598    #[test]
9599    fn rest_param_with_fixed_prefix_parses() {
9600        let (_, diags) =
9601            parse_str("function tag(label: string, ...vals: number[]): string { return label; }");
9602        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9603    }
9604
9605    #[test]
9606    fn rest_param_in_arrow_parses() {
9607        let (_, diags) = parse_str("const f = (...n: number[]) => n.length;");
9608        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9609    }
9610
9611    #[test]
9612    fn rest_param_in_function_type_annotation_parses() {
9613        let (_, diags) =
9614            parse_str("let f: (a: number, ...rest: string[]) => void = (a, ...r) => {};");
9615        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9616    }
9617
9618    #[test]
9619    fn rest_param_not_last_rejected() {
9620        let (_, diags) = parse_str("function f(...xs: number[], y: number): void {}");
9621        assert!(
9622            diags.iter().any(|d| d
9623                .message
9624                .contains("rest parameter must be the last parameter")),
9625            "expected 'must be last' diagnostic, got {diags:?}",
9626        );
9627    }
9628
9629    #[test]
9630    fn rest_param_with_default_rejected() {
9631        let (_, diags) = parse_str("function f(...xs: number[] = []): void {}");
9632        assert!(
9633            diags.iter().any(|d| d
9634                .message
9635                .contains("rest parameter cannot have a default value")),
9636            "expected default-rejection diagnostic, got {diags:?}",
9637        );
9638    }
9639
9640    #[test]
9641    fn rest_param_without_annotation_rejected() {
9642        let (_, diags) = parse_str("function f(...xs): void {}");
9643        assert!(
9644            diags.iter().any(|d| d
9645                .message
9646                .contains("rest parameter requires a type annotation")),
9647            "expected missing-annotation diagnostic, got {diags:?}",
9648        );
9649    }
9650
9651    #[test]
9652    fn rest_param_destructured_rejected() {
9653        let (_, diags) = parse_str("function f(...{a, b}): void {}");
9654        assert!(
9655            diags
9656                .iter()
9657                .any(|d| d.message.contains("rest parameter cannot be destructured")),
9658            "expected destructured-rejection diagnostic, got {diags:?}",
9659        );
9660    }
9661
9662    #[test]
9663    fn rest_param_in_function_type_with_non_array_rejected() {
9664        let (_, diags) = parse_str("let f: (...xs: number) => void = () => {};");
9665        assert!(
9666            diags
9667                .iter()
9668                .any(|d| d.message.contains("rest parameter type must be an array")),
9669            "expected non-array-type diagnostic, got {diags:?}",
9670        );
9671    }
9672
9673    fn call(e: &crate::Expr) -> (crate::ExprId, &[crate::ExprId]) {
9674        match e.kind {
9675            crate::ExprKind::Call {
9676                callee, ref args, ..
9677            } => (callee, args.as_slice()),
9678            _ => panic!("expected Call, got {:?}", e.kind),
9679        }
9680    }
9681
9682    #[test]
9683    fn parse_call_no_args() {
9684        let (ast, diags) = parse_str("f();");
9685        assert!(diags.is_empty());
9686        let outer = expr_of_single_stmt(&ast);
9687        let (callee, args) = call(outer);
9688        assert!(matches!(
9689            ast.try_expr(callee).unwrap().kind,
9690            crate::ExprKind::Identifier(_)
9691        ));
9692        assert!(args.is_empty());
9693        assert_eq!(outer.span, crate::Span::new(F, 0, 3).unwrap());
9694    }
9695
9696    #[test]
9697    fn parse_call_one_arg() {
9698        let (ast, diags) = parse_str("f(1);");
9699        assert!(diags.is_empty());
9700        let outer = expr_of_single_stmt(&ast);
9701        let (callee, args) = call(outer);
9702        assert!(matches!(
9703            ast.try_expr(callee).unwrap().kind,
9704            crate::ExprKind::Identifier(_)
9705        ));
9706        assert_eq!(args.len(), 1);
9707        assert_eq!(
9708            ast.try_expr(args[0]).unwrap().kind,
9709            crate::ExprKind::Number(1.0)
9710        );
9711    }
9712
9713    #[test]
9714    fn parse_call_three_args() {
9715        let (ast, diags) = parse_str("f(1, 2, 3);");
9716        assert!(diags.is_empty());
9717        let outer = expr_of_single_stmt(&ast);
9718        let (_callee, args) = call(outer);
9719        assert_eq!(args.len(), 3);
9720        assert_eq!(
9721            ast.try_expr(args[0]).unwrap().kind,
9722            crate::ExprKind::Number(1.0)
9723        );
9724        assert_eq!(
9725            ast.try_expr(args[1]).unwrap().kind,
9726            crate::ExprKind::Number(2.0)
9727        );
9728        assert_eq!(
9729            ast.try_expr(args[2]).unwrap().kind,
9730            crate::ExprKind::Number(3.0)
9731        );
9732    }
9733
9734    #[test]
9735    fn parse_call_nested() {
9736        let (ast, diags) = parse_str("f(g(x));");
9737        assert!(diags.is_empty());
9738        let outer = expr_of_single_stmt(&ast);
9739        let (_callee, args) = call(outer);
9740        assert_eq!(args.len(), 1);
9741        let (_inner_callee, inner_args) = call(ast.try_expr(args[0]).unwrap());
9742        assert_eq!(inner_args.len(), 1);
9743        assert!(matches!(
9744            ast.try_expr(inner_args[0]).unwrap().kind,
9745            crate::ExprKind::Identifier(_)
9746        ));
9747    }
9748
9749    #[test]
9750    fn parse_curried_call() {
9751        let (ast, diags) = parse_str("f()(x);");
9752        assert!(diags.is_empty());
9753        let outer = expr_of_single_stmt(&ast);
9754        let (callee, args) = call(outer);
9755        assert_eq!(args.len(), 1);
9756        let (_inner_callee, inner_args) = call(ast.try_expr(callee).unwrap());
9757        assert!(inner_args.is_empty());
9758    }
9759
9760    #[test]
9761    fn unary_binds_looser_than_call() {
9762        let (ast, diags) = parse_str("-f();");
9763        assert!(diags.is_empty());
9764        let outer = expr_of_single_stmt(&ast);
9765        let (op, operand) = unary(outer);
9766        assert_eq!(op, crate::UnOp::Neg);
9767        let (_callee, args) = call(ast.try_expr(operand).unwrap());
9768        assert!(args.is_empty());
9769    }
9770
9771    #[test]
9772    fn call_inside_binary() {
9773        let (ast, diags) = parse_str("f() + 1;");
9774        assert!(diags.is_empty());
9775        let outer = expr_of_single_stmt(&ast);
9776        let (op, lhs, rhs) = binary(outer);
9777        assert_eq!(op, crate::BinOp::Add);
9778        let (_callee, args) = call(ast.try_expr(lhs).unwrap());
9779        assert!(args.is_empty());
9780        assert_eq!(
9781            ast.try_expr(rhs).unwrap().kind,
9782            crate::ExprKind::Number(1.0)
9783        );
9784    }
9785
9786    #[test]
9787    fn call_with_complex_args() {
9788        let (ast, diags) = parse_str("f(1 + 2, g(x));");
9789        assert!(diags.is_empty());
9790        let outer = expr_of_single_stmt(&ast);
9791        let (_callee, args) = call(outer);
9792        assert_eq!(args.len(), 2);
9793        let (op, ..) = binary(ast.try_expr(args[0]).unwrap());
9794        assert_eq!(op, crate::BinOp::Add);
9795        let (_, inner_args) = call(ast.try_expr(args[1]).unwrap());
9796        assert_eq!(inner_args.len(), 1);
9797    }
9798
9799    #[test]
9800    fn call_trailing_comma_ok() {
9801        let (ast, diags) = parse_str("f(1, 2,);");
9802        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9803        let (_callee, args) = call(expr_of_single_stmt(&ast));
9804        assert_eq!(args.len(), 2);
9805    }
9806
9807    #[test]
9808    fn unclosed_call_diagnoses() {
9809        let (ast, diags) = parse_str("f(1, 2");
9810        assert!(!diags.is_empty());
9811        assert!(diags[0].message == "expected `,` or `)`" || diags[0].message == "expected `)`");
9812        assert!(ast.top_level.is_empty());
9813    }
9814
9815    #[test]
9816    fn call_expression_statement() {
9817        let (ast, diags) = parse_str(r#"greet("world");"#);
9818        assert!(diags.is_empty());
9819        let outer = expr_of_single_stmt(&ast);
9820        let (callee, args) = call(outer);
9821        assert!(matches!(
9822            ast.try_expr(callee).unwrap().kind,
9823            crate::ExprKind::Identifier(_)
9824        ));
9825        assert_eq!(args.len(), 1);
9826        assert_eq!(
9827            ast.try_expr(args[0]).unwrap().kind,
9828            crate::ExprKind::String("world".to_string())
9829        );
9830    }
9831
9832    #[test]
9833    fn snapshot_multi_call_program() {
9834        let (ast, diags) = parse_str("f(); g(1); h(f(2), 3);");
9835        assert!(diags.is_empty());
9836        assert_eq!(ast.top_level.len(), 3);
9837        insta::assert_debug_snapshot!(ast);
9838    }
9839
9840    #[test]
9841    fn parse_if_simple() {
9842        let (ast, diags) = parse_str("if (a) { }");
9843        assert!(diags.is_empty());
9844        let stmt = single_stmt(&ast);
9845        match stmt.kind {
9846            crate::StmtKind::If {
9847                condition,
9848                then_block,
9849                else_block,
9850            } => {
9851                assert!(matches!(
9852                    ast.try_expr(condition).unwrap().kind,
9853                    crate::ExprKind::Identifier(_)
9854                ));
9855                assert!(matches!(
9856                    ast.try_stmt(then_block).unwrap().kind,
9857                    crate::StmtKind::Block(ref v) if v.is_empty()
9858                ));
9859                assert!(else_block.is_none());
9860            }
9861            _ => panic!("expected If"),
9862        }
9863    }
9864
9865    #[test]
9866    fn parse_if_else() {
9867        let (ast, diags) = parse_str("if (a) { } else { }");
9868        assert!(diags.is_empty());
9869        let stmt = single_stmt(&ast);
9870        match stmt.kind {
9871            crate::StmtKind::If { else_block, .. } => {
9872                let else_id = else_block.expect("expected else block");
9873                assert!(matches!(
9874                    ast.try_stmt(else_id).unwrap().kind,
9875                    crate::StmtKind::Block(ref v) if v.is_empty()
9876                ));
9877            }
9878            _ => panic!("expected If"),
9879        }
9880    }
9881
9882    #[test]
9883    fn parse_else_if_chain() {
9884        let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
9885        assert!(diags.is_empty());
9886        let stmt = single_stmt(&ast);
9887        let crate::StmtKind::If {
9888            else_block: Some(inner_id),
9889            ..
9890        } = stmt.kind
9891        else {
9892            panic!("expected outer If with else_block");
9893        };
9894        let inner = ast.try_stmt(inner_id).unwrap();
9895        let crate::StmtKind::If {
9896            else_block: Some(tail_id),
9897            ..
9898        } = inner.kind
9899        else {
9900            panic!("expected inner If for `else if`");
9901        };
9902        assert!(matches!(
9903            ast.try_stmt(tail_id).unwrap().kind,
9904            crate::StmtKind::Block(_)
9905        ));
9906    }
9907
9908    #[test]
9909    fn parse_if_with_body_statements() {
9910        let (ast, diags) = parse_str("if (a) { let x = 1; }");
9911        assert!(diags.is_empty());
9912        let stmt = single_stmt(&ast);
9913        match stmt.kind {
9914            crate::StmtKind::If { then_block, .. } => {
9915                let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind
9916                else {
9917                    panic!("expected Block")
9918                };
9919                assert_eq!(body.len(), 1);
9920            }
9921            _ => panic!("expected If"),
9922        }
9923    }
9924
9925    #[test]
9926    fn parse_if_braceless_body() {
9927        let (ast, diags) = parse_str("if (a) return b;");
9928        assert!(diags.is_empty(), "{diags:?}");
9929        let stmt = single_stmt(&ast);
9930        let crate::StmtKind::If {
9931            then_block,
9932            else_block,
9933            ..
9934        } = stmt.kind
9935        else {
9936            panic!("expected If");
9937        };
9938        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9939            panic!("braceless body should wrap in a Block");
9940        };
9941        assert_eq!(body.len(), 1);
9942        assert!(matches!(
9943            ast.try_stmt(body[0]).unwrap().kind,
9944            crate::StmtKind::Return(_)
9945        ));
9946        assert!(else_block.is_none());
9947    }
9948
9949    #[test]
9950    fn parse_braceless_dangling_else_binds_nearest_if() {
9951        // `else` attaches to the inner `if`, not the outer one.
9952        let (ast, diags) = parse_str("if (a) if (b) x(); else y();");
9953        assert!(diags.is_empty(), "{diags:?}");
9954        let crate::StmtKind::If {
9955            then_block,
9956            else_block: outer_else,
9957            ..
9958        } = single_stmt(&ast).kind
9959        else {
9960            panic!("expected outer If");
9961        };
9962        assert!(outer_else.is_none(), "else must bind to the inner if");
9963        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9964            panic!("expected wrapped Block");
9965        };
9966        let crate::StmtKind::If {
9967            else_block: Some(_),
9968            ..
9969        } = ast.try_stmt(body[0]).unwrap().kind
9970        else {
9971            panic!("inner If should own the else");
9972        };
9973    }
9974
9975    #[test]
9976    fn parse_while_braceless_body() {
9977        let (ast, diags) = parse_str("while (a) x();");
9978        assert!(diags.is_empty(), "{diags:?}");
9979        let crate::StmtKind::While { body, .. } = single_stmt(&ast).kind else {
9980            panic!("expected While");
9981        };
9982        assert!(matches!(
9983            ast.try_stmt(body).unwrap().kind,
9984            crate::StmtKind::Block(ref v) if v.len() == 1
9985        ));
9986    }
9987
9988    #[test]
9989    fn parse_loop_with_empty_body() {
9990        for src in [
9991            "while (a());",
9992            "for (const x of xs);",
9993            "for (let i = 0; i < 3; i++);",
9994            "do ; while (a());",
9995        ] {
9996            let (ast, diags) = parse_str(src);
9997            assert!(diags.is_empty(), "{src}: {diags:?}");
9998            let body = match single_stmt(&ast).kind {
9999                crate::StmtKind::While { body, .. }
10000                | crate::StmtKind::DoWhile { body, .. }
10001                | crate::StmtKind::For { body, .. }
10002                | crate::StmtKind::ForOf { body, .. } => body,
10003                ref other => panic!("{src}: expected a loop, got {other:?}"),
10004            };
10005            assert!(
10006                matches!(ast.try_stmt(body).unwrap().kind, crate::StmtKind::Block(ref v) if v.is_empty()),
10007                "{src}"
10008            );
10009        }
10010    }
10011
10012    #[test]
10013    fn parse_if_with_empty_body_is_an_error() {
10014        let (_ast, diags) = parse_str("if (a);");
10015        assert_eq!(diags[0].message, "`if` has an empty body");
10016    }
10017
10018    #[test]
10019    fn parse_empty_branch_body_reports_once() {
10020        let (_ast, diags) = parse_str("if (a) ; else b();");
10021        assert_eq!(diags.len(), 1, "{diags:?}");
10022        assert_eq!(diags[0].message, "`if` has an empty body");
10023        let (_ast, diags) = parse_str("if (a) b(); else ;");
10024        assert_eq!(diags.len(), 1, "{diags:?}");
10025        assert_eq!(diags[0].message, "`else` has an empty body");
10026        // One diagnostic per stray `;`, and the `else if` chain still parses.
10027        let (_ast, diags) = parse_str("if (a) ; else if (b) ; else ;");
10028        let messages: Vec<_> = diags.iter().map(|d| d.message.as_str()).collect();
10029        assert_eq!(
10030            messages,
10031            [
10032                "`if` has an empty body",
10033                "`if` has an empty body",
10034                "`else` has an empty body"
10035            ]
10036        );
10037    }
10038
10039    #[test]
10040    fn parse_for_of_braceless_body() {
10041        let (ast, diags) = parse_str("for (const x of xs) f(x);");
10042        assert!(diags.is_empty(), "{diags:?}");
10043        let crate::StmtKind::ForOf { body, .. } = single_stmt(&ast).kind else {
10044            panic!("expected ForOf");
10045        };
10046        assert!(matches!(
10047            ast.try_stmt(body).unwrap().kind,
10048            crate::StmtKind::Block(ref v) if v.len() == 1
10049        ));
10050    }
10051
10052    #[test]
10053    fn parse_nested_if() {
10054        let (ast, diags) = parse_str("if (a) { if (b) { } }");
10055        assert!(diags.is_empty());
10056        let outer = single_stmt(&ast);
10057        let crate::StmtKind::If { then_block, .. } = outer.kind else {
10058            panic!("expected outer If");
10059        };
10060        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10061            panic!("expected Block")
10062        };
10063        assert_eq!(body.len(), 1);
10064        assert!(matches!(
10065            ast.try_stmt(body[0]).unwrap().kind,
10066            crate::StmtKind::If { .. }
10067        ));
10068    }
10069
10070    #[test]
10071    fn if_missing_open_paren_diagnoses() {
10072        let (ast, diags) = parse_str("if a { }");
10073        assert!(!diags.is_empty());
10074        assert_eq!(diags[0].message, "expected `(`");
10075        assert!(ast.top_level.is_empty());
10076    }
10077
10078    #[test]
10079    fn if_missing_close_paren_diagnoses() {
10080        let (ast, diags) = parse_str("if (a { }");
10081        assert!(!diags.is_empty());
10082        assert_eq!(diags[0].message, "expected `)`");
10083        assert!(ast.top_level.is_empty());
10084    }
10085
10086    #[test]
10087    fn if_braceless_body_parses() {
10088        let (ast, diags) = parse_str("if (a) x;");
10089        assert!(diags.is_empty(), "{diags:?}");
10090        let crate::StmtKind::If { then_block, .. } = single_stmt(&ast).kind else {
10091            panic!("expected If");
10092        };
10093        assert!(matches!(
10094            ast.try_stmt(then_block).unwrap().kind,
10095            crate::StmtKind::Block(ref v) if v.len() == 1
10096        ));
10097    }
10098
10099    #[test]
10100    fn snapshot_else_if_chain() {
10101        let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10102        assert!(diags.is_empty());
10103        insta::assert_debug_snapshot!(ast);
10104    }
10105
10106    #[test]
10107    fn parse_while_simple() {
10108        let (ast, diags) = parse_str("while (a) { }");
10109        assert!(diags.is_empty());
10110        let stmt = single_stmt(&ast);
10111        match stmt.kind {
10112            crate::StmtKind::While { condition, body } => {
10113                assert!(matches!(
10114                    ast.try_expr(condition).unwrap().kind,
10115                    crate::ExprKind::Identifier(_)
10116                ));
10117                assert!(matches!(
10118                    ast.try_stmt(body).unwrap().kind,
10119                    crate::StmtKind::Block(ref v) if v.is_empty()
10120                ));
10121            }
10122            _ => panic!("expected While"),
10123        }
10124    }
10125
10126    #[test]
10127    fn parse_while_with_body() {
10128        let (ast, diags) = parse_str("while (a) { x; y; }");
10129        assert!(diags.is_empty());
10130        let stmt = single_stmt(&ast);
10131        let crate::StmtKind::While { body, .. } = stmt.kind else {
10132            panic!("expected While");
10133        };
10134        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10135            panic!("expected Block")
10136        };
10137        assert_eq!(stmts.len(), 2);
10138    }
10139
10140    #[test]
10141    fn while_missing_paren_diagnoses() {
10142        let (ast, diags) = parse_str("while a { }");
10143        assert!(!diags.is_empty());
10144        assert_eq!(diags[0].message, "expected `(`");
10145        assert!(ast.top_level.is_empty());
10146    }
10147
10148    #[test]
10149    fn parse_for_all_slots() {
10150        let (ast, diags) = parse_str("for (let i = 0; i < 10; i = i + 1) { x; }");
10151        assert!(diags.is_empty(), "{diags:?}");
10152        let stmt = single_stmt(&ast);
10153        let crate::StmtKind::For {
10154            init,
10155            condition,
10156            update,
10157            body,
10158        } = stmt.kind
10159        else {
10160            panic!("expected For");
10161        };
10162        assert!(init.is_some(), "init missing");
10163        assert!(condition.is_some(), "condition missing");
10164        assert!(update.is_some(), "update missing");
10165        assert!(matches!(
10166            ast.try_stmt(body).unwrap().kind,
10167            crate::StmtKind::Block(_)
10168        ));
10169    }
10170
10171    #[test]
10172    fn parse_for_empty_slots() {
10173        let (ast, diags) = parse_str("for (;;) { break; }");
10174        assert!(diags.is_empty(), "{diags:?}");
10175        let stmt = single_stmt(&ast);
10176        let crate::StmtKind::For {
10177            init,
10178            condition,
10179            update,
10180            ..
10181        } = stmt.kind
10182        else {
10183            panic!("expected For");
10184        };
10185        assert!(init.is_none() && condition.is_none() && update.is_none());
10186    }
10187
10188    #[test]
10189    fn parse_for_of_const_binding() {
10190        let (ast, diags) = parse_str("for (const x of arr) { y; }");
10191        assert!(diags.is_empty(), "{diags:?}");
10192        let stmt = single_stmt(&ast);
10193        let crate::StmtKind::ForOf {
10194            binding_kind,
10195            ref name,
10196            ref ty,
10197            ..
10198        } = stmt.kind
10199        else {
10200            panic!("expected ForOf");
10201        };
10202        assert!(matches!(binding_kind, crate::BindingKind::Const));
10203        assert_eq!(name.name, "x");
10204        assert!(ty.is_none());
10205    }
10206
10207    #[test]
10208    fn parse_for_of_let_with_annotation() {
10209        let (ast, diags) = parse_str("for (let x: number of arr) { y; }");
10210        assert!(diags.is_empty(), "{diags:?}");
10211        let stmt = single_stmt(&ast);
10212        let crate::StmtKind::ForOf {
10213            binding_kind,
10214            ref ty,
10215            ..
10216        } = stmt.kind
10217        else {
10218            panic!("expected ForOf");
10219        };
10220        assert!(matches!(binding_kind, crate::BindingKind::Let));
10221        assert!(ty.is_some());
10222    }
10223
10224    #[test]
10225    fn parse_do_while_simple() {
10226        let (ast, diags) = parse_str("do { x; } while (a);");
10227        assert!(diags.is_empty(), "{diags:?}");
10228        let stmt = single_stmt(&ast);
10229        assert!(matches!(stmt.kind, crate::StmtKind::DoWhile { .. }));
10230    }
10231
10232    #[test]
10233    fn parse_break_and_continue() {
10234        let (ast, diags) = parse_str("while (a) { break; continue; }");
10235        assert!(diags.is_empty(), "{diags:?}");
10236        let stmt = single_stmt(&ast);
10237        let crate::StmtKind::While { body, .. } = stmt.kind else {
10238            panic!("expected While");
10239        };
10240        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10241            panic!("expected Block");
10242        };
10243        assert!(matches!(
10244            ast.try_stmt(stmts[0]).unwrap().kind,
10245            crate::StmtKind::Break
10246        ));
10247        assert!(matches!(
10248            ast.try_stmt(stmts[1]).unwrap().kind,
10249            crate::StmtKind::Continue
10250        ));
10251    }
10252
10253    #[test]
10254    fn parse_bare_return_in_function() {
10255        let (ast, diags) = parse_str("function f(): void { return; }");
10256        assert!(diags.is_empty());
10257        let f = single_stmt(&ast);
10258        let crate::StmtKind::Function { body, .. } = f.kind else {
10259            panic!("expected Function");
10260        };
10261        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10262            panic!("expected Block")
10263        };
10264        assert_eq!(stmts.len(), 1);
10265        match ast.try_stmt(stmts[0]).unwrap().kind {
10266            crate::StmtKind::Return(None) => {}
10267            _ => panic!("expected Return(None)"),
10268        }
10269    }
10270
10271    #[test]
10272    fn parse_return_with_value() {
10273        let (ast, diags) = parse_str("function f(): number { return 1; }");
10274        assert!(diags.is_empty());
10275        let f = single_stmt(&ast);
10276        let crate::StmtKind::Function { body, .. } = f.kind else {
10277            panic!("expected Function");
10278        };
10279        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10280            panic!("expected Block")
10281        };
10282        match ast.try_stmt(stmts[0]).unwrap().kind {
10283            crate::StmtKind::Return(Some(value)) => {
10284                assert_eq!(
10285                    ast.try_expr(value).unwrap().kind,
10286                    crate::ExprKind::Number(1.0)
10287                );
10288            }
10289            _ => panic!("expected Return(Some)"),
10290        }
10291    }
10292
10293    #[test]
10294    fn parse_return_with_expression() {
10295        let (ast, diags) = parse_str("function f(): number { return 1 + 2; }");
10296        assert!(diags.is_empty());
10297        let f = single_stmt(&ast);
10298        let crate::StmtKind::Function { body, .. } = f.kind else {
10299            panic!("expected Function");
10300        };
10301        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10302            panic!("expected Block")
10303        };
10304        match ast.try_stmt(stmts[0]).unwrap().kind {
10305            crate::StmtKind::Return(Some(value)) => {
10306                let (op, ..) = binary(ast.try_expr(value).unwrap());
10307                assert_eq!(op, crate::BinOp::Add);
10308            }
10309            _ => panic!("expected Return(Some(Binary))"),
10310        }
10311    }
10312
10313    #[test]
10314    fn return_missing_semicolon_diagnoses() {
10315        let (_, diags) = parse_str("function f(): number { return x x; }");
10316        assert!(!diags.is_empty());
10317        assert_eq!(diags[0].message, "expected `;` after return");
10318    }
10319
10320    #[test]
10321    fn parse_bare_block() {
10322        let (ast, diags) = parse_str("{ x; y; }");
10323        assert!(diags.is_empty());
10324        let stmt = single_stmt(&ast);
10325        let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10326            panic!("expected Block");
10327        };
10328        assert_eq!(stmts.len(), 2);
10329    }
10330
10331    #[test]
10332    fn parse_empty_bare_block() {
10333        let (ast, diags) = parse_str("{ }");
10334        assert!(diags.is_empty());
10335        let stmt = single_stmt(&ast);
10336        let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10337            panic!("expected Block");
10338        };
10339        assert!(stmts.is_empty());
10340    }
10341
10342    #[test]
10343    fn parse_nested_bare_blocks() {
10344        let (ast, diags) = parse_str("{ { } }");
10345        assert!(diags.is_empty());
10346        let outer = single_stmt(&ast);
10347        let crate::StmtKind::Block(ref stmts) = outer.kind else {
10348            panic!("expected outer Block");
10349        };
10350        assert_eq!(stmts.len(), 1);
10351        assert!(matches!(
10352            ast.try_stmt(stmts[0]).unwrap().kind,
10353            crate::StmtKind::Block(ref v) if v.is_empty()
10354        ));
10355    }
10356
10357    #[test]
10358    fn snapshot_function_with_if_and_return() {
10359        let source = "function isPositive(n: number): boolean {\n  if (n > 0) {\n    return true;\n  } else {\n    return false;\n  }\n}";
10360        let (ast, diags) = parse_str(source);
10361        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10362        insta::assert_debug_snapshot!(ast);
10363    }
10364
10365    fn object_literal(e: &crate::Expr) -> &[crate::ObjectLiteralMember] {
10366        match e.kind {
10367            crate::ExprKind::ObjectLiteral { ref members } => members.as_slice(),
10368            _ => panic!("expected ObjectLiteral, got {:?}", e.kind),
10369        }
10370    }
10371
10372    fn object_literal_field(m: &crate::ObjectLiteralMember) -> &crate::ObjectLiteralField {
10373        match m {
10374            crate::ObjectLiteralMember::Field(f) => f,
10375            crate::ObjectLiteralMember::Spread { .. }
10376            | crate::ObjectLiteralMember::Computed { .. } => {
10377                panic!("expected Field member, got Spread")
10378            }
10379        }
10380    }
10381
10382    fn array_literal(e: &crate::Expr) -> &[crate::ArrayLiteralElement] {
10383        match e.kind {
10384            crate::ExprKind::ArrayLiteral { ref elements } => elements.as_slice(),
10385            _ => panic!("expected ArrayLiteral, got {:?}", e.kind),
10386        }
10387    }
10388
10389    fn array_literal_value(el: &crate::ArrayLiteralElement) -> crate::ExprId {
10390        match el {
10391            crate::ArrayLiteralElement::Value(id) => *id,
10392            crate::ArrayLiteralElement::Spread { .. } => {
10393                panic!("expected Value element, got Spread")
10394            }
10395        }
10396    }
10397
10398    #[test]
10399    fn parse_empty_object_literal() {
10400        let (ast, diags) = parse_str("let x = {};");
10401        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10402        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10403            panic!("expected Let");
10404        };
10405        assert!(object_literal(ast.try_expr(value).unwrap()).is_empty());
10406    }
10407
10408    #[test]
10409    fn parse_object_literal_simple() {
10410        let (ast, diags) = parse_str("let p = { x: 1, y: 2 };");
10411        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10412        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10413            panic!("expected Let");
10414        };
10415        let members = object_literal(ast.try_expr(value).unwrap());
10416        assert_eq!(members.len(), 2);
10417        let f0 = object_literal_field(&members[0]);
10418        let f1 = object_literal_field(&members[1]);
10419        assert_eq!(f0.name.name, "x");
10420        assert_eq!(f1.name.name, "y");
10421        assert_eq!(
10422            ast.try_expr(f0.value).unwrap().kind,
10423            crate::ExprKind::Number(1.0)
10424        );
10425        assert_eq!(
10426            ast.try_expr(f1.value).unwrap().kind,
10427            crate::ExprKind::Number(2.0)
10428        );
10429    }
10430
10431    #[test]
10432    fn parse_object_literal_keyword_keys() {
10433        let (ast, diags) = parse_str("let p = { type: 1, default: 2, null: 3, true: 4 };");
10434        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10435        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10436            panic!("expected Let");
10437        };
10438        let names: Vec<&str> = object_literal(ast.try_expr(value).unwrap())
10439            .iter()
10440            .map(object_literal_field)
10441            .map(|f| f.name.name.as_str())
10442            .collect();
10443        assert_eq!(names, vec!["type", "default", "null", "true"]);
10444    }
10445
10446    #[test]
10447    fn parse_object_literal_string_key() {
10448        let (ast, diags) = parse_str(r#"let p = { "hello": 1 };"#);
10449        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10450        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10451            panic!("expected Let");
10452        };
10453        let members = object_literal(ast.try_expr(value).unwrap());
10454        assert_eq!(members.len(), 1);
10455        let f0 = object_literal_field(&members[0]);
10456        assert_eq!(f0.name.name, "hello");
10457    }
10458
10459    #[test]
10460    fn parse_object_literal_string_key_decodes_escape() {
10461        let (ast, diags) = parse_str(r#"let p = { "content\u002dtype": 1 };"#);
10462        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10463        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10464            panic!("expected Let");
10465        };
10466        let members = object_literal(ast.try_expr(value).unwrap());
10467        assert_eq!(members.len(), 1);
10468        let f0 = object_literal_field(&members[0]);
10469        assert_eq!(f0.name.name, "content-type");
10470    }
10471
10472    #[test]
10473    fn parse_object_literal_shorthand() {
10474        let (ast, diags) = parse_str("let p = { x, y: 2 };");
10475        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10476        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10477            panic!("expected Let");
10478        };
10479        let members = object_literal(ast.try_expr(value).unwrap());
10480        assert_eq!(members.len(), 2);
10481        let f0 = object_literal_field(&members[0]);
10482        assert_eq!(f0.name.name, "x");
10483        // `{ x }` desugars to `{ x: x }` — value is an identifier reference.
10484        assert_eq!(
10485            ast.try_expr(f0.value).unwrap().kind,
10486            crate::ExprKind::Identifier(crate::Ident {
10487                name: "x".into(),
10488                span: f0.name.span,
10489            })
10490        );
10491        let f1 = object_literal_field(&members[1]);
10492        assert_eq!(f1.name.name, "y");
10493        assert_eq!(
10494            ast.try_expr(f1.value).unwrap().kind,
10495            crate::ExprKind::Number(2.0)
10496        );
10497    }
10498
10499    #[test]
10500    fn parse_object_literal_string_key_shorthand_rejected() {
10501        let (_ast, diags) = parse_str(r#"let p = { "x" };"#);
10502        assert!(
10503            diags
10504                .iter()
10505                .any(|d| d.message.contains("expected `:` after field name")),
10506            "string-key shorthand should be rejected: {diags:?}"
10507        );
10508    }
10509
10510    #[test]
10511    fn parse_object_literal_trailing_comma() {
10512        let (ast, diags) = parse_str("let p = { x: 1, y: 2, };");
10513        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10514        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10515            panic!("expected Let");
10516        };
10517        assert_eq!(object_literal(ast.try_expr(value).unwrap()).len(), 2);
10518    }
10519
10520    #[test]
10521    fn parse_object_literal_duplicate_key_diagnoses() {
10522        let (_, diags) = parse_str("let p = { x: 1, x: 2 };");
10523        assert_eq!(diags.len(), 1);
10524        assert_eq!(diags[0].message, "duplicate field `x` in object literal");
10525    }
10526
10527    #[test]
10528    fn parse_empty_array_literal() {
10529        let (ast, diags) = parse_str("let xs = [];");
10530        assert!(diags.is_empty());
10531        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10532            panic!("expected Let");
10533        };
10534        assert!(array_literal(ast.try_expr(value).unwrap()).is_empty());
10535    }
10536
10537    #[test]
10538    fn parse_array_literal_simple() {
10539        let (ast, diags) = parse_str("let xs = [1, 2, 3];");
10540        assert!(diags.is_empty());
10541        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10542            panic!("expected Let");
10543        };
10544        let elements = array_literal(ast.try_expr(value).unwrap());
10545        assert_eq!(elements.len(), 3);
10546        assert_eq!(
10547            ast.try_expr(array_literal_value(&elements[0]))
10548                .unwrap()
10549                .kind,
10550            crate::ExprKind::Number(1.0)
10551        );
10552        assert_eq!(
10553            ast.try_expr(array_literal_value(&elements[2]))
10554                .unwrap()
10555                .kind,
10556            crate::ExprKind::Number(3.0)
10557        );
10558    }
10559
10560    #[test]
10561    fn parse_array_literal_trailing_comma() {
10562        let (ast, diags) = parse_str("let xs = [1, 2,];");
10563        assert!(diags.is_empty());
10564        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10565            panic!("expected Let");
10566        };
10567        assert_eq!(array_literal(ast.try_expr(value).unwrap()).len(), 2);
10568    }
10569
10570    #[test]
10571    fn parse_nested_array_in_object() {
10572        let (ast, diags) = parse_str("let p = { items: [1, 2] };");
10573        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10574        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10575            panic!("expected Let");
10576        };
10577        let members = object_literal(ast.try_expr(value).unwrap());
10578        let f0 = object_literal_field(&members[0]);
10579        assert_eq!(f0.name.name, "items");
10580        let inner = array_literal(ast.try_expr(f0.value).unwrap());
10581        assert_eq!(inner.len(), 2);
10582    }
10583
10584    #[test]
10585    fn parse_nested_object_in_array() {
10586        let (ast, diags) = parse_str("let xs = [{ x: 1 }, { x: 2 }];");
10587        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10588        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10589            panic!("expected Let");
10590        };
10591        let elements = array_literal(ast.try_expr(value).unwrap());
10592        assert_eq!(elements.len(), 2);
10593        assert_eq!(
10594            object_literal(ast.try_expr(array_literal_value(&elements[0])).unwrap()).len(),
10595            1
10596        );
10597        assert_eq!(
10598            object_literal(ast.try_expr(array_literal_value(&elements[1])).unwrap()).len(),
10599            1
10600        );
10601    }
10602
10603    #[test]
10604    fn brace_at_statement_start_is_block_not_object() {
10605        let (_, diags) = parse_str("{ x: 1 }");
10606        assert!(
10607            !diags.is_empty(),
10608            "expected a diagnostic for `x:` inside block"
10609        );
10610    }
10611
10612    fn field_access(e: &crate::Expr) -> (crate::ExprId, &str) {
10613        match e.kind {
10614            crate::ExprKind::FieldAccess { receiver, ref name } => (receiver, name.name.as_str()),
10615            _ => panic!("expected FieldAccess, got {:?}", e.kind),
10616        }
10617    }
10618
10619    fn index_access(e: &crate::Expr) -> (crate::ExprId, crate::ExprId) {
10620        match e.kind {
10621            crate::ExprKind::IndexAccess { receiver, index } => (receiver, index),
10622            _ => panic!("expected IndexAccess, got {:?}", e.kind),
10623        }
10624    }
10625
10626    #[test]
10627    fn parse_field_access_simple() {
10628        let (ast, diags) = parse_str("a.b;");
10629        assert!(diags.is_empty());
10630        let outer = expr_of_single_stmt(&ast);
10631        let (receiver, name) = field_access(outer);
10632        assert!(matches!(
10633            ast.try_expr(receiver).unwrap().kind,
10634            crate::ExprKind::Identifier(_)
10635        ));
10636        assert_eq!(name, "b");
10637    }
10638
10639    #[test]
10640    fn parse_field_access_keyword_name() {
10641        let (ast, diags) = parse_str("status.type.default.null;");
10642        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10643        let outer = expr_of_single_stmt(&ast);
10644        let (receiver, name) = field_access(outer);
10645        assert_eq!(name, "null");
10646        let (receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10647        assert_eq!(name, "default");
10648        let (_receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10649        assert_eq!(name, "type");
10650    }
10651
10652    #[test]
10653    fn parse_field_access_chain_left_assoc() {
10654        let (ast, diags) = parse_str("a.b.c;");
10655        assert!(diags.is_empty());
10656        let outer = expr_of_single_stmt(&ast);
10657        let (mid_id, c) = field_access(outer);
10658        assert_eq!(c, "c");
10659        let (a_id, b) = field_access(ast.try_expr(mid_id).unwrap());
10660        assert_eq!(b, "b");
10661        assert!(matches!(
10662            ast.try_expr(a_id).unwrap().kind,
10663            crate::ExprKind::Identifier(_)
10664        ));
10665    }
10666
10667    #[test]
10668    fn parse_index_access_simple() {
10669        let (ast, diags) = parse_str("a[0];");
10670        assert!(diags.is_empty());
10671        let outer = expr_of_single_stmt(&ast);
10672        let (receiver, idx) = index_access(outer);
10673        assert!(matches!(
10674            ast.try_expr(receiver).unwrap().kind,
10675            crate::ExprKind::Identifier(_)
10676        ));
10677        assert_eq!(
10678            ast.try_expr(idx).unwrap().kind,
10679            crate::ExprKind::Number(0.0)
10680        );
10681    }
10682
10683    #[test]
10684    fn parse_index_access_chain() {
10685        let (ast, diags) = parse_str("a[0][1];");
10686        assert!(diags.is_empty());
10687        let outer = expr_of_single_stmt(&ast);
10688        let (mid_id, one) = index_access(outer);
10689        assert_eq!(
10690            ast.try_expr(one).unwrap().kind,
10691            crate::ExprKind::Number(1.0)
10692        );
10693        let (a_id, zero) = index_access(ast.try_expr(mid_id).unwrap());
10694        assert_eq!(
10695            ast.try_expr(zero).unwrap().kind,
10696            crate::ExprKind::Number(0.0)
10697        );
10698        assert!(matches!(
10699            ast.try_expr(a_id).unwrap().kind,
10700            crate::ExprKind::Identifier(_)
10701        ));
10702    }
10703
10704    #[test]
10705    fn parse_mixed_access_and_call_chain() {
10706        let (ast, diags) = parse_str("a.b[c].d();");
10707        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10708        let outer = expr_of_single_stmt(&ast);
10709        let (callee, args) = call(outer);
10710        assert!(args.is_empty());
10711        let (idx_id, d_name) = field_access(ast.try_expr(callee).unwrap());
10712        assert_eq!(d_name, "d");
10713        let (b_id, c_idx) = index_access(ast.try_expr(idx_id).unwrap());
10714        assert!(matches!(
10715            ast.try_expr(c_idx).unwrap().kind,
10716            crate::ExprKind::Identifier(_)
10717        ));
10718        let (a_id, b_name) = field_access(ast.try_expr(b_id).unwrap());
10719        assert_eq!(b_name, "b");
10720        assert!(matches!(
10721            ast.try_expr(a_id).unwrap().kind,
10722            crate::ExprKind::Identifier(_)
10723        ));
10724    }
10725
10726    #[test]
10727    fn parse_call_then_field() {
10728        let (ast, diags) = parse_str("f().x;");
10729        assert!(diags.is_empty());
10730        let outer = expr_of_single_stmt(&ast);
10731        let (call_id, x_name) = field_access(outer);
10732        assert_eq!(x_name, "x");
10733        let (_callee, args) = call(ast.try_expr(call_id).unwrap());
10734        assert!(args.is_empty());
10735    }
10736
10737    #[test]
10738    fn parse_index_with_complex_expression() {
10739        let (ast, diags) = parse_str("a[i + 1];");
10740        assert!(diags.is_empty());
10741        let outer = expr_of_single_stmt(&ast);
10742        let (_, idx) = index_access(outer);
10743        let (op, ..) = binary(ast.try_expr(idx).unwrap());
10744        assert_eq!(op, crate::BinOp::Add);
10745    }
10746
10747    #[test]
10748    fn field_access_missing_name_diagnoses() {
10749        let (_, diags) = parse_str("a.;");
10750        assert!(!diags.is_empty());
10751        assert_eq!(diags[0].message, "expected field name after `.`");
10752    }
10753
10754    #[test]
10755    fn parse_postfix_increment() {
10756        let (ast, diags) = parse_str("x++;");
10757        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10758        let expr = expr_of_single_stmt(&ast);
10759        let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10760            panic!("expected PostfixUnary, got {:?}", expr.kind);
10761        };
10762        assert_eq!(op, crate::PostfixOp::Inc);
10763        assert!(matches!(
10764            ast.try_expr(operand).unwrap().kind,
10765            crate::ExprKind::Identifier(_)
10766        ));
10767        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
10768    }
10769
10770    #[test]
10771    fn parse_postfix_decrement() {
10772        let (ast, diags) = parse_str("x--;");
10773        assert!(diags.is_empty());
10774        let expr = expr_of_single_stmt(&ast);
10775        let crate::ExprKind::PostfixUnary { op, .. } = expr.kind else {
10776            panic!("expected PostfixUnary");
10777        };
10778        assert_eq!(op, crate::PostfixOp::Dec);
10779    }
10780
10781    #[test]
10782    fn parse_postfix_non_null_assertion() {
10783        let (ast, diags) = parse_str("x!;");
10784        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10785        let expr = expr_of_single_stmt(&ast);
10786        let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10787            panic!("expected PostfixUnary, got {:?}", expr.kind);
10788        };
10789        assert_eq!(op, crate::PostfixOp::NonNullAssert);
10790        assert!(matches!(
10791            ast.try_expr(operand).unwrap().kind,
10792            crate::ExprKind::Identifier(_)
10793        ));
10794        assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
10795    }
10796
10797    #[test]
10798    fn parse_non_null_assertion_continues_chain() {
10799        let (ast, diags) = parse_str("x!.y;");
10800        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10801        let expr = expr_of_single_stmt(&ast);
10802        let crate::ExprKind::FieldAccess { receiver, name } = &expr.kind else {
10803            panic!("expected FieldAccess, got {:?}", expr.kind);
10804        };
10805        assert_eq!(name.name, "y");
10806        assert!(matches!(
10807            ast.try_expr(*receiver).unwrap().kind,
10808            crate::ExprKind::PostfixUnary {
10809                op: crate::PostfixOp::NonNullAssert,
10810                ..
10811            }
10812        ));
10813    }
10814
10815    #[test]
10816    fn parse_postfix_on_field_access() {
10817        let (ast, diags) = parse_str("o.f++;");
10818        assert!(diags.is_empty());
10819        let expr = expr_of_single_stmt(&ast);
10820        let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
10821            panic!("expected PostfixUnary");
10822        };
10823        assert!(matches!(
10824            ast.try_expr(operand).unwrap().kind,
10825            crate::ExprKind::FieldAccess { .. }
10826        ));
10827    }
10828
10829    #[test]
10830    fn parse_postfix_on_index_access() {
10831        let (ast, diags) = parse_str("xs[0]++;");
10832        assert!(diags.is_empty());
10833        let expr = expr_of_single_stmt(&ast);
10834        let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
10835            panic!("expected PostfixUnary");
10836        };
10837        assert!(matches!(
10838            ast.try_expr(operand).unwrap().kind,
10839            crate::ExprKind::IndexAccess { .. }
10840        ));
10841    }
10842
10843    #[test]
10844    fn parse_postfix_terminates_chain() {
10845        let (_, diags) = parse_str("x++.y;");
10846        assert!(!diags.is_empty());
10847        assert_eq!(diags[0].message, "expected `;` after expression");
10848    }
10849
10850    #[test]
10851    fn parse_postfix_in_binary_continues() {
10852        let (ast, diags) = parse_str("let y = x++ + 1;");
10853        assert!(diags.is_empty(), "unexpected: {diags:?}");
10854        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10855            panic!("expected Let");
10856        };
10857        let (op, lhs, _) = binary(ast.try_expr(value).unwrap());
10858        assert_eq!(op, crate::BinOp::Add);
10859        assert!(matches!(
10860            ast.try_expr(lhs).unwrap().kind,
10861            crate::ExprKind::PostfixUnary { .. }
10862        ));
10863    }
10864
10865    #[test]
10866    fn parse_index_assignment() {
10867        let (ast, diags) = parse_str("xs[0] = 1;");
10868        assert!(diags.is_empty(), "unexpected: {diags:?}");
10869        let stmt = single_stmt(&ast);
10870        let crate::StmtKind::AssignIndex {
10871            receiver,
10872            index,
10873            value,
10874        } = stmt.kind
10875        else {
10876            panic!("expected AssignIndex, got {:?}", stmt.kind);
10877        };
10878        assert!(matches!(
10879            ast.try_expr(receiver).unwrap().kind,
10880            crate::ExprKind::Identifier(_)
10881        ));
10882        assert_eq!(
10883            ast.try_expr(index).unwrap().kind,
10884            crate::ExprKind::Number(0.0)
10885        );
10886        assert_eq!(
10887            ast.try_expr(value).unwrap().kind,
10888            crate::ExprKind::Number(1.0)
10889        );
10890    }
10891
10892    #[test]
10893    fn parse_for_update_postfix() {
10894        let (ast, diags) = parse_str("function main(): void { for (let i = 0; i < 3; i++) {} }");
10895        assert!(diags.is_empty(), "unexpected: {diags:?}");
10896        assert_eq!(ast.top_level.len(), 1);
10897    }
10898
10899    #[test]
10900    fn parse_for_update_index_assignment() {
10901        let (ast, diags) =
10902            parse_str("function main(): void { for (let i = 0; i < 3; xs[i] = i) {} }");
10903        assert!(diags.is_empty(), "unexpected: {diags:?}");
10904        assert_eq!(ast.top_level.len(), 1);
10905    }
10906
10907    #[test]
10908    fn index_access_missing_close_bracket_diagnoses() {
10909        let (_, diags) = parse_str("a[1;");
10910        assert!(!diags.is_empty());
10911        assert_eq!(diags[0].message, "expected `]`");
10912    }
10913
10914    #[test]
10915    fn field_access_inside_object_literal_value_does_not_steal_dot() {
10916        let (ast, diags) = parse_str("let x = { a: 1 }.foo;");
10917        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10918        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10919            panic!("expected Let");
10920        };
10921        let (recv_id, name) = field_access(ast.try_expr(value).unwrap());
10922        assert_eq!(name, "foo");
10923        assert_eq!(object_literal(ast.try_expr(recv_id).unwrap()).len(), 1);
10924    }
10925
10926    fn type_of_let(stmt: &crate::Stmt) -> &crate::TypeAnnotation {
10927        match stmt.kind {
10928            crate::StmtKind::Let { ref ty, .. } => ty.as_ref().expect("expected type annotation"),
10929            _ => panic!("expected Let"),
10930        }
10931    }
10932
10933    #[test]
10934    fn parse_array_type_annotation() {
10935        let (ast, diags) = parse_str("let xs: number[] = null;");
10936        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10937        let ty = type_of_let(single_stmt(&ast));
10938        match ty.kind {
10939            crate::TypeAnnotationKind::Array(ref inner) => {
10940                assert!(matches!(inner.kind, crate::TypeAnnotationKind::Name { .. }));
10941            }
10942            _ => panic!("expected Array, got {:?}", ty.kind),
10943        }
10944    }
10945
10946    #[test]
10947    fn parse_nested_array_type_annotation() {
10948        let (ast, diags) = parse_str("let xs: number[][] = null;");
10949        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10950        let ty = type_of_let(single_stmt(&ast));
10951        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
10952            panic!("expected outer Array");
10953        };
10954        let crate::TypeAnnotationKind::Array(ref inner2) = inner.kind else {
10955            panic!("expected inner Array");
10956        };
10957        assert!(matches!(
10958            inner2.kind,
10959            crate::TypeAnnotationKind::Name { .. }
10960        ));
10961    }
10962
10963    #[test]
10964    fn parse_object_type_annotation_simple() {
10965        let (ast, diags) = parse_str("let p: { x: number; y: number } = null;");
10966        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10967        let ty = type_of_let(single_stmt(&ast));
10968        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10969            panic!("expected Object, got {:?}", ty.kind);
10970        };
10971        assert_eq!(fields.len(), 2);
10972        assert_eq!(fields[0].name.name, "x");
10973        assert_eq!(fields[1].name.name, "y");
10974    }
10975
10976    #[test]
10977    fn readonly_is_rejected_on_methods_accessors_and_method_signatures() {
10978        let source = "class K {\n\
10979             readonly m(): number { return 1; }\n\
10980             public readonly n<T>(): number { return 1; }\n\
10981             readonly get g(): number { return 1; }\n\
10982             readonly p: number = 1;\n\
10983             }\n\
10984             type T = { readonly r(a: number): boolean; readonly f: () => void };\n\
10985             interface I { readonly s(): void; readonly q: number; }\n";
10986        let (_, diags) = parse_str(source);
10987        let lines: Vec<usize> = diags
10988            .iter()
10989            .map(|d| {
10990                assert_eq!(
10991                    d.message,
10992                    "`readonly` can only modify a property or index signature"
10993                );
10994                source[..d.span.start as usize].matches('\n').count() + 1
10995            })
10996            .collect();
10997        assert_eq!(lines, vec![2, 3, 4, 7, 8]);
10998    }
10999
11000    #[test]
11001    fn parse_object_type_method_members() {
11002        // `m(): T` is the same member as `m: () => T`, so it parses to a function-typed
11003        // field — including through the `?` modifier.
11004        let (ast, diags) =
11005            parse_str("let p: { m(): number; opt?(): string; r(a: number): boolean } = null;");
11006        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11007        let ty = type_of_let(single_stmt(&ast));
11008        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11009            panic!("expected Object, got {:?}", ty.kind);
11010        };
11011        assert_eq!(fields.len(), 3);
11012        for field in fields {
11013            assert!(
11014                matches!(field.ty.kind, crate::TypeAnnotationKind::Function { .. }),
11015                "member `{}` should be function-typed, got {:?}",
11016                field.name.name,
11017                field.ty.kind,
11018            );
11019        }
11020        assert!(fields[1].optional, "`opt?()` is an optional member");
11021        let crate::TypeAnnotationKind::Function { ref params, .. } = fields[2].ty.kind else {
11022            unreachable!("checked above");
11023        };
11024        assert_eq!(params.len(), 1);
11025        assert_eq!(params[0].name.name, "a");
11026    }
11027
11028    #[test]
11029    fn parse_object_type_annotation_with_commas() {
11030        let (ast, diags) = parse_str("let p: { x: number, y: number } = null;");
11031        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11032        let ty = type_of_let(single_stmt(&ast));
11033        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11034            panic!("expected Object");
11035        };
11036        assert_eq!(fields.len(), 2);
11037    }
11038
11039    #[test]
11040    fn parse_object_type_annotation_empty() {
11041        let (ast, diags) = parse_str("let p: {} = null;");
11042        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11043        let ty = type_of_let(single_stmt(&ast));
11044        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11045            panic!("expected Object");
11046        };
11047        assert!(fields.is_empty());
11048    }
11049
11050    #[test]
11051    fn parse_object_type_with_optional_field() {
11052        let (ast, diags) = parse_str("let p: { id: number; nick?: string } = null;");
11053        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11054        let ty = type_of_let(single_stmt(&ast));
11055        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11056            panic!("expected Object");
11057        };
11058        assert_eq!(fields.len(), 2);
11059        assert_eq!(fields[0].name.name, "id");
11060        assert!(!fields[0].optional);
11061        assert_eq!(fields[1].name.name, "nick");
11062        assert!(fields[1].optional);
11063    }
11064
11065    #[test]
11066    fn parse_object_type_with_readonly_fields() {
11067        let (ast, diags) = parse_str(
11068            "let issue: { readonly id: string; readonly title?: string; body: string } = null;",
11069        );
11070        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11071        let ty = type_of_let(single_stmt(&ast));
11072        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11073            panic!("expected Object");
11074        };
11075        let fields: Vec<(&str, bool, bool)> = fields
11076            .iter()
11077            .map(|field| (field.name.name.as_str(), field.optional, field.readonly))
11078            .collect();
11079        assert_eq!(
11080            fields,
11081            vec![
11082                ("id", false, true),
11083                ("title", true, true),
11084                ("body", false, false),
11085            ]
11086        );
11087    }
11088
11089    #[test]
11090    fn parse_object_type_mixed_optional_and_required() {
11091        let (ast, diags) = parse_str("let p: { a?: number; b: string; c?: boolean } = null;");
11092        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11093        let ty = type_of_let(single_stmt(&ast));
11094        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11095            panic!("expected Object");
11096        };
11097        let opt: Vec<(&str, bool)> = fields
11098            .iter()
11099            .map(|f| (f.name.name.as_str(), f.optional))
11100            .collect();
11101        assert_eq!(opt, vec![("a", true), ("b", false), ("c", true)]);
11102    }
11103
11104    #[test]
11105    fn parse_object_type_keyword_fields() {
11106        let (ast, diags) =
11107            parse_str("let p: { type: string; default?: number; null: boolean } = null;");
11108        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11109        let ty = type_of_let(single_stmt(&ast));
11110        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11111            panic!("expected Object");
11112        };
11113        let opt: Vec<(&str, bool)> = fields
11114            .iter()
11115            .map(|f| (f.name.name.as_str(), f.optional))
11116            .collect();
11117        assert_eq!(
11118            opt,
11119            vec![("type", false), ("default", true), ("null", false)]
11120        );
11121    }
11122
11123    #[test]
11124    fn parse_object_type_quoted_fields() {
11125        let (ast, diags) =
11126            parse_str(r#"let p: { "content-type": string; "x\u002drequest-id"?: string } = null;"#);
11127        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11128        let ty = type_of_let(single_stmt(&ast));
11129        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11130            panic!("expected Object");
11131        };
11132        let opt: Vec<(&str, bool)> = fields
11133            .iter()
11134            .map(|f| (f.name.name.as_str(), f.optional))
11135            .collect();
11136        assert_eq!(opt, vec![("content-type", false), ("x-request-id", true)]);
11137    }
11138
11139    #[test]
11140    fn parse_interface_with_optional_property() {
11141        let (ast, diags) = parse_str("interface User { id: number; deletedAt?: string; }");
11142        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11143        let stmt = single_stmt(&ast);
11144        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11145            panic!("expected interface decl");
11146        };
11147        let opt: Vec<(&str, bool)> = members
11148            .iter()
11149            .map(|m| match m {
11150                crate::InterfaceMember::Property { name, optional, .. } => {
11151                    (name.name.as_str(), *optional)
11152                }
11153                _ => panic!("expected property"),
11154            })
11155            .collect();
11156        assert_eq!(opt, vec![("id", false), ("deletedAt", true)]);
11157    }
11158
11159    #[test]
11160    fn parse_interface_with_readonly_properties() {
11161        let (ast, diags) = parse_str(
11162            "interface Issue { readonly id: string; readonly title?: string; body: string; }",
11163        );
11164        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11165        let stmt = single_stmt(&ast);
11166        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11167            panic!("expected interface decl");
11168        };
11169        let opt: Vec<(&str, bool, bool)> = members
11170            .iter()
11171            .map(|member| match member {
11172                crate::InterfaceMember::Property {
11173                    name,
11174                    optional,
11175                    readonly,
11176                    ..
11177                } => (name.name.as_str(), *optional, *readonly),
11178                _ => panic!("expected property"),
11179            })
11180            .collect();
11181        assert_eq!(
11182            opt,
11183            vec![
11184                ("id", false, true),
11185                ("title", true, true),
11186                ("body", false, false),
11187            ]
11188        );
11189    }
11190
11191    #[test]
11192    fn parse_readonly_property_name_without_modifier() {
11193        let (ast, diags) = parse_str(
11194            "interface Meta { readonly: boolean; }\nlet meta: { readonly: boolean } = null;",
11195        );
11196        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11197        let crate::StmtKind::InterfaceDecl { ref members, .. } =
11198            ast.try_stmt(ast.top_level[0]).unwrap().kind
11199        else {
11200            panic!("expected interface decl");
11201        };
11202        let crate::InterfaceMember::Property {
11203            ref name, readonly, ..
11204        } = members[0]
11205        else {
11206            panic!("expected property");
11207        };
11208        assert_eq!(name.name, "readonly");
11209        assert!(
11210            !readonly,
11211            "`readonly` here is the property name, not a modifier"
11212        );
11213
11214        let ty = type_of_let(ast.try_stmt(ast.top_level[1]).unwrap());
11215        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11216            panic!("expected Object");
11217        };
11218        assert_eq!(fields[0].name.name, "readonly");
11219        assert!(!fields[0].readonly);
11220    }
11221
11222    #[test]
11223    fn parse_interface_keyword_members() {
11224        let (ast, diags) =
11225            parse_str("interface IssueStatus { type: string; default(): string; null: boolean; }");
11226        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11227        let stmt = single_stmt(&ast);
11228        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11229            panic!("expected interface decl");
11230        };
11231        let names: Vec<&str> = members
11232            .iter()
11233            .map(|m| match m {
11234                crate::InterfaceMember::Method { name, .. }
11235                | crate::InterfaceMember::Property { name, .. } => name.name.as_str(),
11236                crate::InterfaceMember::IndexSignature(_) => panic!("unexpected index signature"),
11237            })
11238            .collect();
11239        assert_eq!(names, vec!["type", "default", "null"]);
11240    }
11241
11242    #[test]
11243    fn parse_interface_quoted_property_members() {
11244        let (ast, diags) = parse_str(
11245            r#"interface Headers { "content-type": string; "x\u002drequest-id"?: string; }"#,
11246        );
11247        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11248        let stmt = single_stmt(&ast);
11249        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11250            panic!("expected interface decl");
11251        };
11252        let names: Vec<(&str, bool)> = members
11253            .iter()
11254            .map(|m| match m {
11255                crate::InterfaceMember::Property { name, optional, .. } => {
11256                    (name.name.as_str(), *optional)
11257                }
11258                _ => panic!("expected property"),
11259            })
11260            .collect();
11261        assert_eq!(names, vec![("content-type", false), ("x-request-id", true)]);
11262    }
11263
11264    #[test]
11265    fn parse_param_optional_marker() {
11266        let (ast, diags) = parse_str("function f(x?: number): void {}");
11267        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11268        let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
11269            panic!("expected function")
11270        };
11271        assert!(params[0].optional);
11272        assert!(params[0].default.is_none());
11273    }
11274
11275    #[test]
11276    fn parse_interface_optional_method() {
11277        let (ast, diags) = parse_str("interface F { foo?(x?: number): void; }");
11278        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11279        let crate::StmtKind::InterfaceDecl { ref members, .. } = single_stmt(&ast).kind else {
11280            panic!("expected interface")
11281        };
11282        let crate::InterfaceMember::Property { optional, ty, .. } = &members[0] else {
11283            panic!("expected callable property")
11284        };
11285        assert!(*optional);
11286        let crate::TypeAnnotationKind::Function { params, .. } = &ty.kind else {
11287            panic!("expected function type")
11288        };
11289        assert!(params[0].optional);
11290    }
11291
11292    #[test]
11293    fn parse_interface_with_call_signature() {
11294        let (ast, diags) = parse_str("interface F { (s: string): number; }");
11295        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11296        let stmt = single_stmt(&ast);
11297        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11298            panic!("expected interface decl");
11299        };
11300        assert_eq!(members.len(), 1);
11301        match &members[0] {
11302            crate::InterfaceMember::Method { name, params, .. } => {
11303                assert_eq!(name.name, "@call");
11304                assert_eq!(params.len(), 1);
11305                assert_eq!(params[0].name.name, "s");
11306            }
11307            other => panic!("expected method (call signature), got {other:?}"),
11308        }
11309    }
11310
11311    #[test]
11312    fn parse_interface_mixed_call_signature_and_method() {
11313        let (ast, diags) = parse_str("interface F { greet(): string; (x: number): number; }");
11314        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11315        let stmt = single_stmt(&ast);
11316        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11317            panic!("expected interface decl");
11318        };
11319        let names: Vec<&str> = members
11320            .iter()
11321            .map(|m| match m {
11322                crate::InterfaceMember::Method { name, .. } => name.name.as_str(),
11323                _ => "<property>",
11324            })
11325            .collect();
11326        assert_eq!(names, vec!["greet", "@call"]);
11327    }
11328
11329    #[test]
11330    fn parse_interface_rejects_generic_call_signature() {
11331        let (_ast, diags) = parse_str("interface F { <T>(x: T): T; }");
11332        assert!(
11333            diags
11334                .iter()
11335                .any(|d| d.message.contains("generic call signatures")),
11336            "expected generic-call-signature diagnostic, got {diags:?}"
11337        );
11338    }
11339
11340    #[test]
11341    fn parse_object_type_with_array_field() {
11342        let (ast, diags) = parse_str("let p: { xs: number[] } = null;");
11343        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11344        let ty = type_of_let(single_stmt(&ast));
11345        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11346            panic!("expected Object");
11347        };
11348        assert_eq!(fields[0].name.name, "xs");
11349        assert!(matches!(
11350            fields[0].ty.kind,
11351            crate::TypeAnnotationKind::Array(_)
11352        ));
11353    }
11354
11355    #[test]
11356    fn parse_array_of_objects() {
11357        let (ast, diags) = parse_str("let xs: { x: number }[] = null;");
11358        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11359        let ty = type_of_let(single_stmt(&ast));
11360        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11361            panic!("expected Array");
11362        };
11363        assert!(matches!(
11364            inner.kind,
11365            crate::TypeAnnotationKind::Object { .. }
11366        ));
11367    }
11368
11369    #[test]
11370    fn parse_tuple_type_annotation() {
11371        let (ast, diags) = parse_str("let p: [string, number] = null;");
11372        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11373        let ty = type_of_let(single_stmt(&ast));
11374        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11375            panic!("expected Tuple, got {:?}", ty.kind);
11376        };
11377        assert_eq!(elems.len(), 2);
11378        assert!(matches!(
11379            elems[0].kind,
11380            crate::TypeAnnotationKind::Name { .. }
11381        ));
11382        assert!(matches!(
11383            elems[1].kind,
11384            crate::TypeAnnotationKind::Name { .. }
11385        ));
11386    }
11387
11388    #[test]
11389    fn parse_singleton_tuple_type() {
11390        let (ast, diags) = parse_str("let p: [number] = null;");
11391        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11392        let ty = type_of_let(single_stmt(&ast));
11393        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11394            panic!("expected Tuple");
11395        };
11396        assert_eq!(elems.len(), 1);
11397    }
11398
11399    #[test]
11400    fn parse_nested_tuple_type() {
11401        let (ast, diags) = parse_str("let p: [[number, string], boolean] = null;");
11402        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11403        let ty = type_of_let(single_stmt(&ast));
11404        let crate::TypeAnnotationKind::Tuple(ref outer) = ty.kind else {
11405            panic!("expected outer Tuple");
11406        };
11407        assert_eq!(outer.len(), 2);
11408        let crate::TypeAnnotationKind::Tuple(ref inner) = outer[0].kind else {
11409            panic!("expected inner Tuple");
11410        };
11411        assert_eq!(inner.len(), 2);
11412    }
11413
11414    #[test]
11415    fn parse_array_of_tuple_type() {
11416        let (ast, diags) = parse_str("let xs: [string, number][] = null;");
11417        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11418        let ty = type_of_let(single_stmt(&ast));
11419        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11420            panic!("expected outer Array, got {:?}", ty.kind);
11421        };
11422        assert!(matches!(inner.kind, crate::TypeAnnotationKind::Tuple(_)));
11423    }
11424
11425    #[test]
11426    fn parse_trailing_comma_tuple_type() {
11427        let (ast, diags) = parse_str("let p: [number, string,] = null;");
11428        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11429        let ty = type_of_let(single_stmt(&ast));
11430        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11431            panic!("expected Tuple");
11432        };
11433        assert_eq!(elems.len(), 2);
11434    }
11435
11436    #[test]
11437    fn parse_empty_tuple_type_rejected() {
11438        let (_ast, diags) = parse_str("let p: [] = null;");
11439        assert!(
11440            diags.iter().any(|d| d
11441                .message
11442                .contains("tuple types must have at least one element")),
11443            "expected empty-tuple diagnostic, got: {diags:?}"
11444        );
11445    }
11446
11447    #[test]
11448    fn parse_labeled_tuple_drops_labels() {
11449        let (ast, diags) = parse_str("let p: [first: number, string] = null;");
11450        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11451        let ty = type_of_let(single_stmt(&ast));
11452        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11453            panic!("expected Tuple, got {:?}", ty.kind);
11454        };
11455        assert_eq!(elems.len(), 2);
11456        assert!(
11457            elems
11458                .iter()
11459                .all(|e| matches!(e.kind, crate::TypeAnnotationKind::Name { .. }))
11460        );
11461    }
11462
11463    #[test]
11464    fn parse_labeled_optional_tuple_element() {
11465        let (ast, diags) = parse_str("let p: [first: number, second?: string] = null;");
11466        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11467        let ty = type_of_let(single_stmt(&ast));
11468        let crate::TypeAnnotationKind::Tuple(elements) = &ty.kind else {
11469            panic!("expected tuple")
11470        };
11471        assert!(matches!(
11472            elements[1].kind,
11473            crate::TypeAnnotationKind::Optional(_)
11474        ));
11475    }
11476
11477    #[test]
11478    fn parse_readonly_binds_to_the_postfix_type() {
11479        let (ast, diags) = parse_str("let p: readonly number[][] | null = null;");
11480        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11481        let ty = type_of_let(single_stmt(&ast));
11482        let crate::TypeAnnotationKind::Union(ref members) = ty.kind else {
11483            panic!("expected Union, got {:?}", ty.kind);
11484        };
11485        let crate::TypeAnnotationKind::Readonly(ref operand) = members[0].kind else {
11486            panic!("expected Readonly, got {:?}", members[0].kind);
11487        };
11488        let crate::TypeAnnotationKind::Array(ref inner) = operand.kind else {
11489            panic!("expected Array, got {:?}", operand.kind);
11490        };
11491        assert!(matches!(inner.kind, crate::TypeAnnotationKind::Array(_)));
11492    }
11493
11494    #[test]
11495    fn parse_readonly_as_a_type_name_is_not_the_operator() {
11496        let (ast, diags) = parse_str("let p: readonly = null;");
11497        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11498        let ty = type_of_let(single_stmt(&ast));
11499        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11500    }
11501
11502    #[test]
11503    fn parse_generic_one_arg() {
11504        let (ast, diags) = parse_str("let x: Foo<number> = null;");
11505        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11506        let ty = type_of_let(single_stmt(&ast));
11507        let crate::TypeAnnotationKind::Name { ref name, ref args } = ty.kind else {
11508            panic!("expected Name, got {:?}", ty.kind);
11509        };
11510        assert_eq!(name.span, crate::Span::new(F, 7, 10).unwrap());
11511        assert_eq!(ty.span, crate::Span::new(F, 7, 18).unwrap());
11512        assert_eq!(args.len(), 1);
11513        assert!(matches!(
11514            args[0].kind,
11515            crate::TypeAnnotationKind::Name { .. }
11516        ));
11517    }
11518
11519    #[test]
11520    fn parse_generic_two_args() {
11521        let (ast, diags) = parse_str("let m: Map<string, number> = null;");
11522        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11523        let ty = type_of_let(single_stmt(&ast));
11524        let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11525            panic!("expected Name");
11526        };
11527        assert_eq!(args.len(), 2);
11528    }
11529
11530    #[test]
11531    fn parse_generic_nested() {
11532        let (ast, diags) = parse_str("let b: Box<Box<T>> = null;");
11533        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11534        let ty = type_of_let(single_stmt(&ast));
11535        let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11536            panic!("expected outer Name");
11537        };
11538        assert_eq!(args.len(), 1);
11539        let crate::TypeAnnotationKind::Name {
11540            args: ref inner, ..
11541        } = args[0].kind
11542        else {
11543            panic!("expected inner Name");
11544        };
11545        assert_eq!(inner.len(), 1);
11546    }
11547
11548    #[test]
11549    fn parse_generic_array_postfix() {
11550        let (ast, diags) = parse_str("let xs: Foo<number>[] = null;");
11551        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11552        let ty = type_of_let(single_stmt(&ast));
11553        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11554            panic!("expected Array");
11555        };
11556        assert!(
11557            matches!(inner.kind, crate::TypeAnnotationKind::Name { ref args, .. } if args.len() == 1)
11558        );
11559    }
11560
11561    #[test]
11562    fn parse_generic_empty_diagnoses() {
11563        let (_ast, diags) = parse_str("let x: Foo<> = null;");
11564        assert!(
11565            diags
11566                .iter()
11567                .any(|d| d.message.contains("empty generic argument list")),
11568            "expected empty-generic-args diagnostic, got: {diags:?}"
11569        );
11570    }
11571
11572    #[test]
11573    fn parse_generic_trailing_comma_ok() {
11574        let (_ast, diags) = parse_str("let x: Foo<number,> = null;");
11575        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11576    }
11577
11578    #[test]
11579    fn parse_generic_missing_close_diagnoses() {
11580        let (_ast, diags) = parse_str("let x: Foo<number = null;");
11581        assert!(
11582            diags
11583                .iter()
11584                .any(|d| d.message.contains("expected `,` or `>`")),
11585            "expected unterminated-generic diagnostic, got: {diags:?}"
11586        );
11587    }
11588
11589    #[test]
11590    fn parse_qualified_two_segments() {
11591        let (ast, diags) = parse_str("let x: Foo.Bar = null;");
11592        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11593        let ty = type_of_let(single_stmt(&ast));
11594        let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11595            panic!("expected Qualified, got {:?}", ty.kind);
11596        };
11597        assert_eq!(path.len(), 2);
11598        // `Foo` at 7..10, `Bar` at 11..14.
11599        assert_eq!(path[0].span, crate::Span::new(F, 7, 10).unwrap());
11600        assert_eq!(path[1].span, crate::Span::new(F, 11, 14).unwrap());
11601        assert!(args.is_empty());
11602        assert_eq!(ty.span, crate::Span::new(F, 7, 14).unwrap());
11603    }
11604
11605    #[test]
11606    fn parse_qualified_three_segments() {
11607        let (ast, diags) = parse_str("let x: Foo.Bar.Baz = null;");
11608        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11609        let ty = type_of_let(single_stmt(&ast));
11610        let crate::TypeAnnotationKind::Qualified { ref path, .. } = ty.kind else {
11611            panic!("expected Qualified, got {:?}", ty.kind);
11612        };
11613        assert_eq!(path.len(), 3);
11614    }
11615
11616    #[test]
11617    fn parse_qualified_with_generic_args() {
11618        let (ast, diags) = parse_str("let x: Foo.Box<number> = null;");
11619        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11620        let ty = type_of_let(single_stmt(&ast));
11621        let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11622            panic!("expected Qualified, got {:?}", ty.kind);
11623        };
11624        assert_eq!(path.len(), 2);
11625        assert_eq!(args.len(), 1);
11626        assert!(matches!(
11627            args[0].kind,
11628            crate::TypeAnnotationKind::Name { .. }
11629        ));
11630    }
11631
11632    #[test]
11633    fn parse_qualified_with_array_postfix() {
11634        let (ast, diags) = parse_str("let xs: Foo.Bar[] = null;");
11635        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11636        let ty = type_of_let(single_stmt(&ast));
11637        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11638            panic!("expected Array, got {:?}", ty.kind);
11639        };
11640        assert!(matches!(
11641            inner.kind,
11642            crate::TypeAnnotationKind::Qualified { .. }
11643        ));
11644    }
11645
11646    #[test]
11647    fn parse_qualified_trailing_dot_diagnoses() {
11648        let (_ast, diags) = parse_str("let x: Foo. = null;");
11649        assert!(
11650            diags.iter().any(|d| d
11651                .message
11652                .contains("expected identifier after `.` in type name")),
11653            "expected dotted-name diagnostic, got: {diags:?}"
11654        );
11655    }
11656
11657    #[test]
11658    fn parse_qualified_void_root_rejected_dot() {
11659        let (ast, diags) = parse_str("function f(): void.Bar { }");
11660        assert!(!diags.is_empty(), "expected a diagnostic for `void.Bar`");
11661        let _ = ast;
11662    }
11663
11664    fn union_members(ty: &crate::TypeAnnotation) -> &[crate::TypeAnnotation] {
11665        match &ty.kind {
11666            crate::TypeAnnotationKind::Union(members) => members,
11667            other => panic!("expected Union, got {other:?}"),
11668        }
11669    }
11670
11671    #[test]
11672    fn parse_union_two_members() {
11673        let (ast, diags) = parse_str("let x: number | string = null;");
11674        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11675        let ty = type_of_let(single_stmt(&ast));
11676        let members = union_members(ty);
11677        assert_eq!(members.len(), 2);
11678        assert!(matches!(
11679            members[0].kind,
11680            crate::TypeAnnotationKind::Name { .. }
11681        ));
11682        assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); // `number`
11683        assert!(matches!(
11684            members[1].kind,
11685            crate::TypeAnnotationKind::Name { .. }
11686        ));
11687        assert_eq!(members[1].span, crate::Span::new(F, 16, 22).unwrap()); // `string`
11688        assert_eq!(ty.span, crate::Span::new(F, 7, 22).unwrap());
11689    }
11690
11691    #[test]
11692    fn parse_union_three_members() {
11693        let (ast, diags) = parse_str("let x: A | B | C = null;");
11694        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11695        let ty = type_of_let(single_stmt(&ast));
11696        let members = union_members(ty);
11697        assert_eq!(members.len(), 3);
11698        for m in members {
11699            assert!(matches!(m.kind, crate::TypeAnnotationKind::Name { .. }));
11700        }
11701    }
11702
11703    #[test]
11704    fn parse_union_lower_than_array() {
11705        let (ast, diags) = parse_str("let x: number[] | string = null;");
11706        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11707        let ty = type_of_let(single_stmt(&ast));
11708        let members = union_members(ty);
11709        assert_eq!(members.len(), 2);
11710        assert!(
11711            matches!(members[0].kind, crate::TypeAnnotationKind::Array(_)),
11712            "expected first member to be Array, got {:?}",
11713            members[0].kind
11714        );
11715        assert!(matches!(
11716            members[1].kind,
11717            crate::TypeAnnotationKind::Name { .. }
11718        ));
11719    }
11720
11721    #[test]
11722    fn parse_union_with_null_member() {
11723        let (ast, diags) = parse_str("let x: number | null = null;");
11724        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11725        let ty = type_of_let(single_stmt(&ast));
11726        let members = union_members(ty);
11727        assert_eq!(members.len(), 2);
11728        assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); // `number`
11729        assert!(matches!(
11730            members[1].kind,
11731            crate::TypeAnnotationKind::Name { .. }
11732        ));
11733        assert_eq!(members[1].span, crate::Span::new(F, 16, 20).unwrap()); // `null`
11734    }
11735
11736    #[test]
11737    fn parse_union_in_function_return_type() {
11738        let (ast, diags) = parse_str("function f(): number | string { }");
11739        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11740        let stmt = single_stmt(&ast);
11741        match stmt.kind {
11742            crate::StmtKind::Function {
11743                ref return_type, ..
11744            } => {
11745                let return_type = return_type.as_ref().expect("plain return type");
11746                let members = union_members(return_type);
11747                assert_eq!(members.len(), 2);
11748            }
11749            _ => panic!("expected Function"),
11750        }
11751    }
11752
11753    #[test]
11754    fn parse_union_in_function_param_type() {
11755        let (ast, diags) = parse_str("function f(x: number | string): void { }");
11756        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11757        let stmt = single_stmt(&ast);
11758        match stmt.kind {
11759            crate::StmtKind::Function { ref params, .. } => {
11760                let p_ty = params[0].ty.as_ref().expect("param type");
11761                let members = union_members(p_ty);
11762                assert_eq!(members.len(), 2);
11763            }
11764            _ => panic!("expected Function"),
11765        }
11766    }
11767
11768    #[test]
11769    fn parse_union_in_object_field_type() {
11770        let (ast, diags) = parse_str("let p: { x: number | string } = null;");
11771        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11772        let ty = type_of_let(single_stmt(&ast));
11773        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11774            panic!("expected Object, got {:?}", ty.kind);
11775        };
11776        assert_eq!(fields.len(), 1);
11777        let members = union_members(&fields[0].ty);
11778        assert_eq!(members.len(), 2);
11779    }
11780
11781    #[test]
11782    fn parse_union_leading_pipe_is_forgiven() {
11783        let (ast, diags) = parse_str("let x: | number | string = null;");
11784        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11785        let ty = type_of_let(single_stmt(&ast));
11786        let members = union_members(ty);
11787        assert_eq!(members.len(), 2);
11788    }
11789
11790    #[test]
11791    fn parse_union_leading_pipe_single_member_unwraps() {
11792        let (ast, diags) = parse_str("let x: | number = null;");
11793        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11794        let ty = type_of_let(single_stmt(&ast));
11795        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11796    }
11797
11798    #[test]
11799    fn parse_union_trailing_pipe_diagnoses() {
11800        let (_, diags) = parse_str("let x: number | = null;");
11801        assert!(!diags.is_empty());
11802        assert_eq!(diags[0].message, "expected type");
11803    }
11804
11805    #[test]
11806    fn snapshot_union_type_mix() {
11807        let (ast, diags) = parse_str("let xs: number[] | string | null = null;");
11808        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11809        insta::assert_debug_snapshot!(ast);
11810    }
11811
11812    #[test]
11813    fn parse_object_type_duplicate_field_diagnoses() {
11814        let (_, diags) = parse_str("let p: { readonly x: number; x: string } = null;");
11815        assert!(!diags.is_empty());
11816        assert!(
11817            diags
11818                .iter()
11819                .any(|d| d.message == "duplicate field `x` in object type"),
11820            "expected duplicate-field diagnostic, got: {diags:?}"
11821        );
11822    }
11823
11824    #[test]
11825    fn snapshot_object_and_array_literals() {
11826        let (ast, diags) = parse_str(r#"let mix = [{ a: 1, b: "hi" }, {}];"#);
11827        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11828        insta::assert_debug_snapshot!(ast);
11829    }
11830
11831    #[test]
11832    fn snapshot_function_with_while() {
11833        let source = "function loop(n: number): void {\n  while (n > 0) {\n    log(n);\n  }\n}";
11834        let (ast, diags) = parse_str(source);
11835        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11836        insta::assert_debug_snapshot!(ast);
11837    }
11838
11839    fn parse_arrow_const(
11840        source: &str,
11841    ) -> (
11842        Ast,
11843        Vec<crate::ParamDecl>,
11844        Option<crate::TypeAnnotation>,
11845        crate::ArrowBody,
11846    ) {
11847        let (ast, diags) = parse_str(source);
11848        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11849        assert_eq!(ast.top_level.len(), 1);
11850        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11851        let value = match &stmt.kind {
11852            StmtKind::Const { value, .. } => *value,
11853            _ => panic!("expected const declaration"),
11854        };
11855        let value = match ast.try_expr(value).unwrap().kind {
11856            ExprKind::FunctionExpression { function, .. } => function,
11857            _ => value,
11858        };
11859        let expr = ast.try_expr(value).unwrap().clone();
11860        let (params, return_type, body) = match expr.kind {
11861            ExprKind::Arrow {
11862                params,
11863                return_type,
11864                body,
11865                ..
11866            } => (params, return_type, body),
11867            other => panic!("expected Arrow, got {other:?}"),
11868        };
11869        (ast, params, return_type, body)
11870    }
11871
11872    #[test]
11873    fn parses_anonymous_function_expression_as_arrow() {
11874        let (ast, params, return_type, body) =
11875            parse_arrow_const("const f = function (x: number): number { return x + 1; };");
11876        assert_eq!(params.len(), 1);
11877        assert_eq!(params[0].name.name, "x");
11878        assert!(return_type.is_some());
11879        assert!(matches!(body, crate::ArrowBody::Block(_)));
11880        let _ = ast;
11881    }
11882
11883    #[test]
11884    fn parses_function_expression_with_no_params() {
11885        let (_ast, params, _return_type, body) =
11886            parse_arrow_const("const f = function (): void {};");
11887        assert!(params.is_empty());
11888        assert!(matches!(body, crate::ArrowBody::Block(_)));
11889    }
11890
11891    /// The named wrapper preserves its body signature.
11892    #[test]
11893    fn parses_named_function_expression_signature() {
11894        let (_ast, params, _return_type, _body) =
11895            parse_arrow_const("const f = function named(x: number): number { return x + 1; };");
11896        assert_eq!(params.len(), 1);
11897    }
11898
11899    /// Self references are resolved in the function expression scope.
11900    #[test]
11901    fn accepts_a_named_function_expression_that_calls_itself() {
11902        let (_ast, diags) =
11903            parse_str("const f = function bar(x: number): number { return bar(x); };");
11904        assert!(diags.is_empty(), "{diags:?}");
11905    }
11906
11907    /// A member name after `.` is never a reference to the function itself.
11908    #[test]
11909    fn accepts_a_named_function_expression_using_the_name_as_a_property() {
11910        let (_ast, diags) = parse_str("const f = function bar(o: O): number { return o.bar; };");
11911        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11912    }
11913
11914    /// Nor is a key before `:`.
11915    #[test]
11916    fn accepts_a_named_function_expression_using_the_name_as_an_object_key() {
11917        let (_ast, diags) = parse_str(
11918            "const f = function bar(x: number): number { const o = { bar: 1 }; return o.bar + x; };",
11919        );
11920        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11921    }
11922
11923    /// A string that happens to spell the name is not a reference.
11924    #[test]
11925    fn accepts_a_named_function_expression_with_its_name_in_a_string() {
11926        let (_ast, diags) =
11927            parse_str("const f = function bar(x: number): string { return \"bar\"; };");
11928        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11929    }
11930
11931    /// A self reference in a template substitution remains a normal expression.
11932    #[test]
11933    fn accepts_a_self_call_inside_a_template_substitution() {
11934        let (_ast, diags) =
11935            parse_str("const f = function bar(x: number): string { return `${bar(0)}`; };");
11936        assert!(diags.is_empty(), "{diags:?}");
11937    }
11938
11939    #[test]
11940    fn rejects_a_generic_function_expression() {
11941        let (_ast, diags) = parse_str("const f = function <T>(x: T): T { return x; };");
11942        assert!(
11943            diags
11944                .iter()
11945                .any(|d| d.message.contains("generic function expressions")),
11946            "generic function expression should be rejected: {diags:?}"
11947        );
11948    }
11949
11950    #[test]
11951    fn accepts_this_inside_a_function_expression_in_a_class_method() {
11952        let (_ast, diags) = parse_str(
11953            "class C { x: number = 1; m(): number { \
11954             const f = function (this: { x: number }): number { return this.x; }; return f(); } }",
11955        );
11956        assert!(diags.is_empty(), "{diags:?}");
11957    }
11958
11959    #[test]
11960    fn retains_this_boundary_inside_method_shorthand() {
11961        let (ast, diags) = parse_str(
11962            "class C { x: number = 1; m(): number { \
11963             const o = { x: 2, g(): number { return this.x; } }; return o.g(); } }",
11964        );
11965        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11966        assert!((0..ast.exprs_len()).any(|index| matches!(
11967            ast.try_expr(crate::ExprId(index as u32)).unwrap().kind,
11968            ExprKind::ThisOutsideReceiver
11969        )));
11970    }
11971
11972    #[test]
11973    fn this_still_parses_in_an_ordinary_class_method() {
11974        let (_ast, diags) = parse_str("class C { x: number = 1; m(): number { return this.x; } }");
11975        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11976    }
11977
11978    /// `function` is a valid property name; the expression form must not shadow that.
11979    #[test]
11980    fn function_keyword_still_parses_as_an_object_key() {
11981        let (_ast, diags) = parse_str("let o = { function: 1 };");
11982        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11983    }
11984
11985    fn object_literal_member_values(source: &str) -> (Ast, Vec<crate::ExprId>) {
11986        let (ast, diags) = parse_str(source);
11987        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11988        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11989        let value = match &stmt.kind {
11990            StmtKind::Const { value, .. } => *value,
11991            other => panic!("expected a const declaration, got {other:?}"),
11992        };
11993        let members = match &ast.try_expr(value).unwrap().kind {
11994            ExprKind::ObjectLiteral { members } => members
11995                .iter()
11996                .map(crate::ObjectLiteralMember::value)
11997                .collect::<Vec<_>>(),
11998            other => panic!("expected ObjectLiteral, got {other:?}"),
11999        };
12000        (ast, members)
12001    }
12002
12003    #[test]
12004    fn parses_object_literal_method_shorthand_as_arrow() {
12005        let (ast, members) =
12006            object_literal_member_values("const o = { m(x: number): number { return x; } };");
12007        assert_eq!(members.len(), 1);
12008        match &ast.try_expr(members[0]).unwrap().kind {
12009            ExprKind::Arrow { params, body, .. } => {
12010                assert_eq!(params.len(), 1);
12011                assert_eq!(params[0].name.name, "x");
12012                assert!(matches!(body, crate::ArrowBody::Block(_)));
12013            }
12014            other => panic!("expected Arrow, got {other:?}"),
12015        }
12016    }
12017
12018    /// A keyword is a valid method name, just as it is a valid property name.
12019    #[test]
12020    fn parses_method_shorthand_with_a_keyword_name() {
12021        let (ast, members) =
12022            object_literal_member_values("const o = { if(x: number): number { return x; } };");
12023        assert!(matches!(
12024            ast.try_expr(members[0]).unwrap().kind,
12025            ExprKind::Arrow { .. }
12026        ));
12027    }
12028
12029    #[test]
12030    fn parses_method_shorthand_alongside_plain_and_shorthand_properties() {
12031        let (ast, members) =
12032            object_literal_member_values("const o = { a: 1, m(): void {}, b: 2 };");
12033        assert_eq!(members.len(), 3);
12034        assert!(matches!(
12035            ast.try_expr(members[1]).unwrap().kind,
12036            ExprKind::Arrow { .. }
12037        ));
12038    }
12039
12040    #[test]
12041    fn parses_bare_ident_arrow_expression_body() {
12042        let (_ast, params, return_type, body) = parse_arrow_const("const f = x => x + 1;");
12043        assert_eq!(params.len(), 1);
12044        assert_eq!(params[0].name.name, "x");
12045        assert!(params[0].ty.is_none());
12046        assert!(return_type.is_none());
12047        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12048    }
12049
12050    #[test]
12051    fn parses_zero_param_arrow() {
12052        let (_ast, params, return_type, body) = parse_arrow_const("const f = () => 0;");
12053        assert!(params.is_empty());
12054        assert!(return_type.is_none());
12055        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12056    }
12057
12058    #[test]
12059    fn parses_paren_unannotated_arrow() {
12060        let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x) => x;");
12061        assert_eq!(params.len(), 1);
12062        assert_eq!(params[0].name.name, "x");
12063        assert!(params[0].ty.is_none());
12064    }
12065
12066    #[test]
12067    fn parses_typed_param_arrow() {
12068        let (_ast, params, return_type, body) =
12069            parse_arrow_const("const f = (x: number) => x * 2;");
12070        assert_eq!(params.len(), 1);
12071        assert!(params[0].ty.is_some());
12072        assert!(return_type.is_none());
12073        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12074    }
12075
12076    #[test]
12077    fn parses_typed_param_and_return_arrow() {
12078        let (_ast, params, return_type, body) =
12079            parse_arrow_const("const f = (x: number): number => x * 2;");
12080        assert_eq!(params.len(), 1);
12081        assert!(params[0].ty.is_some());
12082        assert!(return_type.is_some());
12083        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12084    }
12085
12086    #[test]
12087    fn parses_multi_param_unannotated_arrow() {
12088        let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x, y) => x + y;");
12089        assert_eq!(params.len(), 2);
12090        assert_eq!(params[0].name.name, "x");
12091        assert_eq!(params[1].name.name, "y");
12092        assert!(params[0].ty.is_none());
12093        assert!(params[1].ty.is_none());
12094    }
12095
12096    #[test]
12097    fn parses_multi_param_typed_arrow() {
12098        let (_ast, params, _return_type, _body) =
12099            parse_arrow_const("const f = (x: number, y: number) => x + y;");
12100        assert_eq!(params.len(), 2);
12101        assert!(params[0].ty.is_some());
12102        assert!(params[1].ty.is_some());
12103    }
12104
12105    #[test]
12106    fn parses_block_body_arrow() {
12107        let (_ast, params, _return_type, body) =
12108            parse_arrow_const("const f = (x: number) => { return x; };");
12109        assert_eq!(params.len(), 1);
12110        assert!(matches!(body, crate::ArrowBody::Block(_)));
12111    }
12112
12113    #[test]
12114    fn rejects_typed_bare_ident_arrow() {
12115        let (_ast, diags) = parse_str("const f = x: number => x;");
12116        assert!(
12117            !diags.is_empty(),
12118            "expected diagnostics for `x: number => …`",
12119        );
12120    }
12121
12122    #[test]
12123    fn arrow_inside_call_argument_parses() {
12124        let (ast, diags) = parse_str("f((x: number) => x + 1, 2);");
12125        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12126        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12127        let call = match &stmt.kind {
12128            StmtKind::Expr(eid) => ast.try_expr(*eid).unwrap(),
12129            _ => panic!("expected expression statement"),
12130        };
12131        let args = match &call.kind {
12132            ExprKind::Call { args, .. } => args.clone(),
12133            other => panic!("expected Call, got {other:?}"),
12134        };
12135        assert_eq!(args.len(), 2);
12136        assert!(matches!(
12137            ast.try_expr(args[0]).unwrap().kind,
12138            ExprKind::Arrow { .. }
12139        ));
12140    }
12141
12142    #[test]
12143    fn function_decl_param_still_requires_annotation() {
12144        let (_ast, diags) = parse_str("function f(x): number { return x; }");
12145        assert!(
12146            diags.iter().any(|d| d.message.contains("type annotation")),
12147            "expected `parameter requires a type annotation` diagnostic, got {diags:?}",
12148        );
12149    }
12150
12151    #[test]
12152    fn paren_expression_still_parses_when_no_arrow() {
12153        let (ast, diags) = parse_str("const a = (1 + 2);");
12154        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12155        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12156        let value = match &stmt.kind {
12157            StmtKind::Const { value, .. } => *value,
12158            _ => panic!("expected const declaration"),
12159        };
12160        assert!(matches!(
12161            ast.try_expr(value).unwrap().kind,
12162            ExprKind::Paren(_)
12163        ));
12164    }
12165
12166    #[test]
12167    fn parse_named_import_single() {
12168        let (ast, diags) = parse_str("import { v4 } from \"submilli:uuid\";");
12169        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12170        assert_eq!(ast.top_level.len(), 1);
12171        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12172        let (module, kind) = match &stmt.kind {
12173            StmtKind::Import { module, kind, .. } => (module, kind),
12174            other => panic!("expected Import, got {other:?}"),
12175        };
12176        assert_eq!(module, "submilli:uuid");
12177        let specs = match kind {
12178            ImportKind::Named(specs) => specs,
12179            other => panic!("expected Named, got {other:?}"),
12180        };
12181        assert_eq!(specs.len(), 1);
12182        assert_eq!(specs[0].imported_name.name, "v4");
12183        assert_eq!(specs[0].local_name.name, "v4");
12184    }
12185
12186    #[test]
12187    fn parse_named_import_multi_and_alias() {
12188        let (ast, diags) =
12189            parse_str("import { v4, v7 as makeId, validate } from \"submilli:uuid\";");
12190        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12191        let specs = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12192            StmtKind::Import {
12193                kind: ImportKind::Named(s),
12194                ..
12195            } => s.clone(),
12196            other => panic!("expected named import, got {other:?}"),
12197        };
12198        assert_eq!(specs.len(), 3);
12199        assert_eq!(specs[0].imported_name.name, "v4");
12200        assert_eq!(specs[0].local_name.name, "v4");
12201        assert_eq!(specs[1].imported_name.name, "v7");
12202        assert_eq!(specs[1].local_name.name, "makeId");
12203        assert_eq!(specs[2].imported_name.name, "validate");
12204        assert_eq!(specs[2].local_name.name, "validate");
12205    }
12206
12207    #[test]
12208    fn parse_namespace_import() {
12209        let (ast, diags) = parse_str("import uuid from \"submilli:uuid\";");
12210        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12211        let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12212            StmtKind::Import { kind, .. } => kind.clone(),
12213            other => panic!("expected import, got {other:?}"),
12214        };
12215        let local = match kind {
12216            ImportKind::Namespace { local_name } => local_name,
12217            other => panic!("expected Namespace, got {other:?}"),
12218        };
12219        assert_eq!(local.name, "uuid");
12220    }
12221
12222    #[test]
12223    fn parse_wildcard_namespace_import_synonym() {
12224        let (ast, diags) = parse_str("import * as uuid from \"submilli:uuid\";");
12225        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12226        let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12227            StmtKind::Import { kind, .. } => kind.clone(),
12228            other => panic!("expected import, got {other:?}"),
12229        };
12230        let local = match kind {
12231            ImportKind::Namespace { local_name } => local_name,
12232            other => panic!("expected Namespace, got {other:?}"),
12233        };
12234        assert_eq!(local.name, "uuid");
12235    }
12236
12237    #[test]
12238    fn parse_wildcard_namespace_import_without_as_rejected() {
12239        let (_ast, diags) = parse_str("import * from \"submilli:uuid\";");
12240        assert!(
12241            diags
12242                .iter()
12243                .any(|d| d.message.contains("expected `as <name>`")),
12244            "expected missing-as diagnostic, got: {diags:?}",
12245        );
12246    }
12247
12248    #[test]
12249    fn parse_combined_default_and_named_rejected() {
12250        let (_ast, diags) = parse_str("import uuid, { v4 } from \"submilli:uuid\";");
12251        assert!(
12252            diags
12253                .iter()
12254                .any(|d| d.message.contains("combining default and named")),
12255            "expected combined-import diagnostic, got: {diags:?}",
12256        );
12257    }
12258
12259    #[test]
12260    fn parse_side_effect_import_rejected() {
12261        let (_ast, diags) = parse_str("import \"submilli:uuid\";");
12262        assert!(
12263            diags
12264                .iter()
12265                .any(|d| d.message.contains("side-effect-only imports")),
12266            "expected side-effect-import diagnostic, got: {diags:?}",
12267        );
12268    }
12269
12270    #[test]
12271    fn parse_empty_specifier_list_rejected() {
12272        let (_ast, diags) = parse_str("import {} from \"submilli:uuid\";");
12273        assert!(
12274            diags
12275                .iter()
12276                .any(|d| d.message.contains("empty import specifier list")),
12277            "expected empty-specifier diagnostic, got: {diags:?}",
12278        );
12279    }
12280
12281    #[test]
12282    fn parse_import_inside_function_body_rejected() {
12283        let (_ast, diags) =
12284            parse_str("function main(): void { import { v4 } from \"submilli:uuid\"; }");
12285        assert!(
12286            diags
12287                .iter()
12288                .any(|d| d.message.contains("must appear at the top of the file")),
12289            "expected top-level diagnostic, got: {diags:?}",
12290        );
12291    }
12292
12293    #[test]
12294    fn parse_missing_from_rejected() {
12295        let (_ast, diags) = parse_str("import { v4 } \"submilli:uuid\";");
12296        assert!(
12297            diags.iter().any(|d| d.message.contains("expected `from`")),
12298            "expected missing-from diagnostic, got: {diags:?}",
12299        );
12300    }
12301
12302    #[test]
12303    fn parse_missing_module_specifier_rejected() {
12304        let (_ast, diags) = parse_str("import { v4 } from;");
12305        assert!(
12306            diags
12307                .iter()
12308                .any(|d| d.message.contains("expected module specifier")),
12309            "expected missing-module diagnostic, got: {diags:?}",
12310        );
12311    }
12312
12313    #[test]
12314    fn parse_export_function_marks_exported() {
12315        // `export function main() {}` parses to a single function declaration in
12316        // `top_level`, plus an `exported_decls` entry pointing at it.
12317        let (ast, diags) = parse_str("export function main(): void {}");
12318        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12319        let stmt = single_stmt(&ast);
12320        assert!(
12321            matches!(stmt.kind, crate::StmtKind::Function { .. }),
12322            "expected Function decl, got {:?}",
12323            stmt.kind,
12324        );
12325        assert_eq!(ast.exported_decls.len(), 1, "one exported decl");
12326        assert_eq!(ast.exported_decls[0].stmt, ast.top_level[0]);
12327    }
12328
12329    #[test]
12330    fn parse_export_const_type_interface_enum_are_marked() {
12331        for src in [
12332            "export const X: number = 1;",
12333            "export type Id = string;",
12334            "export interface P { x: number; }",
12335            "export enum E { A, B }",
12336        ] {
12337            let (ast, diags) = parse_str(src);
12338            assert!(diags.is_empty(), "unexpected diags for {src:?}: {diags:?}");
12339            assert_eq!(ast.top_level.len(), 1, "one decl for {src:?}");
12340            assert_eq!(ast.exported_decls.len(), 1, "one exported decl for {src:?}");
12341        }
12342    }
12343
12344    #[test]
12345    fn parse_export_default_is_rejected_with_focused_message() {
12346        let (_ast, diags) = parse_str("export default function main(): void {}");
12347        assert!(
12348            diags
12349                .iter()
12350                .any(|d| d.message.contains("`export default` is not supported")),
12351            "expected focused export-default diagnostic, got: {diags:?}",
12352        );
12353        assert!(
12354            !diags.iter().any(|d| d.message.contains("expected `;`")),
12355            "the spurious missing-semicolon error must be gone: {diags:?}",
12356        );
12357    }
12358
12359    #[test]
12360    fn parse_export_list_parses_as_export_from() {
12361        // Form 2 (`export { x };`, no `from`) now parses cleanly — the
12362        // single-file gating happens in the typechecker, not the parser.
12363        let (ast, diags) = parse_str("function main(): void {}\nexport { main };");
12364        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12365        assert_eq!(ast.top_level.len(), 2, "function + export-from");
12366        let export = ast.try_stmt(ast.top_level[1]).unwrap();
12367        let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12368            panic!("expected ExportFrom, got {:?}", export.kind);
12369        };
12370        assert!(source.is_none(), "bare `export {{ x }}` has no source");
12371        assert_eq!(specs.len(), 1);
12372        assert_eq!(specs[0].imported_name.name, "main");
12373        assert_eq!(specs[0].local_name.name, "main");
12374    }
12375
12376    #[test]
12377    fn parse_export_from_with_source_and_alias_parses() {
12378        let (ast, diags) = parse_str("export { foo, bar as baz } from \"./util\";");
12379        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12380        let export = ast.try_stmt(ast.top_level[0]).unwrap();
12381        let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12382            panic!("expected ExportFrom, got {:?}", export.kind);
12383        };
12384        let Some((module, _)) = source else {
12385            panic!("expected a `from` source");
12386        };
12387        assert_eq!(module, "./util");
12388        assert_eq!(specs.len(), 2);
12389        assert_eq!(specs[1].imported_name.name, "bar");
12390        assert_eq!(specs[1].local_name.name, "baz");
12391    }
12392
12393    #[test]
12394    fn parse_export_inside_function_body_rejected() {
12395        let (_ast, diags) = parse_str("function main(): void { export const X: number = 1; }");
12396        assert!(
12397            diags
12398                .iter()
12399                .any(|d| d.message.contains("must appear at the top of the file")),
12400            "expected top-level diagnostic, got: {diags:?}",
12401        );
12402    }
12403
12404    #[test]
12405    fn parse_throw_with_expression() {
12406        let (ast, diags) = parse_str(r#"throw new Error("oops");"#);
12407        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12408        let stmt = single_stmt(&ast);
12409        let crate::StmtKind::Throw { value } = stmt.kind else {
12410            panic!("expected Throw, got {:?}", stmt.kind);
12411        };
12412        assert!(matches!(
12413            ast.try_expr(value).unwrap().kind,
12414            crate::ExprKind::New { .. }
12415        ));
12416    }
12417
12418    #[test]
12419    fn parse_new_with_dotted_callee() {
12420        let (ast, diags) = parse_str("new Temporal.Duration({});");
12421        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12422        let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12423            panic!("expected expression statement");
12424        };
12425        let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12426            panic!("expected New");
12427        };
12428        let crate::ExprKind::FieldAccess { name, .. } = &ast.try_expr(*callee).unwrap().kind else {
12429            panic!("expected FieldAccess callee");
12430        };
12431        assert_eq!(name.name, "Duration");
12432    }
12433
12434    #[test]
12435    fn parse_new_with_three_level_dotted_callee() {
12436        let (ast, diags) = parse_str("new Foo.Bar.Baz();");
12437        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12438        let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12439            panic!("expected expression statement");
12440        };
12441        let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12442            panic!("expected New");
12443        };
12444        let crate::ExprKind::FieldAccess { receiver, name } = &ast.try_expr(*callee).unwrap().kind
12445        else {
12446            panic!("expected outer FieldAccess");
12447        };
12448        assert_eq!(name.name, "Baz");
12449        let crate::ExprKind::FieldAccess { name: mid_name, .. } =
12450            &ast.try_expr(*receiver).unwrap().kind
12451        else {
12452            panic!("expected inner FieldAccess");
12453        };
12454        assert_eq!(mid_name.name, "Bar");
12455    }
12456
12457    #[test]
12458    fn parse_new_with_trailing_dot_diagnoses() {
12459        let (_ast, diags) = parse_str("new Foo.;");
12460        assert!(
12461            diags.iter().any(|d| d
12462                .message
12463                .contains("expected identifier after `.` in constructor name")),
12464            "expected trailing-dot diagnostic, got: {diags:?}",
12465        );
12466    }
12467
12468    #[test]
12469    fn throw_without_expression_diagnoses() {
12470        let (_ast, diags) = parse_str("throw;");
12471        assert!(
12472            diags
12473                .iter()
12474                .any(|d| d.message == "expected expression after `throw`"),
12475            "expected bare-throw diagnostic, got: {diags:?}",
12476        );
12477    }
12478
12479    #[test]
12480    fn parse_try_catch() {
12481        let (ast, diags) = parse_str("try { } catch (e: Error) { }");
12482        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12483        let stmt = single_stmt(&ast);
12484        let crate::StmtKind::Try {
12485            body,
12486            ref catches,
12487            ref finally,
12488        } = stmt.kind
12489        else {
12490            panic!("expected Try, got {:?}", stmt.kind);
12491        };
12492        assert!(matches!(
12493            ast.try_stmt(body).unwrap().kind,
12494            crate::StmtKind::Block(ref v) if v.is_empty()
12495        ));
12496        let [clause] = catches.as_slice() else {
12497            panic!("expected one catch clause, got {catches:?}");
12498        };
12499        assert_eq!(clause.binding.name, "e");
12500        assert!(clause.ty.is_some());
12501        assert!(finally.is_none());
12502    }
12503
12504    #[test]
12505    fn parse_try_finally() {
12506        let (ast, diags) = parse_str("try { } finally { }");
12507        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12508        let stmt = single_stmt(&ast);
12509        let crate::StmtKind::Try {
12510            ref catches,
12511            ref finally,
12512            ..
12513        } = stmt.kind
12514        else {
12515            panic!("expected Try");
12516        };
12517        assert!(catches.is_empty());
12518        let finally_id = finally.expect("finally block");
12519        assert!(matches!(
12520            ast.try_stmt(finally_id).unwrap().kind,
12521            crate::StmtKind::Block(_)
12522        ));
12523    }
12524
12525    #[test]
12526    fn parse_try_catch_finally() {
12527        let (ast, diags) = parse_str("try { } catch (e: Error) { } finally { }");
12528        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12529        let stmt = single_stmt(&ast);
12530        let crate::StmtKind::Try {
12531            ref catches,
12532            ref finally,
12533            ..
12534        } = stmt.kind
12535        else {
12536            panic!("expected Try");
12537        };
12538        assert_eq!(catches.len(), 1);
12539        assert!(finally.is_some());
12540    }
12541
12542    #[test]
12543    fn bare_try_diagnoses() {
12544        let (_ast, diags) = parse_str("try { }");
12545        assert!(
12546            diags
12547                .iter()
12548                .any(|d| d.message == "try requires at least one of `catch` or `finally`"),
12549            "expected bare-try diagnostic, got: {diags:?}",
12550        );
12551    }
12552
12553    #[test]
12554    fn parse_untyped_catch() {
12555        let (ast, diags) = parse_str("try { } catch (e) { }");
12556        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12557        let stmt = single_stmt(&ast);
12558        let crate::StmtKind::Try {
12559            ref catches,
12560            ref finally,
12561            ..
12562        } = stmt.kind
12563        else {
12564            panic!("expected Try");
12565        };
12566        let [clause] = catches.as_slice() else {
12567            panic!("expected one catch clause, got {catches:?}");
12568        };
12569        assert_eq!(clause.binding.name, "e");
12570        assert!(clause.ty.is_none());
12571        assert!(finally.is_none());
12572    }
12573
12574    #[test]
12575    fn parse_multiple_catch_clauses() {
12576        let (ast, diags) = parse_str("try { } catch (e: HttpError) { } catch (f) { }");
12577        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12578        let stmt = single_stmt(&ast);
12579        let crate::StmtKind::Try {
12580            ref catches,
12581            ref finally,
12582            ..
12583        } = stmt.kind
12584        else {
12585            panic!("expected Try");
12586        };
12587        let [first, second] = catches.as_slice() else {
12588            panic!("expected two catch clauses, got {catches:?}");
12589        };
12590        assert_eq!(first.binding.name, "e");
12591        assert!(first.ty.is_some());
12592        assert_eq!(second.binding.name, "f");
12593        assert!(second.ty.is_none());
12594        assert!(finally.is_none());
12595    }
12596
12597    #[test]
12598    fn catch_after_finally_diagnoses() {
12599        let (_ast, diags) = parse_str("try { } finally { } catch (e: Error) { }");
12600        assert!(
12601            !diags.is_empty(),
12602            "expected diagnostic for catch-after-finally"
12603        );
12604    }
12605
12606    #[test]
12607    fn catch_missing_open_paren_diagnoses() {
12608        let (_ast, diags) = parse_str("try { } catch e: Error) { }");
12609        assert!(
12610            diags
12611                .iter()
12612                .any(|d| d.message == "expected `(` after `catch`"),
12613            "expected open-paren diagnostic, got: {diags:?}",
12614        );
12615    }
12616
12617    fn class_members(ast: &Ast) -> &[crate::ClassMember] {
12618        match &single_stmt(ast).kind {
12619            StmtKind::ClassDecl { members, .. } => members,
12620            other => panic!("expected ClassDecl, got {other:?}"),
12621        }
12622    }
12623
12624    #[test]
12625    fn parse_animal_class() {
12626        let src = r#"
12627class Animal {
12628  name: string;
12629  private sound: string;
12630  readonly species: string;
12631
12632  constructor(name: string, sound: string, species: string) {
12633    this.name = name;
12634    this.sound = sound;
12635    this.species = species;
12636  }
12637
12638  speak(): string {
12639    return this.name + " says " + this.sound;
12640  }
12641}
12642"#;
12643        let (ast, diags) = parse_str(src);
12644        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12645        let StmtKind::ClassDecl {
12646            name,
12647            generics,
12648            extends,
12649            implements,
12650            members,
12651            ..
12652        } = &single_stmt(&ast).kind
12653        else {
12654            panic!("expected ClassDecl");
12655        };
12656        assert_eq!(name.name, "Animal");
12657        assert!(generics.is_empty());
12658        assert!(extends.is_none());
12659        assert!(implements.is_empty());
12660        assert_eq!(members.len(), 5);
12661
12662        let crate::ClassMember::Field {
12663            name, modifiers, ..
12664        } = &members[0]
12665        else {
12666            panic!("member 0 should be a field");
12667        };
12668        assert_eq!(name.name, "name");
12669        assert_eq!(modifiers.visibility, crate::Visibility::Public);
12670        assert!(modifiers.readonly.is_none());
12671
12672        let crate::ClassMember::Field { modifiers, .. } = &members[1] else {
12673            panic!("member 1 should be a field");
12674        };
12675        assert_eq!(modifiers.visibility, crate::Visibility::Private);
12676
12677        let crate::ClassMember::Field { modifiers, .. } = &members[2] else {
12678            panic!("member 2 should be a field");
12679        };
12680        assert!(modifiers.readonly.is_some());
12681        assert_eq!(modifiers.visibility, crate::Visibility::Public);
12682
12683        assert!(matches!(
12684            &members[3],
12685            crate::ClassMember::Constructor { .. }
12686        ));
12687
12688        let crate::ClassMember::Method { name, .. } = &members[4] else {
12689            panic!("member 4 should be a method");
12690        };
12691        assert_eq!(name.name, "speak");
12692    }
12693
12694    #[test]
12695    fn parse_dog_class_extends_super_and_void_method() {
12696        let src = r#"
12697class Dog extends Animal {
12698  private tricks: string[];
12699
12700  constructor(name: string) {
12701    super(name, "woof", "canis familiaris");
12702    this.tricks = [];
12703  }
12704
12705  learn(trick: string): void {
12706    this.tricks.push(trick);
12707  }
12708}
12709"#;
12710        let (ast, diags) = parse_str(src);
12711        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12712        let StmtKind::ClassDecl {
12713            extends, members, ..
12714        } = &single_stmt(&ast).kind
12715        else {
12716            panic!("expected ClassDecl");
12717        };
12718        assert!(extends.is_some(), "Dog should extend Animal");
12719        assert_eq!(members.len(), 3);
12720        assert!(matches!(&members[0], crate::ClassMember::Field { .. }));
12721        assert!(matches!(
12722            &members[1],
12723            crate::ClassMember::Constructor { .. }
12724        ));
12725        assert!(matches!(&members[2], crate::ClassMember::Method { .. }));
12726    }
12727
12728    #[test]
12729    fn parse_class_implements_list() {
12730        let (ast, diags) = parse_str("class C implements I, J { run(): void {} }");
12731        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12732        let StmtKind::ClassDecl { implements, .. } = &single_stmt(&ast).kind else {
12733            panic!("expected ClassDecl");
12734        };
12735        assert_eq!(implements.len(), 2);
12736    }
12737
12738    #[test]
12739    fn parse_class_extends_and_implements() {
12740        let (ast, diags) = parse_str("class C extends B implements I { run(): void {} }");
12741        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12742        let StmtKind::ClassDecl {
12743            extends,
12744            implements,
12745            ..
12746        } = &single_stmt(&ast).kind
12747        else {
12748            panic!("expected ClassDecl");
12749        };
12750        assert!(extends.is_some());
12751        assert_eq!(implements.len(), 1);
12752    }
12753
12754    #[test]
12755    fn parse_class_generics_optional_field_and_initializer() {
12756        let (ast, diags) = parse_str("class Box<T> { value?: T; items: T[] = []; }");
12757        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12758        let StmtKind::ClassDecl {
12759            generics, members, ..
12760        } = &single_stmt(&ast).kind
12761        else {
12762            panic!("expected ClassDecl");
12763        };
12764        assert_eq!(generics.len(), 1);
12765        assert_eq!(generics[0].name, "T");
12766
12767        let crate::ClassMember::Field {
12768            optional,
12769            initializer,
12770            ..
12771        } = &members[0]
12772        else {
12773            panic!("member 0 should be a field");
12774        };
12775        assert!(*optional);
12776        assert!(initializer.is_none());
12777
12778        let crate::ClassMember::Field { initializer, .. } = &members[1] else {
12779            panic!("member 1 should be a field");
12780        };
12781        assert!(initializer.is_some());
12782    }
12783
12784    #[test]
12785    fn export_class_is_marked() {
12786        let (ast, diags) = parse_str("export class Foo {}");
12787        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12788        assert_eq!(ast.top_level.len(), 1);
12789        assert_eq!(ast.exported_decls.len(), 1);
12790        assert!(matches!(
12791            &single_stmt(&ast).kind,
12792            StmtKind::ClassDecl { .. }
12793        ));
12794    }
12795
12796    #[test]
12797    fn parameterless_index_signature_is_reported_from_its_member_start() {
12798        for (source, expected) in [
12799            ("interface P { readonly []: number }", "readonly []"),
12800            ("let p: { readonly []: number } = {};", "readonly []"),
12801            ("let q: { []: number } = {};", "[]"),
12802        ] {
12803            let (_ast, diags) = parse_str(source);
12804            let diag = diags
12805                .iter()
12806                .find(|d| d.message.contains("must declare exactly one parameter"))
12807                .unwrap_or_else(|| panic!("no diagnostic for {source}"));
12808            let reported = &source[diag.span.start as usize..diag.span.end as usize];
12809            assert_eq!(reported, expected, "{source}");
12810        }
12811    }
12812
12813    #[test]
12814    fn unterminated_object_type_expects_closing_brace() {
12815        let (_ast, diags) = parse_str("let p: { a: number\n");
12816        assert!(
12817            diags.iter().any(|d| d.message == "expected `}`"),
12818            "{diags:?}"
12819        );
12820    }
12821
12822    #[test]
12823    fn conflicting_visibility_modifiers_diagnose() {
12824        let (_ast, diags) = parse_str("class C { public private x: number; }");
12825        assert!(
12826            diags
12827                .iter()
12828                .any(|d| d.message.contains("at most one visibility modifier")),
12829            "got: {diags:?}",
12830        );
12831    }
12832
12833    #[test]
12834    fn duplicate_readonly_modifier_diagnoses() {
12835        let (_ast, diags) = parse_str("class C { readonly readonly x: number; }");
12836        assert!(
12837            diags
12838                .iter()
12839                .any(|d| d.message.contains("duplicate `readonly`")),
12840            "got: {diags:?}",
12841        );
12842    }
12843
12844    #[test]
12845    fn field_named_like_a_modifier_is_allowed() {
12846        let (ast, diags) = parse_str("class C { private: number; }");
12847        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12848        let crate::ClassMember::Field {
12849            name, modifiers, ..
12850        } = &class_members(&ast)[0]
12851        else {
12852            panic!("expected a field");
12853        };
12854        assert_eq!(name.name, "private");
12855        assert_eq!(modifiers.visibility, crate::Visibility::Public);
12856    }
12857
12858    #[test]
12859    fn method_named_get_is_allowed() {
12860        let (ast, diags) = parse_str("class C { get(): number { return 1; } }");
12861        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12862        let crate::ClassMember::Method { name, .. } = &class_members(&ast)[0] else {
12863            panic!("expected a method");
12864        };
12865        assert_eq!(name.name, "get");
12866    }
12867
12868    #[test]
12869    fn static_method_parses() {
12870        let (ast, diags) = parse_str("class C { static f(): number { return 1; } }");
12871        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12872        let crate::ClassMember::Method {
12873            name, modifiers, ..
12874        } = &class_members(&ast)[0]
12875        else {
12876            panic!("expected a method");
12877        };
12878        assert_eq!(name.name, "f");
12879        assert!(modifiers.static_span.is_some());
12880    }
12881
12882    #[test]
12883    fn static_readonly_field_parses() {
12884        let (ast, diags) = parse_str("class C { static readonly x: number = 1; }");
12885        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12886        let crate::ClassMember::Field {
12887            name,
12888            modifiers,
12889            initializer,
12890            ..
12891        } = &class_members(&ast)[0]
12892        else {
12893            panic!("expected a field");
12894        };
12895        assert_eq!(name.name, "x");
12896        assert!(modifiers.static_span.is_some());
12897        assert!(modifiers.readonly.is_some());
12898        assert!(initializer.is_some());
12899    }
12900
12901    #[test]
12902    fn private_static_method_parses() {
12903        let (ast, diags) = parse_str("class C { private static f(): void {} }");
12904        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12905        let crate::ClassMember::Method { modifiers, .. } = &class_members(&ast)[0] else {
12906            panic!("expected a method");
12907        };
12908        assert_eq!(modifiers.visibility, crate::Visibility::Private);
12909        assert!(modifiers.static_span.is_some());
12910    }
12911
12912    #[test]
12913    fn method_named_static_is_allowed() {
12914        let (ast, diags) = parse_str("class C { static(): number { return 1; } }");
12915        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12916        let crate::ClassMember::Method {
12917            name, modifiers, ..
12918        } = &class_members(&ast)[0]
12919        else {
12920            panic!("expected a method");
12921        };
12922        assert_eq!(name.name, "static");
12923        assert!(modifiers.static_span.is_none());
12924    }
12925
12926    #[test]
12927    fn visibility_after_static_diagnoses() {
12928        let (_ast, diags) = parse_str("class C { static private f(): void {} }");
12929        assert!(
12930            diags
12931                .iter()
12932                .any(|d| d.message.contains("`private` must come before `static`")),
12933            "got: {diags:?}",
12934        );
12935    }
12936
12937    #[test]
12938    fn duplicate_static_modifier_diagnoses() {
12939        let (_ast, diags) = parse_str("class C { static static f(): void {} }");
12940        assert!(
12941            diags
12942                .iter()
12943                .any(|d| d.message.contains("duplicate `static` modifier")),
12944            "got: {diags:?}",
12945        );
12946    }
12947
12948    #[test]
12949    fn static_accessor_is_rejected() {
12950        let (_ast, diags) = parse_str("class C { static get x(): number { return 1; } }");
12951        assert!(
12952            diags
12953                .iter()
12954                .any(|d| d.message.contains("static accessors are not supported")),
12955            "got: {diags:?}",
12956        );
12957    }
12958
12959    #[test]
12960    fn static_constructor_is_rejected() {
12961        let (_ast, diags) = parse_str("class C { static constructor() {} }");
12962        assert!(
12963            diags
12964                .iter()
12965                .any(|d| d.message.contains("a constructor cannot be `static`")),
12966            "got: {diags:?}",
12967        );
12968    }
12969
12970    #[test]
12971    fn optional_static_field_is_rejected() {
12972        let (_ast, diags) = parse_str("class C { static x?: number; }");
12973        assert!(
12974            diags
12975                .iter()
12976                .any(|d| d.message.contains("a static field cannot be optional")),
12977            "got: {diags:?}",
12978        );
12979    }
12980
12981    #[test]
12982    fn async_function_names_the_synchronous_rewrite() {
12983        let (_ast, diags) = parse_str(
12984            "async function main(): Promise<string> {\n  const x = await f();\n  return x;\n}",
12985        );
12986        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
12987        assert_eq!(
12988            messages,
12989            [
12990                "`async` functions are not supported",
12991                "`await` is not supported"
12992            ],
12993            "{diags:?}"
12994        );
12995        assert_eq!(
12996            diags[0].help,
12997            [
12998                "calls return their value directly: remove `async`, write the return type `T` \
12999              instead of `Promise<T>`, and drop `await`"
13000            ]
13001        );
13002        assert_eq!(
13003            diags[1].help,
13004            ["calls return their value directly: remove `await`"]
13005        );
13006    }
13007
13008    #[test]
13009    fn async_is_rejected_on_every_function_form() {
13010        for source in [
13011            "export async function f(): void {}",
13012            "const f = async (): void => {};",
13013            "const f = async x => x;",
13014            "const f = async function (): void {};",
13015            "class C { async run(): void {} }",
13016            "class C { static async run(): void {} }",
13017        ] {
13018            let (_ast, diags) = parse_str(source);
13019            assert_eq!(
13020                diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13021                ["`async` functions are not supported"],
13022                "{source}: {diags:?}"
13023            );
13024        }
13025    }
13026
13027    #[test]
13028    fn await_before_a_template_is_reported() {
13029        for source in ["const a = await `hi`;", "const a = await `a${1}b`;"] {
13030            let (_ast, diags) = parse_str(source);
13031            let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13032            assert_eq!(
13033                messages,
13034                ["`await` is not supported"],
13035                "{source}: {diags:?}"
13036            );
13037        }
13038    }
13039
13040    #[test]
13041    fn async_and_await_stay_ordinary_names() {
13042        for source in [
13043            "const async = 1; const y = async + 1;",
13044            "function async(n: number): number { return n; } async(1);",
13045            "const await = 2; const z = await * 2;",
13046            "const await = (n: number): number => n; const a = await(1);",
13047            "const await = [1, 2]; const b = await[0];",
13048            "const async = { k: 1 }; const c = \"k\" in async; const d = async in ({ o: 1 });",
13049            "const async: unknown = 1; const n = async as (number);",
13050            "const await: unknown = 1; const m = await as number;",
13051            "const await = 1; const t = `${await}`;",
13052            "class C { async(): void {} async: number = 1; }",
13053        ] {
13054            let (_ast, diags) = parse_str(source);
13055            assert!(diags.is_empty(), "{source}: {diags:?}");
13056        }
13057    }
13058
13059    #[test]
13060    fn an_async_object_literal_method_names_the_rewrite_once() {
13061        assert_eq!(
13062            crate::typechecker::rules::test_util::compile_messages(
13063                "function main(): string {\n  const o = { async run(): string { return \"a\"; } };\n  return o.run();\n}\n"
13064            ),
13065            ["`async` functions are not supported"]
13066        );
13067        let (_ast, diags) = parse_str("const o = { async run(): string { return \"a\"; } };");
13068        assert_eq!(
13069            diags[0].help,
13070            [
13071                "calls return their value directly: remove `async`, write the return type `T` \
13072              instead of `Promise<T>`, and drop `await`"
13073            ]
13074        );
13075    }
13076
13077    #[test]
13078    fn await_in_an_async_object_literal_method_is_reported_too() {
13079        let (_ast, diags) =
13080            parse_str("const o = { async run(): string { const x = await f(); return x; } };");
13081        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13082        assert_eq!(
13083            messages,
13084            [
13085                "`async` functions are not supported",
13086                "`await` is not supported"
13087            ],
13088            "{diags:?}"
13089        );
13090    }
13091
13092    #[test]
13093    fn every_async_member_form_reports_async() {
13094        for source in [
13095            "const o = { async *run(): void {} };",
13096            "class C { async *run(): void {} }",
13097            "const o = { async [k](): void {} };",
13098            "const o = { async in(): void {} };",
13099            "class C { async as(): void {} }",
13100        ] {
13101            let (_ast, diags) = parse_str(source);
13102            assert_eq!(
13103                diags[0].message, "`async` functions are not supported",
13104                "{source}: {diags:?}"
13105            );
13106        }
13107    }
13108
13109    #[test]
13110    fn async_string_key_methods_report_only_async() {
13111        for source in [
13112            "const o = { async \"s\"(): string { return \"a\"; } };",
13113            "class C { async \"s\"(): string { return \"a\"; } }",
13114        ] {
13115            let (_ast, diags) = parse_str(source);
13116            assert_eq!(
13117                diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13118                ["`async` functions are not supported"],
13119                "{source}: {diags:?}"
13120            );
13121        }
13122    }
13123
13124    #[test]
13125    fn async_before_a_line_break_is_a_member_of_its_own() {
13126        for source in [
13127            "class C {\n  async\n  run(): void {}\n}",
13128            "const o = { async\n run(): void {} };",
13129        ] {
13130            let (_ast, diags) = parse_str(source);
13131            assert!(
13132                diags
13133                    .iter()
13134                    .all(|d| d.message != "`async` functions are not supported"),
13135                "{source}: {diags:?}"
13136            );
13137        }
13138    }
13139
13140    #[test]
13141    fn async_stays_an_object_literal_key() {
13142        let messages = crate::typechecker::rules::test_util::compile_messages(
13143            "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",
13144        );
13145        assert!(messages.is_empty(), "{messages:?}");
13146    }
13147
13148    #[test]
13149    fn for_await_names_the_rewrite_once() {
13150        assert_eq!(
13151            crate::typechecker::rules::test_util::compile_messages(
13152                "import * as fs from \"submilli:fs\";\nfunction main(): string {\n  for await (const e of fs.list(\".\", false)) { }\n  return \"x\";\n}\n"
13153            ),
13154            ["`for await` is not supported"]
13155        );
13156        let (ast, diags) = parse_str("for await (const e of [1]) { }");
13157        assert_eq!(
13158            diags[0].help,
13159            ["calls return their values directly: remove `await` and write `for (const e of …)`"]
13160        );
13161        assert_eq!(ast.top_level.len(), 1, "the loop is still read");
13162    }
13163
13164    #[test]
13165    fn protected_member_is_rejected() {
13166        let (_ast, diags) = parse_str("class C { protected x: number; }");
13167        assert!(
13168            diags
13169                .iter()
13170                .any(|d| d.message.contains("`protected` is not supported")),
13171            "got: {diags:?}",
13172        );
13173    }
13174
13175    #[test]
13176    fn abstract_member_is_rejected() {
13177        let (_ast, diags) = parse_str("class C { abstract run(): void; }");
13178        assert!(
13179            diags
13180                .iter()
13181                .any(|d| d.message.contains("abstract classes are not supported")),
13182            "got: {diags:?}",
13183        );
13184    }
13185
13186    #[test]
13187    fn getter_parses_as_accessor() {
13188        let (ast, diags) = parse_str("class C { get value(): number { return 1; } }");
13189        assert!(diags.is_empty(), "got: {diags:?}");
13190        let crate::ClassMember::Accessor { kind, name, .. } = &class_members(&ast)[0] else {
13191            panic!("expected an accessor member");
13192        };
13193        assert_eq!(*kind, crate::AccessorKind::Get);
13194        assert_eq!(name.name, "value");
13195    }
13196
13197    #[test]
13198    fn getter_without_return_type_is_rejected() {
13199        let (ast, diags) = parse_str("class C { get value() { return 1; } }");
13200        assert_eq!(diags.len(), 1, "got: {diags:?}");
13201        assert_eq!(diags[0].message, "expected `:` and return type");
13202        assert_eq!(diags[0].help, vec!["get name(): T { … }".to_string()]);
13203        assert!(
13204            matches!(
13205                &class_members(&ast)[0],
13206                crate::ClassMember::Accessor {
13207                    return_type: None,
13208                    ..
13209                }
13210            ),
13211            "the getter is still recovered as a member"
13212        );
13213    }
13214
13215    #[test]
13216    fn setter_parses_as_accessor() {
13217        let (ast, diags) = parse_str("class C { set value(v: number) {} }");
13218        assert!(diags.is_empty(), "got: {diags:?}");
13219        let crate::ClassMember::Accessor { kind, param, .. } = &class_members(&ast)[0] else {
13220            panic!("expected an accessor member");
13221        };
13222        assert_eq!(*kind, crate::AccessorKind::Set);
13223        assert!(param.is_some(), "setter should have a parameter");
13224    }
13225
13226    #[test]
13227    fn param_property_modifier_outside_constructor_is_rejected() {
13228        let (_ast, diags) = parse_str("function f(public x: number): void {}");
13229        assert!(
13230            diags.iter().any(|d| d
13231                .message
13232                .contains("parameter properties are only allowed in a constructor")),
13233            "got: {diags:?}",
13234        );
13235    }
13236
13237    #[test]
13238    fn constructor_return_type_is_rejected() {
13239        let (_ast, diags) = parse_str("class C { constructor(): void {} }");
13240        assert!(
13241            diags.iter().any(|d| d
13242                .message
13243                .contains("constructor cannot declare a return type")),
13244            "got: {diags:?}",
13245        );
13246    }
13247
13248    #[test]
13249    fn field_initializer_without_type_is_rejected() {
13250        let (_ast, diags) = parse_str("class C { x = 1; }");
13251        assert!(
13252            diags
13253                .iter()
13254                .any(|d| d.message.contains("class fields require a type annotation")),
13255            "got: {diags:?}",
13256        );
13257    }
13258
13259    #[test]
13260    fn this_outside_method_is_left_to_typechecker() {
13261        let (_ast, diags) = parse_str("function f(): string { return this.name; }");
13262        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13263    }
13264
13265    #[test]
13266    fn super_outside_method_is_rejected() {
13267        let (_ast, diags) = parse_str("function f(): void { super(1); }");
13268        assert!(
13269            diags.iter().any(|d| d
13270                .message
13271                .contains("`super` is only valid inside a class method or constructor body")),
13272            "got: {diags:?}",
13273        );
13274    }
13275
13276    #[test]
13277    fn this_inside_method_is_accepted_by_parser() {
13278        let (_ast, diags) = parse_str("class C { x: number; read(): number { return this.x; } }");
13279        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13280    }
13281    #[test]
13282    fn parse_void_expression_preserves_operand_and_precedence() {
13283        let (ast, diags) = parse_str("void effect() === undefined;");
13284        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13285        let expression = expr_of_single_stmt(&ast);
13286        let crate::ExprKind::Binary { lhs, rhs, .. } = expression.kind else {
13287            panic!("expected equality")
13288        };
13289        let crate::ExprKind::Void { operand } = ast.try_expr(lhs).unwrap().kind else {
13290            panic!("expected void")
13291        };
13292        assert!(matches!(
13293            ast.try_expr(operand).unwrap().kind,
13294            crate::ExprKind::Call { .. }
13295        ));
13296        assert!(matches!(
13297            ast.try_expr(rhs).unwrap().kind,
13298            crate::ExprKind::Identifier(_)
13299        ));
13300    }
13301
13302    #[test]
13303    fn parse_void_newline_and_object_operands() {
13304        for source in [
13305            "void\n0;",
13306            "void { value: 1 };",
13307            "void\n{ value: 1 };",
13308            "void /x/;",
13309            "void void 0;",
13310        ] {
13311            let (_, diags) = parse_str(source);
13312            assert!(diags.is_empty(), "{source}: {diags:?}");
13313        }
13314        let (_, diags) = parse_str("void; 0;");
13315        assert!(
13316            !diags.is_empty(),
13317            "an explicit semicolon cannot split void from its operand"
13318        );
13319    }
13320
13321    #[test]
13322    fn parse_optional_parameters_and_defaults_in_callable_forms() {
13323        for source in [
13324            "function f(x = 1): number { return x; }",
13325            "function f(x: number = 1, y: number): number { return y; }",
13326            "function f({ x } = { x: 1 }): number { return x; }",
13327            "const f = (x?: number) => x;",
13328            "const f = (x: number = effect()) => x;",
13329            "const f = function(x: number = effect()) { return x; };",
13330            "const f = { m(x: number = effect()): number { return x; } };",
13331            "const f = ([x = 1]: number[] = values) => x;",
13332            "let f: (x?: number, ...rest: string[]) => void = value;",
13333            "let f: { method?(x?: number): void } = value;",
13334            "function f(x: number = 1, y?: number): void {}",
13335        ] {
13336            let (_, diags) = parse_str(source);
13337            assert!(diags.is_empty(), "{source}: {diags:?}");
13338        }
13339    }
13340
13341    #[test]
13342    fn parse_invalid_optional_parameters() {
13343        for source in [
13344            "function f(x?: number, y: number): void {}",
13345            "const f = (x?: number, y: number) => y;",
13346            "let f: (x?: number, y: number) => void = value;",
13347            "function f(...x?: number[]): void {}",
13348            "function f(x?: number = 1): void {}",
13349            "const f = (x?: number = 1) => x;",
13350            "function f({x}?: T): void {}",
13351        ] {
13352            let (_, diags) = parse_str(source);
13353            assert!(!diags.is_empty(), "expected diagnostic for {source}");
13354        }
13355    }
13356
13357    #[test]
13358    fn parse_optional_tuple_forms_and_order() {
13359        for source in [
13360            "let x: [number, string?] = value;",
13361            "let x: [x?: number, y?: string] = value;",
13362            "let x: readonly [(number | null)?, string?] = value;",
13363        ] {
13364            let (_, diags) = parse_str(source);
13365            assert!(diags.is_empty(), "{source}: {diags:?}");
13366        }
13367        for source in [
13368            "let x: [number?, string] = value;",
13369            "let x: [x: number?] = value;",
13370        ] {
13371            let (_, diags) = parse_str(source);
13372            assert!(!diags.is_empty(), "expected diagnostic for {source}");
13373        }
13374    }
13375
13376    #[test]
13377    fn parse_array_pattern_defaults_preserve_holes() {
13378        let (ast, diags) = parse_str("const [, a = effect(), b] = values;");
13379        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13380        let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
13381            panic!("expected pattern")
13382        };
13383        let crate::Binding::Array {
13384            elems, defaults, ..
13385        } = binding
13386        else {
13387            panic!("expected array")
13388        };
13389        assert_eq!(elems.len(), 3);
13390        assert_eq!(defaults.len(), elems.len());
13391        assert!(elems[0].is_none());
13392        assert!(defaults[0].is_none());
13393        assert!(defaults[1].is_some());
13394        assert!(defaults[2].is_none());
13395    }
13396
13397    #[test]
13398    fn parse_optional_class_method_forms() {
13399        let (ast, diags) =
13400            parse_str("class C { declared?(x?: number): void; implemented?(): void\n{} }");
13401        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13402        let members = class_members(&ast);
13403        assert!(matches!(
13404            &members[0],
13405            crate::ClassMember::Field {
13406                optional: true,
13407                initializer: None,
13408                ty: crate::TypeAnnotation {
13409                    kind: crate::TypeAnnotationKind::Function { .. },
13410                    ..
13411                },
13412                ..
13413            }
13414        ));
13415        assert!(matches!(
13416            &members[1],
13417            crate::ClassMember::Method { optional: true, .. }
13418        ));
13419    }
13420    #[test]
13421    fn defaults_retain_their_callable_receiver_scope() {
13422        for source in [
13423            "class C { value: number = 1; method(value: number = this.value): number { return value; } }",
13424            "class C { constructor(value: number = this.value) {} value: number = 1; }",
13425            "const f = function(this: { value: number }, value: number = this.value): number { return value; };",
13426        ] {
13427            let (ast, diagnostics) = parse_str(source);
13428            assert!(diagnostics.is_empty(), "{source}: {diagnostics:?}");
13429            assert!(
13430                ast.source_expressions()
13431                    .iter()
13432                    .any(|expr| matches!(expr.kind, ExprKind::This))
13433            );
13434            assert!(
13435                !ast.source_expressions()
13436                    .iter()
13437                    .any(|expr| matches!(expr.kind, ExprKind::ThisOutsideReceiver))
13438            );
13439        }
13440    }
13441}