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        self.advance();
2898        self.block_depth += 1;
2899        let arms = self.parse_switch_arms();
2900        self.block_depth -= 1;
2901        let (cases, default) = arms?;
2902
2903        if !matches!(self.peek().kind, TokenKind::RightBrace) {
2904            self.error_at_peek("expected `}` to close `switch` body");
2905            return None;
2906        }
2907        let close = self.advance();
2908
2909        parse_arena_result(
2910            self.ast.try_push_stmt(Stmt {
2911                kind: StmtKind::Switch {
2912                    discriminant,
2913                    cases,
2914                    default,
2915                },
2916                span: self.span(kw.span.start, close.span.end),
2917            }),
2918            &mut self.fatal,
2919        )
2920    }
2921
2922    // Reads the arms up to the closing `}`, which is left for the caller.
2923    fn parse_switch_arms(&mut self) -> Option<(Vec<SwitchCase>, Option<SwitchDefault>)> {
2924        let mut cases: Vec<SwitchCase> = Vec::new();
2925        let mut default: Option<SwitchDefault> = None;
2926        let mut pending_values: Vec<ExprId> = Vec::new();
2927        let mut pending_label_start: Option<u32> = None;
2928        let mut pending_default: Option<Span> = None;
2929
2930        loop {
2931            match self.peek().kind {
2932                TokenKind::Eof => break,
2933                // ASI inserts `;` after `:` on a newline — treat it as a no-op here.
2934                TokenKind::Semicolon => {
2935                    self.advance();
2936                }
2937                TokenKind::RightBrace => {
2938                    // Case bodies are required (typechecker enforces break/return termination).
2939                    if pending_label_start.is_some() {
2940                        self.error_at_peek(
2941                            "expected case body before `}` — case labels must be followed by statements",
2942                        );
2943                        return None;
2944                    }
2945                    break;
2946                }
2947                TokenKind::Case => {
2948                    let case_kw = self.advance();
2949                    if pending_label_start.is_none() {
2950                        pending_label_start = Some(case_kw.span.start);
2951                    }
2952                    let value = self.parse_expression()?;
2953                    if !matches!(self.peek().kind, TokenKind::Colon) {
2954                        self.error_at_peek("expected `:` after `case` label");
2955                        return None;
2956                    }
2957                    self.advance();
2958                    pending_values.push(value);
2959                }
2960                TokenKind::Default => {
2961                    let kw_tok = self.advance();
2962                    if default.is_some() {
2963                        self.error_at(kw_tok.span, "duplicate `default` clause in `switch`");
2964                        return None;
2965                    }
2966                    if !matches!(self.peek().kind, TokenKind::Colon) {
2967                        self.error_at_peek("expected `:` after `default`");
2968                        return None;
2969                    }
2970                    self.advance();
2971                    if pending_label_start.is_none() {
2972                        pending_label_start = Some(kw_tok.span.start);
2973                    }
2974                    pending_default = Some(kw_tok.span);
2975                }
2976                _ => {
2977                    if pending_label_start.is_none() {
2978                        self.error_at_peek(
2979                            "expected `case` or `default` at the start of a `switch` body",
2980                        );
2981                        return None;
2982                    }
2983                    let Some(label_start) = pending_label_start else {
2984                        return self.invariant_failure("switch arm has no label");
2985                    };
2986                    let body = self.parse_switch_arm_body(label_start)?;
2987                    let arm_span =
2988                        parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
2989                    if !pending_values.is_empty() {
2990                        let first = parse_arena_result(
2991                            self.ast.try_expr(pending_values[0]),
2992                            &mut self.fatal,
2993                        )?
2994                        .span
2995                        .start;
2996                        let case_span = self.span(first, arm_span.end);
2997                        cases.push(SwitchCase {
2998                            values: std::mem::take(&mut pending_values),
2999                            body,
3000                            span: case_span,
3001                        });
3002                    }
3003                    if let Some(def_kw_span) = pending_default.take() {
3004                        default = Some(SwitchDefault {
3005                            body,
3006                            span: self.span(def_kw_span.start, arm_span.end),
3007                        });
3008                    }
3009                    pending_label_start = None;
3010                }
3011            }
3012        }
3013        Some((cases, default))
3014    }
3015
3016    // `body_start` is the label keyword's offset so diagnostics point at the label.
3017    fn parse_switch_arm_body(&mut self, body_start: u32) -> Option<StmtId> {
3018        let mut stmts: Vec<StmtId> = Vec::new();
3019        let mut end_offset = body_start;
3020        loop {
3021            if matches!(
3022                self.peek().kind,
3023                TokenKind::Case | TokenKind::Default | TokenKind::RightBrace | TokenKind::Eof
3024            ) {
3025                break;
3026            }
3027            if matches!(self.peek().kind, TokenKind::Semicolon) {
3028                end_offset = self.peek().span.end;
3029                self.advance();
3030                continue;
3031            }
3032            let pos_before = self.pos;
3033            if let Some(id) = self.parse_statement() {
3034                end_offset = parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
3035                    .span
3036                    .end;
3037                stmts.push(id);
3038            } else {
3039                self.recover();
3040                if self.pos == pos_before && !self.is_at_eof() {
3041                    self.advance();
3042                }
3043                // Recovery consumed tokens but produced no statement; advance the body
3044                // end past them so the arm span can't end before its own case value.
3045                end_offset = end_offset.max(self.prev_token_end());
3046            }
3047        }
3048        let span = self.span(body_start, end_offset);
3049        parse_arena_result(
3050            self.ast.try_push_stmt(Stmt {
3051                kind: StmtKind::Block(stmts),
3052                span,
3053            }),
3054            &mut self.fatal,
3055        )
3056    }
3057
3058    fn parse_break(&mut self) -> Option<StmtId> {
3059        let kw = self.advance();
3060        if !matches!(self.peek().kind, TokenKind::Semicolon) {
3061            self.error_at_peek("expected `;` after `break`");
3062            return None;
3063        }
3064        let semi = self.advance();
3065        parse_arena_result(
3066            self.ast.try_push_stmt(Stmt {
3067                kind: StmtKind::Break,
3068                span: self.span(kw.span.start, semi.span.end),
3069            }),
3070            &mut self.fatal,
3071        )
3072    }
3073
3074    fn parse_continue(&mut self) -> Option<StmtId> {
3075        let kw = self.advance();
3076        if !matches!(self.peek().kind, TokenKind::Semicolon) {
3077            self.error_at_peek("expected `;` after `continue`");
3078            return None;
3079        }
3080        let semi = self.advance();
3081        parse_arena_result(
3082            self.ast.try_push_stmt(Stmt {
3083                kind: StmtKind::Continue,
3084                span: self.span(kw.span.start, semi.span.end),
3085            }),
3086            &mut self.fatal,
3087        )
3088    }
3089
3090    fn parse_return(&mut self) -> Option<StmtId> {
3091        let kw = self.advance();
3092
3093        let value = if matches!(self.peek().kind, TokenKind::Semicolon) {
3094            None
3095        } else {
3096            Some(self.parse_expression()?)
3097        };
3098
3099        if !self.at_statement_end() {
3100            self.error_at_peek("expected `;` after return");
3101            return None;
3102        }
3103        let end = self.finish_statement();
3104
3105        parse_arena_result(
3106            self.ast.try_push_stmt(Stmt {
3107                kind: StmtKind::Return(value),
3108                span: self.span(kw.span.start, end),
3109            }),
3110            &mut self.fatal,
3111        )
3112    }
3113
3114    fn parse_throw(&mut self) -> Option<StmtId> {
3115        let kw = self.advance();
3116
3117        if matches!(self.peek().kind, TokenKind::Semicolon) {
3118            self.error_at_peek_with_help(
3119                "expected expression after `throw`",
3120                vec!["throw new Error(\"message\");".to_string()],
3121            );
3122            return None;
3123        }
3124
3125        let value = self.parse_expression()?;
3126
3127        if !self.at_statement_end() {
3128            self.error_at_peek("expected `;` after `throw`");
3129            return None;
3130        }
3131        let end = self.finish_statement();
3132
3133        parse_arena_result(
3134            self.ast.try_push_stmt(Stmt {
3135                kind: StmtKind::Throw { value },
3136                span: self.span(kw.span.start, end),
3137            }),
3138            &mut self.fatal,
3139        )
3140    }
3141
3142    fn parse_try(&mut self) -> Option<StmtId> {
3143        let kw = self.advance();
3144
3145        let body = self.parse_block()?;
3146
3147        let mut catches: Vec<CatchClause> = Vec::new();
3148        let mut finally: Option<StmtId> = None;
3149        let mut tail_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3150            .span
3151            .end;
3152
3153        loop {
3154            match self.peek().kind {
3155                TokenKind::Catch => {
3156                    if finally.is_some() {
3157                        self.error_at_peek("`catch` clause must appear before `finally`");
3158                        return None;
3159                    }
3160                    let clause = self.parse_catch_clause()?;
3161                    tail_end = clause.span.end;
3162                    catches.push(clause);
3163                }
3164                TokenKind::Finally => {
3165                    if finally.is_some() {
3166                        self.error_at_peek("duplicate `finally` clause");
3167                        return None;
3168                    }
3169                    self.advance();
3170                    let block = self.parse_block()?;
3171                    tail_end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
3172                        .span
3173                        .end;
3174                    finally = Some(block);
3175                }
3176                _ => break,
3177            }
3178        }
3179
3180        if catches.is_empty() && finally.is_none() {
3181            self.error_at(
3182                self.span(kw.span.start, tail_end),
3183                "try requires at least one of `catch` or `finally`",
3184            );
3185            return None;
3186        }
3187
3188        parse_arena_result(
3189            self.ast.try_push_stmt(Stmt {
3190                kind: StmtKind::Try {
3191                    body,
3192                    catches,
3193                    finally,
3194                },
3195                span: self.span(kw.span.start, tail_end),
3196            }),
3197            &mut self.fatal,
3198        )
3199    }
3200
3201    fn parse_catch_clause(&mut self) -> Option<CatchClause> {
3202        let kw = self.advance();
3203
3204        let (binding, ty) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
3205            // This binding is inaccessible to source code, including nested catches.
3206            (
3207                Ident {
3208                    name: "#catch".into(),
3209                    span: kw.span,
3210                },
3211                None,
3212            )
3213        } else {
3214            self.parse_catch_binding()?
3215        };
3216
3217        let body = self.parse_block()?;
3218        let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3219            .span
3220            .end;
3221
3222        Some(CatchClause {
3223            binding,
3224            ty,
3225            body,
3226            span: self.span(kw.span.start, body_end),
3227        })
3228    }
3229
3230    fn parse_catch_binding(&mut self) -> Option<(Ident, Option<TypeAnnotation>)> {
3231        if !matches!(self.peek().kind, TokenKind::LeftParen) {
3232            self.error_at_peek("expected `(` after `catch`");
3233            return None;
3234        }
3235        self.advance();
3236        let name_tok = self.expect_binding_identifier("expected catch binding name")?;
3237        let binding = self.ident_from_token(&name_tok);
3238        let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3239            self.advance();
3240            if self.reject_any_catch_annotation() {
3241                // The error is reported; parsing continues as an unannotated catch.
3242                None
3243            } else {
3244                Some(self.parse_type_annotation()?)
3245            }
3246        } else {
3247            None
3248        };
3249        if !matches!(self.peek().kind, TokenKind::RightParen) {
3250            self.error_at_peek("expected `)` after catch binding");
3251            return None;
3252        }
3253        self.advance();
3254        Some((binding, ty))
3255    }
3256
3257    /// Reports `catch (e: any)` and consumes the `any`, returning whether it did.
3258    /// Only exactly `any)` is handled; compound types keep the general diagnostic.
3259    fn reject_any_catch_annotation(&mut self) -> bool {
3260        let is_bare_any = self.peek_identifier_text_is("any")
3261            && matches!(self.peek_at(1).kind, TokenKind::RightParen);
3262        if !is_bare_any {
3263            return false;
3264        }
3265        let any_tok = self.advance();
3266        // The general `any` help suggests `unknown`, which a catch binding refuses.
3267        self.error_at_with_help(
3268            any_tok.span,
3269            "`any` is not supported",
3270            vec![
3271                "write `catch (e)` to catch every thrown error, or name an error class, \
3272                 as in `catch (e: RangeError)`, to catch only that type"
3273                    .to_string(),
3274            ],
3275        );
3276        true
3277    }
3278
3279    fn parse_paren_condition(&mut self) -> Option<ExprId> {
3280        if !matches!(self.peek().kind, TokenKind::LeftParen) {
3281            self.error_at_peek("expected `(`");
3282            return None;
3283        }
3284        self.advance();
3285        let expr = self.parse_expression()?;
3286        if !matches!(self.peek().kind, TokenKind::RightParen) {
3287            self.error_at_peek("expected `)`");
3288            return None;
3289        }
3290        self.advance();
3291        Some(expr)
3292    }
3293
3294    fn parse_predicate_or_return_type(
3295        &mut self,
3296        type_pos: TypePos,
3297    ) -> Option<(
3298        Option<TypeAnnotation>,
3299        Option<crate::TypePredicateAnnotation>,
3300    )> {
3301        if matches!(self.peek().kind, TokenKind::Identifier) && self.peek_at_is_word(1, "is") {
3302            let param_tok = self.advance();
3303            let param = self.ident_from_token(&param_tok);
3304            self.advance();
3305            let asserted = self.parse_type_annotation_in(type_pos)?;
3306            let span = self.span(param.span.start, asserted.span.end);
3307            return Some((
3308                None,
3309                Some(crate::TypePredicateAnnotation {
3310                    param,
3311                    asserted,
3312                    span,
3313                }),
3314            ));
3315        }
3316        Some((Some(self.parse_type_annotation_in(type_pos)?), None))
3317    }
3318
3319    fn parse_type_annotation(&mut self) -> Option<TypeAnnotation> {
3320        self.parse_type_annotation_in(TypePos::Anywhere)
3321    }
3322
3323    fn parse_type_annotation_in(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3324        // A leading `|` (prettier's multiline format) is accepted and discarded.
3325        let leading_pipe = matches!(self.peek().kind, TokenKind::Pipe);
3326        if leading_pipe {
3327            self.advance();
3328        }
3329        let first = self.parse_type_array(type_pos)?;
3330        if !matches!(self.peek().kind, TokenKind::Pipe) {
3331            return Some(first);
3332        }
3333        let start = first.span.start;
3334        let mut end = first.span.end;
3335        let mut members = vec![first];
3336        while matches!(self.peek().kind, TokenKind::Pipe) {
3337            self.advance();
3338            let member = self.parse_type_array(type_pos)?;
3339            end = member.span.end;
3340            members.push(member);
3341        }
3342        Some(TypeAnnotation {
3343            kind: TypeAnnotationKind::Union(members),
3344            span: self.span(start, end),
3345        })
3346    }
3347
3348    fn parse_type_array(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3349        self.with_recursion_limit(|parser| parser.parse_type_array_inner(type_pos))
3350    }
3351
3352    fn parse_type_array_inner(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3353        // `keyof` is contextual, not reserved: TypeScript allows it as an ordinary
3354        // identifier, and so does Submilli. It is an operator only when another type
3355        // follows it, which an identifier in type position never does.
3356        if self.is_contextual_type_operator(self.pos, "keyof") {
3357            let kw = self.advance();
3358            let operand = self.parse_type_array(type_pos)?;
3359            let span = self.span(kw.span.start, operand.span.end);
3360            return Some(TypeAnnotation {
3361                kind: TypeAnnotationKind::KeyOf(Box::new(operand)),
3362                span,
3363            });
3364        }
3365        if self.is_contextual_type_operator(self.pos, "readonly") {
3366            return self.parse_readonly_type(type_pos);
3367        }
3368
3369        let mut ty = match self.peek().kind {
3370            // `typeof x` names the type of a *value*. Unlike `keyof` this is already a
3371            // reserved word (the `typeof x === "string"` guard uses it), so it needs no
3372            // contextual check — in type position it is only ever the operator. Built
3373            // here rather than returned early so the `[]` suffix loop below still runs,
3374            // making `typeof a[]` an array of it, as TypeScript parses it too.
3375            TokenKind::Typeof => {
3376                let kw = self.advance();
3377                let first = self.expect_identifier("expected a value name after `typeof`")?;
3378                let mut path = vec![self.type_name(first.span)?];
3379                let mut end = first.span.end;
3380                while matches!(self.peek().kind, TokenKind::Dot) {
3381                    self.advance();
3382                    let seg = self.expect_identifier_name("expected a property name after `.`")?;
3383                    end = seg.span.end;
3384                    path.push(self.type_name(seg.span)?);
3385                }
3386                TypeAnnotation {
3387                    kind: TypeAnnotationKind::TypeOf { path },
3388                    span: self.span(kw.span.start, end),
3389                }
3390            }
3391            TokenKind::Identifier | TokenKind::Void => {
3392                // `void` never starts a qualified path.
3393                let leading_is_identifier = matches!(self.peek().kind, TokenKind::Identifier);
3394                let tok = self.advance();
3395                let first_span = tok.span;
3396                let mut path_end = first_span.end;
3397                let first_name = self.type_name(first_span)?;
3398                let mut path = vec![first_name.clone()];
3399                if leading_is_identifier {
3400                    while matches!(self.peek().kind, TokenKind::Dot) {
3401                        self.advance();
3402                        if !matches!(self.peek().kind, TokenKind::Identifier) {
3403                            self.error_at_peek("expected identifier after `.` in type name");
3404                            return None;
3405                        }
3406                        let seg = self.advance();
3407                        path_end = seg.span.end;
3408                        path.push(self.type_name(seg.span)?);
3409                    }
3410                }
3411                // Type arguments, like the `[]` suffix, must start on the type's line.
3412                let (args, end) = if matches!(self.peek().kind, TokenKind::LessThan)
3413                    && !self.line_break_before_peek()
3414                {
3415                    self.parse_type_argument_list()?
3416                } else {
3417                    (Vec::new(), path_end)
3418                };
3419                let outer_span = self.span(first_span.start, end);
3420                let kind = if path.len() == 1 {
3421                    if first_name.name == "any" {
3422                        self.error_at_with_help(
3423                            first_span,
3424                            "`any` is not supported",
3425                            vec![
3426                                "annotate a concrete type instead — an object shape \
3427                                 like `{ id: string }`, a named `interface`, or \
3428                                 `unknown` (a safe dynamic type you narrow before use)"
3429                                    .to_string(),
3430                            ],
3431                        );
3432                    }
3433                    TypeAnnotationKind::Name {
3434                        name: first_name,
3435                        args,
3436                    }
3437                } else {
3438                    TypeAnnotationKind::Qualified { path, args }
3439                };
3440                TypeAnnotation {
3441                    kind,
3442                    span: outer_span,
3443                }
3444            }
3445            // `null<T>` is meaningless — no type args after `null`.
3446            TokenKind::NullLiteral => {
3447                let tok = self.advance();
3448                TypeAnnotation {
3449                    kind: TypeAnnotationKind::Name {
3450                        name: self.type_name(tok.span)?,
3451                        args: Vec::new(),
3452                    },
3453                    span: tok.span,
3454                }
3455            }
3456            TokenKind::StringLiteral(_) => {
3457                let tok = self.advance();
3458                let span = tok.span;
3459                let TokenKind::StringLiteral(s) = tok.kind else {
3460                    // advance returns the token just matched, before moving the cursor.
3461                    unreachable!("advance preserves the matched StringLiteral variant");
3462                };
3463                TypeAnnotation {
3464                    kind: TypeAnnotationKind::StringLiteral(s),
3465                    span,
3466                }
3467            }
3468            TokenKind::NumberLiteral(_) => {
3469                let tok = self.advance();
3470                let span = tok.span;
3471                let TokenKind::NumberLiteral(v) = tok.kind else {
3472                    // advance returns the token just matched, before moving the cursor.
3473                    unreachable!("advance preserves the matched NumberLiteral variant");
3474                };
3475                // Canonicalize `-0.0` → `0.0` so literal type `0` matches both signs.
3476                let canonical = if v == 0.0 { 0.0 } else { v };
3477                TypeAnnotation {
3478                    kind: TypeAnnotationKind::NumberLiteral(crate::types::LiteralF64(canonical)),
3479                    span,
3480                }
3481            }
3482            TokenKind::BigIntLiteral(_) => {
3483                let tok = self.advance();
3484                let TokenKind::BigIntLiteral(digits) = tok.kind else {
3485                    // advance returns the token just matched, before moving the cursor.
3486                    unreachable!("advance preserves the matched BigIntLiteral variant");
3487                };
3488                TypeAnnotation {
3489                    kind: TypeAnnotationKind::BigIntLiteral(digits),
3490                    span: tok.span,
3491                }
3492            }
3493            TokenKind::Minus
3494                if matches!(
3495                    self.peek_at(1).kind,
3496                    TokenKind::NumberLiteral(_) | TokenKind::BigIntLiteral(_)
3497                ) =>
3498            {
3499                let minus = self.advance().span;
3500                let tok = self.advance();
3501                let span = self.span(minus.start, tok.span.end);
3502                let kind =
3503                    match tok.kind {
3504                        TokenKind::NumberLiteral(v) => TypeAnnotationKind::NumberLiteral(
3505                            crate::types::LiteralF64(if v == 0.0 { 0.0 } else { -v }),
3506                        ),
3507                        TokenKind::BigIntLiteral(digits) => TypeAnnotationKind::BigIntLiteral(
3508                            crate::types::negate_bigint_digits(&digits),
3509                        ),
3510                        // The guard admits only the two numeric literal tokens.
3511                        _ => unreachable!("guarded to a numeric literal after `-`"),
3512                    };
3513                TypeAnnotation { kind, span }
3514            }
3515            TokenKind::BooleanLiteral(value) => {
3516                let span = self.advance().span;
3517                TypeAnnotation {
3518                    kind: TypeAnnotationKind::BooleanLiteral(value),
3519                    span,
3520                }
3521            }
3522            TokenKind::LeftBrace => self.parse_object_type_annotation()?,
3523            TokenKind::LeftParen if self.paren_at_opens_function_type(self.pos, type_pos) => {
3524                self.parse_function_type_annotation()?
3525            }
3526            TokenKind::LeftParen => self.parse_grouped_or_function_type()?,
3527            // Leading `[` is unambiguously a tuple (postfix `[]` can only follow a base type).
3528            TokenKind::LeftBracket => self.parse_tuple_type_annotation()?,
3529            _ => {
3530                self.error_at_peek("expected type");
3531                return None;
3532            }
3533        };
3534        // Array suffixes nest boxed annotations without recursing, so each one
3535        // spends the recursive grammar budget to keep the chain's drop bounded.
3536        let mut array_suffixes = 0usize;
3537        // As in TypeScript, a line break ends the type before `[`: in an object type,
3538        // `a: B` ⏎ `[k: string]: V` is a field followed by an index signature.
3539        while matches!(self.peek().kind, TokenKind::LeftBracket) && !self.line_break_before_peek() {
3540            array_suffixes += 1;
3541            if self.recursion_depth.saturating_add(array_suffixes) >= MAX_PARSE_DEPTH {
3542                self.stop_at_recursion_limit();
3543                return None;
3544            }
3545            let open = self.advance();
3546            if !matches!(self.peek().kind, TokenKind::RightBracket) {
3547                self.error_at(open.span, "expected `]` to close array type");
3548                return None;
3549            }
3550            let close = self.advance();
3551            let span = self.span(ty.span.start, close.span.end);
3552            ty = TypeAnnotation {
3553                kind: TypeAnnotationKind::Array(Box::new(ty)),
3554                span,
3555            };
3556        }
3557        Some(ty)
3558    }
3559
3560    /// `readonly T[]` or `readonly [A, B]`. The operand binds like `keyof`'s, so
3561    /// `readonly T[][]` is a readonly array of mutable arrays and
3562    /// `readonly T[] | null` a union with a readonly member.
3563    fn parse_readonly_type(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3564        let kw = self.advance();
3565        let opens_group = matches!(self.peek().kind, TokenKind::LeftParen);
3566        let operand = self.parse_type_array(type_pos)?;
3567        // `readonly (T[])` is rejected as TypeScript rejects it: the operand must be
3568        // written as an array or tuple type, not grouped into one.
3569        let grouped = opens_group
3570            && matches!(
3571                self.pos
3572                    .checked_sub(1)
3573                    .and_then(|index| self.tokens.get(index))
3574                    .map(|token| &token.kind),
3575                Some(TokenKind::RightParen)
3576            );
3577        if grouped
3578            || !matches!(
3579                operand.kind,
3580                TypeAnnotationKind::Array(_) | TypeAnnotationKind::Tuple(_)
3581            )
3582        {
3583            self.error_at_with_help(
3584                kw.span,
3585                "`readonly` only applies to array and tuple types",
3586                vec![
3587                    "write `readonly T[]` or `readonly [A, B]`; to protect an object's \
3588                     properties, mark each one `readonly` instead"
3589                        .to_string(),
3590                ],
3591            );
3592            return Some(operand);
3593        }
3594        let span = self.span(kw.span.start, operand.span.end);
3595        Some(TypeAnnotation {
3596            kind: TypeAnnotationKind::Readonly(Box::new(operand)),
3597            span,
3598        })
3599    }
3600
3601    /// In type position `(` is ambiguous: a function type's parameter list, or a
3602    /// grouping paren the postfix `[]` / `|` then composes with. Decided by what the
3603    /// parens *contain*, not by what follows them — a trailing `=>` can belong to an
3604    /// enclosing arrow function's body (`(): (string | null) => null`), so looking
3605    /// past the `)` would read that return type as a parameter list. The exception is
3606    /// `(T)` / `(T, U)`, spelled identically either way, which only a trailing `=>`
3607    /// can break.
3608    ///
3609    /// Empty parens count as a parameter list: `()` groups nothing, so the
3610    /// function-type parse's "expected `=>`" is the message that names the fix.
3611    fn paren_at_opens_function_type(&self, index: usize, type_pos: TypePos) -> bool {
3612        let after = |offset: usize| self.tokens.get(index + offset).map(|t| &t.kind);
3613        match (after(1), after(2)) {
3614            // Only a parameter list is empty or starts with a rest element.
3615            (Some(TokenKind::RightParen | TokenKind::DotDotDot), _) => true,
3616            // A named, annotated parameter — no type starts this way.
3617            (Some(TokenKind::Identifier), Some(TokenKind::Colon | TokenKind::Question)) => true,
3618            // `(T)` and `(T, U)` are spelled exactly like a grouped type. v1
3619            // rejects bare parameter names, and a trailing `=>` is the only sign
3620            // the writer meant one — routing those to the function-type parse
3621            // keeps its "params require named annotations" message reachable in
3622            // declaration position (`type F = (T) => R`). In an arrow's *return*
3623            // annotation the trailing `=>` is the body's, so `(): (T) => x` loses
3624            // that message and falls back to the expression parse; dropping the
3625            // redundant parens is the fix there.
3626            (Some(TokenKind::Identifier), Some(TokenKind::Comma | TokenKind::RightParen)) => {
3627                type_pos == TypePos::Anywhere
3628                    && self.index_after_matching_paren(index).is_some_and(|i| {
3629                        matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow))
3630                    })
3631            }
3632            // Anything else can only begin a type, so the parens group.
3633            _ => false,
3634        }
3635    }
3636
3637    /// Parse a `(` the contents rule called a group. If that fails and a `=>` follows
3638    /// the matching `)`, the author was writing a function type after all — a typo in
3639    /// its parameter list is what made it look like a group — so re-parse that way to
3640    /// recover the diagnostic naming the parameter rule instead of a stray-`)` error.
3641    fn parse_grouped_or_function_type(&mut self) -> Option<TypeAnnotation> {
3642        let open = self.pos;
3643        let diagnostics_before = self.diagnostics.len();
3644        if let Some(ty) = self.parse_grouped_type_annotation() {
3645            return Some(ty);
3646        }
3647        let arrow_follows = self
3648            .index_after_matching_paren(open)
3649            .is_some_and(|i| matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)));
3650        if !arrow_follows {
3651            return None;
3652        }
3653        self.pos = open;
3654        self.diagnostics.truncate(diagnostics_before);
3655        self.parse_function_type_annotation()
3656    }
3657
3658    /// A parenthesized type is its inner type — the parens only group, so no node
3659    /// records them. The span covers them so a diagnostic points at what was written.
3660    fn parse_grouped_type_annotation(&mut self) -> Option<TypeAnnotation> {
3661        let open = self.advance();
3662        let inner = self.parse_type_annotation()?;
3663        if !matches!(self.peek().kind, TokenKind::RightParen) {
3664            self.error_at_peek("expected `)` to close a parenthesized type");
3665            return None;
3666        }
3667        let close = self.advance();
3668        Some(TypeAnnotation {
3669            span: self.span(open.span.start, close.span.end),
3670            ..inner
3671        })
3672    }
3673
3674    // Param names are required — bare `(T, U) => R` is rejected.
3675    fn parse_function_type_annotation(&mut self) -> Option<TypeAnnotation> {
3676        let open = self.advance();
3677        let params = self.parse_function_type_params()?;
3678        if !matches!(self.peek().kind, TokenKind::Arrow) {
3679            self.error_at_peek("expected `=>` in function-type annotation");
3680            return None;
3681        }
3682        self.advance();
3683        let return_type = self.parse_type_annotation()?;
3684        let end = return_type.span.end;
3685        Some(TypeAnnotation {
3686            kind: TypeAnnotationKind::Function {
3687                params,
3688                return_type: Box::new(return_type),
3689            },
3690            span: self.span(open.span.start, end),
3691        })
3692    }
3693
3694    /// The `(…)` of a function type, its `(` already consumed and its `)` consumed here.
3695    /// Shared with a type literal's method members, which spell the same parameter list
3696    /// before a `:` rather than a `=>`.
3697    fn parse_function_type_params(&mut self) -> Option<Vec<TypeAnnotationField>> {
3698        let mut params: Vec<TypeAnnotationField> = Vec::new();
3699        if !matches!(self.peek().kind, TokenKind::RightParen) {
3700            loop {
3701                let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
3702                    self.advance();
3703                    true
3704                } else {
3705                    false
3706                };
3707                let name_tok = self.expect_binding_identifier(if rest {
3708                    "expected identifier after `...` in function-type annotation"
3709                } else {
3710                    "expected parameter name in function-type annotation"
3711                })?;
3712                let name = self.ident_from_token(&name_tok);
3713                let optional = self.parse_parameter_optional(rest, false)?;
3714                if !matches!(self.peek().kind, TokenKind::Colon) {
3715                    if rest {
3716                        self.error_at_peek_with_help(
3717                            "rest parameter requires a type annotation",
3718                            vec!["(...args: T[]) => R".to_string()],
3719                        );
3720                    } else {
3721                        self.error_at_peek(
3722                            "expected `:` after parameter name (function-type params \
3723                             require named annotations in v1)",
3724                        );
3725                    }
3726                    return None;
3727                }
3728                self.advance();
3729                let ty = self.parse_type_annotation()?;
3730                if rest && !is_rest_array_annotation(&ty) {
3731                    self.error_at(ty.span, "rest parameter type must be an array");
3732                    return None;
3733                }
3734                if matches!(self.peek().kind, TokenKind::Equals) {
3735                    if rest {
3736                        self.error_rest_parameter_default();
3737                    } else {
3738                        self.error_at_peek(
3739                            "default values in function-type annotations are not yet supported",
3740                        );
3741                    }
3742                    return None;
3743                }
3744                params.push(TypeAnnotationField {
3745                    name,
3746                    ty,
3747                    optional,
3748                    readonly: false,
3749                    rest,
3750                    method: false,
3751                });
3752                match self.peek().kind {
3753                    TokenKind::Comma => {
3754                        if rest {
3755                            let comma = self.advance();
3756                            self.error_at(comma.span, "rest parameter must be the last parameter");
3757                            return None;
3758                        }
3759                        self.advance();
3760                        if matches!(self.peek().kind, TokenKind::RightParen) {
3761                            break;
3762                        }
3763                    }
3764                    TokenKind::RightParen => break,
3765                    _ => {
3766                        self.error_at_peek("expected `,` or `)`");
3767                        return None;
3768                    }
3769                }
3770            }
3771            self.check_parameter_order(params.iter().map(ParamOrder::of_field));
3772        }
3773        if !matches!(self.peek().kind, TokenKind::RightParen) {
3774            self.error_at_peek("expected `)`");
3775            return None;
3776        }
3777        self.advance();
3778        Some(params)
3779    }
3780
3781    fn parse_tuple_type_annotation(&mut self) -> Option<TypeAnnotation> {
3782        let open = self.advance();
3783        if matches!(self.peek().kind, TokenKind::RightBracket) {
3784            let close = self.peek().span;
3785            self.error_at(
3786                self.span(open.span.start, close.end),
3787                "tuple types must have at least one element",
3788            );
3789            return None;
3790        }
3791        let mut elements: Vec<TypeAnnotation> = Vec::new();
3792        loop {
3793            if matches!(self.peek().kind, TokenKind::DotDotDot) {
3794                self.error_at_with_help(
3795                    self.peek().span,
3796                    "rest elements in tuple types are not supported",
3797                    vec!["use an array type `T[]` for a list of varying length".to_string()],
3798                );
3799                return None;
3800            }
3801            // Labels may be mixed with unlabeled elements, as TypeScript allows
3802            // since 5.2.
3803            let label = self.skip_tuple_element_label();
3804            let mut ty = self.parse_type_annotation()?;
3805            let suffix_optional = matches!(self.peek().kind, TokenKind::Question);
3806            if suffix_optional && label != TupleElementLabel::None {
3807                self.error_at_peek_with_help(
3808                    "put the optional marker after the tuple element label",
3809                    vec!["write `name?: T` instead of `name: T?`".to_string()],
3810                );
3811                return None;
3812            }
3813            let optional = label == TupleElementLabel::Optional || suffix_optional;
3814            if optional {
3815                let end = if suffix_optional {
3816                    self.advance().span.end
3817                } else {
3818                    ty.span.end
3819                };
3820                let span = self.span(ty.span.start, end);
3821                ty = TypeAnnotation {
3822                    kind: TypeAnnotationKind::Optional(Box::new(ty)),
3823                    span,
3824                };
3825            } else if elements
3826                .iter()
3827                .any(|element| matches!(element.kind, TypeAnnotationKind::Optional(_)))
3828            {
3829                self.error_at(
3830                    ty.span,
3831                    "a required tuple element cannot follow an optional element",
3832                );
3833                return None;
3834            }
3835            elements.push(ty);
3836            match self.peek().kind {
3837                TokenKind::Comma => {
3838                    self.advance();
3839                    if matches!(self.peek().kind, TokenKind::RightBracket) {
3840                        break;
3841                    }
3842                }
3843                TokenKind::RightBracket => break,
3844                _ => {
3845                    self.error_at_peek("expected `,` or `]`");
3846                    return None;
3847                }
3848            }
3849        }
3850        if !matches!(self.peek().kind, TokenKind::RightBracket) {
3851            self.error_at_peek("expected `]`");
3852            return None;
3853        }
3854        let close = self.advance();
3855        Some(TypeAnnotation {
3856            kind: TypeAnnotationKind::Tuple(elements),
3857            span: self.span(open.span.start, close.span.end),
3858        })
3859    }
3860
3861    /// Labels document positions; retain only whether the label was optional.
3862    fn skip_tuple_element_label(&mut self) -> TupleElementLabel {
3863        if !is_property_name(&self.peek().kind) {
3864            return TupleElementLabel::None;
3865        }
3866        match self.peek_at(1).kind {
3867            TokenKind::Colon => {
3868                self.advance();
3869                self.advance();
3870                TupleElementLabel::Required
3871            }
3872            TokenKind::Question if matches!(self.peek_at(2).kind, TokenKind::Colon) => {
3873                self.advance();
3874                self.advance();
3875                self.advance();
3876                TupleElementLabel::Optional
3877            }
3878            _ => TupleElementLabel::None,
3879        }
3880    }
3881
3882    /// `[]` or `[]: V` — an index signature with no parameter, which TypeScript
3883    /// reads and rejects.
3884    fn peek_is_parameterless_index_signature(&self) -> bool {
3885        matches!(self.peek().kind, TokenKind::LeftBracket)
3886            && matches!(self.peek_at(1).kind, TokenKind::RightBracket)
3887    }
3888
3889    /// Diagnoses and skips a parameterless index signature (see
3890    /// `peek_is_parameterless_index_signature`) so the members after it still parse.
3891    /// `member_start` is where the member begins, at a `readonly` modifier if any.
3892    fn skip_parameterless_index_signature(&mut self, member_start: u32) -> Option<()> {
3893        self.advance();
3894        let close = self.advance();
3895        self.error_at_with_help(
3896            self.span(member_start, close.span.end),
3897            "an index signature must declare exactly one parameter",
3898            vec!["write `[key: string]: V`".to_string()],
3899        );
3900        if matches!(self.peek().kind, TokenKind::Colon) {
3901            self.advance();
3902            self.parse_type_annotation()?;
3903        }
3904        Some(())
3905    }
3906
3907    fn parse_index_signature(&mut self, readonly: bool) -> Option<crate::IndexSignatureAnnotation> {
3908        let open = self.advance();
3909        let parameter = self.expect_property_ident("expected index parameter name")?;
3910        self.reject_eval_or_arguments(parameter.span);
3911        if !matches!(self.peek().kind, TokenKind::Colon) {
3912            self.error_at_peek("expected `:` after index parameter name");
3913            return None;
3914        }
3915        self.advance();
3916        if !self.peek_identifier_text_is("string") {
3917            self.error_at_peek("index signatures require string keys; use `[key: string]: V`");
3918            return None;
3919        }
3920        self.advance();
3921        if !matches!(self.peek().kind, TokenKind::RightBracket) {
3922            self.error_at_peek("expected `]` after string index key type");
3923            return None;
3924        }
3925        self.advance();
3926        if !matches!(self.peek().kind, TokenKind::Colon) {
3927            self.error_at_peek("expected `:` before index value type");
3928            return None;
3929        }
3930        self.advance();
3931        let value = self.parse_type_annotation()?;
3932        Some(crate::IndexSignatureAnnotation {
3933            span: self.span(open.span.start, value.span.end),
3934            value,
3935            readonly,
3936        })
3937    }
3938
3939    fn parse_object_type_annotation(&mut self) -> Option<TypeAnnotation> {
3940        let open = self.advance();
3941        let mut fields: Vec<TypeAnnotationField> = Vec::new();
3942        let mut index = None;
3943        while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
3944            && !self.peek_starts_top_level_item()
3945        {
3946            let member_start = self.peek().span.start;
3947            let readonly = self.eat_readonly_property_modifier();
3948            if self.peek_is_parameterless_index_signature() {
3949                self.skip_parameterless_index_signature(member_start)?;
3950            } else if self.peek_is_construct_signature() {
3951                self.reject_construct_signature()?;
3952            } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
3953                let signature = self.parse_index_signature(readonly)?;
3954                if index.is_some() {
3955                    self.error_at_with_help(
3956                        signature.span,
3957                        "duplicate string index signature",
3958                        vec![],
3959                    );
3960                }
3961                index = Some(Box::new(signature));
3962            } else {
3963                let field = self.parse_object_type_field(readonly, member_start)?;
3964                if let Some(existing) = fields.iter().find(|f| f.name.name == field.name.name) {
3965                    self.diagnostics.push(Diagnostic {
3966                        severity: Severity::Error,
3967                        span: field.name.span,
3968                        message: format!("duplicate field `{}` in object type", field.name.name),
3969                        help: vec![],
3970                        notes: vec![(existing.name.span, "first defined here".into())],
3971                    });
3972                }
3973                fields.push(field);
3974            }
3975            self.finish_type_member(self.prev_token_end(), "expected `;`, `,`, or `}`")?;
3976        }
3977        if !matches!(self.peek().kind, TokenKind::RightBrace) {
3978            self.error_at_peek("expected `}`");
3979            return None;
3980        }
3981        let close = self.advance();
3982        Some(TypeAnnotation {
3983            kind: TypeAnnotationKind::Object { index, fields },
3984            span: self.span(open.span.start, close.span.end),
3985        })
3986    }
3987
3988    /// A declaration or an `import` statement: what a top-level item starts with.
3989    /// `import(…)` and `import.meta` are expressions, not statements.
3990    fn peek_starts_top_level_item(&self) -> bool {
3991        let import_statement = matches!(self.peek().kind, TokenKind::Import)
3992            && matches!(
3993                self.peek_at(1).kind,
3994                TokenKind::LeftBrace
3995                    | TokenKind::Star
3996                    | TokenKind::Identifier
3997                    | TokenKind::StringLiteral(_)
3998            );
3999        import_statement || self.peek_starts_declaration()
4000    }
4001
4002    /// Whether the next tokens start a declaration (`function f`, `class C`, `type T`,
4003    /// …, optionally after `export`, `declare`, `async` or `abstract`) or an export
4004    /// statement (`export {`, `export *`, `export default`), which no type or class
4005    /// member can: a keyword is a member name only before `:`, `?` or `(`. A member
4006    /// list that reaches one was left unclosed, so it stops there and leaves the
4007    /// declaration to be parsed.
4008    fn peek_starts_declaration(&self) -> bool {
4009        if matches!(self.peek().kind, TokenKind::Export)
4010            && matches!(
4011                self.peek_at(1).kind,
4012                TokenKind::Default | TokenKind::LeftBrace | TokenKind::Star
4013            )
4014        {
4015            return true;
4016        }
4017        let mut offset = 0;
4018        while self.peek_at_is_declaration_modifier(offset) {
4019            offset += 1;
4020        }
4021        let declaration_keyword = matches!(
4022            self.peek_at(offset).kind,
4023            TokenKind::Function
4024                | TokenKind::Class
4025                | TokenKind::Let
4026                | TokenKind::Const
4027                | TokenKind::Enum
4028                | TokenKind::Interface
4029        ) || self.peek_at_is_word(offset, "type");
4030        declaration_keyword && matches!(self.peek_at(offset + 1).kind, TokenKind::Identifier)
4031    }
4032
4033    /// `export`, `declare`, `async` or `abstract` before a declaration keyword.
4034    fn peek_at_is_declaration_modifier(&self, offset: usize) -> bool {
4035        matches!(self.peek_at(offset).kind, TokenKind::Export)
4036            || ["declare", "async", "abstract"]
4037                .iter()
4038                .any(|word| self.peek_at_is_word(offset, word))
4039    }
4040
4041    /// `new (params): T` or `new <T>(params): T` inside a type literal: a construct
4042    /// signature, which TypeScript reads where it would otherwise see a method named
4043    /// `new`. A quoted `"new"()`, an optional `new?()` and a field `new: T` are
4044    /// ordinary members, as in TypeScript.
4045    fn peek_is_construct_signature(&self) -> bool {
4046        matches!(self.peek().kind, TokenKind::New)
4047            && matches!(
4048                self.peek_at(1).kind,
4049                TokenKind::LeftParen | TokenKind::LessThan
4050            )
4051    }
4052
4053    /// Only interfaces declare construct signatures, so one in a type literal (see
4054    /// `peek_is_construct_signature`) is diagnosed, then skipped rather than misread
4055    /// as a method named `new`.
4056    fn reject_construct_signature(&mut self) -> Option<()> {
4057        let keyword = self.advance();
4058        self.error_at_with_help(
4059            keyword.span,
4060            "construct signatures are not supported in object types",
4061            vec![
4062                "declare an `interface` with the `new (…)` signature, or pass a factory \
4063                 function such as `() => T`"
4064                    .to_string(),
4065            ],
4066        );
4067        if matches!(self.peek().kind, TokenKind::LessThan) {
4068            self.parse_generic_param_list()?;
4069        }
4070        if !matches!(self.peek().kind, TokenKind::LeftParen) {
4071            self.error_at_peek("expected `(` after construct signature type parameters");
4072            return None;
4073        }
4074        self.parse_object_type_method_signature(keyword.span.start)?;
4075        Some(())
4076    }
4077
4078    /// `name: T`, `name?: T` or the method signature `name(params): T`.
4079    fn parse_object_type_field(
4080        &mut self,
4081        readonly: bool,
4082        member_start: u32,
4083    ) -> Option<TypeAnnotationField> {
4084        let name = self.expect_property_ident("expected field name in object type")?;
4085        // `name?: T` — omittable at construction; reads widen to `T | undefined`.
4086        let optional = matches!(self.peek().kind, TokenKind::Question);
4087        if optional {
4088            self.advance();
4089        }
4090        let method = matches!(self.peek().kind, TokenKind::LeftParen);
4091        let ty = if method {
4092            if readonly {
4093                self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
4094            }
4095            self.parse_object_type_method_signature(name.span.start)?
4096        } else {
4097            if !matches!(self.peek().kind, TokenKind::Colon) {
4098                self.error_at_peek("expected `:` after field name");
4099                return None;
4100            }
4101            self.advance();
4102            self.parse_type_annotation()?
4103        };
4104        Some(TypeAnnotationField {
4105            name,
4106            ty,
4107            optional,
4108            readonly,
4109            rest: false,
4110            method,
4111        })
4112    }
4113
4114    /// `m(params): T` inside a type literal, spelled as the function-typed field
4115    /// `m: (params) => T` — the two are interchangeable in a structural shape, and
4116    /// interfaces already accept both spellings for the same member. `start` is the
4117    /// member name's offset, so the annotation's span covers the name too.
4118    fn parse_object_type_method_signature(&mut self, start: u32) -> Option<TypeAnnotation> {
4119        self.advance();
4120        let params = self.parse_function_type_params()?;
4121        if !matches!(self.peek().kind, TokenKind::Colon) {
4122            self.error_at_peek("expected `:` and return type after method parameters");
4123            return None;
4124        }
4125        self.advance();
4126        let return_type = self.parse_type_annotation()?;
4127        let end = return_type.span.end;
4128        Some(TypeAnnotation {
4129            kind: TypeAnnotationKind::Function {
4130                params,
4131                return_type: Box::new(return_type),
4132            },
4133            span: self.span(start, end),
4134        })
4135    }
4136
4137    /// An identifier that names a binding or a type, which rejects the words strict
4138    /// mode reserves (see [`STRICT_MODE_RESERVED_WORDS`]).
4139    fn expect_identifier(&mut self, message: &str) -> Option<Token> {
4140        let tok = self.expect_identifier_name(message)?;
4141        self.reject_strict_mode_reserved_word(&tok);
4142        Some(tok)
4143    }
4144
4145    /// The name a variable, parameter, function or `catch` binds. Strict mode also
4146    /// forbids `eval` and `arguments` there, though not as a class, enum or type
4147    /// name (TypeScript's TS1100 covers the same positions).
4148    fn expect_binding_identifier(&mut self, message: &str) -> Option<Token> {
4149        let tok = self.expect_identifier(message)?;
4150        self.reject_eval_or_arguments(tok.span);
4151        Some(tok)
4152    }
4153
4154    /// An identifier in a position where strict mode's reserved words are allowed: an
4155    /// enum member, a property, or the name an import specifier refers to.
4156    fn expect_identifier_name(&mut self, message: &str) -> Option<Token> {
4157        if matches!(self.peek().kind, TokenKind::Identifier) {
4158            return Some(self.advance());
4159        }
4160        if is_reserved_identifier_word(&self.peek().kind) {
4161            let tok = self.advance();
4162            let keyword = &self.source[tok.span.start as usize..tok.span.end as usize];
4163            self.error_at_with_help(
4164                tok.span,
4165                format!("`{keyword}` is a reserved keyword and can't be used as a name"),
4166                vec![format!(
4167                    "rename it, for example: `{}`",
4168                    reserved_keyword_rename_example(keyword)
4169                )],
4170            );
4171            return None;
4172        }
4173        self.error_at_peek(message.to_string());
4174        None
4175    }
4176
4177    /// Reports a binding named by a word strict mode reserves. Parsing continues
4178    /// with the name: nothing else about it is wrong.
4179    fn reject_strict_mode_reserved_word(&mut self, tok: &Token) {
4180        let word = &self.source[tok.span.start as usize..tok.span.end as usize];
4181        if !STRICT_MODE_RESERVED_WORDS.contains(&word) {
4182            return;
4183        }
4184        self.error_at_with_help(
4185            tok.span,
4186            format!("`{word}` is a reserved word in strict mode and can't be used as a name"),
4187            vec![format!(
4188                "rename it, for example: `{}`",
4189                reserved_keyword_rename_example(word)
4190            )],
4191        );
4192    }
4193
4194    /// Reports a binding named `eval` or `arguments`. Parsing continues with the
4195    /// name: nothing else about it is wrong.
4196    fn reject_eval_or_arguments(&mut self, span: Span) {
4197        let word = &self.source[span.start as usize..span.end as usize];
4198        if !matches!(word, "eval" | "arguments") {
4199            return;
4200        }
4201        self.error_at_with_help(
4202            span,
4203            format!("`{word}` can't name a binding in strict mode"),
4204            vec![format!(
4205                "rename it, for example: `{}`",
4206                reserved_keyword_rename_example(word)
4207            )],
4208        );
4209    }
4210
4211    fn ident_from_token(&self, tok: &Token) -> Ident {
4212        Ident {
4213            name: self.source[tok.span.start as usize..tok.span.end as usize].to_string(),
4214            span: tok.span,
4215        }
4216    }
4217
4218    fn property_ident_from_token(&self, tok: &Token) -> Ident {
4219        if let TokenKind::StringLiteral(s) = &tok.kind {
4220            Ident {
4221                name: s.clone(),
4222                span: tok.span,
4223            }
4224        } else {
4225            self.ident_from_token(tok)
4226        }
4227    }
4228
4229    fn expect_property_name(&mut self, message: &str) -> Option<Token> {
4230        if !is_property_name(&self.peek().kind) {
4231            self.error_at_peek(message.to_string());
4232            return None;
4233        }
4234        Some(self.advance())
4235    }
4236
4237    fn expect_property_ident(&mut self, message: &str) -> Option<Ident> {
4238        if !is_property_name(&self.peek().kind)
4239            && !matches!(self.peek().kind, TokenKind::StringLiteral(_))
4240        {
4241            self.error_at_peek(message.to_string());
4242            return None;
4243        }
4244        let tok = self.advance();
4245        Some(self.property_ident_from_token(&tok))
4246    }
4247
4248    /// The span of a `readonly` modifier that [`Parser::eat_readonly_property_modifier`]
4249    /// consumed at `member_start`.
4250    fn readonly_modifier_span(&self, member_start: u32) -> Span {
4251        self.span(member_start, member_start + "readonly".len() as u32)
4252    }
4253
4254    /// Reports `readonly` on a method, accessor or method signature, as TypeScript
4255    /// does (TS1024). Parsing continues: the modifier changes nothing else.
4256    fn reject_readonly_modifier(&mut self, span: Span) {
4257        self.error_at_with_help(
4258            span,
4259            "`readonly` can only modify a property or index signature",
4260            vec!["remove `readonly`".to_string()],
4261        );
4262    }
4263
4264    fn eat_readonly_property_modifier(&mut self) -> bool {
4265        if self.peek_identifier_text_is("readonly")
4266            && !self.line_break_after_peek()
4267            && (self.peek_starts_property_after_readonly()
4268                || matches!(self.peek_at(1).kind, TokenKind::LeftBracket))
4269        {
4270            self.advance();
4271            return true;
4272        }
4273        false
4274    }
4275
4276    fn peek_starts_property_after_readonly(&self) -> bool {
4277        let name = &self.peek_at(1).kind;
4278        let after_name = &self.peek_at(2).kind;
4279        let after_optional = &self.peek_at(3).kind;
4280        // A member starts its type with `:`, or its parameter list with `(` — otherwise
4281        // `readonly` is the member's own name.
4282        let starts_member_type =
4283            |kind: &TokenKind| matches!(kind, TokenKind::Colon | TokenKind::LeftParen);
4284        (is_property_name(name) || matches!(name, TokenKind::StringLiteral(_)))
4285            && (starts_member_type(after_name)
4286                || matches!(after_name, TokenKind::Question) && starts_member_type(after_optional))
4287    }
4288
4289    /// `as` and `satisfies`, which lex as identifiers but continue an expression.
4290    fn peek_at_is_binary_word(&self, offset: usize) -> bool {
4291        self.peek_at_is_word(offset, "as") || self.peek_at_is_word(offset, "satisfies")
4292    }
4293
4294    fn peek_identifier_text_is(&self, expected: &str) -> bool {
4295        self.token_is_word(self.peek(), expected)
4296    }
4297
4298    fn peek_at_is_word(&self, offset: usize, expected: &str) -> bool {
4299        self.token_is_word(self.peek_at(offset), expected)
4300    }
4301
4302    fn token_is_word(&self, tok: &Token, expected: &str) -> bool {
4303        matches!(tok.kind, TokenKind::Identifier)
4304            && &self.source[tok.span.start as usize..tok.span.end as usize] == expected
4305    }
4306
4307    /// `delete` is lexed as an identifier: `map.delete(k)`, a `delete()` method and
4308    /// `submilli:http`'s `delete(url)` function all use the name. So it is the
4309    /// operator only when an operand that can't be an argument list follows it, and
4310    /// `delete (o.x)` stays a call. `as` and `satisfies` are lexed as identifiers too:
4311    /// `delete as unknown` casts the function value, while `delete as.x` deletes from a
4312    /// binding named `as`.
4313    fn at_delete_operator(&self) -> bool {
4314        if !self.peek_identifier_text_is("delete") {
4315            return false;
4316        }
4317        if !matches!(
4318            self.peek_at(1).kind,
4319            TokenKind::Identifier | TokenKind::This
4320        ) {
4321            return false;
4322        }
4323        let next_is_cast_keyword =
4324            self.peek_at_is_word(1, "as") || self.peek_at_is_word(1, "satisfies");
4325        !next_is_cast_keyword || cannot_start_type(&self.peek_at(2).kind)
4326    }
4327
4328    /// `type X …` commits to a type-alias declaration only when followed by an
4329    /// identifier, mirroring TypeScript; `type = 5`, `type;`, `type(x)` stay
4330    /// expression statements over a binding named `type`.
4331    fn at_type_alias_head(&self) -> bool {
4332        self.peek_identifier_text_is("type")
4333            && matches!(self.peek_at(1).kind, TokenKind::Identifier)
4334    }
4335
4336    /// Assignment is the lowest-precedence expression and right-associative:
4337    /// `a = b = c` assigns `c` to both, and yields it.
4338    fn parse_expression(&mut self) -> Option<ExprId> {
4339        self.with_recursion_limit(Self::parse_expression_inner)
4340    }
4341
4342    fn parse_expression_inner(&mut self) -> Option<ExprId> {
4343        let written = self.parse_conditional()?;
4344        let shift_assignment = self.peek_shift().filter(|shift| shift.assignment);
4345        if !is_assign_lookahead(&self.peek().kind) && shift_assignment.is_none() {
4346            return Some(written);
4347        }
4348        let target_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4349        let target = self.assignment_target(written)?;
4350        let op_tok = self.advance();
4351        let mut op_span = op_tok.span;
4352        let op = if let Some(ShiftToken { op, tokens, .. }) = shift_assignment {
4353            for _ in 1..tokens {
4354                op_span.end = self.advance().span.end;
4355            }
4356            Some(op)
4357        } else {
4358            compound_op_for_token(&op_tok.kind)
4359        };
4360        let value = self.parse_expression()?;
4361        let value_span = parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
4362        parse_arena_result(
4363            self.ast.try_push_expr(Expr {
4364                kind: ExprKind::Assign {
4365                    target,
4366                    op,
4367                    op_span,
4368                    value,
4369                },
4370                span: self.span(target_span.start, value_span.end),
4371            }),
4372            &mut self.fatal,
4373        )
4374    }
4375
4376    /// The binding, field, or element `written` names, through any parentheses:
4377    /// `(a) = 1` assigns `a`, as in JavaScript.
4378    fn assignment_target(&mut self, written: ExprId) -> Option<ExprId> {
4379        let mut target = written;
4380        while let ExprKind::Paren(inner) =
4381            parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind
4382        {
4383            target = inner;
4384        }
4385        if matches!(
4386            parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind,
4387            ExprKind::Identifier(_) | ExprKind::FieldAccess { .. } | ExprKind::IndexAccess { .. }
4388        ) {
4389            return Some(target);
4390        }
4391        let error_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4392        self.error_at_with_help(
4393            error_span,
4394            "invalid assignment target",
4395            vec![
4396                "assign to a variable, a field (`o.f = …`), or an element (`a[i] = …`)".to_string(),
4397            ],
4398        );
4399        None
4400    }
4401
4402    fn parse_conditional(&mut self) -> Option<ExprId> {
4403        self.reject_async_modifier();
4404        if self.is_arrow_start() {
4405            return self.parse_arrow();
4406        }
4407        let cond = self.parse_binary(0)?;
4408        // Ternary sits below every binary op; right-associative.
4409        if !matches!(self.peek().kind, TokenKind::Question) {
4410            return Some(cond);
4411        }
4412        self.advance();
4413        let then_ = self.parse_expression()?;
4414        if !matches!(self.peek().kind, TokenKind::Colon) {
4415            self.error_at_peek_with_help(
4416                "expected `:` to complete ternary expression",
4417                vec!["const x = cond ? then : else;".to_string()],
4418            );
4419            return None;
4420        }
4421        self.advance();
4422        let else_ = self.parse_expression()?;
4423        let cond_span = parse_arena_result(self.ast.try_expr(cond), &mut self.fatal)?.span;
4424        let else_span = parse_arena_result(self.ast.try_expr(else_), &mut self.fatal)?.span;
4425        parse_arena_result(
4426            self.ast.try_push_expr(Expr {
4427                kind: ExprKind::Ternary { cond, then_, else_ },
4428                span: self.span(cond_span.start, else_span.end),
4429            }),
4430            &mut self.fatal,
4431        )
4432    }
4433
4434    fn is_arrow_start(&self) -> bool {
4435        match self.peek().kind {
4436            TokenKind::Identifier => matches!(self.peek_at(1).kind, TokenKind::Arrow),
4437            TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos),
4438            _ => false,
4439        }
4440    }
4441
4442    /// Reject an `async` that modifies a function, arrow or method, and keep a
4443    /// binding named `async` as it is.
4444    fn reject_async_modifier(&mut self) {
4445        if !self.peek_identifier_text_is("async") {
4446            return;
4447        }
4448        let modifies = match self.peek_at(1).kind {
4449            TokenKind::Function => true,
4450            // `async (x) => …`, not a call of a function named `async`.
4451            TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos + 1),
4452            // `async x => …`, or a method `async run(…)` / `async run<T>(…)`.
4453            // `async in (o)` and `async as (T)` are operators on a binding named
4454            // `async`.
4455            ref name
4456                if is_property_name(name)
4457                    && !matches!(name, TokenKind::In)
4458                    && !self.peek_at_is_binary_word(1) =>
4459            {
4460                matches!(
4461                    self.peek_at(2).kind,
4462                    TokenKind::Arrow | TokenKind::LeftParen | TokenKind::LessThan
4463                )
4464            }
4465            _ => false,
4466        };
4467        if modifies {
4468            self.report_async_modifier();
4469        }
4470    }
4471
4472    /// Reject an `async` that starts a class or object-literal method. A member's
4473    /// `async` is never a value an operator applies to, so `async *run`,
4474    /// `async in()` and `async "s"()` are methods too. After a line break,
4475    /// `async` is a member of its own, as in TypeScript.
4476    fn reject_async_member_modifier(&mut self) {
4477        if !self.peek_identifier_text_is("async") || self.line_break_after_peek() {
4478            return;
4479        }
4480        let modifies = match self.peek_at(1).kind {
4481            TokenKind::Star
4482            | TokenKind::LeftBracket
4483            | TokenKind::StringLiteral(_)
4484            | TokenKind::NumberLiteral(_) => true,
4485            ref name if is_property_name(name) => {
4486                matches!(
4487                    self.peek_at(2).kind,
4488                    TokenKind::LeftParen | TokenKind::LessThan
4489                )
4490            }
4491            _ => false,
4492        };
4493        if modifies {
4494            self.report_async_modifier();
4495        }
4496    }
4497
4498    /// There are no promises here, so name the rewrite, then consume `async`
4499    /// and parse on as if it were absent: the body's `await`s are reported in
4500    /// the same run.
4501    fn report_async_modifier(&mut self) {
4502        let tok = self.advance();
4503        self.error_at_with_help(
4504            tok.span,
4505            "`async` functions are not supported",
4506            vec![
4507                "calls return their value directly: remove `async`, write the return type \
4508                 `T` instead of `Promise<T>`, and drop `await`"
4509                    .to_string(),
4510            ],
4511        );
4512    }
4513
4514    /// `await` followed on the same line by an operand that cannot continue a
4515    /// binding named `await`: an identifier, a keyword, a literal or a template. `await(x)`
4516    /// and `await[i]` stay a call and an index; the typechecker names the fix if
4517    /// `await` then does not resolve. Calls are synchronous, so report it and
4518    /// parse on: the operand stays, and one run names every `await` in the program.
4519    fn reject_await_operator(&mut self) -> bool {
4520        if !self.peek_identifier_text_is("await") {
4521            return false;
4522        }
4523        let operand_starts = matches!(
4524            self.peek_at(1).kind,
4525            TokenKind::Identifier
4526                | TokenKind::This
4527                | TokenKind::Super
4528                | TokenKind::New
4529                | TokenKind::Function
4530                | TokenKind::StringLiteral(_)
4531                | TokenKind::NumberLiteral(_)
4532                | TokenKind::NullLiteral
4533                | TokenKind::BooleanLiteral(_)
4534                // A tagged template is not supported, so a template after `await`
4535                // can only be its operand.
4536                | TokenKind::TemplateHead(_)
4537                | TokenKind::TemplateNoSubstitution(_)
4538        ) && !self.peek_at_is_binary_word(1);
4539        if !operand_starts || self.line_break_after_peek() {
4540            return false;
4541        }
4542        let tok = self.advance();
4543        self.error_at_with_help(
4544            tok.span,
4545            "`await` is not supported",
4546            vec!["calls return their value directly: remove `await`".to_string()],
4547        );
4548        true
4549    }
4550
4551    /// Index just past the `)` matching the `(` at `index`; `None` when the parens
4552    /// never balance. Pure lookahead — emits no diagnostics.
4553    fn index_after_matching_paren(&self, index: usize) -> Option<usize> {
4554        if !matches!(
4555            self.tokens.get(index).map(|t| &t.kind),
4556            Some(TokenKind::LeftParen)
4557        ) {
4558            return None;
4559        }
4560        self.scan_past_balanced(
4561            index,
4562            |k| matches!(k, TokenKind::LeftParen),
4563            |k| matches!(k, TokenKind::RightParen),
4564        )
4565    }
4566
4567    fn find_arrow_after_matching_paren(&self, open: usize) -> bool {
4568        let Some(i) = self.index_after_matching_paren(open) else {
4569            return false;
4570        };
4571        let after_type = if matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Colon)) {
4572            match self.scan_past_type_annotation(i + 1, TypePos::ArrowReturn) {
4573                Some(p) => p,
4574                None => return false,
4575            }
4576        } else {
4577            i
4578        };
4579        matches!(
4580            self.tokens.get(after_type).map(|t| &t.kind),
4581            Some(TokenKind::Arrow),
4582        )
4583    }
4584
4585    // Pure lookahead used only by the arrow-disambiguator. No diagnostics.
4586    // Must accept every form `parse_type_annotation` accepts: a form missing
4587    // here makes the disambiguator reject a valid arrow, so the expression
4588    // parser reports a misleading `expected expression` at the return type.
4589    // `arrow_return_type_scanner_matches_type_grammar` pins the two together.
4590    fn scan_past_type_annotation(&self, start: usize, type_pos: TypePos) -> Option<usize> {
4591        self.scan_past_type_annotation_at_depth(start, type_pos, 0)
4592    }
4593
4594    fn scan_past_type_annotation_at_depth(
4595        &self,
4596        start: usize,
4597        type_pos: TypePos,
4598        depth: usize,
4599    ) -> Option<usize> {
4600        if depth >= MAX_PARSE_DEPTH {
4601            return None;
4602        }
4603        if matches!(
4604            self.tokens.get(start).map(|t| &t.kind),
4605            Some(TokenKind::Identifier)
4606        ) && self
4607            .tokens
4608            .get(start + 1)
4609            .is_some_and(|t| self.token_is_word(t, "is"))
4610        {
4611            return self.scan_past_type_annotation_at_depth(start + 2, type_pos, depth + 1);
4612        }
4613        // Leading `|` (prettier's multiline union format).
4614        let start = if matches!(
4615            self.tokens.get(start).map(|t| &t.kind),
4616            Some(TokenKind::Pipe)
4617        ) {
4618            start + 1
4619        } else {
4620            start
4621        };
4622        let mut i = self.scan_past_single_type_member(start, type_pos, depth + 1)?;
4623        while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Pipe)) {
4624            i = self.scan_past_single_type_member(i + 1, type_pos, depth + 1)?;
4625        }
4626        Some(i)
4627    }
4628
4629    fn scan_past_single_type_member(
4630        &self,
4631        start: usize,
4632        type_pos: TypePos,
4633        depth: usize,
4634    ) -> Option<usize> {
4635        if depth >= MAX_PARSE_DEPTH {
4636            return None;
4637        }
4638        if self.is_contextual_type_operator(start, "keyof")
4639            || self.is_contextual_type_operator(start, "readonly")
4640        {
4641            return self.scan_past_single_type_member(start + 1, type_pos, depth + 1);
4642        }
4643        let mut i = start;
4644        match self.tokens.get(i)?.kind {
4645            TokenKind::Identifier | TokenKind::Void => {
4646                i += 1;
4647                while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Dot)) {
4648                    if !matches!(
4649                        self.tokens.get(i + 1).map(|t| &t.kind),
4650                        Some(TokenKind::Identifier)
4651                    ) {
4652                        return None;
4653                    }
4654                    i += 2;
4655                }
4656                if matches!(
4657                    self.tokens.get(i).map(|t| &t.kind),
4658                    Some(TokenKind::LessThan)
4659                ) {
4660                    i = self.scan_past_balanced(
4661                        i,
4662                        |k| matches!(k, TokenKind::LessThan),
4663                        |k| matches!(k, TokenKind::GreaterThan),
4664                    )?;
4665                }
4666            }
4667            TokenKind::NullLiteral
4668            | TokenKind::StringLiteral(_)
4669            | TokenKind::NumberLiteral(_)
4670            | TokenKind::BooleanLiteral(_) => {
4671                i += 1;
4672            }
4673            TokenKind::LeftBrace => {
4674                i = self.scan_past_balanced(
4675                    i,
4676                    |k| matches!(k, TokenKind::LeftBrace),
4677                    |k| matches!(k, TokenKind::RightBrace),
4678                )?;
4679            }
4680            TokenKind::LeftBracket => {
4681                i = self.scan_past_balanced(
4682                    i,
4683                    |k| matches!(k, TokenKind::LeftBracket),
4684                    |k| matches!(k, TokenKind::RightBracket),
4685                )?;
4686            }
4687            // Same `(`-disambiguation `parse_type_array` uses, and it has to agree:
4688            // a function type carries its own `=>`, so everything after it is the
4689            // return type and the scan ends with that. Grouping parens instead fall
4690            // through to the postfix `[]` loop below.
4691            TokenKind::LeftParen => {
4692                let is_function_type = self.paren_at_opens_function_type(i, type_pos);
4693                i = self.scan_past_balanced(
4694                    i,
4695                    |k| matches!(k, TokenKind::LeftParen),
4696                    |k| matches!(k, TokenKind::RightParen),
4697                )?;
4698                if is_function_type {
4699                    if !matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)) {
4700                        return None;
4701                    }
4702                    return self.scan_past_type_annotation_at_depth(
4703                        i + 1,
4704                        TypePos::Anywhere,
4705                        depth + 1,
4706                    );
4707                }
4708            }
4709            _ => return None,
4710        }
4711        while matches!(
4712            self.tokens.get(i).map(|t| &t.kind),
4713            Some(TokenKind::LeftBracket)
4714        ) {
4715            i += 1;
4716            if !matches!(
4717                self.tokens.get(i).map(|t| &t.kind),
4718                Some(TokenKind::RightBracket)
4719            ) {
4720                return None;
4721            }
4722            i += 1;
4723        }
4724        Some(i)
4725    }
4726
4727    // `start` must index the opening token; returns the index just past the
4728    // matching close, or None if the tokens run out first.
4729    fn scan_past_balanced(
4730        &self,
4731        start: usize,
4732        is_open: fn(&TokenKind) -> bool,
4733        is_close: fn(&TokenKind) -> bool,
4734    ) -> Option<usize> {
4735        let mut depth: i32 = 1;
4736        let mut i = start + 1;
4737        while i < self.tokens.len() && depth > 0 {
4738            let token = self.tokens.get(i)?;
4739            let kind = &token.kind;
4740            if is_open(kind) {
4741                depth += 1;
4742            } else if is_close(kind) {
4743                depth -= 1;
4744            } else if matches!(kind, TokenKind::Eof) {
4745                return None;
4746            }
4747            i += 1;
4748        }
4749        if depth == 0 { Some(i) } else { None }
4750    }
4751
4752    fn parse_arrow(&mut self) -> Option<ExprId> {
4753        let start_span = self.peek().span;
4754
4755        let params = match self.peek().kind {
4756            TokenKind::Identifier => {
4757                let name_tok = self.advance();
4758                self.reject_eval_or_arguments(name_tok.span);
4759                let name = self.ident_from_token(&name_tok);
4760                vec![ParamDecl {
4761                    name,
4762                    ty: None,
4763                    default: None,
4764                    optional: false,
4765                    pattern: None,
4766                    rest: false,
4767                    modifiers: None,
4768                }]
4769            }
4770            TokenKind::LeftParen => {
4771                self.advance();
4772                let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4773                    Vec::new()
4774                } else {
4775                    self.parse_arrow_param_list()?
4776                };
4777                if !matches!(self.peek().kind, TokenKind::RightParen) {
4778                    self.error_at_peek("expected `)`");
4779                    return None;
4780                }
4781                self.advance();
4782                params
4783            }
4784            _ => return self.invariant_failure("is_arrow_start guarded the entry"),
4785        };
4786
4787        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4788            self.advance();
4789            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4790        } else {
4791            (None, None)
4792        };
4793
4794        if !matches!(self.peek().kind, TokenKind::Arrow) {
4795            self.error_at_peek("expected `=>` in arrow function");
4796            return None;
4797        }
4798        // Like TypeScript, the arrow still parses, so later errors keep their spans.
4799        if self.line_break_before_peek() {
4800            let arrow_span = self.peek().span;
4801            self.error_at(arrow_span, "a line break is not allowed before `=>`");
4802        }
4803        self.advance();
4804
4805        let (body, end_pos) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
4806            let block = self.parse_block()?;
4807            let span = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?.span;
4808            (ArrowBody::Block(block), span.end)
4809        } else {
4810            let expr = self.parse_expression()?;
4811            let span = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?.span;
4812            (ArrowBody::Expr(expr), span.end)
4813        };
4814
4815        parse_arena_result(
4816            self.ast.try_push_expr(Expr {
4817                kind: ExprKind::Arrow {
4818                    params,
4819                    return_type,
4820                    type_predicate,
4821                    body,
4822                },
4823                span: self.span(start_span.start, end_pos),
4824            }),
4825            &mut self.fatal,
4826        )
4827    }
4828
4829    /// Function expressions share parameter/body parsing with arrows, but retain
4830    /// their own receiver and optional recursive-name binding.
4831    fn parse_function_expression(&mut self) -> Option<ExprId> {
4832        let kw = self.advance();
4833
4834        if matches!(self.peek().kind, TokenKind::LessThan) {
4835            self.error_at_peek("generic function expressions are not supported");
4836            return None;
4837        }
4838
4839        let self_name = if matches!(self.peek().kind, TokenKind::Identifier) {
4840            let name_tok = self.advance();
4841            self.reject_eval_or_arguments(name_tok.span);
4842            Some(self.ident_from_token(&name_tok))
4843        } else {
4844            None
4845        };
4846
4847        if !matches!(self.peek().kind, TokenKind::LeftParen) {
4848            self.error_at_peek("expected `(` after `function`");
4849            return None;
4850        }
4851        self.advance();
4852
4853        let this_type = if matches!(self.peek().kind, TokenKind::This) {
4854            self.advance();
4855            if !matches!(self.peek().kind, TokenKind::Colon) {
4856                self.error_at_peek("expected a type annotation after `this`");
4857                return None;
4858            }
4859            self.advance();
4860            let ty = self.parse_type_annotation()?;
4861            if matches!(self.peek().kind, TokenKind::Comma) {
4862                self.advance();
4863            } else if !matches!(self.peek().kind, TokenKind::RightParen) {
4864                self.error_at_peek("expected `,` or `)` after the `this` parameter");
4865                return None;
4866            }
4867            Some(ty)
4868        } else {
4869            None
4870        };
4871        let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4872            Vec::new()
4873        } else {
4874            let saved_class = self.class_member_body_depth;
4875            let saved_function = self.function_expression_body_depth;
4876            self.class_member_body_depth = 0;
4877            self.function_expression_body_depth = 1;
4878            let params = self.parse_arrow_param_list();
4879            self.class_member_body_depth = saved_class;
4880            self.function_expression_body_depth = saved_function;
4881            params?
4882        };
4883        if !matches!(self.peek().kind, TokenKind::RightParen) {
4884            self.error_at_peek("expected `)`");
4885            return None;
4886        }
4887        self.advance();
4888
4889        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4890            self.advance();
4891            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4892        } else {
4893            (None, None)
4894        };
4895
4896        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
4897            self.error_at_peek("expected `{` to open the function body");
4898            return None;
4899        }
4900        let saved = self.class_member_body_depth;
4901        self.class_member_body_depth = 0;
4902        self.function_expression_body_depth += 1;
4903        let block = self.parse_block();
4904        self.function_expression_body_depth -= 1;
4905        self.class_member_body_depth = saved;
4906        let block = block?;
4907        let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
4908            .span
4909            .end;
4910
4911        let function = parse_arena_result(
4912            self.ast.try_push_expr(Expr {
4913                kind: ExprKind::Arrow {
4914                    params,
4915                    return_type,
4916                    type_predicate,
4917                    body: ArrowBody::Block(block),
4918                },
4919                span: self.span(kw.span.start, end),
4920            }),
4921            &mut self.fatal,
4922        )?;
4923        parse_arena_result(
4924            self.ast.try_push_expr(Expr {
4925                kind: ExprKind::FunctionExpression {
4926                    name: self_name,
4927                    this_type,
4928                    function,
4929                },
4930                span: self.span(kw.span.start, end),
4931            }),
4932            &mut self.fatal,
4933        )
4934    }
4935
4936    /// Shorthand methods still use arrow lowering. Reject their receiver syntax
4937    /// so an enclosing class or function receiver cannot leak into the body.
4938    fn parse_outer_this_boundary<T>(
4939        &mut self,
4940        parse: impl FnOnce(&mut Self) -> Option<T>,
4941    ) -> Option<T> {
4942        let saved = self.class_member_body_depth;
4943        self.class_member_body_depth = 0;
4944        let saved_function = self.function_expression_body_depth;
4945        self.function_expression_body_depth = 0;
4946        let parsed = parse(self);
4947        self.class_member_body_depth = saved;
4948        self.function_expression_body_depth = saved_function;
4949        parsed
4950    }
4951
4952    fn parse_arrow_param_list(&mut self) -> Option<Vec<ParamDecl>> {
4953        let mut params = Vec::new();
4954        loop {
4955            let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
4956                self.advance();
4957                true
4958            } else {
4959                false
4960            };
4961            // The lowering pass rewrites expression-body arrows to block form when any param
4962            // has a destructuring pattern.
4963            let (name, pattern) = match self.peek().kind {
4964                TokenKind::LeftBrace | TokenKind::LeftBracket => {
4965                    if rest {
4966                        self.error_at_peek("rest parameter cannot be destructured");
4967                        return None;
4968                    }
4969                    let binding = self.parse_binding()?;
4970                    let placeholder = Ident {
4971                        name: String::new(),
4972                        span: binding.span(),
4973                    };
4974                    (placeholder, Some(binding))
4975                }
4976                _ => {
4977                    let name_tok = self.expect_binding_identifier(if rest {
4978                        "expected identifier after `...`"
4979                    } else {
4980                        "expected parameter name"
4981                    })?;
4982                    let name = self.ident_from_token(&name_tok);
4983                    (name, None)
4984                }
4985            };
4986            let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
4987            let ty = if matches!(self.peek().kind, TokenKind::Colon) {
4988                self.advance();
4989                Some(self.parse_type_annotation()?)
4990            } else {
4991                // Rest without annotation is OK at parse time; the typechecker fills it
4992                // from a contextual function-type hint if available.
4993                None
4994            };
4995            let default = self.parse_parameter_default(rest, optional)?;
4996            params.push(ParamDecl {
4997                name,
4998                ty,
4999                default,
5000                optional,
5001                pattern,
5002                rest,
5003                modifiers: None,
5004            });
5005            match self.peek().kind {
5006                TokenKind::Comma => {
5007                    if rest {
5008                        let comma = self.advance();
5009                        self.error_at(comma.span, "rest parameter must be the last parameter");
5010                        return None;
5011                    }
5012                    self.advance();
5013                    if matches!(self.peek().kind, TokenKind::RightParen) {
5014                        break;
5015                    }
5016                }
5017                TokenKind::RightParen => break,
5018                _ => {
5019                    self.error_at_peek("expected `,` or `)`");
5020                    return None;
5021                }
5022            }
5023        }
5024        self.check_parameter_order(params.iter().map(ParamOrder::of_param));
5025        Some(params)
5026    }
5027
5028    fn parse_binary(&mut self, min_prec: u8) -> Option<ExprId> {
5029        self.with_recursion_limit(|parser| parser.parse_binary_inner(min_prec))
5030    }
5031
5032    /// Angle tokens stay separate for nested type arguments. Only adjacent
5033    /// source tokens form a shift; whitespace and comments cannot join them.
5034    fn peek_shift(&self) -> Option<ShiftToken> {
5035        let first = self.peek();
5036        let second = self.peek_at(1);
5037        if first.span.end != second.span.start {
5038            return None;
5039        }
5040        match (&first.kind, &second.kind) {
5041            (TokenKind::LessThan, TokenKind::LessThan) => {
5042                Some(ShiftToken::new(BinOp::Shl, 2, false))
5043            }
5044            (TokenKind::LessThan, TokenKind::LessEquals) => {
5045                Some(ShiftToken::new(BinOp::Shl, 2, true))
5046            }
5047            (TokenKind::GreaterThan, TokenKind::GreaterEquals) => {
5048                Some(ShiftToken::new(BinOp::Shr, 2, true))
5049            }
5050            (TokenKind::GreaterThan, TokenKind::GreaterThan) => {
5051                let third = self.peek_at(2);
5052                if second.span.end == third.span.start {
5053                    match third.kind {
5054                        TokenKind::GreaterThan => {
5055                            return Some(ShiftToken::new(BinOp::UnsignedShr, 3, false));
5056                        }
5057                        TokenKind::GreaterEquals => {
5058                            return Some(ShiftToken::new(BinOp::UnsignedShr, 3, true));
5059                        }
5060                        _ => {}
5061                    }
5062                }
5063                Some(ShiftToken::new(BinOp::Shr, 2, false))
5064            }
5065            _ => None,
5066        }
5067    }
5068
5069    fn peek_binary(&self) -> Option<BinaryToken> {
5070        if let Some(shift) = self.peek_shift() {
5071            return (!shift.assignment).then_some(BinaryToken {
5072                op: shift.op,
5073                precedence: SHIFT_PRECEDENCE,
5074                tokens: shift.tokens,
5075            });
5076        }
5077        peek_binop(&self.peek().kind).map(|(op, precedence)| BinaryToken {
5078            op,
5079            precedence,
5080            tokens: 1,
5081        })
5082    }
5083
5084    fn parse_binary_inner(&mut self, min_prec: u8) -> Option<ExprId> {
5085        let mut lhs = self.parse_cast()?;
5086        // Track the previous op to reject `a || b ?? c` / `a ?? b || c` mixes. Both
5087        // reach this loop: `??` parses its right operand above `&&` (see
5088        // `right_operand_min_prec`), so `||` / `&&` after it is left for this check.
5089        let mut last_op: Option<BinOp> = None;
5090        let mut reported_mixing = false;
5091        while let Some(BinaryToken {
5092            op,
5093            precedence: prec,
5094            tokens: count,
5095        }) = self.peek_binary()
5096        {
5097            if prec < min_prec {
5098                break;
5099            }
5100            if !reported_mixing
5101                && let Some(last) = last_op
5102                && mixed_logical(last, op)
5103            {
5104                reported_mixing = true;
5105                let op_span = self.peek().span;
5106                // Parsing continues as if the left side were grouped, so one error
5107                // inside an `if (...)` header doesn't cascade through the statement.
5108                // Later mixes in the same chain add nothing, so only the first is reported.
5109                self.error_at_with_help(
5110                    op_span,
5111                    "mixing `??` with `||` / `&&` requires parentheses",
5112                    vec![mixed_logical_help(last, op)],
5113                );
5114            }
5115            if op == BinOp::Pow
5116                && matches!(
5117                    parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.kind,
5118                    ExprKind::Unary { .. }
5119                        | ExprKind::Typeof { .. }
5120                        | ExprKind::Delete { .. }
5121                        | ExprKind::Void { .. }
5122                )
5123            {
5124                let error_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5125                self.error_at_with_help(
5126                    error_span,
5127                    "an unparenthesized unary expression cannot be the left operand of `**`",
5128                    vec!["choose the grouping explicitly: `(-x) ** 2` or `-(x ** 2)`".into()],
5129                );
5130                return None;
5131            }
5132            let loose = matches!(self.peek().kind, TokenKind::EqEq | TokenKind::BangEq);
5133            for _ in 0..count {
5134                self.advance();
5135            }
5136            let rhs = self.parse_binary(right_operand_min_prec(op, prec))?;
5137            let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5138            let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
5139            if loose {
5140                self.reject_loose_nullish_comparison(op, lhs, rhs)?;
5141            }
5142            lhs = parse_arena_result(
5143                self.ast.try_push_expr(Expr {
5144                    kind: ExprKind::Binary { op, lhs, rhs },
5145                    span: self.span(lhs_span.start, rhs_span.end),
5146                }),
5147                &mut self.fatal,
5148            )?;
5149            last_op = Some(op);
5150        }
5151        Some(lhs)
5152    }
5153
5154    // Higher than every binary op, lower than every unary prefix. Left-associative.
5155    fn parse_cast(&mut self) -> Option<ExprId> {
5156        let mut expr = self.parse_unary()?;
5157        while matches!(self.peek().kind, TokenKind::Instanceof)
5158            || self.peek_identifier_text_is("as")
5159        {
5160            let is_instanceof = matches!(self.peek().kind, TokenKind::Instanceof);
5161            self.advance();
5162            let ty = self.parse_type_annotation()?;
5163            let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5164                .span
5165                .start;
5166            let end = ty.span.end;
5167            let kind = if is_instanceof {
5168                ExprKind::InstanceOf { value: expr, ty }
5169            } else {
5170                self.last_as_type_end = Some(end);
5171                ExprKind::As { expr, ty }
5172            };
5173            expr = parse_arena_result(
5174                self.ast.try_push_expr(Expr {
5175                    kind,
5176                    span: self.span(start, end),
5177                }),
5178                &mut self.fatal,
5179            )?;
5180        }
5181        Some(expr)
5182    }
5183
5184    fn parse_unary(&mut self) -> Option<ExprId> {
5185        self.with_recursion_limit(Self::parse_unary_inner)
5186    }
5187
5188    fn parse_unary_inner(&mut self) -> Option<ExprId> {
5189        if self.reject_await_operator() {
5190            return self.parse_unary();
5191        }
5192        // `typeof` is parsed as a unary prefix so `typeof x === "T"` works without a new layer;
5193        // the typechecker rejects `Typeof` outside the recognized equality fold position.
5194        if matches!(self.peek().kind, TokenKind::Typeof) {
5195            return self.parse_prefix_expr(|operand| ExprKind::Typeof { operand });
5196        }
5197        if matches!(self.peek().kind, TokenKind::Void) {
5198            return self.parse_prefix_expr(|operand| ExprKind::Void { operand });
5199        }
5200        if self.at_delete_operator() {
5201            return self.parse_prefix_expr(|operand| ExprKind::Delete { operand });
5202        }
5203        if matches!(self.peek().kind, TokenKind::New) {
5204            let op_tok = self.advance();
5205            let mut callee = self.parse_atom()?;
5206            while matches!(self.peek().kind, TokenKind::Dot) {
5207                self.advance();
5208                if !matches!(self.peek().kind, TokenKind::Identifier) {
5209                    self.error_at_peek("expected identifier after `.` in constructor name");
5210                    return None;
5211                }
5212                let ident_tok = self.advance();
5213                let name = self.ident_from_token(&ident_tok);
5214                let receiver_span =
5215                    parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5216                let combined_span = self.span(receiver_span.start, ident_tok.span.end);
5217                callee = parse_arena_result(
5218                    self.ast.try_push_expr(Expr {
5219                        kind: ExprKind::FieldAccess {
5220                            receiver: callee,
5221                            name,
5222                        },
5223                        span: combined_span,
5224                    }),
5225                    &mut self.fatal,
5226                )?;
5227            }
5228            let type_args = if matches!(self.peek().kind, TokenKind::LessThan) {
5229                let saved_pos = self.pos;
5230                let saved_diag_len = self.diagnostics.len();
5231                if let Some(args) = self.try_parse_type_args_only() {
5232                    Some(args)
5233                } else {
5234                    self.pos = saved_pos;
5235                    self.diagnostics.truncate(saved_diag_len);
5236                    None
5237                }
5238            } else {
5239                None
5240            };
5241            if !matches!(self.peek().kind, TokenKind::LeftParen) {
5242                self.error_at_peek("expected `(` after constructor name in `new` expression");
5243                return None;
5244            }
5245            self.advance();
5246            let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5247                Vec::new()
5248            } else {
5249                self.parse_call_args()?
5250            };
5251            if !matches!(self.peek().kind, TokenKind::RightParen) {
5252                self.error_at_peek("expected `)`");
5253                return None;
5254            }
5255            let close = self.advance();
5256            let new_expr = parse_arena_result(
5257                self.ast.try_push_expr(Expr {
5258                    kind: ExprKind::New {
5259                        callee,
5260                        type_args,
5261                        args,
5262                    },
5263                    span: self.span(op_tok.span.start, close.span.end),
5264                }),
5265                &mut self.fatal,
5266            )?;
5267            // `new Foo()` is a postfix primary: `new Map().set(...)`, `new Map().size`,
5268            // `new Foo()[i]` all continue the chain off the constructor result.
5269            return self.parse_postfix_from(new_expr);
5270        }
5271        let op = match self.peek().kind {
5272            TokenKind::Bang => UnOp::Not,
5273            TokenKind::Tilde => UnOp::BitNot,
5274            TokenKind::Minus => UnOp::Neg,
5275            TokenKind::Plus => UnOp::Pos,
5276            _ => return self.parse_postfix(),
5277        };
5278        self.parse_prefix_expr(|operand| ExprKind::Unary { op, operand })
5279    }
5280
5281    /// Consume a prefix operator and its operand, spanning both.
5282    fn parse_prefix_expr(&mut self, kind: impl FnOnce(ExprId) -> ExprKind) -> Option<ExprId> {
5283        let op_tok = self.advance();
5284        // ASI cannot distinguish the `void` type from the expression operator.
5285        // In expression position a newline after the keyword still awaits an operand.
5286        if matches!(op_tok.kind, TokenKind::Void) && self.at_inserted_semicolon() {
5287            self.advance();
5288        }
5289        let operand = self.parse_unary()?;
5290        let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5291        parse_arena_result(
5292            self.ast.try_push_expr(Expr {
5293                kind: kind(operand),
5294                span: self.span(op_tok.span.start, operand_span.end),
5295            }),
5296            &mut self.fatal,
5297        )
5298    }
5299
5300    fn parse_postfix(&mut self) -> Option<ExprId> {
5301        let expr = self.parse_atom()?;
5302        self.parse_postfix_from(expr)
5303    }
5304
5305    fn parse_postfix_from(&mut self, mut expr: ExprId) -> Option<ExprId> {
5306        // OptionalChain is emitted once at end-of-loop so a speculative-`<` rewind
5307        // never strands a half-built chain. Everything before the first `?.` accumulates
5308        // in `expr` normally; from the first `?.` onward ops go into `parts`.
5309        let mut chain_parts: Option<(ExprId, Vec<crate::ChainPart>)> = None;
5310        loop {
5311            match self.peek().kind {
5312                TokenKind::LeftParen => {
5313                    if let Some((_, parts)) = &mut chain_parts {
5314                        let part = self.parse_chain_call_tail(/*optional=*/ false)?;
5315                        parts.push(part);
5316                    } else {
5317                        expr = self.parse_call_tail(expr, None)?;
5318                    }
5319                }
5320                TokenKind::Dot => {
5321                    if let Some((_, parts)) = &mut chain_parts {
5322                        let part = self.parse_chain_field_tail(/*optional=*/ false)?;
5323                        parts.push(part);
5324                    } else {
5325                        expr = self.parse_field_access_tail(expr)?;
5326                    }
5327                }
5328                TokenKind::LeftBracket => {
5329                    if let Some((_, parts)) = &mut chain_parts {
5330                        let part = self.parse_chain_index_tail(/*optional=*/ false)?;
5331                        parts.push(part);
5332                    } else {
5333                        expr = self.parse_index_access_tail(expr)?;
5334                    }
5335                }
5336                TokenKind::QuestionDot => {
5337                    if chain_parts.is_none() {
5338                        chain_parts = Some((expr, Vec::new()));
5339                    }
5340                    self.advance();
5341                    let part = match self.peek().kind {
5342                        TokenKind::LeftParen => self.parse_chain_call_tail(true)?,
5343                        TokenKind::LeftBracket => self.parse_chain_index_tail(true)?,
5344                        _ if is_property_name(&self.peek().kind) => {
5345                            self.parse_chain_field_tail(true)?
5346                        }
5347                        _ => {
5348                            self.error_at_peek(
5349                                "expected field name, `(` for call, or `[` for index after `?.`",
5350                            );
5351                            return None;
5352                        }
5353                    };
5354                    if let Some((_, parts)) = &mut chain_parts {
5355                        parts.push(part);
5356                    }
5357                }
5358                // `<` after a callable expression might be a generic call `f<T>(args)`.
5359                // Speculatively parse; on failure restore state and let `parse_binary`
5360                // treat `<` as a comparison.
5361                TokenKind::LessThan => {
5362                    if chain_parts.is_some() {
5363                        // `a?.<T>()` not yet supported — terminate the chain.
5364                        break;
5365                    }
5366                    let saved_pos = self.pos;
5367                    let saved_diag_len = self.diagnostics.len();
5368                    if let Some(call_id) = self.try_parse_generic_call_tail(expr) {
5369                        expr = call_id;
5370                    } else {
5371                        self.pos = saved_pos;
5372                        self.diagnostics.truncate(saved_diag_len);
5373                        break;
5374                    }
5375                }
5376                TokenKind::Bang => {
5377                    if let Some((_, parts)) = &mut chain_parts {
5378                        let tok = self.advance();
5379                        parts.push(crate::ChainPart::NonNull { span: tok.span });
5380                        continue;
5381                    }
5382                    let tok = self.advance();
5383                    let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5384                        .span
5385                        .start;
5386                    expr = parse_arena_result(
5387                        self.ast.try_push_expr(Expr {
5388                            kind: ExprKind::PostfixUnary {
5389                                op: PostfixOp::NonNullAssert,
5390                                operand: expr,
5391                            },
5392                            span: self.span(start, tok.span.end),
5393                        }),
5394                        &mut self.fatal,
5395                    )?;
5396                }
5397                // Postfix `++`/`--` terminates the chain; `a?.b++` is rejected.
5398                TokenKind::PlusPlus | TokenKind::MinusMinus => {
5399                    if chain_parts.is_some() {
5400                        break;
5401                    }
5402                    let tok = self.advance();
5403                    let op = if matches!(tok.kind, TokenKind::PlusPlus) {
5404                        PostfixOp::Inc
5405                    } else {
5406                        PostfixOp::Dec
5407                    };
5408                    let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5409                        .span
5410                        .start;
5411                    expr = parse_arena_result(
5412                        self.ast.try_push_expr(Expr {
5413                            kind: ExprKind::PostfixUnary { op, operand: expr },
5414                            span: self.span(start, tok.span.end),
5415                        }),
5416                        &mut self.fatal,
5417                    )?;
5418                    break;
5419                }
5420                _ => break,
5421            }
5422        }
5423        if let Some((base, parts)) = chain_parts {
5424            let base_span = parse_arena_result(self.ast.try_expr(base), &mut self.fatal)?.span;
5425            let end = parts.last().map_or(base_span.end, |p| p.span().end);
5426            Some(parse_arena_result(
5427                self.ast.try_push_expr(Expr {
5428                    kind: ExprKind::OptionalChain { base, parts },
5429                    span: self.span(base_span.start, end),
5430                }),
5431                &mut self.fatal,
5432            )?)
5433        } else {
5434            Some(expr)
5435        }
5436    }
5437
5438    // `.` is consumed here for the non-optional case; `?.` was consumed by the caller.
5439    fn parse_chain_field_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5440        let start_pos = self.peek().span.start;
5441        if !optional {
5442            self.advance();
5443        }
5444        let name_tok = if is_property_name(&self.peek().kind) {
5445            self.advance()
5446        } else {
5447            let what = if optional { "?." } else { "." };
5448            self.error_at_peek(format!("expected field name after `{what}`"));
5449            return None;
5450        };
5451        let name = self.ident_from_token(&name_tok);
5452        let end = name.span.end;
5453        Some(crate::ChainPart::Field {
5454            name,
5455            optional,
5456            span: self.span(start_pos, end),
5457        })
5458    }
5459
5460    fn parse_chain_index_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5461        let open = self.advance();
5462        let idx = self.parse_expression()?;
5463        if !matches!(self.peek().kind, TokenKind::RightBracket) {
5464            self.error_at_peek("expected `]`");
5465            return None;
5466        }
5467        let close = self.advance();
5468        Some(crate::ChainPart::Index {
5469            idx,
5470            optional,
5471            span: self.span(open.span.start, close.span.end),
5472        })
5473    }
5474
5475    fn parse_chain_call_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5476        let open = self.advance();
5477        let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5478            Vec::new()
5479        } else {
5480            self.parse_call_args()?
5481        };
5482        if !matches!(self.peek().kind, TokenKind::RightParen) {
5483            self.error_at_peek("expected `)`");
5484            return None;
5485        }
5486        let close = self.advance();
5487        Some(crate::ChainPart::Call {
5488            args,
5489            type_args: None,
5490            optional,
5491            span: self.span(open.span.start, close.span.end),
5492        })
5493    }
5494
5495    fn parse_field_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5496        self.advance();
5497        // Keywords are valid field names — `Foo.new(args)` is the canonical constructor call.
5498        let name_tok = if is_property_name(&self.peek().kind) {
5499            self.advance()
5500        } else {
5501            self.error_at_peek("expected field name after `.`");
5502            return None;
5503        };
5504        let name = self.ident_from_token(&name_tok);
5505        let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5506        parse_arena_result(
5507            self.ast.try_push_expr(Expr {
5508                kind: ExprKind::FieldAccess {
5509                    receiver,
5510                    name: name.clone(),
5511                },
5512                span: self.span(receiver_span.start, name.span.end),
5513            }),
5514            &mut self.fatal,
5515        )
5516    }
5517
5518    fn parse_index_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5519        self.advance();
5520        let index = self.parse_expression()?;
5521        if !matches!(self.peek().kind, TokenKind::RightBracket) {
5522            self.error_at_peek("expected `]`");
5523            return None;
5524        }
5525        let close = self.advance();
5526        let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5527        parse_arena_result(
5528            self.ast.try_push_expr(Expr {
5529                kind: ExprKind::IndexAccess { receiver, index },
5530                span: self.span(receiver_span.start, close.span.end),
5531            }),
5532            &mut self.fatal,
5533        )
5534    }
5535
5536    fn parse_call_tail(
5537        &mut self,
5538        callee: ExprId,
5539        type_args: Option<Vec<TypeAnnotation>>,
5540    ) -> Option<ExprId> {
5541        self.advance();
5542        let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5543            Vec::new()
5544        } else {
5545            self.parse_call_args()?
5546        };
5547        if !matches!(self.peek().kind, TokenKind::RightParen) {
5548            self.error_at_peek("expected `)`");
5549            return None;
5550        }
5551        let close = self.advance();
5552
5553        let callee_span = parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5554        parse_arena_result(
5555            self.ast.try_push_expr(Expr {
5556                kind: ExprKind::Call {
5557                    callee,
5558                    type_args,
5559                    args,
5560                },
5561                span: self.span(callee_span.start, close.span.end),
5562            }),
5563            &mut self.fatal,
5564        )
5565    }
5566
5567    // Caller restores position + diagnostics on `None` so `<` is re-interpreted as comparison.
5568    fn try_parse_generic_call_tail(&mut self, callee: ExprId) -> Option<ExprId> {
5569        let type_args = self.try_parse_type_args_only()?;
5570        if !matches!(self.peek().kind, TokenKind::LeftParen) {
5571            return None;
5572        }
5573        self.parse_call_tail(callee, Some(type_args))
5574    }
5575
5576    fn try_parse_type_args_only(&mut self) -> Option<Vec<TypeAnnotation>> {
5577        if !matches!(self.peek().kind, TokenKind::LessThan) {
5578            return self.invariant_failure("type argument parser expected `<`");
5579        }
5580        self.advance();
5581
5582        if matches!(self.peek().kind, TokenKind::GreaterThan) {
5583            return None;
5584        }
5585
5586        let mut type_args = Vec::new();
5587        loop {
5588            let ta = self.parse_type_annotation()?;
5589            type_args.push(ta);
5590            match self.peek().kind {
5591                TokenKind::Comma => {
5592                    self.advance();
5593                    if matches!(self.peek().kind, TokenKind::GreaterThan) {
5594                        self.advance();
5595                        break;
5596                    }
5597                }
5598                TokenKind::GreaterThan => {
5599                    self.advance();
5600                    break;
5601                }
5602                _ => return None,
5603            }
5604        }
5605        Some(type_args)
5606    }
5607
5608    fn parse_call_args(&mut self) -> Option<Vec<ExprId>> {
5609        let mut args = Vec::new();
5610        loop {
5611            let arg = self.parse_expression()?;
5612            args.push(arg);
5613            match self.peek().kind {
5614                TokenKind::Comma => {
5615                    self.advance();
5616                    if matches!(self.peek().kind, TokenKind::RightParen) {
5617                        return Some(args);
5618                    }
5619                }
5620                TokenKind::RightParen => return Some(args),
5621                _ => {
5622                    self.error_at_peek("expected `,` or `)`");
5623                    return None;
5624                }
5625            }
5626        }
5627    }
5628
5629    /// `<T>expr`, TypeScript's original cast syntax, lowered to the same
5630    /// [`ExprKind::As`] node `expr as T` produces — so it is checked and emitted
5631    /// identically, including the runtime `ref.test` our `as` performs.
5632    ///
5633    /// The operand is a unary expression, which is what binds `<T>-1` as a cast of
5634    /// `-1` rather than a cast of `1` that is then negated.
5635    fn parse_angle_cast(&mut self) -> Option<ExprId> {
5636        let open = self.advance();
5637        let ty = self.parse_type_annotation()?;
5638        if !matches!(self.peek().kind, TokenKind::GreaterThan) {
5639            self.error_at_peek("expected `>` to close a type assertion");
5640            return None;
5641        }
5642        self.advance();
5643        let expr = self.parse_unary()?;
5644        let end = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5645            .span
5646            .end;
5647        parse_arena_result(
5648            self.ast.try_push_expr(Expr {
5649                kind: ExprKind::As { expr, ty },
5650                span: self.span(open.span.start, end),
5651            }),
5652            &mut self.fatal,
5653        )
5654    }
5655
5656    fn parse_atom(&mut self) -> Option<ExprId> {
5657        match self.peek().kind {
5658            // `<T>expr` — the older spelling of `expr as T`, lowered to the same node.
5659            // Unambiguous only because this is an atom's first token: a `<` that means
5660            // comparison always has a left operand, and one that opens a generic
5661            // argument list always follows a callee, so neither reaches here.
5662            TokenKind::LessThan => return self.parse_angle_cast(),
5663            TokenKind::LeftParen => return self.parse_paren(),
5664            // `{` here is an object literal; statement-start `{` is dispatched to
5665            // `parse_block` before `parse_atom` runs, so the two stay disjoint.
5666            TokenKind::LeftBrace => return self.parse_object_literal(),
5667            TokenKind::LeftBracket => return self.parse_array_literal(),
5668            TokenKind::TemplateHead(_) => return self.parse_template_literal(),
5669            TokenKind::This => return self.parse_this_or_super(true),
5670            TokenKind::Super => return self.parse_this_or_super(false),
5671            TokenKind::Function => return self.parse_function_expression(),
5672            _ => {}
5673        }
5674        if !matches!(
5675            self.peek().kind,
5676            TokenKind::NumberLiteral(_)
5677                | TokenKind::BigIntLiteral(_)
5678                | TokenKind::StringLiteral(_)
5679                | TokenKind::TemplateNoSubstitution(_)
5680                | TokenKind::BooleanLiteral(_)
5681                | TokenKind::NullLiteral
5682                | TokenKind::Identifier
5683                | TokenKind::RegexLiteral { .. }
5684        ) {
5685            self.error_at_peek("expected expression");
5686            return None;
5687        }
5688        let tok = self.advance();
5689        let span = tok.span;
5690        let kind = match tok.kind {
5691            TokenKind::NumberLiteral(v) => ExprKind::Number(v),
5692            TokenKind::BigIntLiteral(s) => ExprKind::BigInt(s),
5693            TokenKind::StringLiteral(s) => ExprKind::String(s),
5694            TokenKind::TemplateNoSubstitution(s) => ExprKind::String(s),
5695            TokenKind::BooleanLiteral(b) => ExprKind::Boolean(b),
5696            TokenKind::NullLiteral => ExprKind::Null,
5697            TokenKind::Identifier => ExprKind::Identifier(Ident {
5698                name: self.source[span.start as usize..span.end as usize].to_string(),
5699                span,
5700            }),
5701            TokenKind::RegexLiteral { source, flags } => ExprKind::Regex { source, flags },
5702            // No cursor movement occurs between the accepted-kind check and advance.
5703            _ => unreachable!("advance preserves the accepted expression token"),
5704        };
5705        parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5706    }
5707
5708    /// Ordinary functions establish a `this` boundary and exclude an enclosing
5709    /// class's `super`; arrows inherit the current parsing context.
5710    fn parse_this_or_super(&mut self, is_this: bool) -> Option<ExprId> {
5711        let tok = self.advance();
5712        let span = tok.span;
5713        if !is_this && self.class_member_body_depth == 0 {
5714            self.error_at_with_help(
5715                span,
5716                "`super` is only valid inside a class method or constructor body",
5717                vec![
5718                    "call `super(...)` in a subclass constructor or `super.method(...)` \
5719                     in a subclass method"
5720                        .to_string(),
5721                ],
5722            );
5723        }
5724        let kind = if is_this
5725            && self.class_member_body_depth == 0
5726            && self.function_expression_body_depth == 0
5727        {
5728            ExprKind::ThisOutsideReceiver
5729        } else if is_this {
5730            ExprKind::This
5731        } else {
5732            ExprKind::Super
5733        };
5734        parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5735    }
5736
5737    // parse_atom dispatches immediately after matching TemplateHead.
5738    // The remaining template tokens are still parsed and diagnosed normally.
5739    fn parse_template_literal(&mut self) -> Option<ExprId> {
5740        let head_tok = self.advance();
5741        let TokenKind::TemplateHead(head) = head_tok.kind else {
5742            unreachable!("template dispatch requires TemplateHead");
5743        };
5744        let start = head_tok.span.start;
5745        let mut parts: Vec<String> = vec![head];
5746        let mut exprs: Vec<ExprId> = Vec::new();
5747        let mut substitution_spans: Vec<Span> = Vec::new();
5748        // The token before a substitution ends just past its `${`; the one after
5749        // starts at its `}`.
5750        let mut substitution_start = head_tok.span.end.saturating_sub(2);
5751        loop {
5752            let expr = self.parse_expression()?;
5753            exprs.push(expr);
5754            let closing = self.peek().span.start;
5755            let substitution_span = self.span(substitution_start, closing.saturating_add(1));
5756            match self.peek().kind.clone() {
5757                TokenKind::TemplateMiddle(s) => {
5758                    let tok = self.advance();
5759                    substitution_spans.push(substitution_span);
5760                    substitution_start = tok.span.end.saturating_sub(2);
5761                    parts.push(s);
5762                }
5763                TokenKind::TemplateTail(s) => {
5764                    let tok = self.advance();
5765                    substitution_spans.push(substitution_span);
5766                    parts.push(s);
5767                    return parse_arena_result(
5768                        self.ast.try_push_expr(Expr {
5769                            kind: ExprKind::TemplateLiteral {
5770                                parts,
5771                                exprs,
5772                                substitution_spans,
5773                            },
5774                            span: self.span(start, tok.span.end),
5775                        }),
5776                        &mut self.fatal,
5777                    );
5778                }
5779                _ => {
5780                    self.error_at_peek("expected `}` to close template interpolation");
5781                    return None;
5782                }
5783            }
5784        }
5785    }
5786
5787    fn parse_object_literal(&mut self) -> Option<ExprId> {
5788        let open = self.advance();
5789
5790        let mut members: Vec<ObjectLiteralMember> = Vec::new();
5791        if !matches!(self.peek().kind, TokenKind::RightBrace) {
5792            loop {
5793                if matches!(self.peek().kind, TokenKind::DotDotDot) {
5794                    let dots = self.advance();
5795                    let value = self.parse_expression()?;
5796                    let value_span =
5797                        parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5798                    members.push(ObjectLiteralMember::Spread {
5799                        value,
5800                        span: self.span(dots.span.start, value_span.end),
5801                    });
5802                } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
5803                    self.advance();
5804                    let key = self.parse_expression()?;
5805                    if !matches!(self.peek().kind, TokenKind::RightBracket) {
5806                        self.error_at_peek("expected `]` after computed property key");
5807                        return None;
5808                    }
5809                    self.advance();
5810                    if !matches!(self.peek().kind, TokenKind::Colon) {
5811                        self.error_at_peek("expected `:` after computed property key; computed methods are not supported");
5812                        return None;
5813                    }
5814                    self.advance();
5815                    let value = self.parse_expression()?;
5816                    members.push(ObjectLiteralMember::Computed { key, value });
5817                } else {
5818                    let field = self.parse_object_literal_field()?;
5819                    // Spread members are dynamic — only literal fields participate in
5820                    // the duplicate-key check.
5821                    if let Some(existing) = members.iter().find_map(|m| match m {
5822                        ObjectLiteralMember::Field(f) if f.name.name == field.name.name => Some(f),
5823                        _ => None,
5824                    }) {
5825                        self.diagnostics.push(Diagnostic {
5826                            severity: Severity::Error,
5827                            span: field.name.span,
5828                            message: format!(
5829                                "duplicate field `{}` in object literal",
5830                                field.name.name
5831                            ),
5832                            help: vec![],
5833                            notes: vec![(existing.name.span, "first defined here".into())],
5834                        });
5835                        // Keep parsing so the user sees all duplicates at once.
5836                    }
5837                    members.push(ObjectLiteralMember::Field(field));
5838                }
5839                if self.at_inserted_semicolon()
5840                    && matches!(self.peek_at(1).kind, TokenKind::RightBrace)
5841                {
5842                    self.advance();
5843                }
5844                match self.peek().kind {
5845                    TokenKind::Comma => {
5846                        self.advance();
5847                        if matches!(self.peek().kind, TokenKind::RightBrace) {
5848                            break;
5849                        }
5850                    }
5851                    TokenKind::RightBrace => break,
5852                    _ => {
5853                        self.error_at_peek("expected `,` or `}`");
5854                        return None;
5855                    }
5856                }
5857            }
5858        }
5859
5860        if !matches!(self.peek().kind, TokenKind::RightBrace) {
5861            self.error_at_peek("expected `}`");
5862            return None;
5863        }
5864        let close = self.advance();
5865
5866        parse_arena_result(
5867            self.ast.try_push_expr(Expr {
5868                kind: ExprKind::ObjectLiteral { members },
5869                span: self.span(open.span.start, close.span.end),
5870            }),
5871            &mut self.fatal,
5872        )
5873    }
5874
5875    fn parse_object_literal_field(&mut self) -> Option<ObjectLiteralField> {
5876        self.reject_async_member_modifier();
5877        let (name, shorthandable) = match self.peek().kind.clone() {
5878            TokenKind::Identifier => {
5879                let tok = self.advance();
5880                (self.ident_from_token(&tok), true)
5881            }
5882            kind if is_property_name(&kind) => {
5883                let tok = self.advance();
5884                (self.ident_from_token(&tok), false)
5885            }
5886            TokenKind::StringLiteral(_) => {
5887                let tok = self.advance();
5888                (self.property_ident_from_token(&tok), false)
5889            }
5890            _ => {
5891                self.error_at_peek("expected field name");
5892                return None;
5893            }
5894        };
5895
5896        // `{ m(x: T): R { … } }` is shorthand for `{ m: (x: T): R => { … } }`. Method
5897        // shorthand carries no `this` of its own here, for the same reason a function
5898        // expression does not, so the arrow is an exact lowering. Checked before the
5899        // `:` branch and independently of `shorthandable`, because a keyword or string
5900        // key can name a method too.
5901        if matches!(self.peek().kind, TokenKind::LeftParen) {
5902            let value = self.parse_method_shorthand_body(name.span)?;
5903            return Some(ObjectLiteralField { name, value });
5904        }
5905
5906        if !matches!(self.peek().kind, TokenKind::Colon) {
5907            // `{ x }` is shorthand for `{ x: x }`. Only identifier keys can use
5908            // it — a string key like `{ "x" }` has no binding to reference.
5909            if shorthandable {
5910                let value = parse_arena_result(
5911                    self.ast.try_push_expr(Expr {
5912                        kind: ExprKind::Identifier(name.clone()),
5913                        span: name.span,
5914                    }),
5915                    &mut self.fatal,
5916                )?;
5917                return Some(ObjectLiteralField { name, value });
5918            }
5919            self.error_at_peek("expected `:` after field name");
5920            return None;
5921        }
5922        self.advance();
5923
5924        let value = self.parse_expression()?;
5925        Some(ObjectLiteralField { name, value })
5926    }
5927
5928    /// The `(params): R { … }` tail of an object-literal method, lowered to an arrow.
5929    /// `name_span` is the key's span, so the arrow spans the whole member.
5930    fn parse_method_shorthand_body(&mut self, name_span: Span) -> Option<ExprId> {
5931        self.advance();
5932
5933        let params = if matches!(self.peek().kind, TokenKind::RightParen) {
5934            Vec::new()
5935        } else {
5936            self.parse_arrow_param_list()?
5937        };
5938        if !matches!(self.peek().kind, TokenKind::RightParen) {
5939            self.error_at_peek("expected `)`");
5940            return None;
5941        }
5942        self.advance();
5943
5944        let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
5945            self.advance();
5946            self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
5947        } else {
5948            (None, None)
5949        };
5950
5951        if !matches!(self.peek().kind, TokenKind::LeftBrace) {
5952            self.error_at_peek("expected `{` to open the method body");
5953            return None;
5954        }
5955        let block = self.parse_outer_this_boundary(Self::parse_block)?;
5956        let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
5957            .span
5958            .end;
5959
5960        parse_arena_result(
5961            self.ast.try_push_expr(Expr {
5962                kind: ExprKind::Arrow {
5963                    params,
5964                    return_type,
5965                    type_predicate,
5966                    body: ArrowBody::Block(block),
5967                },
5968                span: self.span(name_span.start, end),
5969            }),
5970            &mut self.fatal,
5971        )
5972    }
5973
5974    fn parse_array_literal(&mut self) -> Option<ExprId> {
5975        let open = self.advance();
5976
5977        let mut elements: Vec<ArrayLiteralElement> = Vec::new();
5978        if !matches!(self.peek().kind, TokenKind::RightBracket) {
5979            loop {
5980                let element = if matches!(self.peek().kind, TokenKind::DotDotDot) {
5981                    let dots = self.advance();
5982                    let value = self.parse_expression()?;
5983                    let value_span =
5984                        parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5985                    ArrayLiteralElement::Spread {
5986                        value,
5987                        span: self.span(dots.span.start, value_span.end),
5988                    }
5989                } else {
5990                    ArrayLiteralElement::Value(self.parse_expression()?)
5991                };
5992                elements.push(element);
5993                match self.peek().kind {
5994                    TokenKind::Comma => {
5995                        self.advance();
5996                        if matches!(self.peek().kind, TokenKind::RightBracket) {
5997                            break;
5998                        }
5999                    }
6000                    TokenKind::RightBracket => break,
6001                    _ => {
6002                        self.error_at_peek("expected `,` or `]`");
6003                        return None;
6004                    }
6005                }
6006            }
6007        }
6008
6009        if !matches!(self.peek().kind, TokenKind::RightBracket) {
6010            self.error_at_peek("expected `]`");
6011            return None;
6012        }
6013        let close = self.advance();
6014
6015        parse_arena_result(
6016            self.ast.try_push_expr(Expr {
6017                kind: ExprKind::ArrayLiteral { elements },
6018                span: self.span(open.span.start, close.span.end),
6019            }),
6020            &mut self.fatal,
6021        )
6022    }
6023
6024    fn parse_paren(&mut self) -> Option<ExprId> {
6025        let open = self.advance();
6026        let inner = self.parse_expression()?;
6027        if !matches!(self.peek().kind, TokenKind::RightParen) {
6028            self.error_at_peek("expected `)`");
6029            return None;
6030        }
6031        let close = self.advance();
6032        let span = self.span(open.span.start, close.span.end);
6033        parse_arena_result(
6034            self.ast.try_push_expr(Expr {
6035                kind: ExprKind::Paren(inner),
6036                span,
6037            }),
6038            &mut self.fatal,
6039        )
6040    }
6041
6042    fn with_recursion_limit<T>(&mut self, parse: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
6043        if self.fatal.is_some() {
6044            return None;
6045        }
6046        if self.recursion_limit_span.is_some() || self.recursion_depth >= MAX_PARSE_DEPTH {
6047            self.stop_at_recursion_limit();
6048            return None;
6049        }
6050        self.recursion_depth += 1;
6051        let result = parse(self);
6052        self.recursion_depth -= 1;
6053        result
6054    }
6055
6056    fn stop_at_recursion_limit(&mut self) {
6057        self.recursion_limit_span.get_or_insert(self.peek().span);
6058        // Stop recovery and enclosing block loops as well as recursive calls.
6059        self.pos = self.tokens.len().saturating_sub(1);
6060    }
6061
6062    fn recover(&mut self) {
6063        while !self.is_at_eof() {
6064            match self.peek().kind {
6065                TokenKind::Semicolon => {
6066                    self.advance();
6067                    return;
6068                }
6069                TokenKind::RightBrace
6070                | TokenKind::Let
6071                | TokenKind::Const
6072                | TokenKind::Function
6073                | TokenKind::Export
6074                | TokenKind::If
6075                | TokenKind::While
6076                | TokenKind::Return
6077                | TokenKind::Try
6078                | TokenKind::Throw => return,
6079                _ => {
6080                    self.advance();
6081                }
6082            }
6083        }
6084    }
6085
6086    fn peek(&self) -> &Token {
6087        self.tokens.get(self.pos).unwrap_or(&self.eof)
6088    }
6089
6090    fn peek_at(&self, offset: usize) -> &Token {
6091        self.pos
6092            .checked_add(offset)
6093            .and_then(|index| self.tokens.get(index))
6094            .unwrap_or(&self.eof)
6095    }
6096
6097    /// ASI inserts its `;` as a zero-width token; a written `;` always has width.
6098    fn at_inserted_semicolon(&self) -> bool {
6099        let token = self.peek();
6100        matches!(token.kind, TokenKind::Semicolon) && token.span.start == token.span.end
6101    }
6102
6103    fn advance(&mut self) -> Token {
6104        let tok = self.peek().clone();
6105        if self.pos < self.tokens.len().saturating_sub(1) {
6106            self.pos += 1;
6107        }
6108        tok
6109    }
6110
6111    fn is_at_eof(&self) -> bool {
6112        self.fatal.is_some() || matches!(self.peek().kind, TokenKind::Eof)
6113    }
6114
6115    /// Whether a line break separates the next token from the one before it.
6116    fn line_break_before_peek(&self) -> bool {
6117        self.source_has_line_break(self.prev_token_end(), self.peek().span.start)
6118    }
6119
6120    /// Whether a line break separates the next token from the one after it.
6121    fn line_break_after_peek(&self) -> bool {
6122        self.source_has_line_break(self.peek().span.end, self.peek_at(1).span.start)
6123    }
6124
6125    /// The ASI pass drops newline tokens, so line breaks between tokens are read
6126    /// from the source.
6127    fn source_has_line_break(&self, start: u32, end: u32) -> bool {
6128        self.source
6129            .get(start as usize..end as usize)
6130            .is_some_and(|gap| gap.contains(['\n', '\r']))
6131    }
6132
6133    /// Whether the statement being parsed ends here: at a `;`, or at a line break
6134    /// right after an `as T`. ASI keeps a `[` or `(` on the next line attached, but
6135    /// the type already stopped at the break (see `parse_type_array_inner`), and no
6136    /// expression continues past a type, so TypeScript ends the statement there.
6137    fn at_statement_end(&self) -> bool {
6138        matches!(self.peek().kind, TokenKind::Semicolon)
6139            || (self.last_as_type_end == Some(self.prev_token_end())
6140                && self.line_break_before_peek())
6141    }
6142
6143    /// Consumes the `;` that [`Parser::at_statement_end`] found, if there is one, and
6144    /// returns the statement's end offset.
6145    fn finish_statement(&mut self) -> u32 {
6146        if matches!(self.peek().kind, TokenKind::Semicolon) {
6147            return self.advance().span.end;
6148        }
6149        self.prev_token_end()
6150    }
6151
6152    /// End offset of the most recently consumed token (the body of the file's first
6153    /// token if nothing has been consumed yet).
6154    fn prev_token_end(&self) -> u32 {
6155        self.tokens
6156            .get(self.pos.saturating_sub(1))
6157            .unwrap_or(&self.eof)
6158            .span
6159            .end
6160    }
6161
6162    fn take_leading_doc(&mut self) -> Option<crate::DocComment> {
6163        let parsed = self
6164            .peek()
6165            .leading_doc
6166            .as_ref()
6167            .map(crate::parse_doc_comment)
6168            .transpose();
6169        match parsed {
6170            Ok(doc) => doc,
6171            Err(error) => {
6172                self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6173                None
6174            }
6175        }
6176    }
6177
6178    fn parse_doc(&mut self, raw: &crate::RawDoc) -> Option<crate::DocComment> {
6179        match crate::parse_doc_comment(raw) {
6180            Ok(doc) => Some(doc),
6181            Err(error) => {
6182                self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6183                None
6184            }
6185        }
6186    }
6187
6188    /// JavaScript forbids a `"use strict"` directive in a function whose parameters
6189    /// aren't all plain names: one with a default, a pattern, or `...`. As TypeScript
6190    /// does, each such parameter and the directive are reported.
6191    fn reject_use_strict_with_non_simple_params(&mut self) {
6192        let mut functions: Vec<(Vec<Span>, StmtId)> = Vec::new();
6193        let non_simple = |param: &ParamDecl| {
6194            (param.default.is_some() || param.pattern.is_some() || param.rest).then(|| {
6195                param
6196                    .pattern
6197                    .as_ref()
6198                    .map_or(param.name.span, Binding::span)
6199            })
6200        };
6201        for expr in self.ast.source_expressions() {
6202            if let ExprKind::Arrow {
6203                params,
6204                body: ArrowBody::Block(body),
6205                ..
6206            } = &expr.kind
6207            {
6208                functions.push((params.iter().filter_map(non_simple).collect(), *body));
6209            }
6210        }
6211        for stmt in self.ast.source_statements() {
6212            match &stmt.kind {
6213                StmtKind::Function { params, body, .. } => {
6214                    functions.push((params.iter().filter_map(non_simple).collect(), *body));
6215                }
6216                StmtKind::ClassDecl { members, .. } => {
6217                    for member in members {
6218                        match member {
6219                            ClassMember::Method { params, body, .. }
6220                            | ClassMember::Constructor { params, body, .. } => {
6221                                functions
6222                                    .push((params.iter().filter_map(non_simple).collect(), *body));
6223                            }
6224                            ClassMember::Accessor { param, body, .. } => {
6225                                let params = param.as_deref().and_then(non_simple);
6226                                functions.push((params.into_iter().collect(), *body));
6227                            }
6228                            _ => {}
6229                        }
6230                    }
6231                }
6232                _ => {}
6233            }
6234        }
6235        for (params, body) in functions {
6236            if params.is_empty() {
6237                continue;
6238            }
6239            let Some(directive) = self.use_strict_directive(body) else {
6240                continue;
6241            };
6242            for span in params {
6243                self.error_at(
6244                    span,
6245                    "this parameter is not allowed with a `\"use strict\"` directive",
6246                );
6247            }
6248            self.error_at_with_help(
6249                directive,
6250                "a `\"use strict\"` directive can't be used with a parameter list that has defaults, patterns, or a rest parameter",
6251                vec!["remove the directive; Submilli code is always strict".into()],
6252            );
6253        }
6254    }
6255
6256    /// The span of a `"use strict"` directive in the prologue of a function body:
6257    /// the string-literal statements it starts with. As in TypeScript, the string's
6258    /// value decides, so an escape spelling it counts too; a template doesn't.
6259    fn use_strict_directive(&self, body: StmtId) -> Option<Span> {
6260        let StmtKind::Block(stmts) = &self.ast.try_stmt(body).ok()?.kind else {
6261            return None;
6262        };
6263        for stmt in stmts {
6264            let stmt = self.ast.try_stmt(*stmt).ok()?;
6265            let StmtKind::Expr(expr) = stmt.kind else {
6266                return None;
6267            };
6268            let expr = self.ast.try_expr(expr).ok()?;
6269            let ExprKind::String(value) = &expr.kind else {
6270                return None;
6271            };
6272            if self.source.as_bytes().get(expr.span.start as usize) == Some(&b'`') {
6273                return None;
6274            }
6275            if value == "use strict" {
6276                return Some(stmt.span);
6277            }
6278        }
6279        None
6280    }
6281
6282    fn error_at(&mut self, span: Span, message: impl Into<String>) {
6283        self.diagnostics.push(Diagnostic {
6284            severity: Severity::Error,
6285            span,
6286            message: message.into(),
6287            help: vec![],
6288            notes: vec![],
6289        });
6290    }
6291
6292    /// `==` is `===` here, so `x == null` would silently miss `undefined` — the
6293    /// one case where TypeScript's loose equality differs in a way code relies on.
6294    fn reject_loose_nullish_comparison(
6295        &mut self,
6296        op: BinOp,
6297        lhs: ExprId,
6298        rhs: ExprId,
6299    ) -> Option<()> {
6300        let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
6301        let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
6302        let (nullish, operand, nullish_first) =
6303            match (self.nullish_operand(lhs)?, self.nullish_operand(rhs)?) {
6304                (Some(left), Some(right)) => {
6305                    self.reject_nullish_against_nullish(op, lhs_span, rhs_span, [left, right]);
6306                    return Some(());
6307                }
6308                (None, Some(nullish)) => (nullish, lhs, false),
6309                (Some(nullish), None) => (nullish, rhs, true),
6310                (None, None) => return Some(()),
6311            };
6312        let comparison = LooseComparison {
6313            nullish,
6314            nullish_first,
6315            negated: op == BinOp::NotEq,
6316        };
6317        let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
6318        let x = self.single_line_source(operand_span).unwrap_or("x");
6319        let strict = self.strict_suggestion(&comparison, x);
6320        let either = self.either_suggestion(&comparison, operand, x)?;
6321        let literal = comparison.nullish.literal.text();
6322        // Quote the comparison as written, so `null == z` stays in that order.
6323        let written = self
6324            .single_line_source(self.span(lhs_span.start, rhs_span.end))
6325            .map_or_else(
6326                || {
6327                    let written_op = if comparison.negated { "!=" } else { "==" };
6328                    comparison.ordered(x, written_op, literal)
6329                },
6330                str::to_string,
6331            );
6332        self.error_at_with_help(
6333            self.span(lhs_span.start, rhs_span.end),
6334            format!(
6335                "`{written}` does not also match `{}`: `==` compares like `===`",
6336                comparison.nullish.literal.other().text()
6337            ),
6338            vec![
6339                either,
6340                format!("to test for `{literal}` alone, write `{strict}`"),
6341            ],
6342        );
6343        Some(())
6344    }
6345
6346    /// Both sides are always nullish. TypeScript's `==` treats `null` and
6347    /// `undefined` as equal, so the comparison has one value, which the help
6348    /// names; a `void` operand with effects still runs, as a statement.
6349    fn reject_nullish_against_nullish(
6350        &mut self,
6351        op: BinOp,
6352        lhs: Span,
6353        rhs: Span,
6354        sides: [NullishOperand; 2],
6355    ) {
6356        let span = self.span(lhs.start, rhs.end);
6357        // A comparison written over several lines can't be quoted on one, so
6358        // the message names it without the source.
6359        let subject = self.single_line_source(span).map_or_else(
6360            || "this comparison".to_string(),
6361            |written| format!("`{written}`"),
6362        );
6363        let value = if op == BinOp::NotEq { "false" } else { "true" };
6364        let effects: Vec<String> = sides
6365            .iter()
6366            .filter_map(|side| side.effect)
6367            .map(|effect| {
6368                self.single_line_source(effect.operand).map_or_else(
6369                    || "the `void` operand's expression".to_string(),
6370                    |statement| format!("`{statement};`"),
6371                )
6372            })
6373            .collect();
6374        let help = if effects.is_empty() {
6375            format!("in TypeScript this is always `{value}`; write `{value}`")
6376        } else {
6377            format!(
6378                "in TypeScript this is always `{value}`; run {} {}, then use `{value}`",
6379                effects.join(" and "),
6380                if effects.len() > 1 {
6381                    "each as its own statement"
6382                } else {
6383                    "as its own statement"
6384                }
6385            )
6386        };
6387        self.error_at_with_help(
6388            span,
6389            format!("{subject} compares two nullish values: `==` compares like `===`"),
6390            vec![help],
6391        );
6392    }
6393
6394    /// The strict comparison against the written nullish value. A `void`
6395    /// operand with effects stays as written, so the operand runs once.
6396    fn strict_suggestion(&self, comparison: &LooseComparison, x: &str) -> String {
6397        let eq = comparison.strict_op();
6398        match comparison.nullish.effect {
6399            Some(effect) => {
6400                let void = self.single_line_source(effect.void).unwrap_or("void expr");
6401                comparison.ordered(x, eq, void)
6402            }
6403            None => format!("{x} {eq} {}", comparison.nullish.literal.text()),
6404        }
6405    }
6406
6407    /// "To test for either" names the operand twice, so one with effects is
6408    /// bound to a `const` first, and a `void` operand's expression runs as a
6409    /// statement of its own.
6410    fn either_suggestion(
6411        &mut self,
6412        comparison: &LooseComparison,
6413        operand: ExprId,
6414        x: &str,
6415    ) -> Option<String> {
6416        let mut steps = Vec::new();
6417        let tested = if self.repeats_without_effect(operand)? {
6418            x
6419        } else {
6420            steps.push(format!("bind `{x}` to a `const` (`const value = {x};`)"));
6421            "value"
6422        };
6423        if let Some(effect) = comparison.nullish.effect {
6424            let statement = self.single_line_source(effect.operand).unwrap_or("expr");
6425            let step = format!("run `{statement};` as its own statement");
6426            if comparison.nullish_first {
6427                steps.insert(0, step);
6428            } else {
6429                steps.push(step);
6430            }
6431        }
6432        let eq = comparison.strict_op();
6433        let joiner = if comparison.negated { "&&" } else { "||" };
6434        let both = format!("{tested} {eq} null {joiner} {tested} {eq} undefined");
6435        Some(if steps.is_empty() {
6436            format!("to test for either, write `{both}`")
6437        } else {
6438            format!(
6439                "to test for either, first {}, then write `{both}`",
6440                steps.join(", then ")
6441            )
6442        })
6443    }
6444
6445    /// Whether naming `id` twice evaluates it twice with no effect: a literal,
6446    /// or a reference path.
6447    fn repeats_without_effect(&mut self, id: ExprId) -> Option<bool> {
6448        let unwrapped = self.unparenthesized(id)?;
6449        let expr = parse_arena_result(self.ast.try_expr(unwrapped), &mut self.fatal)?;
6450        if is_literal(&expr.kind) {
6451            return Some(true);
6452        }
6453        if self
6454            .nullish_operand(unwrapped)?
6455            .is_some_and(|nullish| nullish.effect.is_none())
6456        {
6457            return Some(true);
6458        }
6459        self.is_reference_path(id)
6460    }
6461
6462    /// `null`, `undefined` or `void e`, looking through parentheses.
6463    fn nullish_operand(&mut self, id: ExprId) -> Option<Option<NullishOperand>> {
6464        let id = self.unparenthesized(id)?;
6465        let expr = parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?;
6466        let span = expr.span;
6467        Some(match &expr.kind {
6468            ExprKind::Null => Some(NullishOperand {
6469                literal: Nullish::Null,
6470                effect: None,
6471            }),
6472            ExprKind::Identifier(ident) if ident.name == "undefined" => Some(NullishOperand {
6473                literal: Nullish::Undefined,
6474                effect: None,
6475            }),
6476            ExprKind::Void { operand } => {
6477                let operand_id = self.unparenthesized(*operand)?;
6478                let operand =
6479                    parse_arena_result(self.ast.try_expr(operand_id), &mut self.fatal)?.clone();
6480                // A literal or a reference path reads with no effect to keep.
6481                let effect_free =
6482                    is_literal(&operand.kind) || self.is_reference_path(operand_id)?;
6483                let effect = (!effect_free).then_some(VoidEffect {
6484                    void: span,
6485                    operand: operand.span,
6486                });
6487                Some(NullishOperand {
6488                    literal: Nullish::Undefined,
6489                    effect,
6490                })
6491            }
6492            _ => None,
6493        })
6494    }
6495
6496    /// An identifier, `this`, or a `.field` chain on one: naming it twice evaluates
6497    /// it twice with no effect.
6498    fn is_reference_path(&mut self, id: ExprId) -> Option<bool> {
6499        let mut id = id;
6500        loop {
6501            match &parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6502                ExprKind::Identifier(_) | ExprKind::This => return Some(true),
6503                ExprKind::FieldAccess { receiver, .. } => id = *receiver,
6504                ExprKind::Paren(inner) => id = *inner,
6505                _ => return Some(false),
6506            }
6507        }
6508    }
6509
6510    fn unparenthesized(&mut self, id: ExprId) -> Option<ExprId> {
6511        let mut id = id;
6512        loop {
6513            match parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6514                ExprKind::Paren(inner) => id = inner,
6515                _ => return Some(id),
6516            }
6517        }
6518    }
6519
6520    fn single_line_source(&self, span: Span) -> Option<&'a str> {
6521        self.source
6522            .get(span.start as usize..span.end as usize)
6523            .filter(|text| !text.contains('\n'))
6524    }
6525
6526    fn error_at_with_help(&mut self, span: Span, message: impl Into<String>, help: Vec<String>) {
6527        self.diagnostics.push(Diagnostic {
6528            severity: Severity::Error,
6529            span,
6530            message: message.into(),
6531            help,
6532            notes: vec![],
6533        });
6534    }
6535
6536    fn invariant_failure<T>(&mut self, message: &str) -> Option<T> {
6537        self.fatal.get_or_insert_with(|| CompilerFailure::Internal {
6538            stage: CompilerStage::Parse,
6539            span: None,
6540            message: message.into(),
6541        });
6542        self.pos = self.tokens.len();
6543        None
6544    }
6545
6546    fn error_at_peek(&mut self, message: impl Into<String>) {
6547        let span = self.peek().span;
6548        self.error_at(span, message);
6549    }
6550
6551    fn error_at_peek_with_help(&mut self, message: impl Into<String>, help: Vec<String>) {
6552        let span = self.peek().span;
6553        self.error_at_with_help(span, message, help);
6554    }
6555
6556    fn error_count(&self) -> usize {
6557        self.diagnostics
6558            .iter()
6559            .filter(|d| d.severity == Severity::Error)
6560            .count()
6561    }
6562}
6563
6564pub(crate) fn is_property_name(kind: &TokenKind) -> bool {
6565    matches!(
6566        kind,
6567        TokenKind::Identifier
6568            | TokenKind::BooleanLiteral(_)
6569            | TokenKind::NullLiteral
6570            | TokenKind::Let
6571            | TokenKind::Const
6572            | TokenKind::Function
6573            | TokenKind::If
6574            | TokenKind::Else
6575            | TokenKind::While
6576            | TokenKind::Do
6577            | TokenKind::For
6578            | TokenKind::Break
6579            | TokenKind::Continue
6580            | TokenKind::Return
6581            | TokenKind::Switch
6582            | TokenKind::Case
6583            | TokenKind::Default
6584            | TokenKind::Void
6585            | TokenKind::Interface
6586            | TokenKind::Enum
6587            | TokenKind::In
6588            | TokenKind::Typeof
6589            | TokenKind::Import
6590            | TokenKind::Export
6591            | TokenKind::New
6592            | TokenKind::Try
6593            | TokenKind::Catch
6594            | TokenKind::Finally
6595            | TokenKind::Throw
6596            | TokenKind::Class
6597            | TokenKind::Extends
6598            | TokenKind::Implements
6599            | TokenKind::Super
6600            | TokenKind::This
6601    )
6602}
6603
6604fn is_reserved_identifier_word(kind: &TokenKind) -> bool {
6605    matches!(
6606        kind,
6607        TokenKind::BooleanLiteral(_)
6608            | TokenKind::NullLiteral
6609            | TokenKind::Let
6610            | TokenKind::Const
6611            | TokenKind::Function
6612            | TokenKind::If
6613            | TokenKind::Else
6614            | TokenKind::While
6615            | TokenKind::Do
6616            | TokenKind::For
6617            | TokenKind::Break
6618            | TokenKind::Continue
6619            | TokenKind::Return
6620            | TokenKind::Switch
6621            | TokenKind::Case
6622            | TokenKind::Default
6623            | TokenKind::Export
6624            | TokenKind::Void
6625            | TokenKind::Interface
6626            | TokenKind::Enum
6627            | TokenKind::In
6628            | TokenKind::Typeof
6629            | TokenKind::Import
6630            | TokenKind::New
6631            | TokenKind::Try
6632            | TokenKind::Catch
6633            | TokenKind::Finally
6634            | TokenKind::Throw
6635            | TokenKind::Class
6636            | TokenKind::Extends
6637            | TokenKind::Implements
6638            | TokenKind::Super
6639            | TokenKind::This
6640    )
6641}
6642
6643/// Which statement a `{ … }` specifier list belongs to. An import binds local
6644/// names, which strict mode restricts; an export's are module export names,
6645/// which it doesn't.
6646#[derive(Clone, Copy, PartialEq, Eq)]
6647enum SpecifierList {
6648    Import,
6649    Export,
6650}
6651
6652impl SpecifierList {
6653    fn noun(self) -> &'static str {
6654        match self {
6655            SpecifierList::Import => "import",
6656            SpecifierList::Export => "export",
6657        }
6658    }
6659}
6660
6661/// Words strict mode reserves that Submilli otherwise lexes as identifiers: they
6662/// can't name a binding or a type, but stay valid as property names and modifiers.
6663/// (`implements`, `interface` and `let` are keywords already.)
6664const STRICT_MODE_RESERVED_WORDS: [&str; 6] = [
6665    "package",
6666    "private",
6667    "protected",
6668    "public",
6669    "static",
6670    "yield",
6671];
6672
6673fn reserved_keyword_rename_example(keyword: &str) -> &'static str {
6674    match keyword {
6675        "default" => "defaultValue",
6676        _ => "valueName",
6677    }
6678}
6679
6680fn mixed_logical(prev: BinOp, next: BinOp) -> bool {
6681    let prev_logical = matches!(prev, BinOp::Or | BinOp::And);
6682    let next_logical = matches!(next, BinOp::Or | BinOp::And);
6683    let prev_nullish = matches!(prev, BinOp::NullishCoalesce);
6684    let next_nullish = matches!(next, BinOp::NullishCoalesce);
6685    (prev_logical && next_nullish) || (prev_nullish && next_logical)
6686}
6687
6688/// Spells out both groupings in the order the operators were written. Called
6689/// only for pairs `mixed_logical` accepts, so the non-`??` operator is `||` or `&&`.
6690fn mixed_logical_help(prev: BinOp, next: BinOp) -> String {
6691    let symbol = |op: BinOp| if op == BinOp::And { "&&" } else { "||" };
6692    let (first, second) = if prev == BinOp::NullishCoalesce {
6693        ("??", symbol(next))
6694    } else {
6695        (symbol(prev), "??")
6696    };
6697    format!(
6698        "add parentheses to choose the grouping: `(a {first} b) {second} c` or `a {first} (b {second} c)`"
6699    )
6700}
6701
6702const LOGICAL_AND_PREC: u8 = 2;
6703
6704/// One side of a loose comparison that is always nullish.
6705#[derive(Clone, Copy)]
6706struct NullishOperand {
6707    literal: Nullish,
6708    /// Set for `void e` when `e` is not a literal and may have effects.
6709    effect: Option<VoidEffect>,
6710}
6711
6712#[derive(Clone, Copy, PartialEq, Eq)]
6713enum Nullish {
6714    Null,
6715    Undefined,
6716}
6717
6718impl Nullish {
6719    fn text(self) -> &'static str {
6720        match self {
6721            Nullish::Null => "null",
6722            Nullish::Undefined => "undefined",
6723        }
6724    }
6725
6726    /// The nullish value a loose comparison against `self` fails to match.
6727    fn other(self) -> Self {
6728        match self {
6729            Nullish::Null => Nullish::Undefined,
6730            Nullish::Undefined => Nullish::Null,
6731        }
6732    }
6733}
6734
6735/// A rejected `x == null`-style comparison, as written.
6736struct LooseComparison {
6737    nullish: NullishOperand,
6738    /// Whether the nullish side is written first, as in `null == x`.
6739    nullish_first: bool,
6740    /// `!=` rather than `==`.
6741    negated: bool,
6742}
6743
6744impl LooseComparison {
6745    fn strict_op(&self) -> &'static str {
6746        if self.negated { "!==" } else { "===" }
6747    }
6748
6749    /// `x op nullish`, in the order the comparison was written.
6750    fn ordered(&self, x: &str, op: &str, nullish: &str) -> String {
6751        if self.nullish_first {
6752            format!("{nullish} {op} {x}")
6753        } else {
6754            format!("{x} {op} {nullish}")
6755        }
6756    }
6757}
6758
6759#[derive(Clone, Copy)]
6760struct VoidEffect {
6761    /// The whole `void e`.
6762    void: Span,
6763    /// The `e`, without enclosing parentheses.
6764    operand: Span,
6765}
6766
6767fn is_literal(kind: &ExprKind) -> bool {
6768    matches!(
6769        kind,
6770        ExprKind::Number(_)
6771            | ExprKind::BigInt(_)
6772            | ExprKind::String(_)
6773            | ExprKind::Boolean(_)
6774            | ExprKind::Null
6775    )
6776}
6777
6778/// What `Parser::check_parameter_order` needs from either kind of parameter list.
6779struct ParamOrder<'p> {
6780    optional: bool,
6781    /// Neither optional, defaulted, nor rest.
6782    required: bool,
6783    /// `None` for a destructuring pattern.
6784    name: Option<&'p str>,
6785    span: Span,
6786}
6787
6788impl<'p> ParamOrder<'p> {
6789    fn of_param(param: &'p ParamDecl) -> Self {
6790        let (name, span) = match &param.pattern {
6791            Some(binding) => (None, binding.span()),
6792            None => (Some(param.name.name.as_str()), param.name.span),
6793        };
6794        Self {
6795            optional: param.optional,
6796            required: !param.is_omittable() && !param.rest,
6797            name,
6798            span,
6799        }
6800    }
6801
6802    fn of_field(field: &'p TypeAnnotationField) -> Self {
6803        Self {
6804            optional: field.optional,
6805            required: !field.optional && !field.rest,
6806            name: Some(field.name.name.as_str()),
6807            span: field.name.span,
6808        }
6809    }
6810}
6811
6812/// A binary operator at the cursor, which may span several tokens.
6813struct BinaryToken {
6814    op: BinOp,
6815    precedence: u8,
6816    tokens: usize,
6817}
6818
6819/// `<<`, `>>`, `>>>` or their compound assignments, read from adjacent angle tokens.
6820struct ShiftToken {
6821    op: BinOp,
6822    tokens: usize,
6823    /// The operator ends in `=`: `<<=`, `>>=`, `>>>=`.
6824    assignment: bool,
6825}
6826
6827impl ShiftToken {
6828    fn new(op: BinOp, tokens: usize, assignment: bool) -> Self {
6829        Self {
6830            op,
6831            tokens,
6832            assignment,
6833        }
6834    }
6835}
6836
6837/// Between relational (7) and additive (9) in the `peek_binop` table.
6838const SHIFT_PRECEDENCE: u8 = 8;
6839
6840#[derive(Clone, Copy, PartialEq)]
6841enum TupleElementLabel {
6842    None,
6843    /// `name: T`
6844    Required,
6845    /// `name?: T`
6846    Optional,
6847}
6848
6849fn peek_binop(kind: &TokenKind) -> Option<(BinOp, u8)> {
6850    Some(match kind {
6851        TokenKind::QuestionQuestion => (BinOp::NullishCoalesce, 0),
6852        TokenKind::PipePipe => (BinOp::Or, 1),
6853        TokenKind::AmpAmp => (BinOp::And, LOGICAL_AND_PREC),
6854        TokenKind::Pipe => (BinOp::BitOr, 3),
6855        TokenKind::Caret => (BinOp::BitXor, 4),
6856        TokenKind::Amp => (BinOp::BitAnd, 5),
6857        TokenKind::EqEqEq | TokenKind::EqEq => (BinOp::Eq, 6),
6858        TokenKind::BangEqEq | TokenKind::BangEq => (BinOp::NotEq, 6),
6859        TokenKind::LessThan => (BinOp::Lt, 7),
6860        TokenKind::GreaterThan => (BinOp::Gt, 7),
6861        TokenKind::LessEquals => (BinOp::Le, 7),
6862        TokenKind::GreaterEquals => (BinOp::Ge, 7),
6863        TokenKind::In => (BinOp::In, 7),
6864        // Shifts take `SHIFT_PRECEDENCE` (8); they span several angle tokens, so
6865        // `Parser::peek_shift` recognizes them rather than this table.
6866        TokenKind::Plus => (BinOp::Add, 9),
6867        TokenKind::Minus => (BinOp::Sub, 9),
6868        TokenKind::Star => (BinOp::Mul, 10),
6869        TokenKind::Slash => (BinOp::Div, 10),
6870        TokenKind::Percent => (BinOp::Rem, 10),
6871        // `**` recurses with `prec` not `prec + 1` — that's what makes it right-associative.
6872        TokenKind::StarStar => (BinOp::Pow, 11),
6873        _ => return None,
6874    })
6875}
6876
6877/// After `as` or `satisfies`, a token that can only extend or end an operand, never
6878/// begin a type. `[` counts, as TypeScript reads `delete as [0]` as an index, so a
6879/// tuple cast of a binding named `delete` needs parentheses.
6880fn cannot_start_type(kind: &TokenKind) -> bool {
6881    matches!(
6882        kind,
6883        TokenKind::Dot
6884            | TokenKind::QuestionDot
6885            | TokenKind::LeftBracket
6886            | TokenKind::Bang
6887            | TokenKind::Semicolon
6888            | TokenKind::RightParen
6889            | TokenKind::RightBrace
6890            | TokenKind::Comma
6891            | TokenKind::Eof
6892    )
6893}
6894
6895/// The lowest precedence the right operand of `op` may contain. `**` is
6896/// right-associative, so it recurses at its own level. An operand of `??` may
6897/// not be an unparenthesized `||` / `&&` expression, so it stops below them too.
6898fn right_operand_min_prec(op: BinOp, prec: u8) -> u8 {
6899    match op {
6900        BinOp::Pow => prec,
6901        BinOp::NullishCoalesce => LOGICAL_AND_PREC + 1,
6902        _ => prec + 1,
6903    }
6904}
6905
6906fn compound_op_for_token(kind: &TokenKind) -> Option<BinOp> {
6907    Some(match kind {
6908        TokenKind::PlusEquals => BinOp::Add,
6909        TokenKind::MinusEquals => BinOp::Sub,
6910        TokenKind::StarEquals => BinOp::Mul,
6911        TokenKind::SlashEquals => BinOp::Div,
6912        TokenKind::PercentEquals => BinOp::Rem,
6913        TokenKind::StarStarEquals => BinOp::Pow,
6914        TokenKind::AmpEquals => BinOp::BitAnd,
6915        TokenKind::PipeEquals => BinOp::BitOr,
6916        TokenKind::CaretEquals => BinOp::BitXor,
6917        _ => return None,
6918    })
6919}
6920
6921fn is_assign_lookahead(kind: &TokenKind) -> bool {
6922    matches!(kind, TokenKind::Equals) || compound_op_for_token(kind).is_some()
6923}
6924
6925/// Parser productions use `None` for recovery; arena errors also latch a fatal
6926/// cause so speculative parsing cannot turn them into ordinary syntax errors.
6927fn parse_arena_result<T>(
6928    result: Result<T, crate::arena::ArenaError>,
6929    fatal: &mut Option<CompilerFailure>,
6930) -> Option<T> {
6931    match result {
6932        Ok(value) => Some(value),
6933        Err(error) => {
6934            fatal.get_or_insert_with(|| error.into_compiler_failure(CompilerStage::Parse));
6935            None
6936        }
6937    }
6938}
6939
6940/// Whether a rest parameter's annotation names an array: `T[]`, or `readonly T[]`.
6941fn is_rest_array_annotation(ty: &crate::TypeAnnotation) -> bool {
6942    use crate::ast::TypeAnnotationKind;
6943    match &ty.kind {
6944        TypeAnnotationKind::Array(_) => true,
6945        TypeAnnotationKind::Readonly(inner) => matches!(inner.kind, TypeAnnotationKind::Array(_)),
6946        _ => false,
6947    }
6948}
6949
6950#[cfg(test)]
6951mod tests {
6952    use super::{MAX_ERRORS, MAX_PARSE_DEPTH, Parser, parse};
6953    use crate::source::Sources;
6954    use crate::{Asi, Ast, ExprKind, FileId, ImportKind, StmtKind, Token, TokenKind, diagnostics};
6955
6956    const F: FileId = FileId(0);
6957
6958    #[test]
6959    fn malformed_direct_token_streams_return_typed_failures() {
6960        use crate::compiler_error::CompilerFailure;
6961        for tokens in [
6962            Vec::new(),
6963            vec![Token::new(
6964                TokenKind::Identifier,
6965                crate::Span::new(F, 0, 2).unwrap(),
6966            )],
6967            vec![
6968                Token::new(
6969                    TokenKind::Identifier,
6970                    crate::Span {
6971                        file: F,
6972                        start: 1,
6973                        end: 2,
6974                    },
6975                ),
6976                Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
6977            ],
6978            vec![
6979                Token::new(TokenKind::Eof, crate::Span::at(F)),
6980                Token::new(TokenKind::Eof, crate::Span::at(F)),
6981            ],
6982            vec![
6983                Token::new(
6984                    TokenKind::Identifier,
6985                    crate::Span {
6986                        file: F,
6987                        start: 2,
6988                        end: 0,
6989                    },
6990                ),
6991                Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
6992            ],
6993            vec![Token::new(TokenKind::Eof, crate::Span::at(FileId(9)))],
6994        ] {
6995            let error = super::parse_checked("é", tokens, F).expect_err("invalid token metadata");
6996            assert!(matches!(
6997                error.fatal,
6998                Some(CompilerFailure::Internal { .. })
6999            ));
7000        }
7001    }
7002
7003    #[test]
7004    fn malformed_documentation_metadata_returns_a_fatal_error() {
7005        let source = "/** hello */ function main(): void {}";
7006        let valid = tokens_of(source);
7007        for doc in [
7008            crate::RawDoc {
7009                text: "/** hi */".into(),
7010                span: crate::Span {
7011                    file: F,
7012                    start: u32::MAX,
7013                    end: u32::MAX,
7014                },
7015            },
7016            crate::RawDoc {
7017                text: "/** different */".into(),
7018                span: crate::Span::new(F, 0, 12).unwrap(),
7019            },
7020            crate::RawDoc {
7021                text: "/** hello */".into(),
7022                span: crate::Span::new(FileId(9), 0, 12).unwrap(),
7023            },
7024        ] {
7025            let mut tokens = valid.clone();
7026            tokens[0].leading_doc = Some(doc);
7027            let error = super::parse_checked(source, tokens, F).expect_err("invalid doc metadata");
7028            assert!(error.to_string().contains("documentation metadata"));
7029        }
7030        assert!(super::parse_checked(source, valid, F).is_ok());
7031    }
7032
7033    #[test]
7034    fn invalid_parser_dispatch_survives_diagnostic_rollback() {
7035        let mut parser = parser_from_source("let x = 1;");
7036        assert!(parser.try_parse_type_args_only().is_none());
7037        parser.diagnostics.clear();
7038        assert!(parser.fatal.is_some());
7039        assert!(parser.parse_expression().is_none());
7040    }
7041
7042    #[test]
7043    fn invalid_arena_read_stops_parser_and_survives_diagnostic_rollback() {
7044        use crate::{
7045            ExprId,
7046            compiler_error::{CompilerFailure, CompilerStage},
7047        };
7048        let mut parser = parser_from_source("let x = 1;");
7049        assert!(parser.statement_kind_for(ExprId(u32::MAX)).is_none());
7050        assert!(matches!(
7051            parser.fatal,
7052            Some(CompilerFailure::Internal {
7053                stage: CompilerStage::Parse,
7054                span: None,
7055                ..
7056            })
7057        ));
7058        parser.diagnostics.clear();
7059        let position = parser.pos;
7060        assert!(parser.parse_statement().is_none());
7061        parser.recover();
7062        assert_eq!(parser.pos, position);
7063        assert!(parser.ast.expr_ids().unwrap().next().is_none());
7064    }
7065
7066    fn tokens_of(source: &str) -> Vec<Token> {
7067        let mut asi = Asi::new(source, crate::FileId(0));
7068        let mut tokens = Vec::new();
7069        loop {
7070            let tok = asi.next_token();
7071            let is_eof = matches!(tok.kind, TokenKind::Eof);
7072            tokens.push(tok);
7073            if is_eof {
7074                break;
7075            }
7076        }
7077        let diags = asi.into_diagnostics();
7078        assert!(diags.is_empty(), "unexpected lexer diagnostics: {diags:?}");
7079        tokens
7080    }
7081
7082    fn parse_str(source: &str) -> (Ast, Vec<crate::Diagnostic>) {
7083        parse(source, tokens_of(source), crate::FileId(0))
7084    }
7085
7086    fn parser_from_source(source: &str) -> Parser<'_> {
7087        Parser {
7088            source,
7089            tokens: tokens_of(source),
7090            file: F,
7091            pos: 0,
7092            ast: Ast::new(),
7093            diagnostics: Vec::new(),
7094            block_depth: 0,
7095            class_member_body_depth: 0,
7096            function_expression_body_depth: 0,
7097            recursion_depth: 0,
7098            recursion_limit_span: None,
7099            resume_at_next_member: false,
7100            last_as_type_end: None,
7101            eof: Token::new(
7102                TokenKind::Eof,
7103                crate::Span::new(F, source.len() as u32, source.len() as u32).unwrap(),
7104            ),
7105            fatal: None,
7106        }
7107    }
7108
7109    #[test]
7110    fn recursion_budget_accepts_boundary_and_restores_depth() {
7111        let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH - 1));
7112        let mut parser = parser_from_source(&source);
7113        assert!(parser.parse_unary().is_some());
7114        assert_eq!(parser.recursion_depth, 0);
7115        assert!(parser.recursion_limit_span.is_none());
7116
7117        let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH));
7118        let mut parser = parser_from_source(&source);
7119        assert!(parser.parse_unary().is_none());
7120        assert_eq!(parser.recursion_depth, 0);
7121        assert!(parser.recursion_limit_span.is_some());
7122    }
7123
7124    #[test]
7125    fn parse_empty_input() {
7126        let (ast, diags) = parse_str("");
7127        assert!(diags.is_empty());
7128        assert!(ast.top_level.is_empty());
7129    }
7130
7131    #[test]
7132    fn any_type_is_rejected() {
7133        // `any` is not supported — the parser rejects it in every type position
7134        // (annotation, cast target, array/generic element) and points to the fix.
7135        for src in [
7136            "function main(): void { const x: any = 1; }",
7137            "function main(): void { const x = 1 as any; }",
7138            "function main(): void { const x: any[] = []; }",
7139        ] {
7140            let (_ast, diags) = parse_str(src);
7141            assert!(
7142                diags
7143                    .iter()
7144                    .any(|d| d.message.contains("`any` is not supported")),
7145                "expected an `any` rejection for {src:?}, got: {diags:?}",
7146            );
7147            assert!(
7148                diags
7149                    .iter()
7150                    .any(|d| d.help.iter().any(|h| h.contains("`unknown`"))),
7151                "expected the fix-shape help for {src:?}, got: {diags:?}",
7152            );
7153        }
7154    }
7155
7156    #[test]
7157    fn any_catch_binding_help_suggests_catch_forms() {
7158        // `unknown` is not a valid catch binding type, so the general `any`
7159        // help would send the reader to a second error.
7160        let (_ast, diags) = parse_str("function main(): void { try { } catch (e: any) { } }");
7161        let any_diags: Vec<_> = diags
7162            .iter()
7163            .filter(|d| d.message.contains("`any` is not supported"))
7164            .collect();
7165        assert_eq!(any_diags.len(), 1, "got: {diags:?}");
7166        assert!(
7167            any_diags[0].help.iter().any(|h| h.contains("`catch (e)`")),
7168            "got: {diags:?}"
7169        );
7170        assert!(
7171            !any_diags[0].help.iter().any(|h| h.contains("`unknown`")),
7172            "got: {diags:?}"
7173        );
7174    }
7175
7176    #[test]
7177    fn arrow_return_type_scanner_matches_type_grammar() {
7178        // Every form the annotation grammar accepts must also be accepted by
7179        // the arrow-disambiguator's lookahead (scan_past_type_annotation):
7180        // a miss there turns a valid arrow into `expected expression`.
7181        // When adding a type form to parse_type_array, add a row here.
7182        for ty in [
7183            "number",
7184            "void",
7185            "null",
7186            "ns.Foo",
7187            "Map<string, number>",
7188            "ns.Foo<string>[]",
7189            "number[]",
7190            "[number, number]",
7191            "[number, [string, boolean]]",
7192            "[number, string][]",
7193            "{ a: number }",
7194            "{ a: number }[]",
7195            "\"on\" | \"off\"",
7196            "1 | 2",
7197            "Point | null",
7198            "| number | string",
7199            "(n: number) => number",
7200            "(n: number) => [number, number]",
7201            "readonly number[]",
7202            "readonly number[][]",
7203            "readonly [number, string]",
7204            "readonly number[] | null",
7205            "[first: number, second: string]",
7206            "[new: number, string]",
7207            "keyof { a: number }",
7208        ] {
7209            let annotation = format!("function f(): void {{ const a: {ty} = x; }}");
7210            let (_ast, diags) = parse_str(&annotation);
7211            assert!(
7212                diags.is_empty(),
7213                "annotation grammar rejected {ty:?}: {diags:?}",
7214            );
7215
7216            let arrow = format!("function f(): void {{ const g = (): {ty} => x; }}");
7217            let (_ast, diags) = parse_str(&arrow);
7218            assert!(
7219                diags.is_empty(),
7220                "arrow lookahead rejected return type {ty:?}: {diags:?}",
7221            );
7222        }
7223
7224        // Type-predicate returns ride the same scanner.
7225        let (_ast, diags) =
7226            parse_str("function f(): void { const g = (v: unknown): v is string => x; }");
7227        assert!(
7228            diags.is_empty(),
7229            "arrow lookahead rejected predicate return: {diags:?}",
7230        );
7231    }
7232
7233    #[test]
7234    fn bare_semicolons_are_silent_empty_statements() {
7235        let (ast, diags) = parse_str(";");
7236        assert!(diags.is_empty());
7237        assert!(ast.top_level.is_empty());
7238    }
7239
7240    #[test]
7241    fn parse_single_non_expression_token_errors() {
7242        let (ast, diags) = parse_str("}");
7243        assert_eq!(diags.len(), 1);
7244        assert_eq!(diags[0].message, "expected expression");
7245        assert!(ast.top_level.is_empty());
7246    }
7247
7248    #[test]
7249    fn recovery_across_many_bad_starts() {
7250        let (ast, diags) = parse_str("} } } x;");
7251        assert_eq!(diags.len(), 3);
7252        for d in &diags {
7253            assert_eq!(d.message, "expected expression");
7254        }
7255        assert_eq!(ast.top_level.len(), 1);
7256    }
7257
7258    #[test]
7259    fn recovery_consumes_semicolon() {
7260        let mut p = parser_from_source("foo ;");
7261        p.error_at_peek("test");
7262        p.recover();
7263        assert!(matches!(
7264            p.peek().kind,
7265            TokenKind::Semicolon | TokenKind::Eof
7266        ));
7267        assert!(matches!(p.peek().kind, TokenKind::Eof));
7268    }
7269
7270    #[test]
7271    fn recovery_stops_at_right_brace() {
7272        let mut p = parser_from_source("foo }");
7273        p.error_at_peek("test");
7274        p.recover();
7275        assert!(matches!(p.peek().kind, TokenKind::RightBrace));
7276    }
7277
7278    #[test]
7279    fn recovery_stops_at_let_keyword() {
7280        let mut p = parser_from_source("foo let x");
7281        p.error_at_peek("test");
7282        p.recover();
7283        assert!(matches!(p.peek().kind, TokenKind::Let));
7284    }
7285
7286    #[test]
7287    fn recovery_stops_at_function_keyword() {
7288        let mut p = parser_from_source("foo function bar");
7289        p.error_at_peek("test");
7290        p.recover();
7291        assert!(matches!(p.peek().kind, TokenKind::Function));
7292    }
7293
7294    #[test]
7295    fn error_cap_at_20() {
7296        let source = "} ".repeat(25);
7297        let (_, diags) = parse_str(&source);
7298        assert_eq!(diags.len(), MAX_ERRORS);
7299    }
7300
7301    #[test]
7302    fn driver_makes_progress_past_unmatched_brace() {
7303        let (ast, diags) = parse_str("} x;");
7304        assert_eq!(diags.len(), 1);
7305        assert_eq!(diags[0].message, "expected expression");
7306        assert_eq!(ast.top_level.len(), 1);
7307    }
7308
7309    #[test]
7310    fn snapshot_multi_error_render() {
7311        let source = "foo bar;\nbaz qux;\nhello world;";
7312        let (_, diags) = parse_str(source);
7313        let (sources, _) = Sources::single("script.subm", source).unwrap();
7314        let rendered: String = diags
7315            .iter()
7316            .map(|d| diagnostics::render(d, &sources))
7317            .collect::<Vec<_>>()
7318            .join("\n");
7319        insta::assert_snapshot!(rendered);
7320    }
7321
7322    fn single_stmt(ast: &Ast) -> &crate::Stmt {
7323        assert_eq!(ast.top_level.len(), 1);
7324        ast.try_stmt(ast.top_level[0]).unwrap()
7325    }
7326
7327    fn expr_of_single_stmt(ast: &Ast) -> &crate::Expr {
7328        let stmt = single_stmt(ast);
7329        match stmt.kind {
7330            crate::StmtKind::Expr(id) => ast.try_expr(id).unwrap(),
7331            _ => panic!("expected expression statement, got {:?}", stmt.kind),
7332        }
7333    }
7334
7335    #[test]
7336    fn parse_number_literal() {
7337        let (ast, diags) = parse_str("42;");
7338        assert!(diags.is_empty());
7339        let expr = expr_of_single_stmt(&ast);
7340        assert_eq!(expr.kind, crate::ExprKind::Number(42.0));
7341        assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
7342    }
7343
7344    #[test]
7345    fn parse_string_literal() {
7346        let (ast, diags) = parse_str(r#""s";"#);
7347        assert!(diags.is_empty());
7348        let expr = expr_of_single_stmt(&ast);
7349        assert_eq!(expr.kind, crate::ExprKind::String("s".to_string()));
7350        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7351    }
7352
7353    #[test]
7354    fn parse_boolean_true() {
7355        let (ast, diags) = parse_str("true;");
7356        assert!(diags.is_empty());
7357        let expr = expr_of_single_stmt(&ast);
7358        assert_eq!(expr.kind, crate::ExprKind::Boolean(true));
7359        assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7360    }
7361
7362    #[test]
7363    fn parse_boolean_false() {
7364        let (ast, diags) = parse_str("false;");
7365        assert!(diags.is_empty());
7366        let expr = expr_of_single_stmt(&ast);
7367        assert_eq!(expr.kind, crate::ExprKind::Boolean(false));
7368        assert_eq!(expr.span, crate::Span::new(F, 0, 5).unwrap());
7369    }
7370
7371    #[test]
7372    fn parse_null_literal() {
7373        let (ast, diags) = parse_str("null;");
7374        assert!(diags.is_empty());
7375        let expr = expr_of_single_stmt(&ast);
7376        assert_eq!(expr.kind, crate::ExprKind::Null);
7377        assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7378    }
7379
7380    #[test]
7381    fn parse_identifier() {
7382        let (ast, diags) = parse_str("x;");
7383        assert!(diags.is_empty());
7384        let expr = expr_of_single_stmt(&ast);
7385        assert!(matches!(expr.kind, crate::ExprKind::Identifier(_)));
7386        assert_eq!(expr.span, crate::Span::new(F, 0, 1).unwrap());
7387    }
7388
7389    #[test]
7390    fn reserved_keywords_remain_invalid_declaration_names() {
7391        let (_ast, diags) = parse_str("function default(): void {}");
7392        assert_eq!(diags.len(), 1, "{diags:#?}");
7393        assert_eq!(
7394            diags[0].message,
7395            "`default` is a reserved keyword and can't be used as a name"
7396        );
7397        assert!(
7398            diags[0]
7399                .help
7400                .iter()
7401                .any(|h| h == "rename it, for example: `defaultValue`"),
7402            "expected rename help, got: {diags:?}"
7403        );
7404    }
7405
7406    #[test]
7407    fn eval_and_arguments_cannot_name_bindings() {
7408        let bindings = [
7409            ("let arguments = 1;", "arguments"),
7410            ("const [eval] = [1];", "eval"),
7411            ("const { a: eval } = { a: 1 };", "eval"),
7412            ("const { eval } = { eval: 1 };", "eval"),
7413            ("const [...arguments] = [1];", "arguments"),
7414            ("function eval(): void {}", "eval"),
7415            ("function f(arguments: number): void {}", "arguments"),
7416            ("const f = arguments => arguments;", "arguments"),
7417            ("const f = (...eval: number[]) => 1;", "eval"),
7418            ("const f = function eval(): void {};", "eval"),
7419            ("for (const eval of [1]) {}", "eval"),
7420            ("try {} catch (arguments) {}", "arguments"),
7421            ("type F = (eval: number) => void;", "eval"),
7422            ("type G = (...arguments: number[]) => void;", "arguments"),
7423            ("type H = { [eval: string]: number };", "eval"),
7424        ];
7425        for (source, word) in bindings {
7426            let (_ast, diags) = parse_str(source);
7427            assert!(
7428                diags
7429                    .iter()
7430                    .any(|d| d.message == format!("`{word}` can't name a binding in strict mode")),
7431                "{source}: {diags:#?}"
7432            );
7433        }
7434    }
7435
7436    #[test]
7437    fn eval_and_arguments_still_name_types_and_properties() {
7438        for source in [
7439            "class eval {}",
7440            "interface arguments { eval: number }",
7441            "type eval = number;",
7442            "enum arguments { eval }",
7443            "const o = { eval: 1, arguments: 2 };",
7444            "import { eval } from \"./m\";",
7445        ] {
7446            let (_ast, diags) = parse_str(source);
7447            assert!(diags.is_empty(), "{source}: {diags:#?}");
7448        }
7449    }
7450
7451    #[test]
7452    fn reserved_keywords_remain_invalid_parameter_names() {
7453        let (_ast, diags) = parse_str("function f(default: string): void {}");
7454        assert_eq!(diags.len(), 1, "{diags:#?}");
7455        assert_eq!(
7456            diags[0].message,
7457            "`default` is a reserved keyword and can't be used as a name"
7458        );
7459        assert!(
7460            diags[0]
7461                .help
7462                .iter()
7463                .any(|h| h == "rename it, for example: `defaultValue`"),
7464            "expected rename help, got: {diags:?}"
7465        );
7466    }
7467
7468    #[test]
7469    fn contextual_keywords_are_valid_names() {
7470        for src in [
7471            "const type = \"x\";",
7472            "const from = 1;",
7473            "let of = 2;",
7474            "const as = 3;",
7475            "const is = 4;",
7476            "function f(from: string): void {}",
7477        ] {
7478            let (_ast, diags) = parse_str(src);
7479            assert!(
7480                diags.is_empty(),
7481                "unexpected diagnostics for {src:?}: {diags:#?}"
7482            );
7483        }
7484    }
7485
7486    #[test]
7487    fn type_alias_dispatch_table() {
7488        let (ast, diags) = parse_str("type X = number;");
7489        assert!(diags.is_empty(), "{diags:#?}");
7490        assert!(matches!(
7491            ast.try_stmt(ast.top_level[0]).unwrap().kind,
7492            crate::StmtKind::TypeAliasDecl { .. }
7493        ));
7494
7495        // A binding named `type` keeps expression/assignment statements working.
7496        for src in ["type = 5;", "type;", "type(1);", "type < 3;", "type.foo;"] {
7497            let (ast, diags) = parse_str(src);
7498            assert!(
7499                diags.is_empty(),
7500                "unexpected diagnostics for {src:?}: {diags:#?}"
7501            );
7502            assert!(
7503                !matches!(
7504                    ast.try_stmt(ast.top_level[0]).unwrap().kind,
7505                    crate::StmtKind::TypeAliasDecl { .. }
7506                ),
7507                "{src:?} must not parse as a type alias"
7508            );
7509        }
7510
7511        let (_ast, diags) = parse_str("type X;");
7512        assert!(
7513            diags
7514                .iter()
7515                .any(|d| d.message == "expected `=` after type alias name"),
7516            "bare `type X` should commit to the alias diagnostic, got: {diags:#?}"
7517        );
7518    }
7519
7520    #[test]
7521    fn export_type_alias_dispatch() {
7522        let (ast, diags) = parse_str("export type X = number;");
7523        assert!(diags.is_empty(), "{diags:#?}");
7524        assert_eq!(ast.exported_decls.len(), 1);
7525        assert!(matches!(
7526            ast.try_stmt(ast.exported_decls[0].stmt).unwrap().kind,
7527            crate::StmtKind::TypeAliasDecl { .. }
7528        ));
7529
7530        let (_ast, diags) = parse_str("export type = 5;");
7531        assert!(
7532            diags
7533                .iter()
7534                .any(|d| d.message == "expected a declaration after `export`"),
7535            "`export type = 5` must not become an exported assignment, got: {diags:#?}"
7536        );
7537    }
7538
7539    #[test]
7540    fn parse_paren_simple() {
7541        let (ast, diags) = parse_str("(x);");
7542        assert!(diags.is_empty());
7543        let expr = expr_of_single_stmt(&ast);
7544        let crate::ExprKind::Paren(inner_id) = expr.kind else {
7545            panic!("expected Paren, got {:?}", expr.kind);
7546        };
7547        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7548        let inner = ast.try_expr(inner_id).unwrap();
7549        assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7550        assert_eq!(inner.span, crate::Span::new(F, 1, 2).unwrap());
7551    }
7552
7553    #[test]
7554    fn parse_paren_nested() {
7555        let (ast, diags) = parse_str("((x));");
7556        assert!(diags.is_empty());
7557        let outer = expr_of_single_stmt(&ast);
7558        let crate::ExprKind::Paren(mid_id) = outer.kind else {
7559            panic!("expected outer Paren");
7560        };
7561        assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7562        let mid = ast.try_expr(mid_id).unwrap();
7563        let crate::ExprKind::Paren(inner_id) = mid.kind else {
7564            panic!("expected inner Paren");
7565        };
7566        assert_eq!(mid.span, crate::Span::new(F, 1, 4).unwrap());
7567        let inner = ast.try_expr(inner_id).unwrap();
7568        assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7569        assert_eq!(inner.span, crate::Span::new(F, 2, 3).unwrap());
7570    }
7571
7572    #[test]
7573    fn parse_sequence_of_atoms() {
7574        let (ast, diags) = parse_str(r#"42; "hi"; true; null; x; (x);"#);
7575        assert!(diags.is_empty());
7576        assert_eq!(ast.top_level.len(), 6);
7577        insta::assert_debug_snapshot!(ast);
7578    }
7579
7580    #[test]
7581    fn missing_semicolon_after_expression() {
7582        let (ast, diags) = parse_str("42 43");
7583        assert_eq!(diags.len(), 1);
7584        assert_eq!(diags[0].message, "expected `;` after expression");
7585        assert!(ast.top_level.is_empty());
7586    }
7587
7588    #[test]
7589    fn empty_paren_diagnoses() {
7590        let (ast, diags) = parse_str("();");
7591        assert_eq!(diags.len(), 1);
7592        assert_eq!(diags[0].message, "expected expression");
7593        assert!(ast.top_level.is_empty());
7594    }
7595
7596    #[test]
7597    fn unterminated_paren_diagnoses() {
7598        let (ast, diags) = parse_str("(x;");
7599        assert_eq!(diags.len(), 1);
7600        assert_eq!(diags[0].message, "expected `)`");
7601        assert!(ast.top_level.is_empty());
7602    }
7603
7604    fn binary(e: &crate::Expr) -> (crate::BinOp, crate::ExprId, crate::ExprId) {
7605        match e.kind {
7606            crate::ExprKind::Binary { op, lhs, rhs } => (op, lhs, rhs),
7607            _ => panic!("expected Binary, got {:?}", e.kind),
7608        }
7609    }
7610
7611    #[test]
7612    fn parse_binary_add() {
7613        let (ast, diags) = parse_str("a + b;");
7614        assert!(diags.is_empty());
7615        let outer = expr_of_single_stmt(&ast);
7616        let (op, lhs, rhs) = binary(outer);
7617        assert_eq!(op, crate::BinOp::Add);
7618        assert!(matches!(
7619            ast.try_expr(lhs).unwrap().kind,
7620            crate::ExprKind::Identifier(_)
7621        ));
7622        assert!(matches!(
7623            ast.try_expr(rhs).unwrap().kind,
7624            crate::ExprKind::Identifier(_)
7625        ));
7626        assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7627    }
7628
7629    #[test]
7630    fn bitwise_precedence_and_shift_adjacency() {
7631        let (ast, diags) = parse_str("a | b ^ c & d == e >> f + g;");
7632        assert!(diags.is_empty(), "{diags:?}");
7633        let (op, _, mut rhs) = binary(expr_of_single_stmt(&ast));
7634        assert_eq!(op, crate::BinOp::BitOr);
7635        for expected in [
7636            crate::BinOp::BitXor,
7637            crate::BinOp::BitAnd,
7638            crate::BinOp::Eq,
7639            crate::BinOp::Shr,
7640            crate::BinOp::Add,
7641        ] {
7642            let (op, _, next) = binary(ast.try_expr(rhs).unwrap());
7643            assert_eq!(op, expected);
7644            rhs = next;
7645        }
7646        for source in ["a > > b;", "a >/*gap*/> b;", "a < < b;"] {
7647            let (_, diags) = parse_str(source);
7648            assert!(!diags.is_empty(), "{source}");
7649        }
7650    }
7651
7652    #[test]
7653    fn bitwise_shift_assignment_span() {
7654        let (ast, diags) = parse_str("a >>>= b;");
7655        assert!(diags.is_empty(), "{diags:?}");
7656        let crate::StmtKind::CompoundAssign { op, op_span, .. } = single_stmt(&ast).kind else {
7657            panic!("expected assignment");
7658        };
7659        assert_eq!(op, crate::BinOp::UnsignedShr);
7660        assert_eq!(op_span, crate::Span::new(F, 2, 6).unwrap());
7661    }
7662
7663    #[test]
7664    fn precedence_mul_binds_tighter_than_add() {
7665        let (ast, diags) = parse_str("a + b * c;");
7666        assert!(diags.is_empty());
7667        let outer = expr_of_single_stmt(&ast);
7668        let (op, lhs, rhs) = binary(outer);
7669        assert_eq!(op, crate::BinOp::Add);
7670        assert!(matches!(
7671            ast.try_expr(lhs).unwrap().kind,
7672            crate::ExprKind::Identifier(_)
7673        ));
7674        let (rop, rlhs, rrhs) = binary(ast.try_expr(rhs).unwrap());
7675        assert_eq!(rop, crate::BinOp::Mul);
7676        assert!(matches!(
7677            ast.try_expr(rlhs).unwrap().kind,
7678            crate::ExprKind::Identifier(_)
7679        ));
7680        assert!(matches!(
7681            ast.try_expr(rrhs).unwrap().kind,
7682            crate::ExprKind::Identifier(_)
7683        ));
7684    }
7685
7686    #[test]
7687    fn precedence_mul_left_of_add() {
7688        let (ast, diags) = parse_str("a * b + c;");
7689        assert!(diags.is_empty());
7690        let outer = expr_of_single_stmt(&ast);
7691        let (op, lhs, rhs) = binary(outer);
7692        assert_eq!(op, crate::BinOp::Add);
7693        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7694        assert_eq!(lop, crate::BinOp::Mul);
7695        assert!(matches!(
7696            ast.try_expr(rhs).unwrap().kind,
7697            crate::ExprKind::Identifier(_)
7698        ));
7699    }
7700
7701    #[test]
7702    fn left_associative_same_precedence() {
7703        let (ast, diags) = parse_str("a - b - c;");
7704        assert!(diags.is_empty());
7705        let outer = expr_of_single_stmt(&ast);
7706        let (op, lhs, rhs) = binary(outer);
7707        assert_eq!(op, crate::BinOp::Sub);
7708        let (lop, llhs, lrhs) = binary(ast.try_expr(lhs).unwrap());
7709        assert_eq!(lop, crate::BinOp::Sub);
7710        assert!(matches!(
7711            ast.try_expr(llhs).unwrap().kind,
7712            crate::ExprKind::Identifier(_)
7713        ));
7714        assert!(matches!(
7715            ast.try_expr(lrhs).unwrap().kind,
7716            crate::ExprKind::Identifier(_)
7717        ));
7718        assert!(matches!(
7719            ast.try_expr(rhs).unwrap().kind,
7720            crate::ExprKind::Identifier(_)
7721        ));
7722    }
7723
7724    #[test]
7725    fn equality_inside_logical() {
7726        let (ast, diags) = parse_str("a === b && c === d;");
7727        assert!(diags.is_empty());
7728        let outer = expr_of_single_stmt(&ast);
7729        let (op, lhs, rhs) = binary(outer);
7730        assert_eq!(op, crate::BinOp::And);
7731        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7732        assert_eq!(lop, crate::BinOp::Eq);
7733        let (rop, ..) = binary(ast.try_expr(rhs).unwrap());
7734        assert_eq!(rop, crate::BinOp::Eq);
7735    }
7736
7737    #[test]
7738    fn double_equal_is_same_variant_as_triple() {
7739        let (ast, _) = parse_str("a == b;");
7740        let outer = expr_of_single_stmt(&ast);
7741        let (op, ..) = binary(outer);
7742        assert_eq!(op, crate::BinOp::Eq);
7743    }
7744
7745    #[test]
7746    fn bang_eq_is_not_eq() {
7747        let (ast, _) = parse_str("a != b;");
7748        let outer = expr_of_single_stmt(&ast);
7749        let (op, ..) = binary(outer);
7750        assert_eq!(op, crate::BinOp::NotEq);
7751    }
7752
7753    #[test]
7754    fn comparison_has_higher_prec_than_logical() {
7755        let (ast, _) = parse_str("a < b && c;");
7756        let outer = expr_of_single_stmt(&ast);
7757        let (op, lhs, _) = binary(outer);
7758        assert_eq!(op, crate::BinOp::And);
7759        let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7760        assert_eq!(lop, crate::BinOp::Lt);
7761    }
7762
7763    #[test]
7764    fn full_precedence_chain_snapshot() {
7765        let (ast, diags) = parse_str("a || b && c === d < e + f * g;");
7766        assert!(diags.is_empty());
7767        insta::assert_debug_snapshot!(ast);
7768    }
7769
7770    #[test]
7771    fn paren_overrides_precedence() {
7772        let (ast, _) = parse_str("(a + b) * c;");
7773        let outer = expr_of_single_stmt(&ast);
7774        let (op, lhs, rhs) = binary(outer);
7775        assert_eq!(op, crate::BinOp::Mul);
7776        let crate::ExprKind::Paren(inner_id) = ast.try_expr(lhs).unwrap().kind else {
7777            panic!("expected Paren on lhs");
7778        };
7779        let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7780        assert_eq!(inner_op, crate::BinOp::Add);
7781        assert!(matches!(
7782            ast.try_expr(rhs).unwrap().kind,
7783            crate::ExprKind::Identifier(_)
7784        ));
7785    }
7786
7787    #[test]
7788    fn binary_missing_rhs_diagnoses() {
7789        let (ast, diags) = parse_str("a +;");
7790        assert_eq!(diags.len(), 1);
7791        assert_eq!(diags[0].message, "expected expression");
7792        assert!(ast.top_level.is_empty());
7793    }
7794
7795    fn unary(e: &crate::Expr) -> (crate::UnOp, crate::ExprId) {
7796        match e.kind {
7797            crate::ExprKind::Unary { op, operand } => (op, operand),
7798            _ => panic!("expected Unary, got {:?}", e.kind),
7799        }
7800    }
7801
7802    #[test]
7803    fn parse_unary_not() {
7804        let (ast, _) = parse_str("!x;");
7805        let outer = expr_of_single_stmt(&ast);
7806        let (op, operand) = unary(outer);
7807        assert_eq!(op, crate::UnOp::Not);
7808        assert!(matches!(
7809            ast.try_expr(operand).unwrap().kind,
7810            crate::ExprKind::Identifier(_)
7811        ));
7812        assert_eq!(outer.span, crate::Span::new(F, 0, 2).unwrap());
7813    }
7814
7815    #[test]
7816    fn parse_unary_neg() {
7817        let (ast, _) = parse_str("-x;");
7818        let (op, _) = unary(expr_of_single_stmt(&ast));
7819        assert_eq!(op, crate::UnOp::Neg);
7820    }
7821
7822    #[test]
7823    fn parse_unary_pos() {
7824        let (ast, _) = parse_str("+x;");
7825        let (op, _) = unary(expr_of_single_stmt(&ast));
7826        assert_eq!(op, crate::UnOp::Pos);
7827    }
7828
7829    #[test]
7830    fn repeated_unary_not() {
7831        let (ast, _) = parse_str("!!x;");
7832        let outer = expr_of_single_stmt(&ast);
7833        let (op, operand) = unary(outer);
7834        assert_eq!(op, crate::UnOp::Not);
7835        let (inner_op, inner_operand) = unary(ast.try_expr(operand).unwrap());
7836        assert_eq!(inner_op, crate::UnOp::Not);
7837        assert!(matches!(
7838            ast.try_expr(inner_operand).unwrap().kind,
7839            crate::ExprKind::Identifier(_)
7840        ));
7841    }
7842
7843    #[test]
7844    fn unary_binds_tighter_than_binary() {
7845        let (ast, _) = parse_str("-x + 1;");
7846        let outer = expr_of_single_stmt(&ast);
7847        let (op, lhs, rhs) = binary(outer);
7848        assert_eq!(op, crate::BinOp::Add);
7849        let (unop, _) = unary(ast.try_expr(lhs).unwrap());
7850        assert_eq!(unop, crate::UnOp::Neg);
7851        assert_eq!(
7852            ast.try_expr(rhs).unwrap().kind,
7853            crate::ExprKind::Number(1.0)
7854        );
7855    }
7856
7857    #[test]
7858    fn unary_over_paren() {
7859        let (ast, _) = parse_str("-(x + 1);");
7860        let outer = expr_of_single_stmt(&ast);
7861        let (op, operand) = unary(outer);
7862        assert_eq!(op, crate::UnOp::Neg);
7863        let crate::ExprKind::Paren(inner_id) = ast.try_expr(operand).unwrap().kind else {
7864            panic!("expected Paren");
7865        };
7866        let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7867        assert_eq!(inner_op, crate::BinOp::Add);
7868    }
7869
7870    #[test]
7871    fn parse_is_as_expression_rejected() {
7872        let (_, diags) = parse_str("x is number;");
7873        assert!(!diags.is_empty(), "expected parse error for `x is T`");
7874    }
7875
7876    fn as_cast(e: &crate::Expr) -> (crate::ExprId, &crate::TypeAnnotation) {
7877        match &e.kind {
7878            crate::ExprKind::As { expr, ty } => (*expr, ty),
7879            other => panic!("expected ExprKind::As, got {other:?}"),
7880        }
7881    }
7882
7883    #[test]
7884    fn parse_instanceof_class() {
7885        let (ast, diags) = parse_str("x instanceof Foo;");
7886        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7887        let outer = expr_of_single_stmt(&ast);
7888        let crate::ExprKind::InstanceOf { value, ty } = &outer.kind else {
7889            panic!("expected ExprKind::InstanceOf, got {:?}", outer.kind);
7890        };
7891        assert!(matches!(
7892            ast.try_expr(*value).unwrap().kind,
7893            crate::ExprKind::Identifier(_)
7894        ));
7895        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7896    }
7897
7898    #[test]
7899    fn parse_instanceof_binds_tighter_than_logical_and() {
7900        // `a instanceof B && c` parses as `(a instanceof B) && c`.
7901        let (ast, diags) = parse_str("a instanceof B && c;");
7902        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7903        let outer = expr_of_single_stmt(&ast);
7904        let (op, lhs, rhs) = binary(outer);
7905        assert_eq!(op, crate::BinOp::And);
7906        assert!(matches!(
7907            ast.try_expr(lhs).unwrap().kind,
7908            crate::ExprKind::InstanceOf { .. }
7909        ));
7910        assert!(matches!(
7911            ast.try_expr(rhs).unwrap().kind,
7912            crate::ExprKind::Identifier(_)
7913        ));
7914    }
7915
7916    /// `<T>x` produces the same node as `x as T`, so everything downstream of the
7917    /// parser sees one construct rather than two.
7918    #[test]
7919    fn parse_angle_cast_lowers_to_the_as_node() {
7920        let (ast, diags) = parse_str("<number>x;");
7921        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7922        let outer = expr_of_single_stmt(&ast);
7923        let (inner, ty) = as_cast(outer);
7924        assert!(matches!(
7925            ast.try_expr(inner).unwrap().kind,
7926            crate::ExprKind::Identifier(_)
7927        ));
7928        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7929    }
7930
7931    /// A `<` that means comparison always has a left operand, so it never reaches
7932    /// the cast branch. This is the regression the cast syntax could most easily
7933    /// have caused.
7934    #[test]
7935    fn a_less_than_comparison_is_not_read_as_a_cast() {
7936        let (ast, diags) = parse_str("a < b;");
7937        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7938        let e = expr_of_single_stmt(&ast);
7939        assert!(matches!(e.kind, crate::ExprKind::Binary { .. }));
7940    }
7941
7942    #[test]
7943    fn parse_typeof_in_type_position() {
7944        let (_ast, diags) = parse_str("let a: typeof b = 1;");
7945        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7946    }
7947
7948    #[test]
7949    fn parse_typeof_with_a_dotted_path() {
7950        let (_ast, diags) = parse_str("let a: typeof b.c.d = 1;");
7951        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7952    }
7953
7954    #[test]
7955    fn parse_as_primitive() {
7956        let (ast, diags) = parse_str("x as number;");
7957        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7958        let outer = expr_of_single_stmt(&ast);
7959        let (inner, ty) = as_cast(outer);
7960        assert!(matches!(
7961            ast.try_expr(inner).unwrap().kind,
7962            crate::ExprKind::Identifier(_)
7963        ));
7964        match &ty.kind {
7965            crate::TypeAnnotationKind::Name { args, .. } => {
7966                assert!(args.is_empty());
7967            }
7968            other => panic!("expected Name target, got {other:?}"),
7969        }
7970        assert_eq!(outer.span, crate::Span::new(F, 0, 11).unwrap());
7971    }
7972
7973    #[test]
7974    fn parse_as_object_literal_type() {
7975        let (ast, diags) = parse_str("x as { a: number };");
7976        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7977        let outer = expr_of_single_stmt(&ast);
7978        let (_, ty) = as_cast(outer);
7979        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Object { .. }));
7980    }
7981
7982    #[test]
7983    fn parse_as_array_short() {
7984        let (ast, diags) = parse_str("x as string[];");
7985        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7986        let outer = expr_of_single_stmt(&ast);
7987        let (_, ty) = as_cast(outer);
7988        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
7989    }
7990
7991    #[test]
7992    fn parse_as_array_generic() {
7993        let (ast, diags) = parse_str("x as T[];");
7994        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7995        let outer = expr_of_single_stmt(&ast);
7996        let (_, ty) = as_cast(outer);
7997        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
7998    }
7999
8000    #[test]
8001    fn parse_as_generic_instantiation() {
8002        let (ast, diags) = parse_str("x as Foo<Bar>;");
8003        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8004        let outer = expr_of_single_stmt(&ast);
8005        let (_, ty) = as_cast(outer);
8006        match &ty.kind {
8007            crate::TypeAnnotationKind::Name { args, .. } => {
8008                assert_eq!(args.len(), 1, "Foo<Bar> has one type arg");
8009            }
8010            other => panic!("expected Name with type args, got {other:?}"),
8011        }
8012    }
8013
8014    #[test]
8015    fn parse_as_precedence_higher_than_additive() {
8016        let (ast, diags) = parse_str("x as number + 1;");
8017        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8018        let outer = expr_of_single_stmt(&ast);
8019        let (op, lhs, rhs) = binary(outer);
8020        assert_eq!(op, crate::BinOp::Add);
8021        assert!(matches!(
8022            ast.try_expr(lhs).unwrap().kind,
8023            crate::ExprKind::As { .. }
8024        ));
8025        assert!(matches!(
8026            ast.try_expr(rhs).unwrap().kind,
8027            crate::ExprKind::Number(_)
8028        ));
8029    }
8030
8031    #[test]
8032    fn parse_as_precedence_lower_than_unary() {
8033        let (ast, diags) = parse_str("!x as boolean;");
8034        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8035        let outer = expr_of_single_stmt(&ast);
8036        let (inner, _) = as_cast(outer);
8037        assert!(matches!(
8038            ast.try_expr(inner).unwrap().kind,
8039            crate::ExprKind::Unary {
8040                op: crate::UnOp::Not,
8041                ..
8042            }
8043        ));
8044    }
8045
8046    #[test]
8047    fn parse_as_left_associative_chain() {
8048        let (ast, diags) = parse_str("x as A as B;");
8049        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8050        let outer = expr_of_single_stmt(&ast);
8051        let (inner, _outer_ty) = as_cast(outer);
8052        let (innermost, _inner_ty) = as_cast(ast.try_expr(inner).unwrap());
8053        assert!(matches!(
8054            ast.try_expr(innermost).unwrap().kind,
8055            crate::ExprKind::Identifier(_)
8056        ));
8057    }
8058
8059    #[test]
8060    fn parse_as_inside_call_arg() {
8061        let (ast, diags) = parse_str("f(x as T);");
8062        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8063        let outer = expr_of_single_stmt(&ast);
8064        match &outer.kind {
8065            crate::ExprKind::Call { args, .. } => {
8066                assert_eq!(args.len(), 1);
8067                assert!(matches!(
8068                    ast.try_expr(args[0]).unwrap().kind,
8069                    crate::ExprKind::As { .. }
8070                ));
8071            }
8072            other => panic!("expected Call, got {other:?}"),
8073        }
8074    }
8075
8076    #[test]
8077    fn parse_as_after_field_access() {
8078        let (ast, diags) = parse_str("obj.field as T;");
8079        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8080        let outer = expr_of_single_stmt(&ast);
8081        let (inner, _) = as_cast(outer);
8082        assert!(matches!(
8083            ast.try_expr(inner).unwrap().kind,
8084            crate::ExprKind::FieldAccess { .. }
8085        ));
8086    }
8087
8088    #[test]
8089    fn parse_type_guard_return_annotation_on_function() {
8090        let src = "function isCircle(s: Shape): s is Circle { return true; }";
8091        let (ast, diags) = parse_str(src);
8092        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8093        let stmt = single_stmt(&ast);
8094        match stmt.kind {
8095            crate::StmtKind::Function {
8096                ref return_type,
8097                ref type_predicate,
8098                ..
8099            } => {
8100                assert!(
8101                    return_type.is_none(),
8102                    "predicate form leaves return_type empty",
8103                );
8104                let tp = type_predicate.as_ref().expect("predicate parsed");
8105                assert_eq!(tp.param.name, "s");
8106                assert!(matches!(
8107                    tp.asserted.kind,
8108                    crate::TypeAnnotationKind::Name { .. }
8109                ));
8110                assert_eq!(tp.span, crate::Span::new(F, 29, 40).unwrap());
8111            }
8112            _ => panic!("expected Function"),
8113        }
8114    }
8115
8116    #[test]
8117    fn parse_type_guard_return_annotation_on_arrow() {
8118        let src = "const f = (s: Shape): s is Circle => true;";
8119        let (ast, diags) = parse_str(src);
8120        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8121        let value = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
8122            crate::StmtKind::Const { value, .. } => *value,
8123            _ => panic!("expected const"),
8124        };
8125        match &ast.try_expr(value).unwrap().kind {
8126            crate::ExprKind::Arrow {
8127                return_type,
8128                type_predicate,
8129                ..
8130            } => {
8131                assert!(return_type.is_none());
8132                let tp = type_predicate.as_ref().expect("predicate parsed");
8133                assert_eq!(tp.param.name, "s");
8134            }
8135            other => panic!("expected Arrow, got {other:?}"),
8136        }
8137    }
8138
8139    #[test]
8140    fn parse_plain_return_type_unaffected_by_predicate_parser() {
8141        let src = "function f(s: Shape): boolean { return true; }";
8142        let (ast, diags) = parse_str(src);
8143        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8144        let stmt = single_stmt(&ast);
8145        match stmt.kind {
8146            crate::StmtKind::Function {
8147                ref return_type,
8148                ref type_predicate,
8149                ..
8150            } => {
8151                assert!(return_type.is_some(), "plain form has return_type");
8152                assert!(type_predicate.is_none(), "plain form has no type_predicate",);
8153            }
8154            _ => panic!("expected Function"),
8155        }
8156    }
8157
8158    #[test]
8159    fn parse_type_guard_rejects_non_return_position() {
8160        let (_, diags) = parse_str("let x: y is Foo = null;");
8161        assert!(
8162            !diags.is_empty(),
8163            "expected parse error for `is` outside return-type position",
8164        );
8165    }
8166
8167    #[test]
8168    fn parse_typeof_prefix() {
8169        let (ast, diags) = parse_str("typeof x;");
8170        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8171        let outer = expr_of_single_stmt(&ast);
8172        let crate::ExprKind::Typeof { operand } = outer.kind else {
8173            panic!("expected Typeof, got {:?}", outer.kind);
8174        };
8175        assert!(matches!(
8176            ast.try_expr(operand).unwrap().kind,
8177            crate::ExprKind::Identifier(_)
8178        ));
8179        assert_eq!(outer.span, crate::Span::new(F, 0, 8).unwrap());
8180    }
8181
8182    #[test]
8183    fn parse_typeof_in_equality() {
8184        let (ast, diags) = parse_str(r#"typeof x === "number";"#);
8185        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8186        let outer = expr_of_single_stmt(&ast);
8187        let (op, lhs, rhs) = binary(outer);
8188        assert_eq!(op, crate::BinOp::Eq);
8189        assert!(matches!(
8190            ast.try_expr(lhs).unwrap().kind,
8191            crate::ExprKind::Typeof { .. }
8192        ));
8193        assert!(matches!(
8194            ast.try_expr(rhs).unwrap().kind,
8195            crate::ExprKind::String(ref s) if s == "number"
8196        ));
8197    }
8198
8199    #[test]
8200    fn parse_let_without_type() {
8201        let (ast, diags) = parse_str("let x = 1;");
8202        assert!(diags.is_empty());
8203        let stmt = single_stmt(&ast);
8204        assert_eq!(stmt.span, crate::Span::new(F, 0, 10).unwrap());
8205        match stmt.kind {
8206            crate::StmtKind::Let {
8207                ref name,
8208                ref ty,
8209                value,
8210                ..
8211            } => {
8212                assert_eq!(name.name, "x");
8213                assert_eq!(name.span, crate::Span::new(F, 4, 5).unwrap());
8214                assert!(ty.is_none());
8215                assert_eq!(
8216                    ast.try_expr(value).unwrap().kind,
8217                    crate::ExprKind::Number(1.0)
8218                );
8219            }
8220            _ => panic!("expected Let"),
8221        }
8222    }
8223
8224    #[test]
8225    fn parse_const_with_type_and_string() {
8226        let (ast, diags) = parse_str(r#"const y: string = "hi";"#);
8227        assert!(diags.is_empty());
8228        let stmt = single_stmt(&ast);
8229        match stmt.kind {
8230            crate::StmtKind::Const {
8231                ref name,
8232                ref ty,
8233                value,
8234                ..
8235            } => {
8236                assert_eq!(name.name, "y");
8237                assert_eq!(name.span, crate::Span::new(F, 6, 7).unwrap());
8238                let ty = ty.as_ref().expect("expected type annotation");
8239                assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8240                assert_eq!(ty.span, crate::Span::new(F, 9, 15).unwrap());
8241                assert_eq!(
8242                    ast.try_expr(value).unwrap().kind,
8243                    crate::ExprKind::String("hi".to_string())
8244                );
8245            }
8246            _ => panic!("expected Const"),
8247        }
8248    }
8249
8250    #[test]
8251    fn parse_let_with_type_and_binary_initializer() {
8252        let (ast, diags) = parse_str("let z: number = 1 + 2;");
8253        assert!(diags.is_empty());
8254        let stmt = single_stmt(&ast);
8255        match stmt.kind {
8256            crate::StmtKind::Let { ref ty, value, .. } => {
8257                assert!(ty.is_some());
8258                let (op, ..) = binary(ast.try_expr(value).unwrap());
8259                assert_eq!(op, crate::BinOp::Add);
8260            }
8261            _ => panic!("expected Let"),
8262        }
8263    }
8264
8265    #[test]
8266    fn void_type_annotation_is_accepted_syntactically() {
8267        let (ast, diags) = parse_str("let v: void = null;");
8268        assert!(diags.is_empty());
8269        let stmt = single_stmt(&ast);
8270        match stmt.kind {
8271            crate::StmtKind::Let { ref ty, .. } => {
8272                let ty = ty.as_ref().unwrap();
8273                assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8274                assert_eq!(ty.span, crate::Span::new(F, 7, 11).unwrap());
8275            }
8276            _ => panic!("expected Let"),
8277        }
8278    }
8279
8280    #[test]
8281    fn const_missing_initializer_diagnoses() {
8282        let (ast, diags) = parse_str("const x;");
8283        assert_eq!(diags.len(), 1);
8284        assert_eq!(
8285            diags[0].message,
8286            "`const` declaration requires an initializer"
8287        );
8288        assert!(ast.top_level.is_empty());
8289    }
8290
8291    #[test]
8292    fn parse_const_object_shorthand_pattern() {
8293        let (ast, diags) = parse_str("const { a, b } = obj;");
8294        assert!(diags.is_empty(), "unexpected: {diags:?}");
8295        let stmt = single_stmt(&ast);
8296        match stmt.kind {
8297            crate::StmtKind::ConstPattern {
8298                binding:
8299                    crate::Binding::Object {
8300                        ref fields,
8301                        ref rest,
8302                        ..
8303                    },
8304                ..
8305            } => {
8306                assert_eq!(fields.len(), 2);
8307                assert_eq!(fields[0].source.name, "a");
8308                assert_eq!(fields[0].local.name, "a");
8309                assert_eq!(fields[1].source.name, "b");
8310                assert_eq!(fields[1].local.name, "b");
8311                assert!(rest.is_none());
8312            }
8313            _ => panic!("expected ConstPattern Object, got {:?}", stmt.kind),
8314        }
8315    }
8316
8317    #[test]
8318    fn parse_const_object_pattern_keyword_source_key() {
8319        let (ast, diags) = parse_str("const { type: statusType, default: fallback } = obj;");
8320        assert!(diags.is_empty(), "unexpected: {diags:?}");
8321        let stmt = single_stmt(&ast);
8322        let crate::StmtKind::ConstPattern {
8323            binding: crate::Binding::Object { ref fields, .. },
8324            ..
8325        } = stmt.kind
8326        else {
8327            panic!("expected object const pattern");
8328        };
8329
8330        let names: Vec<(&str, &str)> = fields
8331            .iter()
8332            .map(|f| (f.source.name.as_str(), f.local.name.as_str()))
8333            .collect();
8334        assert_eq!(names, vec![("type", "statusType"), ("default", "fallback")]);
8335    }
8336
8337    #[test]
8338    fn parse_const_object_pattern_keyword_shorthand_rejected() {
8339        let (_ast, diags) = parse_str("const { default } = obj;");
8340        assert!(
8341            diags
8342                .iter()
8343                .any(|d| d.message == "expected `:` after keyword field name in object pattern"),
8344            "expected keyword-shorthand diagnostic, got: {diags:?}"
8345        );
8346    }
8347
8348    #[test]
8349    fn parse_const_object_renamed_pattern() {
8350        let (ast, diags) = parse_str("const { a: x, b: y } = obj;");
8351        assert!(diags.is_empty(), "unexpected: {diags:?}");
8352        match single_stmt(&ast).kind {
8353            crate::StmtKind::ConstPattern {
8354                binding: crate::Binding::Object { ref fields, .. },
8355                ..
8356            } => {
8357                assert_eq!(fields[0].source.name, "a");
8358                assert_eq!(fields[0].local.name, "x");
8359                assert_eq!(fields[1].source.name, "b");
8360                assert_eq!(fields[1].local.name, "y");
8361            }
8362            ref other => panic!("expected ConstPattern Object, got {other:?}"),
8363        }
8364    }
8365
8366    #[test]
8367    fn parse_const_object_with_rest() {
8368        let (ast, diags) = parse_str("const { a, ...rest } = obj;");
8369        assert!(diags.is_empty(), "unexpected: {diags:?}");
8370        match single_stmt(&ast).kind {
8371            crate::StmtKind::ConstPattern {
8372                binding:
8373                    crate::Binding::Object {
8374                        ref fields,
8375                        ref rest,
8376                        ..
8377                    },
8378                ..
8379            } => {
8380                assert_eq!(fields.len(), 1);
8381                assert_eq!(fields[0].source.name, "a");
8382                let r = rest.as_ref().expect("expected rest");
8383                assert_eq!(r.name, "rest");
8384            }
8385            ref other => panic!("expected ConstPattern Object, got {other:?}"),
8386        }
8387    }
8388
8389    #[test]
8390    fn parse_const_array_pattern() {
8391        let (ast, diags) = parse_str("const [x, y] = arr;");
8392        assert!(diags.is_empty(), "unexpected: {diags:?}");
8393        match single_stmt(&ast).kind {
8394            crate::StmtKind::ConstPattern {
8395                binding:
8396                    crate::Binding::Array {
8397                        ref elems,
8398                        ref rest,
8399                        ..
8400                    },
8401                ..
8402            } => {
8403                assert_eq!(elems.len(), 2);
8404                assert_eq!(elems[0].as_ref().unwrap().name, "x");
8405                assert_eq!(elems[1].as_ref().unwrap().name, "y");
8406                assert!(rest.is_none());
8407            }
8408            ref other => panic!("expected ConstPattern Array, got {other:?}"),
8409        }
8410    }
8411
8412    #[test]
8413    fn parse_const_array_with_hole_and_rest() {
8414        let (ast, diags) = parse_str("const [, x, ...rest] = arr;");
8415        assert!(diags.is_empty(), "unexpected: {diags:?}");
8416        match single_stmt(&ast).kind {
8417            crate::StmtKind::ConstPattern {
8418                binding:
8419                    crate::Binding::Array {
8420                        ref elems,
8421                        ref rest,
8422                        ..
8423                    },
8424                ..
8425            } => {
8426                assert_eq!(elems.len(), 2);
8427                assert!(elems[0].is_none(), "first slot should be a hole");
8428                assert_eq!(elems[1].as_ref().unwrap().name, "x");
8429                assert_eq!(rest.as_ref().unwrap().name, "rest");
8430            }
8431            ref other => panic!("expected ConstPattern Array, got {other:?}"),
8432        }
8433    }
8434
8435    #[test]
8436    fn parse_let_object_pattern() {
8437        let (ast, diags) = parse_str("let { a, b } = obj;");
8438        assert!(diags.is_empty(), "unexpected: {diags:?}");
8439        assert!(matches!(
8440            single_stmt(&ast).kind,
8441            crate::StmtKind::LetPattern {
8442                binding: crate::Binding::Object { .. },
8443                ..
8444            }
8445        ));
8446    }
8447
8448    #[test]
8449    fn parse_function_param_object_pattern() {
8450        let src = "function f({ a, b }: T): void { return; }";
8451        let (ast, diags) = parse_str(src);
8452        assert!(diags.is_empty(), "unexpected: {diags:?}");
8453        match single_stmt(&ast).kind {
8454            crate::StmtKind::Function { ref params, .. } => {
8455                assert_eq!(params.len(), 1);
8456                let p = &params[0];
8457                assert!(p.pattern.is_some(), "expected pattern param");
8458                assert_eq!(p.name.name, "", "pattern placeholder uses empty name");
8459                match p.pattern.as_ref().unwrap() {
8460                    crate::Binding::Object { fields, .. } => {
8461                        assert_eq!(fields.len(), 2);
8462                        assert_eq!(fields[0].source.name, "a");
8463                    }
8464                    other => panic!("expected Object binding, got {other:?}"),
8465                }
8466            }
8467            ref other => panic!("expected Function, got {other:?}"),
8468        }
8469    }
8470
8471    #[test]
8472    fn parse_arrow_param_array_pattern() {
8473        let src = "const g = ([x, y]: number[]): number => x + y;";
8474        let (_ast, diags) = parse_str(src);
8475        assert!(diags.is_empty(), "unexpected: {diags:?}");
8476    }
8477
8478    #[test]
8479    fn parse_destructure_nested_rejected() {
8480        let (_ast, diags) = parse_str("const { a: { b } } = obj;");
8481        assert!(
8482            diags
8483                .iter()
8484                .any(|d| d.message.contains("nested destructuring")),
8485            "expected nested-destructuring diagnostic, got: {diags:?}",
8486        );
8487    }
8488
8489    #[test]
8490    fn parse_destructure_array_nested_rejected() {
8491        let (_ast, diags) = parse_str("const [[a]] = arr;");
8492        assert!(
8493            diags
8494                .iter()
8495                .any(|d| d.message.contains("nested destructuring")),
8496            "expected nested-destructuring diagnostic, got: {diags:?}",
8497        );
8498    }
8499
8500    #[test]
8501    fn parse_destructure_pattern_defaults() {
8502        let (ast, diags) = parse_str("const { a = 1, b: c = 2 } = obj;");
8503        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8504        let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
8505            panic!("expected pattern")
8506        };
8507        let crate::Binding::Object { fields, .. } = binding else {
8508            panic!("expected object")
8509        };
8510        assert_eq!(fields.len(), 2);
8511        assert!(fields.iter().all(|field| field.default.is_some()));
8512        assert_eq!(fields[1].local.name, "c");
8513    }
8514
8515    #[test]
8516    fn parse_destructure_param_default() {
8517        let (ast, diags) = parse_str("function f({ a = 1 }: T = obj): void { return; }");
8518        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8519        let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
8520            panic!("expected function")
8521        };
8522        assert!(params[0].default.is_some());
8523        assert!(params[0].pattern.is_some());
8524    }
8525
8526    #[test]
8527    fn parse_destructure_rest_not_last_rejected() {
8528        let (_ast, diags) = parse_str("const { ...rest, a } = obj;");
8529        assert!(
8530            diags
8531                .iter()
8532                .any(|d| d.message.contains("rest element must be the last element")),
8533            "expected rest-not-last diagnostic, got: {diags:?}",
8534        );
8535    }
8536
8537    #[test]
8538    fn parse_destructure_empty_object_rejected() {
8539        let (_ast, diags) = parse_str("const { } = obj;");
8540        assert!(
8541            diags
8542                .iter()
8543                .any(|d| d.message.contains("empty object destructuring pattern")),
8544            "expected empty-pattern diagnostic, got: {diags:?}",
8545        );
8546    }
8547
8548    #[test]
8549    fn parse_ternary_simple() {
8550        let (ast, diags) = parse_str("const x: number = a ? 1 : 2;");
8551        assert!(diags.is_empty(), "unexpected: {diags:?}");
8552        let stmt = single_stmt(&ast);
8553        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8554            panic!("expected Const, got {:?}", stmt.kind);
8555        };
8556        assert!(matches!(
8557            ast.try_expr(value).unwrap().kind,
8558            crate::ExprKind::Ternary { .. }
8559        ));
8560    }
8561
8562    #[test]
8563    fn parse_ternary_nested_right_assoc() {
8564        let (ast, diags) = parse_str("const x: number = a ? 1 : b ? 2 : 3;");
8565        assert!(diags.is_empty(), "unexpected: {diags:?}");
8566        let stmt = single_stmt(&ast);
8567        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8568            panic!("expected Const");
8569        };
8570        let crate::ExprKind::Ternary { else_, .. } = ast.try_expr(value).unwrap().kind else {
8571            panic!("expected outer Ternary");
8572        };
8573        assert!(matches!(
8574            ast.try_expr(else_).unwrap().kind,
8575            crate::ExprKind::Ternary { .. }
8576        ));
8577    }
8578
8579    #[test]
8580    fn parse_ternary_missing_colon_diagnoses() {
8581        let (_, diags) = parse_str("const x: number = a ? 1;");
8582        assert!(
8583            diags
8584                .iter()
8585                .any(|d| d.message.contains("expected `:` to complete ternary")),
8586            "expected colon diagnostic, got: {diags:?}",
8587        );
8588    }
8589
8590    #[test]
8591    fn parse_nullish_coalesce_binop() {
8592        let (ast, diags) = parse_str("const x: number = a ?? 1;");
8593        assert!(diags.is_empty(), "unexpected: {diags:?}");
8594        let stmt = single_stmt(&ast);
8595        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8596            panic!("expected Const");
8597        };
8598        match ast.try_expr(value).unwrap().kind {
8599            crate::ExprKind::Binary { op, .. } => {
8600                assert_eq!(op, crate::BinOp::NullishCoalesce);
8601            }
8602            ref other => panic!("expected Binary NullishCoalesce, got {other:?}"),
8603        }
8604    }
8605
8606    #[test]
8607    fn parse_nullish_left_associative() {
8608        let (ast, diags) = parse_str("const x: number = a ?? b ?? c;");
8609        assert!(diags.is_empty(), "unexpected: {diags:?}");
8610        let stmt = single_stmt(&ast);
8611        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8612            panic!("expected Const");
8613        };
8614        let crate::ExprKind::Binary { lhs, .. } = ast.try_expr(value).unwrap().kind else {
8615            panic!("expected outer Binary");
8616        };
8617        match ast.try_expr(lhs).unwrap().kind {
8618            crate::ExprKind::Binary { op, .. } => {
8619                assert_eq!(op, crate::BinOp::NullishCoalesce);
8620            }
8621            ref other => panic!("expected inner Binary, got {other:?}"),
8622        }
8623    }
8624
8625    #[test]
8626    fn parse_pow_is_right_associative() {
8627        let (ast, diags) = parse_str("const x: number = 2 ** 3 ** 2;");
8628        assert!(diags.is_empty(), "unexpected: {diags:?}");
8629        let stmt = single_stmt(&ast);
8630        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8631            panic!("expected Const");
8632        };
8633        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8634            panic!("expected outer Binary");
8635        };
8636        assert_eq!(op, crate::BinOp::Pow);
8637        match ast.try_expr(rhs).unwrap().kind {
8638            crate::ExprKind::Binary { op: inner_op, .. } => {
8639                assert_eq!(inner_op, crate::BinOp::Pow);
8640            }
8641            ref other => panic!("expected inner Pow Binary on RHS, got {other:?}"),
8642        }
8643    }
8644
8645    #[test]
8646    fn parse_pow_higher_than_multiply() {
8647        let (ast, diags) = parse_str("const x: number = 2 * 3 ** 2;");
8648        assert!(diags.is_empty(), "unexpected: {diags:?}");
8649        let stmt = single_stmt(&ast);
8650        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8651            panic!("expected Const");
8652        };
8653        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8654            panic!("expected outer Binary");
8655        };
8656        assert_eq!(op, crate::BinOp::Mul);
8657        match ast.try_expr(rhs).unwrap().kind {
8658            crate::ExprKind::Binary { op: inner_op, .. } => {
8659                assert_eq!(inner_op, crate::BinOp::Pow);
8660            }
8661            ref other => panic!("expected Pow on the RHS of *, got {other:?}"),
8662        }
8663    }
8664
8665    #[test]
8666    fn parse_compound_assign_ident() {
8667        let (ast, diags) = parse_str("function main(): void { let x: number = 0; x += 5; }");
8668        assert!(diags.is_empty(), "unexpected: {diags:?}");
8669        let func = ast.top_level.iter().find_map(|sid| {
8670            if let crate::StmtKind::Function { body, .. } = &ast.try_stmt(*sid).unwrap().kind {
8671                Some(*body)
8672            } else {
8673                None
8674            }
8675        });
8676        let crate::StmtKind::Block(stmts) = &ast.try_stmt(func.expect("main")).unwrap().kind else {
8677            panic!("expected function body to be a block");
8678        };
8679        let target = stmts
8680            .iter()
8681            .find_map(|sid| match &ast.try_stmt(*sid).unwrap().kind {
8682                crate::StmtKind::CompoundAssign { target, op, .. } => {
8683                    Some((target.name.clone(), *op))
8684                }
8685                _ => None,
8686            });
8687        let (name, op) = target.expect("expected a CompoundAssign in the body");
8688        assert_eq!(name, "x");
8689        assert_eq!(op, crate::BinOp::Add);
8690    }
8691
8692    #[test]
8693    fn parse_nullish_mixed_with_or_rejected() {
8694        let (_, diags) = parse_str("const x: number = a || b ?? c;");
8695        assert!(
8696            diags
8697                .iter()
8698                .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8699            "expected mixing diagnostic, got: {diags:?}",
8700        );
8701    }
8702
8703    #[test]
8704    fn parse_nullish_mixed_with_and_rejected() {
8705        let (_, diags) = parse_str("const x: number = a && b ?? c;");
8706        assert!(
8707            diags
8708                .iter()
8709                .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8710            "expected mixing diagnostic, got: {diags:?}",
8711        );
8712    }
8713
8714    #[test]
8715    fn parse_nullish_followed_by_logical_rejected() {
8716        for (source, help) in [
8717            (
8718                "const x: number = a ?? b || c;",
8719                "`(a ?? b) || c` or `a ?? (b || c)`",
8720            ),
8721            (
8722                "const x: number = a ?? b && c;",
8723                "`(a ?? b) && c` or `a ?? (b && c)`",
8724            ),
8725            (
8726                "const x: number = a ?? b ?? c || d;",
8727                "`(a ?? b) || c` or `a ?? (b || c)`",
8728            ),
8729            // One error per chain, however many mixes it has.
8730            (
8731                "const x: number = a || b ?? c || d;",
8732                "`(a || b) ?? c` or `a || (b ?? c)`",
8733            ),
8734        ] {
8735            let (_, diags) = parse_str(source);
8736            assert_eq!(diags.len(), 1, "diags for {source:?}: {diags:?}");
8737            assert!(
8738                diags[0].message.contains("mixing `??` with `||` / `&&`"),
8739                "message for {source:?}: {diags:?}",
8740            );
8741            assert!(
8742                diags[0].help.iter().any(|h| h.contains(help)),
8743                "help for {source:?}: {diags:?}",
8744            );
8745        }
8746    }
8747
8748    #[test]
8749    fn parse_nullish_operand_keeps_tighter_operators() {
8750        // `a ?? (b == c)`: only `||` and `&&` are excluded from a `??` operand.
8751        let (ast, diags) = parse_str("const x: boolean = a ?? b == c;");
8752        assert!(diags.is_empty(), "unexpected: {diags:?}");
8753        let stmt = single_stmt(&ast);
8754        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8755            panic!("expected Const");
8756        };
8757        let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8758            panic!("expected outer Binary");
8759        };
8760        assert_eq!(op, crate::BinOp::NullishCoalesce);
8761        assert!(matches!(
8762            ast.try_expr(rhs).unwrap().kind,
8763            crate::ExprKind::Binary {
8764                op: crate::BinOp::Eq,
8765                ..
8766            }
8767        ));
8768    }
8769
8770    #[test]
8771    fn parse_nullish_with_parens_ok() {
8772        let (_, diags) = parse_str("const x: number = (a || b) ?? c;");
8773        assert!(diags.is_empty(), "unexpected: {diags:?}");
8774    }
8775
8776    #[test]
8777    fn parse_optional_field_chain() {
8778        let (ast, diags) = parse_str("const x: number = a?.b;");
8779        assert!(diags.is_empty(), "unexpected: {diags:?}");
8780        let stmt = single_stmt(&ast);
8781        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8782            panic!("expected Const");
8783        };
8784        match ast.try_expr(value).unwrap().kind {
8785            crate::ExprKind::OptionalChain { ref parts, .. } => {
8786                assert_eq!(parts.len(), 1);
8787                match &parts[0] {
8788                    crate::ChainPart::Field { name, optional, .. } => {
8789                        assert_eq!(name.name, "b");
8790                        assert!(*optional);
8791                    }
8792                    other => panic!("expected Field, got {other:?}"),
8793                }
8794            }
8795            ref other => panic!("expected OptionalChain, got {other:?}"),
8796        }
8797    }
8798
8799    #[test]
8800    fn parse_optional_chain_continues_after_first_dot() {
8801        let (ast, diags) = parse_str("const x: number = a?.b.c;");
8802        assert!(diags.is_empty(), "unexpected: {diags:?}");
8803        let stmt = single_stmt(&ast);
8804        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8805            panic!("expected Const");
8806        };
8807        match ast.try_expr(value).unwrap().kind {
8808            crate::ExprKind::OptionalChain { ref parts, .. } => {
8809                assert_eq!(parts.len(), 2);
8810                if let crate::ChainPart::Field { optional, .. } = &parts[0] {
8811                    assert!(*optional);
8812                }
8813                if let crate::ChainPart::Field { optional, .. } = &parts[1] {
8814                    assert!(!*optional);
8815                }
8816            }
8817            ref other => panic!("expected OptionalChain, got {other:?}"),
8818        }
8819    }
8820
8821    #[test]
8822    fn parse_optional_chain_multiple_short_circuits() {
8823        let (ast, diags) = parse_str("const x: number = a?.b?.c;");
8824        assert!(diags.is_empty(), "unexpected: {diags:?}");
8825        let stmt = single_stmt(&ast);
8826        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8827            panic!("expected Const");
8828        };
8829        let crate::ExprKind::OptionalChain { parts, .. } = &ast.try_expr(value).unwrap().kind
8830        else {
8831            panic!("expected OptionalChain");
8832        };
8833        assert_eq!(parts.len(), 2);
8834        for p in parts {
8835            if let crate::ChainPart::Field { optional, .. } = p {
8836                assert!(*optional, "both parts should be optional");
8837            }
8838        }
8839    }
8840
8841    #[test]
8842    fn parse_optional_call_and_index() {
8843        let (_, diags) = parse_str("const x: number = f?.();");
8844        assert!(diags.is_empty(), "unexpected: {diags:?}");
8845        let (_, diags) = parse_str("const y: number = arr?.[0];");
8846        assert!(diags.is_empty(), "unexpected: {diags:?}");
8847    }
8848
8849    #[test]
8850    fn parse_optional_chain_terminates_at_questionmark_for_ternary() {
8851        let (ast, diags) = parse_str("const x: number = a?.b ? c : d;");
8852        assert!(diags.is_empty(), "unexpected: {diags:?}");
8853        let stmt = single_stmt(&ast);
8854        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8855            panic!("expected Const");
8856        };
8857        let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8858            panic!(
8859                "expected top-level Ternary, got {:?}",
8860                ast.try_expr(value).unwrap().kind
8861            );
8862        };
8863        assert!(matches!(
8864            ast.try_expr(cond).unwrap().kind,
8865            crate::ExprKind::OptionalChain { .. }
8866        ));
8867    }
8868
8869    #[test]
8870    fn parse_nullish_below_ternary() {
8871        let (ast, diags) = parse_str("const x: number = a ?? b ? c : d;");
8872        assert!(diags.is_empty(), "unexpected: {diags:?}");
8873        let stmt = single_stmt(&ast);
8874        let crate::StmtKind::Const { value, .. } = stmt.kind else {
8875            panic!("expected Const");
8876        };
8877        let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8878            panic!("expected top-level Ternary");
8879        };
8880        match ast.try_expr(cond).unwrap().kind {
8881            crate::ExprKind::Binary { op, .. } => {
8882                assert_eq!(op, crate::BinOp::NullishCoalesce);
8883            }
8884            ref other => panic!("expected Binary ??, got {other:?}"),
8885        }
8886    }
8887
8888    #[test]
8889    fn let_missing_initializer_diagnoses() {
8890        let (ast, diags) = parse_str("let x;");
8891        assert_eq!(diags.len(), 1);
8892        assert_eq!(
8893            diags[0].message,
8894            "`let` declaration requires an initializer"
8895        );
8896        assert!(ast.top_level.is_empty());
8897    }
8898
8899    #[test]
8900    fn missing_identifier_after_let_diagnoses() {
8901        let (ast, diags) = parse_str("let = 1;");
8902        assert_eq!(diags.len(), 1);
8903        assert_eq!(diags[0].message, "expected identifier after `let`/`const`");
8904        assert!(ast.top_level.is_empty());
8905    }
8906
8907    #[test]
8908    fn missing_type_after_colon_diagnoses() {
8909        let (ast, diags) = parse_str("let x: = 1;");
8910        assert_eq!(diags.len(), 1);
8911        assert_eq!(diags[0].message, "expected type");
8912        assert!(ast.top_level.is_empty());
8913    }
8914
8915    #[test]
8916    fn missing_semicolon_after_declaration_diagnoses() {
8917        let (ast, diags) = parse_str("let x = 1 y;");
8918        assert_eq!(diags.len(), 1);
8919        assert_eq!(diags[0].message, "expected `;` after declaration");
8920        assert!(ast.top_level.is_empty());
8921    }
8922
8923    #[test]
8924    fn expression_statements_still_parse() {
8925        let (ast, diags) = parse_str("42;");
8926        assert!(diags.is_empty());
8927        assert_eq!(ast.top_level.len(), 1);
8928    }
8929
8930    #[test]
8931    fn snapshot_mixed_declarations() {
8932        let (ast, diags) =
8933            parse_str(r#"let x = 1; const y: string = "hi"; let z: number = 1 + 2;"#);
8934        assert!(diags.is_empty());
8935        assert_eq!(ast.top_level.len(), 3);
8936        insta::assert_debug_snapshot!(ast);
8937    }
8938
8939    #[test]
8940    fn parse_function_typical() {
8941        let (ast, diags) = parse_str("function f(a: number, b: string): boolean { }");
8942        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8943        let stmt = single_stmt(&ast);
8944        match stmt.kind {
8945            crate::StmtKind::Function {
8946                ref name,
8947                ref generics,
8948                ref params,
8949                ref return_type,
8950                body,
8951                ..
8952            } => {
8953                assert_eq!(name.name, "f");
8954                assert_eq!(name.span, crate::Span::new(F, 9, 10).unwrap());
8955                assert!(generics.is_empty());
8956                assert_eq!(params.len(), 2);
8957                assert_eq!(params[0].name.name, "a");
8958                assert_eq!(params[0].name.span, crate::Span::new(F, 11, 12).unwrap());
8959                let p0_ty = params[0]
8960                    .ty
8961                    .as_ref()
8962                    .expect("function-decl param requires annotation");
8963                assert!(matches!(p0_ty.kind, crate::TypeAnnotationKind::Name { .. }));
8964                assert_eq!(p0_ty.span, crate::Span::new(F, 14, 20).unwrap()); // `number`
8965                assert_eq!(params[1].name.name, "b");
8966                assert_eq!(params[1].name.span, crate::Span::new(F, 22, 23).unwrap());
8967                let p1_ty = params[1]
8968                    .ty
8969                    .as_ref()
8970                    .expect("function-decl param requires annotation");
8971                assert_eq!(p1_ty.span, crate::Span::new(F, 25, 31).unwrap()); // `string`
8972                let return_type = return_type.as_ref().expect("plain return type");
8973                assert!(matches!(
8974                    return_type.kind,
8975                    crate::TypeAnnotationKind::Name { .. }
8976                ));
8977                assert_eq!(return_type.span, crate::Span::new(F, 34, 41).unwrap()); // `boolean`
8978                let block = ast.try_stmt(body).unwrap();
8979                assert!(matches!(block.kind, crate::StmtKind::Block(ref v) if v.is_empty()));
8980            }
8981            _ => panic!("expected Function"),
8982        }
8983    }
8984
8985    #[test]
8986    fn parse_function_with_doc_comment() {
8987        let (ast, diags) = parse_str(
8988            "/**\n * Sum two numbers.\n * @param a First.\n * @param b Second.\n */\nfunction add(a: number, b: number): number { return a + b; }",
8989        );
8990        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8991        let stmt = single_stmt(&ast);
8992        match &stmt.kind {
8993            crate::StmtKind::Function { doc, .. } => {
8994                let d = doc.as_ref().expect("doc attached");
8995                assert_eq!(d.summary, "Sum two numbers.");
8996                assert_eq!(d.params.len(), 2);
8997                assert_eq!(d.params[0].name, "a");
8998                assert_eq!(d.params[1].name, "b");
8999            }
9000            _ => panic!("expected Function"),
9001        }
9002    }
9003
9004    #[test]
9005    fn parse_interface_member_docs() {
9006        let (ast, diags) = parse_str(
9007            "interface Foo {\n  /** First method. */ first(): void;\n  /** A field. */ field: number;\n}",
9008        );
9009        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9010        let stmt = single_stmt(&ast);
9011        match &stmt.kind {
9012            crate::StmtKind::InterfaceDecl { members, .. } => {
9013                assert_eq!(members.len(), 2);
9014                match &members[0] {
9015                    crate::InterfaceMember::Method { doc, .. } => {
9016                        assert_eq!(doc.as_ref().unwrap().summary, "First method.");
9017                    }
9018                    _ => panic!("expected Method first"),
9019                }
9020                match &members[1] {
9021                    crate::InterfaceMember::Property { doc, .. } => {
9022                        assert_eq!(doc.as_ref().unwrap().summary, "A field.");
9023                    }
9024                    _ => panic!("expected Property second"),
9025                }
9026            }
9027            _ => panic!("expected InterfaceDecl"),
9028        }
9029    }
9030
9031    #[test]
9032    fn parse_numeric_enum_all_implicit() {
9033        let (ast, diags) = parse_str("enum Direction { Up, Down, Left, Right }");
9034        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9035        let stmt = single_stmt(&ast);
9036        match &stmt.kind {
9037            crate::StmtKind::EnumDecl { name, members, doc } => {
9038                assert_eq!(name.name, "Direction");
9039                assert!(doc.is_none());
9040                assert_eq!(members.len(), 4);
9041                for (m, expected) in members.iter().zip(["Up", "Down", "Left", "Right"]) {
9042                    assert_eq!(m.name.name, expected);
9043                    assert!(m.value.is_none(), "expected no initializer on {expected}");
9044                }
9045            }
9046            _ => panic!("expected EnumDecl"),
9047        }
9048    }
9049
9050    #[test]
9051    fn parse_string_enum_all_explicit() {
9052        let (ast, diags) =
9053            parse_str("enum Status { Active = \"active\", Inactive = \"inactive\" }");
9054        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9055        let stmt = single_stmt(&ast);
9056        match &stmt.kind {
9057            crate::StmtKind::EnumDecl { name, members, .. } => {
9058                assert_eq!(name.name, "Status");
9059                assert_eq!(members.len(), 2);
9060                match &members[0].value {
9061                    Some(crate::EnumInitializer::String { value, .. }) => {
9062                        assert_eq!(value, "active");
9063                    }
9064                    other => panic!("expected String init, got {other:?}"),
9065                }
9066                match &members[1].value {
9067                    Some(crate::EnumInitializer::String { value, .. }) => {
9068                        assert_eq!(value, "inactive");
9069                    }
9070                    other => panic!("expected String init, got {other:?}"),
9071                }
9072            }
9073            _ => panic!("expected EnumDecl"),
9074        }
9075    }
9076
9077    #[test]
9078    fn parse_numeric_enum_with_negative_init() {
9079        let (ast, diags) = parse_str("enum D { Up = 1, Down = -1 }");
9080        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9081        let stmt = single_stmt(&ast);
9082        match &stmt.kind {
9083            crate::StmtKind::EnumDecl { members, .. } => {
9084                assert_eq!(members.len(), 2);
9085                match &members[0].value {
9086                    Some(crate::EnumInitializer::Number { value, .. }) => {
9087                        assert_eq!(*value, 1.0);
9088                    }
9089                    other => panic!("expected Number init, got {other:?}"),
9090                }
9091                match &members[1].value {
9092                    Some(crate::EnumInitializer::Number { value, .. }) => {
9093                        assert_eq!(*value, -1.0);
9094                    }
9095                    other => panic!("expected Number init, got {other:?}"),
9096                }
9097            }
9098            _ => panic!("expected EnumDecl"),
9099        }
9100    }
9101
9102    #[test]
9103    fn parse_enum_mixed_kinds_is_parse_error() {
9104        let (_ast, diags) = parse_str("enum Mix { A = 1, B = \"two\" }");
9105        assert!(
9106            diags
9107                .iter()
9108                .any(|d| d.message.contains("mixed numeric and string")),
9109            "expected mixed-kind diagnostic, got: {diags:?}"
9110        );
9111    }
9112
9113    #[test]
9114    fn parse_enum_member_docs() {
9115        let (ast, diags) = parse_str(
9116            "enum D {\n  /** The first one. */ A = 1,\n  /** The second one. */ B = 2,\n}",
9117        );
9118        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9119        let stmt = single_stmt(&ast);
9120        match &stmt.kind {
9121            crate::StmtKind::EnumDecl { members, .. } => {
9122                assert_eq!(members.len(), 2);
9123                assert_eq!(
9124                    members[0].doc.as_ref().expect("first doc").summary,
9125                    "The first one."
9126                );
9127                assert_eq!(
9128                    members[1].doc.as_ref().expect("second doc").summary,
9129                    "The second one."
9130                );
9131            }
9132            _ => panic!("expected EnumDecl"),
9133        }
9134    }
9135
9136    #[test]
9137    fn parse_enum_trailing_comma_ok() {
9138        let (ast, diags) = parse_str("enum D { A, B, }");
9139        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9140        let stmt = single_stmt(&ast);
9141        match &stmt.kind {
9142            crate::StmtKind::EnumDecl { members, .. } => assert_eq!(members.len(), 2),
9143            _ => panic!("expected EnumDecl"),
9144        }
9145    }
9146
9147    #[test]
9148    fn parse_function_empty_params() {
9149        let (ast, diags) = parse_str("function noop(): void { }");
9150        assert!(diags.is_empty());
9151        let stmt = single_stmt(&ast);
9152        match stmt.kind {
9153            crate::StmtKind::Function { ref params, .. } => assert!(params.is_empty()),
9154            _ => panic!("expected Function"),
9155        }
9156    }
9157
9158    #[test]
9159    fn parse_generic_function_single_param() {
9160        let (ast, diags) = parse_str("function identity<T>(x: T): T { return x; }");
9161        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9162        let stmt = single_stmt(&ast);
9163        match stmt.kind {
9164            crate::StmtKind::Function {
9165                ref name,
9166                ref generics,
9167                ref params,
9168                ..
9169            } => {
9170                assert_eq!(name.name, "identity");
9171                assert_eq!(generics.len(), 1);
9172                assert_eq!(generics[0].name, "T");
9173                assert_eq!(params.len(), 1);
9174                assert_eq!(params[0].name.name, "x");
9175            }
9176            _ => panic!("expected Function"),
9177        }
9178    }
9179
9180    #[test]
9181    fn parse_generic_function_multiple_params() {
9182        let (ast, diags) = parse_str("function pair<T, U>(a: T, b: U): T { return a; }");
9183        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9184        let stmt = single_stmt(&ast);
9185        match stmt.kind {
9186            crate::StmtKind::Function { ref generics, .. } => {
9187                assert_eq!(generics.len(), 2);
9188                assert_eq!(generics[0].name, "T");
9189                assert_eq!(generics[1].name, "U");
9190            }
9191            _ => panic!("expected Function"),
9192        }
9193    }
9194
9195    #[test]
9196    fn parse_non_generic_function_has_empty_generics() {
9197        let (ast, diags) = parse_str("function noop(): void { }");
9198        assert!(diags.is_empty());
9199        let stmt = single_stmt(&ast);
9200        match stmt.kind {
9201            crate::StmtKind::Function { ref generics, .. } => assert!(generics.is_empty()),
9202            _ => panic!("expected Function"),
9203        }
9204    }
9205
9206    #[test]
9207    fn parse_empty_generic_list_diagnoses() {
9208        let (_ast, diags) = parse_str("function f<>(): void { }");
9209        assert!(
9210            diags
9211                .iter()
9212                .any(|d| d.message.contains("empty generic parameter list")),
9213            "expected empty-generic-list diagnostic, got: {diags:?}",
9214        );
9215    }
9216
9217    #[test]
9218    fn parse_trailing_comma_in_generic_list_ok() {
9219        let (ast, diags) = parse_str("function f<T,>(): void { }");
9220        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9221        match single_stmt(&ast).kind {
9222            crate::StmtKind::Function { ref generics, .. } => assert_eq!(generics.len(), 1),
9223            _ => panic!("expected Function"),
9224        }
9225    }
9226
9227    #[test]
9228    fn parse_generic_call_single_type_arg() {
9229        let (ast, diags) = parse_str("identity<number>(42);");
9230        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9231        let stmt = single_stmt(&ast);
9232        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9233            panic!("expected Expr stmt");
9234        };
9235        match &ast.try_expr(call_id).unwrap().kind {
9236            crate::ExprKind::Call {
9237                type_args, args, ..
9238            } => {
9239                let ta = type_args.as_ref().expect("type_args set for generic call");
9240                assert_eq!(ta.len(), 1);
9241                assert_eq!(args.len(), 1);
9242            }
9243            other => panic!("expected Call, got {other:?}"),
9244        }
9245    }
9246
9247    #[test]
9248    fn parse_generic_call_trailing_comma_ok() {
9249        let (ast, diags) = parse_str("identity<number,>(42);");
9250        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9251        let stmt = single_stmt(&ast);
9252        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9253            panic!("expected Expr stmt");
9254        };
9255        match &ast.try_expr(call_id).unwrap().kind {
9256            crate::ExprKind::Call {
9257                type_args, args, ..
9258            } => {
9259                assert_eq!(type_args.as_ref().expect("type_args set").len(), 1);
9260                assert_eq!(args.len(), 1);
9261            }
9262            other => panic!("expected Call, got {other:?}"),
9263        }
9264    }
9265
9266    #[test]
9267    fn parse_generic_call_multi_type_args() {
9268        let (ast, diags) = parse_str("pair<number, string>(1, \"x\");");
9269        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9270        let stmt = single_stmt(&ast);
9271        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9272            panic!("expected Expr stmt");
9273        };
9274        match &ast.try_expr(call_id).unwrap().kind {
9275            crate::ExprKind::Call { type_args, .. } => {
9276                let ta = type_args.as_ref().expect("type_args set for generic call");
9277                assert_eq!(ta.len(), 2);
9278            }
9279            other => panic!("expected Call, got {other:?}"),
9280        }
9281    }
9282
9283    #[test]
9284    fn parse_comparison_chain_unaffected_by_generic_lookahead() {
9285        let (ast, diags) = parse_str("let z = a < b > c;");
9286        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9287        assert!(
9288            !ast_contains_call_with_type_args(&ast),
9289            "should not have parsed any generic call",
9290        );
9291    }
9292
9293    #[test]
9294    fn parse_lt_with_paren_rhs_is_comparison_not_generic() {
9295        let (ast, diags) = parse_str("let z = a < (b);");
9296        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9297        assert!(!ast_contains_call_with_type_args(&ast));
9298    }
9299
9300    #[test]
9301    fn parse_generic_call_no_args() {
9302        let (ast, diags) = parse_str("none<number>();");
9303        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9304        let stmt = single_stmt(&ast);
9305        let crate::StmtKind::Expr(call_id) = stmt.kind else {
9306            panic!("expected Expr stmt");
9307        };
9308        match &ast.try_expr(call_id).unwrap().kind {
9309            crate::ExprKind::Call {
9310                type_args, args, ..
9311            } => {
9312                assert_eq!(type_args.as_ref().unwrap().len(), 1);
9313                assert!(args.is_empty());
9314            }
9315            other => panic!("expected Call, got {other:?}"),
9316        }
9317    }
9318
9319    fn ast_contains_call_with_type_args(ast: &crate::Ast) -> bool {
9320        for id in 0..ast.top_level.len() {
9321            let stmt = ast.try_stmt(ast.top_level[id]).unwrap();
9322            if walk_stmt_for_typed_call(ast, stmt) {
9323                return true;
9324            }
9325        }
9326        false
9327    }
9328
9329    fn walk_stmt_for_typed_call(ast: &crate::Ast, stmt: &crate::Stmt) -> bool {
9330        match &stmt.kind {
9331            crate::StmtKind::Let { value, .. } | crate::StmtKind::Const { value, .. } => {
9332                walk_expr_for_typed_call(ast, *value)
9333            }
9334            crate::StmtKind::Expr(e) => walk_expr_for_typed_call(ast, *e),
9335            crate::StmtKind::Block(stmts) => stmts
9336                .iter()
9337                .any(|sid| walk_stmt_for_typed_call(ast, ast.try_stmt(*sid).unwrap())),
9338            _ => false,
9339        }
9340    }
9341
9342    fn walk_expr_for_typed_call(ast: &crate::Ast, id: crate::ExprId) -> bool {
9343        match &ast.try_expr(id).unwrap().kind {
9344            crate::ExprKind::Call {
9345                type_args: Some(_), ..
9346            } => true,
9347            crate::ExprKind::Call { callee, args, .. } => {
9348                walk_expr_for_typed_call(ast, *callee)
9349                    || args.iter().any(|a| walk_expr_for_typed_call(ast, *a))
9350            }
9351            crate::ExprKind::Binary { lhs, rhs, .. } => {
9352                walk_expr_for_typed_call(ast, *lhs) || walk_expr_for_typed_call(ast, *rhs)
9353            }
9354            crate::ExprKind::Unary { operand, .. } => walk_expr_for_typed_call(ast, *operand),
9355            crate::ExprKind::Paren(e) => walk_expr_for_typed_call(ast, *e),
9356            crate::ExprKind::FieldAccess { receiver, .. } => {
9357                walk_expr_for_typed_call(ast, *receiver)
9358            }
9359            crate::ExprKind::IndexAccess { receiver, index } => {
9360                walk_expr_for_typed_call(ast, *receiver) || walk_expr_for_typed_call(ast, *index)
9361            }
9362            _ => false,
9363        }
9364    }
9365
9366    #[test]
9367    fn parse_function_body_with_statements() {
9368        let (ast, diags) = parse_str("function g(x: number): number { let y = x; y; }");
9369        assert!(diags.is_empty());
9370        let stmt = single_stmt(&ast);
9371        match stmt.kind {
9372            crate::StmtKind::Function { body, .. } => {
9373                let block = ast.try_stmt(body).unwrap();
9374                match block.kind {
9375                    crate::StmtKind::Block(ref v) => assert_eq!(v.len(), 2),
9376                    _ => panic!("expected Block"),
9377                }
9378            }
9379            _ => panic!("expected Function"),
9380        }
9381    }
9382
9383    #[test]
9384    fn function_missing_return_type_diagnoses() {
9385        let (ast, diags) = parse_str("function f() { }");
9386        assert!(!diags.is_empty());
9387        assert_eq!(diags[0].message, "expected `:` and return type");
9388        assert!(ast.top_level.is_empty());
9389    }
9390
9391    #[test]
9392    fn method_missing_return_type_still_reads_the_body() {
9393        let (_ast, diags) = parse_str(
9394            "class Box {\n  async run() {\n    const x = await f();\n    return x;\n  }\n  ok(): void {}\n}",
9395        );
9396        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9397        assert_eq!(
9398            messages,
9399            [
9400                "`async` functions are not supported",
9401                "expected `:` and return type",
9402                "`await` is not supported"
9403            ],
9404            "{diags:?}"
9405        );
9406        assert_eq!(
9407            diags[1].help,
9408            ["write the return type: `run(…): T { … }`, with `void` when it returns nothing"]
9409        );
9410    }
9411
9412    #[test]
9413    fn an_unclosed_method_body_leaves_the_next_declaration_alone() {
9414        let (ast, diags) =
9415            parse_str("class Box {\n  a() { return 1;\n}\nfunction main(): number { return 0; }");
9416        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9417        assert_eq!(
9418            messages,
9419            [
9420                "expected `:` and return type",
9421                "expected `}` to close class"
9422            ],
9423            "{diags:?}"
9424        );
9425        assert_eq!(ast.top_level.len(), 1, "`main` is still declared");
9426    }
9427
9428    #[test]
9429    fn every_declaration_after_an_unclosed_method_body_is_kept() {
9430        for next in [
9431            "class B { ok(): number { return 2; } }",
9432            "interface I { a: number; }",
9433            "enum E { A }",
9434            "type X = number;",
9435            "import * as fs from \"submilli:fs\";",
9436            "async function g(): number { return 1; }",
9437        ] {
9438            let source = format!(
9439                "class A {{\n  bad() {{\n    return 1;\n}}\n{next}\nfunction main(): void {{}}"
9440            );
9441            let (ast, diags) = parse_str(&source);
9442            let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9443            let expected_async = next.starts_with("async");
9444            let mut expected = vec![
9445                "expected `:` and return type",
9446                "expected `}` to close class",
9447            ];
9448            if expected_async {
9449                expected.push("`async` functions are not supported");
9450            }
9451            assert_eq!(messages, expected, "{next}: {diags:?}");
9452            assert_eq!(
9453                ast.top_level.len(),
9454                2,
9455                "{next} and `main` are kept: {diags:?}"
9456            );
9457        }
9458    }
9459
9460    #[test]
9461    fn a_failed_statement_leaves_the_next_declaration_to_be_parsed() {
9462        for (source, kept) in [
9463            ("let x = 1 +\nimport { exists } from \"submilli:fs\";", 1),
9464            ("const y =\nclass C {}\nfunction main(): void {}", 2),
9465            (
9466                "interface I {\n  a: number;\nimport * as fs from \"submilli:fs\";",
9467                1,
9468            ),
9469        ] {
9470            let (ast, diags) = parse_str(source);
9471            assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9472            assert_eq!(ast.top_level.len(), kept, "{source}: {diags:?}");
9473        }
9474    }
9475
9476    #[test]
9477    fn a_dynamic_import_after_a_failed_statement_reports_once() {
9478        for source in [
9479            "const m = import(\"submilli:fs\");\nclass C {}",
9480            "const d = import.meta.dirname;\nclass C {}",
9481        ] {
9482            let (_ast, diags) = parse_str(source);
9483            assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9484        }
9485    }
9486
9487    #[test]
9488    fn members_named_like_declarations_after_a_bad_method_stay_members() {
9489        let (_ast, diags) = parse_str(
9490            "class A {\n  bad() {}\n  type: number = 1;\n  function(): void {}\n  declare(): void {}\n  async(): void {}\n}",
9491        );
9492        assert_eq!(diags.len(), 1, "{diags:?}");
9493    }
9494
9495    #[test]
9496    fn every_member_after_a_bad_method_is_still_read() {
9497        let (_ast, diags) = parse_str(
9498            "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}",
9499        );
9500        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9501        assert_eq!(messages, ["expected `:` and return type"; 3], "{diags:?}");
9502    }
9503
9504    #[test]
9505    fn a_member_failing_after_a_type_literal_reports_once() {
9506        let (_ast, diags) = parse_str("class A {\n  x: { a: number } 5\n}");
9507        assert_eq!(diags.len(), 1, "{diags:?}");
9508    }
9509
9510    #[test]
9511    fn multiple_declarators_name_the_split() {
9512        let (_ast, diags) = parse_str("let a = 0, b = 0;");
9513        assert_eq!(diags.len(), 1, "{diags:?}");
9514        assert_eq!(diags[0].message, "one declaration declares one variable");
9515        assert_eq!(
9516            diags[0].help,
9517            [
9518                "declare one variable per statement: `let a = …; let b = …;`; in a `for (…)` header, declare `b` before the loop"
9519            ]
9520        );
9521        let (_ast, diags) = parse_str("for (let i = 0, j = 10; i < j; i++) {}");
9522        assert_eq!(
9523            diags[0].message, "one declaration declares one variable",
9524            "{diags:?}"
9525        );
9526        assert!(
9527            diags[0].help[0].ends_with("declare `j` before the loop"),
9528            "{diags:?}"
9529        );
9530    }
9531
9532    #[test]
9533    fn missing_return_type_still_reads_the_body() {
9534        let (_ast, diags) = parse_str(
9535            "async function main() {\n  const x = await f();\n  if (x) { return 1; }\n  return 2;\n}\nfunction g(): void {}",
9536        );
9537        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9538        assert_eq!(
9539            messages,
9540            [
9541                "`async` functions are not supported",
9542                "expected `:` and return type",
9543                "`await` is not supported"
9544            ],
9545            "{diags:?}"
9546        );
9547        assert_eq!(
9548            diags[1].help,
9549            [
9550                "write the return type: `function main(…): T { … }`, with `void` when it returns nothing"
9551            ]
9552        );
9553    }
9554
9555    #[test]
9556    fn function_missing_param_type_diagnoses() {
9557        let (ast, diags) = parse_str("function f(a): void { }");
9558        assert!(!diags.is_empty());
9559        assert_eq!(diags[0].message, "parameter requires a type annotation");
9560        assert!(ast.top_level.is_empty());
9561    }
9562
9563    #[test]
9564    fn function_missing_name_diagnoses() {
9565        let (ast, diags) = parse_str("function (x: number): void { }");
9566        assert!(!diags.is_empty());
9567        assert_eq!(diags[0].message, "expected function name");
9568        assert!(ast.top_level.is_empty());
9569    }
9570
9571    #[test]
9572    fn function_trailing_param_comma_ok() {
9573        let (ast, diags) = parse_str("function f(a: number,): void { }");
9574        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9575        match single_stmt(&ast).kind {
9576            crate::StmtKind::Function { ref params, .. } => assert_eq!(params.len(), 1),
9577            _ => panic!("expected Function"),
9578        }
9579    }
9580
9581    #[test]
9582    fn function_missing_open_brace_diagnoses() {
9583        let (ast, diags) = parse_str("function f(): void");
9584        assert_eq!(diags.len(), 1);
9585        assert_eq!(diags[0].message, "expected `{`");
9586        assert!(ast.top_level.is_empty());
9587    }
9588
9589    #[test]
9590    fn snapshot_function_declaration() {
9591        let (ast, diags) =
9592            parse_str("function add(a: number, b: number): number { let sum = a + b; sum; }");
9593        assert!(diags.is_empty());
9594        insta::assert_debug_snapshot!(ast);
9595    }
9596
9597    #[test]
9598    fn rest_param_basic_parses() {
9599        let (_, diags) = parse_str("function sum(...nums: number[]): number { return 0; }");
9600        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9601    }
9602
9603    #[test]
9604    fn rest_param_with_fixed_prefix_parses() {
9605        let (_, diags) =
9606            parse_str("function tag(label: string, ...vals: number[]): string { return label; }");
9607        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9608    }
9609
9610    #[test]
9611    fn rest_param_in_arrow_parses() {
9612        let (_, diags) = parse_str("const f = (...n: number[]) => n.length;");
9613        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9614    }
9615
9616    #[test]
9617    fn rest_param_in_function_type_annotation_parses() {
9618        let (_, diags) =
9619            parse_str("let f: (a: number, ...rest: string[]) => void = (a, ...r) => {};");
9620        assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9621    }
9622
9623    #[test]
9624    fn rest_param_not_last_rejected() {
9625        let (_, diags) = parse_str("function f(...xs: number[], y: number): void {}");
9626        assert!(
9627            diags.iter().any(|d| d
9628                .message
9629                .contains("rest parameter must be the last parameter")),
9630            "expected 'must be last' diagnostic, got {diags:?}",
9631        );
9632    }
9633
9634    #[test]
9635    fn rest_param_with_default_rejected() {
9636        let (_, diags) = parse_str("function f(...xs: number[] = []): void {}");
9637        assert!(
9638            diags.iter().any(|d| d
9639                .message
9640                .contains("rest parameter cannot have a default value")),
9641            "expected default-rejection diagnostic, got {diags:?}",
9642        );
9643    }
9644
9645    #[test]
9646    fn rest_param_without_annotation_rejected() {
9647        let (_, diags) = parse_str("function f(...xs): void {}");
9648        assert!(
9649            diags.iter().any(|d| d
9650                .message
9651                .contains("rest parameter requires a type annotation")),
9652            "expected missing-annotation diagnostic, got {diags:?}",
9653        );
9654    }
9655
9656    #[test]
9657    fn rest_param_destructured_rejected() {
9658        let (_, diags) = parse_str("function f(...{a, b}): void {}");
9659        assert!(
9660            diags
9661                .iter()
9662                .any(|d| d.message.contains("rest parameter cannot be destructured")),
9663            "expected destructured-rejection diagnostic, got {diags:?}",
9664        );
9665    }
9666
9667    #[test]
9668    fn rest_param_in_function_type_with_non_array_rejected() {
9669        let (_, diags) = parse_str("let f: (...xs: number) => void = () => {};");
9670        assert!(
9671            diags
9672                .iter()
9673                .any(|d| d.message.contains("rest parameter type must be an array")),
9674            "expected non-array-type diagnostic, got {diags:?}",
9675        );
9676    }
9677
9678    fn call(e: &crate::Expr) -> (crate::ExprId, &[crate::ExprId]) {
9679        match e.kind {
9680            crate::ExprKind::Call {
9681                callee, ref args, ..
9682            } => (callee, args.as_slice()),
9683            _ => panic!("expected Call, got {:?}", e.kind),
9684        }
9685    }
9686
9687    #[test]
9688    fn parse_call_no_args() {
9689        let (ast, diags) = parse_str("f();");
9690        assert!(diags.is_empty());
9691        let outer = expr_of_single_stmt(&ast);
9692        let (callee, args) = call(outer);
9693        assert!(matches!(
9694            ast.try_expr(callee).unwrap().kind,
9695            crate::ExprKind::Identifier(_)
9696        ));
9697        assert!(args.is_empty());
9698        assert_eq!(outer.span, crate::Span::new(F, 0, 3).unwrap());
9699    }
9700
9701    #[test]
9702    fn parse_call_one_arg() {
9703        let (ast, diags) = parse_str("f(1);");
9704        assert!(diags.is_empty());
9705        let outer = expr_of_single_stmt(&ast);
9706        let (callee, args) = call(outer);
9707        assert!(matches!(
9708            ast.try_expr(callee).unwrap().kind,
9709            crate::ExprKind::Identifier(_)
9710        ));
9711        assert_eq!(args.len(), 1);
9712        assert_eq!(
9713            ast.try_expr(args[0]).unwrap().kind,
9714            crate::ExprKind::Number(1.0)
9715        );
9716    }
9717
9718    #[test]
9719    fn parse_call_three_args() {
9720        let (ast, diags) = parse_str("f(1, 2, 3);");
9721        assert!(diags.is_empty());
9722        let outer = expr_of_single_stmt(&ast);
9723        let (_callee, args) = call(outer);
9724        assert_eq!(args.len(), 3);
9725        assert_eq!(
9726            ast.try_expr(args[0]).unwrap().kind,
9727            crate::ExprKind::Number(1.0)
9728        );
9729        assert_eq!(
9730            ast.try_expr(args[1]).unwrap().kind,
9731            crate::ExprKind::Number(2.0)
9732        );
9733        assert_eq!(
9734            ast.try_expr(args[2]).unwrap().kind,
9735            crate::ExprKind::Number(3.0)
9736        );
9737    }
9738
9739    #[test]
9740    fn parse_call_nested() {
9741        let (ast, diags) = parse_str("f(g(x));");
9742        assert!(diags.is_empty());
9743        let outer = expr_of_single_stmt(&ast);
9744        let (_callee, args) = call(outer);
9745        assert_eq!(args.len(), 1);
9746        let (_inner_callee, inner_args) = call(ast.try_expr(args[0]).unwrap());
9747        assert_eq!(inner_args.len(), 1);
9748        assert!(matches!(
9749            ast.try_expr(inner_args[0]).unwrap().kind,
9750            crate::ExprKind::Identifier(_)
9751        ));
9752    }
9753
9754    #[test]
9755    fn parse_curried_call() {
9756        let (ast, diags) = parse_str("f()(x);");
9757        assert!(diags.is_empty());
9758        let outer = expr_of_single_stmt(&ast);
9759        let (callee, args) = call(outer);
9760        assert_eq!(args.len(), 1);
9761        let (_inner_callee, inner_args) = call(ast.try_expr(callee).unwrap());
9762        assert!(inner_args.is_empty());
9763    }
9764
9765    #[test]
9766    fn unary_binds_looser_than_call() {
9767        let (ast, diags) = parse_str("-f();");
9768        assert!(diags.is_empty());
9769        let outer = expr_of_single_stmt(&ast);
9770        let (op, operand) = unary(outer);
9771        assert_eq!(op, crate::UnOp::Neg);
9772        let (_callee, args) = call(ast.try_expr(operand).unwrap());
9773        assert!(args.is_empty());
9774    }
9775
9776    #[test]
9777    fn call_inside_binary() {
9778        let (ast, diags) = parse_str("f() + 1;");
9779        assert!(diags.is_empty());
9780        let outer = expr_of_single_stmt(&ast);
9781        let (op, lhs, rhs) = binary(outer);
9782        assert_eq!(op, crate::BinOp::Add);
9783        let (_callee, args) = call(ast.try_expr(lhs).unwrap());
9784        assert!(args.is_empty());
9785        assert_eq!(
9786            ast.try_expr(rhs).unwrap().kind,
9787            crate::ExprKind::Number(1.0)
9788        );
9789    }
9790
9791    #[test]
9792    fn call_with_complex_args() {
9793        let (ast, diags) = parse_str("f(1 + 2, g(x));");
9794        assert!(diags.is_empty());
9795        let outer = expr_of_single_stmt(&ast);
9796        let (_callee, args) = call(outer);
9797        assert_eq!(args.len(), 2);
9798        let (op, ..) = binary(ast.try_expr(args[0]).unwrap());
9799        assert_eq!(op, crate::BinOp::Add);
9800        let (_, inner_args) = call(ast.try_expr(args[1]).unwrap());
9801        assert_eq!(inner_args.len(), 1);
9802    }
9803
9804    #[test]
9805    fn call_trailing_comma_ok() {
9806        let (ast, diags) = parse_str("f(1, 2,);");
9807        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9808        let (_callee, args) = call(expr_of_single_stmt(&ast));
9809        assert_eq!(args.len(), 2);
9810    }
9811
9812    #[test]
9813    fn unclosed_call_diagnoses() {
9814        let (ast, diags) = parse_str("f(1, 2");
9815        assert!(!diags.is_empty());
9816        assert!(diags[0].message == "expected `,` or `)`" || diags[0].message == "expected `)`");
9817        assert!(ast.top_level.is_empty());
9818    }
9819
9820    #[test]
9821    fn call_expression_statement() {
9822        let (ast, diags) = parse_str(r#"greet("world");"#);
9823        assert!(diags.is_empty());
9824        let outer = expr_of_single_stmt(&ast);
9825        let (callee, args) = call(outer);
9826        assert!(matches!(
9827            ast.try_expr(callee).unwrap().kind,
9828            crate::ExprKind::Identifier(_)
9829        ));
9830        assert_eq!(args.len(), 1);
9831        assert_eq!(
9832            ast.try_expr(args[0]).unwrap().kind,
9833            crate::ExprKind::String("world".to_string())
9834        );
9835    }
9836
9837    #[test]
9838    fn snapshot_multi_call_program() {
9839        let (ast, diags) = parse_str("f(); g(1); h(f(2), 3);");
9840        assert!(diags.is_empty());
9841        assert_eq!(ast.top_level.len(), 3);
9842        insta::assert_debug_snapshot!(ast);
9843    }
9844
9845    #[test]
9846    fn parse_if_simple() {
9847        let (ast, diags) = parse_str("if (a) { }");
9848        assert!(diags.is_empty());
9849        let stmt = single_stmt(&ast);
9850        match stmt.kind {
9851            crate::StmtKind::If {
9852                condition,
9853                then_block,
9854                else_block,
9855            } => {
9856                assert!(matches!(
9857                    ast.try_expr(condition).unwrap().kind,
9858                    crate::ExprKind::Identifier(_)
9859                ));
9860                assert!(matches!(
9861                    ast.try_stmt(then_block).unwrap().kind,
9862                    crate::StmtKind::Block(ref v) if v.is_empty()
9863                ));
9864                assert!(else_block.is_none());
9865            }
9866            _ => panic!("expected If"),
9867        }
9868    }
9869
9870    #[test]
9871    fn parse_if_else() {
9872        let (ast, diags) = parse_str("if (a) { } else { }");
9873        assert!(diags.is_empty());
9874        let stmt = single_stmt(&ast);
9875        match stmt.kind {
9876            crate::StmtKind::If { else_block, .. } => {
9877                let else_id = else_block.expect("expected else block");
9878                assert!(matches!(
9879                    ast.try_stmt(else_id).unwrap().kind,
9880                    crate::StmtKind::Block(ref v) if v.is_empty()
9881                ));
9882            }
9883            _ => panic!("expected If"),
9884        }
9885    }
9886
9887    #[test]
9888    fn parse_else_if_chain() {
9889        let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
9890        assert!(diags.is_empty());
9891        let stmt = single_stmt(&ast);
9892        let crate::StmtKind::If {
9893            else_block: Some(inner_id),
9894            ..
9895        } = stmt.kind
9896        else {
9897            panic!("expected outer If with else_block");
9898        };
9899        let inner = ast.try_stmt(inner_id).unwrap();
9900        let crate::StmtKind::If {
9901            else_block: Some(tail_id),
9902            ..
9903        } = inner.kind
9904        else {
9905            panic!("expected inner If for `else if`");
9906        };
9907        assert!(matches!(
9908            ast.try_stmt(tail_id).unwrap().kind,
9909            crate::StmtKind::Block(_)
9910        ));
9911    }
9912
9913    #[test]
9914    fn parse_if_with_body_statements() {
9915        let (ast, diags) = parse_str("if (a) { let x = 1; }");
9916        assert!(diags.is_empty());
9917        let stmt = single_stmt(&ast);
9918        match stmt.kind {
9919            crate::StmtKind::If { then_block, .. } => {
9920                let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind
9921                else {
9922                    panic!("expected Block")
9923                };
9924                assert_eq!(body.len(), 1);
9925            }
9926            _ => panic!("expected If"),
9927        }
9928    }
9929
9930    #[test]
9931    fn parse_if_braceless_body() {
9932        let (ast, diags) = parse_str("if (a) return b;");
9933        assert!(diags.is_empty(), "{diags:?}");
9934        let stmt = single_stmt(&ast);
9935        let crate::StmtKind::If {
9936            then_block,
9937            else_block,
9938            ..
9939        } = stmt.kind
9940        else {
9941            panic!("expected If");
9942        };
9943        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9944            panic!("braceless body should wrap in a Block");
9945        };
9946        assert_eq!(body.len(), 1);
9947        assert!(matches!(
9948            ast.try_stmt(body[0]).unwrap().kind,
9949            crate::StmtKind::Return(_)
9950        ));
9951        assert!(else_block.is_none());
9952    }
9953
9954    #[test]
9955    fn parse_braceless_dangling_else_binds_nearest_if() {
9956        // `else` attaches to the inner `if`, not the outer one.
9957        let (ast, diags) = parse_str("if (a) if (b) x(); else y();");
9958        assert!(diags.is_empty(), "{diags:?}");
9959        let crate::StmtKind::If {
9960            then_block,
9961            else_block: outer_else,
9962            ..
9963        } = single_stmt(&ast).kind
9964        else {
9965            panic!("expected outer If");
9966        };
9967        assert!(outer_else.is_none(), "else must bind to the inner if");
9968        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9969            panic!("expected wrapped Block");
9970        };
9971        let crate::StmtKind::If {
9972            else_block: Some(_),
9973            ..
9974        } = ast.try_stmt(body[0]).unwrap().kind
9975        else {
9976            panic!("inner If should own the else");
9977        };
9978    }
9979
9980    #[test]
9981    fn parse_while_braceless_body() {
9982        let (ast, diags) = parse_str("while (a) x();");
9983        assert!(diags.is_empty(), "{diags:?}");
9984        let crate::StmtKind::While { body, .. } = single_stmt(&ast).kind else {
9985            panic!("expected While");
9986        };
9987        assert!(matches!(
9988            ast.try_stmt(body).unwrap().kind,
9989            crate::StmtKind::Block(ref v) if v.len() == 1
9990        ));
9991    }
9992
9993    #[test]
9994    fn parse_loop_with_empty_body() {
9995        for src in [
9996            "while (a());",
9997            "for (const x of xs);",
9998            "for (let i = 0; i < 3; i++);",
9999            "do ; while (a());",
10000        ] {
10001            let (ast, diags) = parse_str(src);
10002            assert!(diags.is_empty(), "{src}: {diags:?}");
10003            let body = match single_stmt(&ast).kind {
10004                crate::StmtKind::While { body, .. }
10005                | crate::StmtKind::DoWhile { body, .. }
10006                | crate::StmtKind::For { body, .. }
10007                | crate::StmtKind::ForOf { body, .. } => body,
10008                ref other => panic!("{src}: expected a loop, got {other:?}"),
10009            };
10010            assert!(
10011                matches!(ast.try_stmt(body).unwrap().kind, crate::StmtKind::Block(ref v) if v.is_empty()),
10012                "{src}"
10013            );
10014        }
10015    }
10016
10017    #[test]
10018    fn parse_if_with_empty_body_is_an_error() {
10019        let (_ast, diags) = parse_str("if (a);");
10020        assert_eq!(diags[0].message, "`if` has an empty body");
10021    }
10022
10023    #[test]
10024    fn parse_empty_branch_body_reports_once() {
10025        let (_ast, diags) = parse_str("if (a) ; else b();");
10026        assert_eq!(diags.len(), 1, "{diags:?}");
10027        assert_eq!(diags[0].message, "`if` has an empty body");
10028        let (_ast, diags) = parse_str("if (a) b(); else ;");
10029        assert_eq!(diags.len(), 1, "{diags:?}");
10030        assert_eq!(diags[0].message, "`else` has an empty body");
10031        // One diagnostic per stray `;`, and the `else if` chain still parses.
10032        let (_ast, diags) = parse_str("if (a) ; else if (b) ; else ;");
10033        let messages: Vec<_> = diags.iter().map(|d| d.message.as_str()).collect();
10034        assert_eq!(
10035            messages,
10036            [
10037                "`if` has an empty body",
10038                "`if` has an empty body",
10039                "`else` has an empty body"
10040            ]
10041        );
10042    }
10043
10044    #[test]
10045    fn parse_for_of_braceless_body() {
10046        let (ast, diags) = parse_str("for (const x of xs) f(x);");
10047        assert!(diags.is_empty(), "{diags:?}");
10048        let crate::StmtKind::ForOf { body, .. } = single_stmt(&ast).kind else {
10049            panic!("expected ForOf");
10050        };
10051        assert!(matches!(
10052            ast.try_stmt(body).unwrap().kind,
10053            crate::StmtKind::Block(ref v) if v.len() == 1
10054        ));
10055    }
10056
10057    #[test]
10058    fn parse_nested_if() {
10059        let (ast, diags) = parse_str("if (a) { if (b) { } }");
10060        assert!(diags.is_empty());
10061        let outer = single_stmt(&ast);
10062        let crate::StmtKind::If { then_block, .. } = outer.kind else {
10063            panic!("expected outer If");
10064        };
10065        let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10066            panic!("expected Block")
10067        };
10068        assert_eq!(body.len(), 1);
10069        assert!(matches!(
10070            ast.try_stmt(body[0]).unwrap().kind,
10071            crate::StmtKind::If { .. }
10072        ));
10073    }
10074
10075    #[test]
10076    fn if_missing_open_paren_diagnoses() {
10077        let (ast, diags) = parse_str("if a { }");
10078        assert!(!diags.is_empty());
10079        assert_eq!(diags[0].message, "expected `(`");
10080        assert!(ast.top_level.is_empty());
10081    }
10082
10083    #[test]
10084    fn if_missing_close_paren_diagnoses() {
10085        let (ast, diags) = parse_str("if (a { }");
10086        assert!(!diags.is_empty());
10087        assert_eq!(diags[0].message, "expected `)`");
10088        assert!(ast.top_level.is_empty());
10089    }
10090
10091    #[test]
10092    fn if_braceless_body_parses() {
10093        let (ast, diags) = parse_str("if (a) x;");
10094        assert!(diags.is_empty(), "{diags:?}");
10095        let crate::StmtKind::If { then_block, .. } = single_stmt(&ast).kind else {
10096            panic!("expected If");
10097        };
10098        assert!(matches!(
10099            ast.try_stmt(then_block).unwrap().kind,
10100            crate::StmtKind::Block(ref v) if v.len() == 1
10101        ));
10102    }
10103
10104    #[test]
10105    fn snapshot_else_if_chain() {
10106        let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10107        assert!(diags.is_empty());
10108        insta::assert_debug_snapshot!(ast);
10109    }
10110
10111    #[test]
10112    fn parse_while_simple() {
10113        let (ast, diags) = parse_str("while (a) { }");
10114        assert!(diags.is_empty());
10115        let stmt = single_stmt(&ast);
10116        match stmt.kind {
10117            crate::StmtKind::While { condition, body } => {
10118                assert!(matches!(
10119                    ast.try_expr(condition).unwrap().kind,
10120                    crate::ExprKind::Identifier(_)
10121                ));
10122                assert!(matches!(
10123                    ast.try_stmt(body).unwrap().kind,
10124                    crate::StmtKind::Block(ref v) if v.is_empty()
10125                ));
10126            }
10127            _ => panic!("expected While"),
10128        }
10129    }
10130
10131    #[test]
10132    fn parse_while_with_body() {
10133        let (ast, diags) = parse_str("while (a) { x; y; }");
10134        assert!(diags.is_empty());
10135        let stmt = single_stmt(&ast);
10136        let crate::StmtKind::While { body, .. } = stmt.kind else {
10137            panic!("expected While");
10138        };
10139        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10140            panic!("expected Block")
10141        };
10142        assert_eq!(stmts.len(), 2);
10143    }
10144
10145    #[test]
10146    fn while_missing_paren_diagnoses() {
10147        let (ast, diags) = parse_str("while a { }");
10148        assert!(!diags.is_empty());
10149        assert_eq!(diags[0].message, "expected `(`");
10150        assert!(ast.top_level.is_empty());
10151    }
10152
10153    #[test]
10154    fn parse_for_all_slots() {
10155        let (ast, diags) = parse_str("for (let i = 0; i < 10; i = i + 1) { x; }");
10156        assert!(diags.is_empty(), "{diags:?}");
10157        let stmt = single_stmt(&ast);
10158        let crate::StmtKind::For {
10159            init,
10160            condition,
10161            update,
10162            body,
10163        } = stmt.kind
10164        else {
10165            panic!("expected For");
10166        };
10167        assert!(init.is_some(), "init missing");
10168        assert!(condition.is_some(), "condition missing");
10169        assert!(update.is_some(), "update missing");
10170        assert!(matches!(
10171            ast.try_stmt(body).unwrap().kind,
10172            crate::StmtKind::Block(_)
10173        ));
10174    }
10175
10176    #[test]
10177    fn parse_for_empty_slots() {
10178        let (ast, diags) = parse_str("for (;;) { break; }");
10179        assert!(diags.is_empty(), "{diags:?}");
10180        let stmt = single_stmt(&ast);
10181        let crate::StmtKind::For {
10182            init,
10183            condition,
10184            update,
10185            ..
10186        } = stmt.kind
10187        else {
10188            panic!("expected For");
10189        };
10190        assert!(init.is_none() && condition.is_none() && update.is_none());
10191    }
10192
10193    #[test]
10194    fn parse_for_of_const_binding() {
10195        let (ast, diags) = parse_str("for (const x of arr) { y; }");
10196        assert!(diags.is_empty(), "{diags:?}");
10197        let stmt = single_stmt(&ast);
10198        let crate::StmtKind::ForOf {
10199            binding_kind,
10200            ref name,
10201            ref ty,
10202            ..
10203        } = stmt.kind
10204        else {
10205            panic!("expected ForOf");
10206        };
10207        assert!(matches!(binding_kind, crate::BindingKind::Const));
10208        assert_eq!(name.name, "x");
10209        assert!(ty.is_none());
10210    }
10211
10212    #[test]
10213    fn parse_for_of_let_with_annotation() {
10214        let (ast, diags) = parse_str("for (let x: number of arr) { y; }");
10215        assert!(diags.is_empty(), "{diags:?}");
10216        let stmt = single_stmt(&ast);
10217        let crate::StmtKind::ForOf {
10218            binding_kind,
10219            ref ty,
10220            ..
10221        } = stmt.kind
10222        else {
10223            panic!("expected ForOf");
10224        };
10225        assert!(matches!(binding_kind, crate::BindingKind::Let));
10226        assert!(ty.is_some());
10227    }
10228
10229    #[test]
10230    fn parse_do_while_simple() {
10231        let (ast, diags) = parse_str("do { x; } while (a);");
10232        assert!(diags.is_empty(), "{diags:?}");
10233        let stmt = single_stmt(&ast);
10234        assert!(matches!(stmt.kind, crate::StmtKind::DoWhile { .. }));
10235    }
10236
10237    #[test]
10238    fn parse_break_and_continue() {
10239        let (ast, diags) = parse_str("while (a) { break; continue; }");
10240        assert!(diags.is_empty(), "{diags:?}");
10241        let stmt = single_stmt(&ast);
10242        let crate::StmtKind::While { body, .. } = stmt.kind else {
10243            panic!("expected While");
10244        };
10245        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10246            panic!("expected Block");
10247        };
10248        assert!(matches!(
10249            ast.try_stmt(stmts[0]).unwrap().kind,
10250            crate::StmtKind::Break
10251        ));
10252        assert!(matches!(
10253            ast.try_stmt(stmts[1]).unwrap().kind,
10254            crate::StmtKind::Continue
10255        ));
10256    }
10257
10258    #[test]
10259    fn parse_bare_return_in_function() {
10260        let (ast, diags) = parse_str("function f(): void { return; }");
10261        assert!(diags.is_empty());
10262        let f = single_stmt(&ast);
10263        let crate::StmtKind::Function { body, .. } = f.kind else {
10264            panic!("expected Function");
10265        };
10266        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10267            panic!("expected Block")
10268        };
10269        assert_eq!(stmts.len(), 1);
10270        match ast.try_stmt(stmts[0]).unwrap().kind {
10271            crate::StmtKind::Return(None) => {}
10272            _ => panic!("expected Return(None)"),
10273        }
10274    }
10275
10276    #[test]
10277    fn parse_return_with_value() {
10278        let (ast, diags) = parse_str("function f(): number { return 1; }");
10279        assert!(diags.is_empty());
10280        let f = single_stmt(&ast);
10281        let crate::StmtKind::Function { body, .. } = f.kind else {
10282            panic!("expected Function");
10283        };
10284        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10285            panic!("expected Block")
10286        };
10287        match ast.try_stmt(stmts[0]).unwrap().kind {
10288            crate::StmtKind::Return(Some(value)) => {
10289                assert_eq!(
10290                    ast.try_expr(value).unwrap().kind,
10291                    crate::ExprKind::Number(1.0)
10292                );
10293            }
10294            _ => panic!("expected Return(Some)"),
10295        }
10296    }
10297
10298    #[test]
10299    fn parse_return_with_expression() {
10300        let (ast, diags) = parse_str("function f(): number { return 1 + 2; }");
10301        assert!(diags.is_empty());
10302        let f = single_stmt(&ast);
10303        let crate::StmtKind::Function { body, .. } = f.kind else {
10304            panic!("expected Function");
10305        };
10306        let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10307            panic!("expected Block")
10308        };
10309        match ast.try_stmt(stmts[0]).unwrap().kind {
10310            crate::StmtKind::Return(Some(value)) => {
10311                let (op, ..) = binary(ast.try_expr(value).unwrap());
10312                assert_eq!(op, crate::BinOp::Add);
10313            }
10314            _ => panic!("expected Return(Some(Binary))"),
10315        }
10316    }
10317
10318    #[test]
10319    fn return_missing_semicolon_diagnoses() {
10320        let (_, diags) = parse_str("function f(): number { return x x; }");
10321        assert!(!diags.is_empty());
10322        assert_eq!(diags[0].message, "expected `;` after return");
10323    }
10324
10325    #[test]
10326    fn parse_bare_block() {
10327        let (ast, diags) = parse_str("{ x; y; }");
10328        assert!(diags.is_empty());
10329        let stmt = single_stmt(&ast);
10330        let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10331            panic!("expected Block");
10332        };
10333        assert_eq!(stmts.len(), 2);
10334    }
10335
10336    #[test]
10337    fn parse_empty_bare_block() {
10338        let (ast, diags) = parse_str("{ }");
10339        assert!(diags.is_empty());
10340        let stmt = single_stmt(&ast);
10341        let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10342            panic!("expected Block");
10343        };
10344        assert!(stmts.is_empty());
10345    }
10346
10347    #[test]
10348    fn parse_nested_bare_blocks() {
10349        let (ast, diags) = parse_str("{ { } }");
10350        assert!(diags.is_empty());
10351        let outer = single_stmt(&ast);
10352        let crate::StmtKind::Block(ref stmts) = outer.kind else {
10353            panic!("expected outer Block");
10354        };
10355        assert_eq!(stmts.len(), 1);
10356        assert!(matches!(
10357            ast.try_stmt(stmts[0]).unwrap().kind,
10358            crate::StmtKind::Block(ref v) if v.is_empty()
10359        ));
10360    }
10361
10362    #[test]
10363    fn snapshot_function_with_if_and_return() {
10364        let source = "function isPositive(n: number): boolean {\n  if (n > 0) {\n    return true;\n  } else {\n    return false;\n  }\n}";
10365        let (ast, diags) = parse_str(source);
10366        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10367        insta::assert_debug_snapshot!(ast);
10368    }
10369
10370    fn object_literal(e: &crate::Expr) -> &[crate::ObjectLiteralMember] {
10371        match e.kind {
10372            crate::ExprKind::ObjectLiteral { ref members } => members.as_slice(),
10373            _ => panic!("expected ObjectLiteral, got {:?}", e.kind),
10374        }
10375    }
10376
10377    fn object_literal_field(m: &crate::ObjectLiteralMember) -> &crate::ObjectLiteralField {
10378        match m {
10379            crate::ObjectLiteralMember::Field(f) => f,
10380            crate::ObjectLiteralMember::Spread { .. }
10381            | crate::ObjectLiteralMember::Computed { .. } => {
10382                panic!("expected Field member, got Spread")
10383            }
10384        }
10385    }
10386
10387    fn array_literal(e: &crate::Expr) -> &[crate::ArrayLiteralElement] {
10388        match e.kind {
10389            crate::ExprKind::ArrayLiteral { ref elements } => elements.as_slice(),
10390            _ => panic!("expected ArrayLiteral, got {:?}", e.kind),
10391        }
10392    }
10393
10394    fn array_literal_value(el: &crate::ArrayLiteralElement) -> crate::ExprId {
10395        match el {
10396            crate::ArrayLiteralElement::Value(id) => *id,
10397            crate::ArrayLiteralElement::Spread { .. } => {
10398                panic!("expected Value element, got Spread")
10399            }
10400        }
10401    }
10402
10403    #[test]
10404    fn parse_empty_object_literal() {
10405        let (ast, diags) = parse_str("let x = {};");
10406        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10407        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10408            panic!("expected Let");
10409        };
10410        assert!(object_literal(ast.try_expr(value).unwrap()).is_empty());
10411    }
10412
10413    #[test]
10414    fn parse_object_literal_simple() {
10415        let (ast, diags) = parse_str("let p = { x: 1, y: 2 };");
10416        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10417        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10418            panic!("expected Let");
10419        };
10420        let members = object_literal(ast.try_expr(value).unwrap());
10421        assert_eq!(members.len(), 2);
10422        let f0 = object_literal_field(&members[0]);
10423        let f1 = object_literal_field(&members[1]);
10424        assert_eq!(f0.name.name, "x");
10425        assert_eq!(f1.name.name, "y");
10426        assert_eq!(
10427            ast.try_expr(f0.value).unwrap().kind,
10428            crate::ExprKind::Number(1.0)
10429        );
10430        assert_eq!(
10431            ast.try_expr(f1.value).unwrap().kind,
10432            crate::ExprKind::Number(2.0)
10433        );
10434    }
10435
10436    #[test]
10437    fn parse_object_literal_keyword_keys() {
10438        let (ast, diags) = parse_str("let p = { type: 1, default: 2, null: 3, true: 4 };");
10439        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10440        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10441            panic!("expected Let");
10442        };
10443        let names: Vec<&str> = object_literal(ast.try_expr(value).unwrap())
10444            .iter()
10445            .map(object_literal_field)
10446            .map(|f| f.name.name.as_str())
10447            .collect();
10448        assert_eq!(names, vec!["type", "default", "null", "true"]);
10449    }
10450
10451    #[test]
10452    fn parse_object_literal_string_key() {
10453        let (ast, diags) = parse_str(r#"let p = { "hello": 1 };"#);
10454        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10455        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10456            panic!("expected Let");
10457        };
10458        let members = object_literal(ast.try_expr(value).unwrap());
10459        assert_eq!(members.len(), 1);
10460        let f0 = object_literal_field(&members[0]);
10461        assert_eq!(f0.name.name, "hello");
10462    }
10463
10464    #[test]
10465    fn parse_object_literal_string_key_decodes_escape() {
10466        let (ast, diags) = parse_str(r#"let p = { "content\u002dtype": 1 };"#);
10467        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10468        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10469            panic!("expected Let");
10470        };
10471        let members = object_literal(ast.try_expr(value).unwrap());
10472        assert_eq!(members.len(), 1);
10473        let f0 = object_literal_field(&members[0]);
10474        assert_eq!(f0.name.name, "content-type");
10475    }
10476
10477    #[test]
10478    fn parse_object_literal_shorthand() {
10479        let (ast, diags) = parse_str("let p = { x, y: 2 };");
10480        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10481        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10482            panic!("expected Let");
10483        };
10484        let members = object_literal(ast.try_expr(value).unwrap());
10485        assert_eq!(members.len(), 2);
10486        let f0 = object_literal_field(&members[0]);
10487        assert_eq!(f0.name.name, "x");
10488        // `{ x }` desugars to `{ x: x }` — value is an identifier reference.
10489        assert_eq!(
10490            ast.try_expr(f0.value).unwrap().kind,
10491            crate::ExprKind::Identifier(crate::Ident {
10492                name: "x".into(),
10493                span: f0.name.span,
10494            })
10495        );
10496        let f1 = object_literal_field(&members[1]);
10497        assert_eq!(f1.name.name, "y");
10498        assert_eq!(
10499            ast.try_expr(f1.value).unwrap().kind,
10500            crate::ExprKind::Number(2.0)
10501        );
10502    }
10503
10504    #[test]
10505    fn parse_object_literal_string_key_shorthand_rejected() {
10506        let (_ast, diags) = parse_str(r#"let p = { "x" };"#);
10507        assert!(
10508            diags
10509                .iter()
10510                .any(|d| d.message.contains("expected `:` after field name")),
10511            "string-key shorthand should be rejected: {diags:?}"
10512        );
10513    }
10514
10515    #[test]
10516    fn parse_object_literal_trailing_comma() {
10517        let (ast, diags) = parse_str("let p = { x: 1, y: 2, };");
10518        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10519        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10520            panic!("expected Let");
10521        };
10522        assert_eq!(object_literal(ast.try_expr(value).unwrap()).len(), 2);
10523    }
10524
10525    #[test]
10526    fn parse_object_literal_duplicate_key_diagnoses() {
10527        let (_, diags) = parse_str("let p = { x: 1, x: 2 };");
10528        assert_eq!(diags.len(), 1);
10529        assert_eq!(diags[0].message, "duplicate field `x` in object literal");
10530    }
10531
10532    #[test]
10533    fn parse_empty_array_literal() {
10534        let (ast, diags) = parse_str("let xs = [];");
10535        assert!(diags.is_empty());
10536        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10537            panic!("expected Let");
10538        };
10539        assert!(array_literal(ast.try_expr(value).unwrap()).is_empty());
10540    }
10541
10542    #[test]
10543    fn parse_array_literal_simple() {
10544        let (ast, diags) = parse_str("let xs = [1, 2, 3];");
10545        assert!(diags.is_empty());
10546        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10547            panic!("expected Let");
10548        };
10549        let elements = array_literal(ast.try_expr(value).unwrap());
10550        assert_eq!(elements.len(), 3);
10551        assert_eq!(
10552            ast.try_expr(array_literal_value(&elements[0]))
10553                .unwrap()
10554                .kind,
10555            crate::ExprKind::Number(1.0)
10556        );
10557        assert_eq!(
10558            ast.try_expr(array_literal_value(&elements[2]))
10559                .unwrap()
10560                .kind,
10561            crate::ExprKind::Number(3.0)
10562        );
10563    }
10564
10565    #[test]
10566    fn parse_array_literal_trailing_comma() {
10567        let (ast, diags) = parse_str("let xs = [1, 2,];");
10568        assert!(diags.is_empty());
10569        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10570            panic!("expected Let");
10571        };
10572        assert_eq!(array_literal(ast.try_expr(value).unwrap()).len(), 2);
10573    }
10574
10575    #[test]
10576    fn parse_nested_array_in_object() {
10577        let (ast, diags) = parse_str("let p = { items: [1, 2] };");
10578        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10579        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10580            panic!("expected Let");
10581        };
10582        let members = object_literal(ast.try_expr(value).unwrap());
10583        let f0 = object_literal_field(&members[0]);
10584        assert_eq!(f0.name.name, "items");
10585        let inner = array_literal(ast.try_expr(f0.value).unwrap());
10586        assert_eq!(inner.len(), 2);
10587    }
10588
10589    #[test]
10590    fn parse_nested_object_in_array() {
10591        let (ast, diags) = parse_str("let xs = [{ x: 1 }, { x: 2 }];");
10592        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10593        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10594            panic!("expected Let");
10595        };
10596        let elements = array_literal(ast.try_expr(value).unwrap());
10597        assert_eq!(elements.len(), 2);
10598        assert_eq!(
10599            object_literal(ast.try_expr(array_literal_value(&elements[0])).unwrap()).len(),
10600            1
10601        );
10602        assert_eq!(
10603            object_literal(ast.try_expr(array_literal_value(&elements[1])).unwrap()).len(),
10604            1
10605        );
10606    }
10607
10608    #[test]
10609    fn brace_at_statement_start_is_block_not_object() {
10610        let (_, diags) = parse_str("{ x: 1 }");
10611        assert!(
10612            !diags.is_empty(),
10613            "expected a diagnostic for `x:` inside block"
10614        );
10615    }
10616
10617    fn field_access(e: &crate::Expr) -> (crate::ExprId, &str) {
10618        match e.kind {
10619            crate::ExprKind::FieldAccess { receiver, ref name } => (receiver, name.name.as_str()),
10620            _ => panic!("expected FieldAccess, got {:?}", e.kind),
10621        }
10622    }
10623
10624    fn index_access(e: &crate::Expr) -> (crate::ExprId, crate::ExprId) {
10625        match e.kind {
10626            crate::ExprKind::IndexAccess { receiver, index } => (receiver, index),
10627            _ => panic!("expected IndexAccess, got {:?}", e.kind),
10628        }
10629    }
10630
10631    #[test]
10632    fn parse_field_access_simple() {
10633        let (ast, diags) = parse_str("a.b;");
10634        assert!(diags.is_empty());
10635        let outer = expr_of_single_stmt(&ast);
10636        let (receiver, name) = field_access(outer);
10637        assert!(matches!(
10638            ast.try_expr(receiver).unwrap().kind,
10639            crate::ExprKind::Identifier(_)
10640        ));
10641        assert_eq!(name, "b");
10642    }
10643
10644    #[test]
10645    fn parse_field_access_keyword_name() {
10646        let (ast, diags) = parse_str("status.type.default.null;");
10647        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10648        let outer = expr_of_single_stmt(&ast);
10649        let (receiver, name) = field_access(outer);
10650        assert_eq!(name, "null");
10651        let (receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10652        assert_eq!(name, "default");
10653        let (_receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10654        assert_eq!(name, "type");
10655    }
10656
10657    #[test]
10658    fn parse_field_access_chain_left_assoc() {
10659        let (ast, diags) = parse_str("a.b.c;");
10660        assert!(diags.is_empty());
10661        let outer = expr_of_single_stmt(&ast);
10662        let (mid_id, c) = field_access(outer);
10663        assert_eq!(c, "c");
10664        let (a_id, b) = field_access(ast.try_expr(mid_id).unwrap());
10665        assert_eq!(b, "b");
10666        assert!(matches!(
10667            ast.try_expr(a_id).unwrap().kind,
10668            crate::ExprKind::Identifier(_)
10669        ));
10670    }
10671
10672    #[test]
10673    fn parse_index_access_simple() {
10674        let (ast, diags) = parse_str("a[0];");
10675        assert!(diags.is_empty());
10676        let outer = expr_of_single_stmt(&ast);
10677        let (receiver, idx) = index_access(outer);
10678        assert!(matches!(
10679            ast.try_expr(receiver).unwrap().kind,
10680            crate::ExprKind::Identifier(_)
10681        ));
10682        assert_eq!(
10683            ast.try_expr(idx).unwrap().kind,
10684            crate::ExprKind::Number(0.0)
10685        );
10686    }
10687
10688    #[test]
10689    fn parse_index_access_chain() {
10690        let (ast, diags) = parse_str("a[0][1];");
10691        assert!(diags.is_empty());
10692        let outer = expr_of_single_stmt(&ast);
10693        let (mid_id, one) = index_access(outer);
10694        assert_eq!(
10695            ast.try_expr(one).unwrap().kind,
10696            crate::ExprKind::Number(1.0)
10697        );
10698        let (a_id, zero) = index_access(ast.try_expr(mid_id).unwrap());
10699        assert_eq!(
10700            ast.try_expr(zero).unwrap().kind,
10701            crate::ExprKind::Number(0.0)
10702        );
10703        assert!(matches!(
10704            ast.try_expr(a_id).unwrap().kind,
10705            crate::ExprKind::Identifier(_)
10706        ));
10707    }
10708
10709    #[test]
10710    fn parse_mixed_access_and_call_chain() {
10711        let (ast, diags) = parse_str("a.b[c].d();");
10712        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10713        let outer = expr_of_single_stmt(&ast);
10714        let (callee, args) = call(outer);
10715        assert!(args.is_empty());
10716        let (idx_id, d_name) = field_access(ast.try_expr(callee).unwrap());
10717        assert_eq!(d_name, "d");
10718        let (b_id, c_idx) = index_access(ast.try_expr(idx_id).unwrap());
10719        assert!(matches!(
10720            ast.try_expr(c_idx).unwrap().kind,
10721            crate::ExprKind::Identifier(_)
10722        ));
10723        let (a_id, b_name) = field_access(ast.try_expr(b_id).unwrap());
10724        assert_eq!(b_name, "b");
10725        assert!(matches!(
10726            ast.try_expr(a_id).unwrap().kind,
10727            crate::ExprKind::Identifier(_)
10728        ));
10729    }
10730
10731    #[test]
10732    fn parse_call_then_field() {
10733        let (ast, diags) = parse_str("f().x;");
10734        assert!(diags.is_empty());
10735        let outer = expr_of_single_stmt(&ast);
10736        let (call_id, x_name) = field_access(outer);
10737        assert_eq!(x_name, "x");
10738        let (_callee, args) = call(ast.try_expr(call_id).unwrap());
10739        assert!(args.is_empty());
10740    }
10741
10742    #[test]
10743    fn parse_index_with_complex_expression() {
10744        let (ast, diags) = parse_str("a[i + 1];");
10745        assert!(diags.is_empty());
10746        let outer = expr_of_single_stmt(&ast);
10747        let (_, idx) = index_access(outer);
10748        let (op, ..) = binary(ast.try_expr(idx).unwrap());
10749        assert_eq!(op, crate::BinOp::Add);
10750    }
10751
10752    #[test]
10753    fn field_access_missing_name_diagnoses() {
10754        let (_, diags) = parse_str("a.;");
10755        assert!(!diags.is_empty());
10756        assert_eq!(diags[0].message, "expected field name after `.`");
10757    }
10758
10759    #[test]
10760    fn parse_postfix_increment() {
10761        let (ast, diags) = parse_str("x++;");
10762        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10763        let expr = expr_of_single_stmt(&ast);
10764        let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10765            panic!("expected PostfixUnary, got {:?}", expr.kind);
10766        };
10767        assert_eq!(op, crate::PostfixOp::Inc);
10768        assert!(matches!(
10769            ast.try_expr(operand).unwrap().kind,
10770            crate::ExprKind::Identifier(_)
10771        ));
10772        assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
10773    }
10774
10775    #[test]
10776    fn parse_postfix_decrement() {
10777        let (ast, diags) = parse_str("x--;");
10778        assert!(diags.is_empty());
10779        let expr = expr_of_single_stmt(&ast);
10780        let crate::ExprKind::PostfixUnary { op, .. } = expr.kind else {
10781            panic!("expected PostfixUnary");
10782        };
10783        assert_eq!(op, crate::PostfixOp::Dec);
10784    }
10785
10786    #[test]
10787    fn parse_postfix_non_null_assertion() {
10788        let (ast, diags) = parse_str("x!;");
10789        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10790        let expr = expr_of_single_stmt(&ast);
10791        let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10792            panic!("expected PostfixUnary, got {:?}", expr.kind);
10793        };
10794        assert_eq!(op, crate::PostfixOp::NonNullAssert);
10795        assert!(matches!(
10796            ast.try_expr(operand).unwrap().kind,
10797            crate::ExprKind::Identifier(_)
10798        ));
10799        assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
10800    }
10801
10802    #[test]
10803    fn parse_non_null_assertion_continues_chain() {
10804        let (ast, diags) = parse_str("x!.y;");
10805        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10806        let expr = expr_of_single_stmt(&ast);
10807        let crate::ExprKind::FieldAccess { receiver, name } = &expr.kind else {
10808            panic!("expected FieldAccess, got {:?}", expr.kind);
10809        };
10810        assert_eq!(name.name, "y");
10811        assert!(matches!(
10812            ast.try_expr(*receiver).unwrap().kind,
10813            crate::ExprKind::PostfixUnary {
10814                op: crate::PostfixOp::NonNullAssert,
10815                ..
10816            }
10817        ));
10818    }
10819
10820    #[test]
10821    fn parse_postfix_on_field_access() {
10822        let (ast, diags) = parse_str("o.f++;");
10823        assert!(diags.is_empty());
10824        let expr = expr_of_single_stmt(&ast);
10825        let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
10826            panic!("expected PostfixUnary");
10827        };
10828        assert!(matches!(
10829            ast.try_expr(operand).unwrap().kind,
10830            crate::ExprKind::FieldAccess { .. }
10831        ));
10832    }
10833
10834    #[test]
10835    fn parse_postfix_on_index_access() {
10836        let (ast, diags) = parse_str("xs[0]++;");
10837        assert!(diags.is_empty());
10838        let expr = expr_of_single_stmt(&ast);
10839        let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
10840            panic!("expected PostfixUnary");
10841        };
10842        assert!(matches!(
10843            ast.try_expr(operand).unwrap().kind,
10844            crate::ExprKind::IndexAccess { .. }
10845        ));
10846    }
10847
10848    #[test]
10849    fn parse_postfix_terminates_chain() {
10850        let (_, diags) = parse_str("x++.y;");
10851        assert!(!diags.is_empty());
10852        assert_eq!(diags[0].message, "expected `;` after expression");
10853    }
10854
10855    #[test]
10856    fn parse_postfix_in_binary_continues() {
10857        let (ast, diags) = parse_str("let y = x++ + 1;");
10858        assert!(diags.is_empty(), "unexpected: {diags:?}");
10859        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10860            panic!("expected Let");
10861        };
10862        let (op, lhs, _) = binary(ast.try_expr(value).unwrap());
10863        assert_eq!(op, crate::BinOp::Add);
10864        assert!(matches!(
10865            ast.try_expr(lhs).unwrap().kind,
10866            crate::ExprKind::PostfixUnary { .. }
10867        ));
10868    }
10869
10870    #[test]
10871    fn parse_index_assignment() {
10872        let (ast, diags) = parse_str("xs[0] = 1;");
10873        assert!(diags.is_empty(), "unexpected: {diags:?}");
10874        let stmt = single_stmt(&ast);
10875        let crate::StmtKind::AssignIndex {
10876            receiver,
10877            index,
10878            value,
10879        } = stmt.kind
10880        else {
10881            panic!("expected AssignIndex, got {:?}", stmt.kind);
10882        };
10883        assert!(matches!(
10884            ast.try_expr(receiver).unwrap().kind,
10885            crate::ExprKind::Identifier(_)
10886        ));
10887        assert_eq!(
10888            ast.try_expr(index).unwrap().kind,
10889            crate::ExprKind::Number(0.0)
10890        );
10891        assert_eq!(
10892            ast.try_expr(value).unwrap().kind,
10893            crate::ExprKind::Number(1.0)
10894        );
10895    }
10896
10897    #[test]
10898    fn parse_for_update_postfix() {
10899        let (ast, diags) = parse_str("function main(): void { for (let i = 0; i < 3; i++) {} }");
10900        assert!(diags.is_empty(), "unexpected: {diags:?}");
10901        assert_eq!(ast.top_level.len(), 1);
10902    }
10903
10904    #[test]
10905    fn parse_for_update_index_assignment() {
10906        let (ast, diags) =
10907            parse_str("function main(): void { for (let i = 0; i < 3; xs[i] = i) {} }");
10908        assert!(diags.is_empty(), "unexpected: {diags:?}");
10909        assert_eq!(ast.top_level.len(), 1);
10910    }
10911
10912    #[test]
10913    fn index_access_missing_close_bracket_diagnoses() {
10914        let (_, diags) = parse_str("a[1;");
10915        assert!(!diags.is_empty());
10916        assert_eq!(diags[0].message, "expected `]`");
10917    }
10918
10919    #[test]
10920    fn field_access_inside_object_literal_value_does_not_steal_dot() {
10921        let (ast, diags) = parse_str("let x = { a: 1 }.foo;");
10922        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10923        let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10924            panic!("expected Let");
10925        };
10926        let (recv_id, name) = field_access(ast.try_expr(value).unwrap());
10927        assert_eq!(name, "foo");
10928        assert_eq!(object_literal(ast.try_expr(recv_id).unwrap()).len(), 1);
10929    }
10930
10931    fn type_of_let(stmt: &crate::Stmt) -> &crate::TypeAnnotation {
10932        match stmt.kind {
10933            crate::StmtKind::Let { ref ty, .. } => ty.as_ref().expect("expected type annotation"),
10934            _ => panic!("expected Let"),
10935        }
10936    }
10937
10938    #[test]
10939    fn parse_array_type_annotation() {
10940        let (ast, diags) = parse_str("let xs: number[] = null;");
10941        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10942        let ty = type_of_let(single_stmt(&ast));
10943        match ty.kind {
10944            crate::TypeAnnotationKind::Array(ref inner) => {
10945                assert!(matches!(inner.kind, crate::TypeAnnotationKind::Name { .. }));
10946            }
10947            _ => panic!("expected Array, got {:?}", ty.kind),
10948        }
10949    }
10950
10951    #[test]
10952    fn parse_nested_array_type_annotation() {
10953        let (ast, diags) = parse_str("let xs: number[][] = null;");
10954        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10955        let ty = type_of_let(single_stmt(&ast));
10956        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
10957            panic!("expected outer Array");
10958        };
10959        let crate::TypeAnnotationKind::Array(ref inner2) = inner.kind else {
10960            panic!("expected inner Array");
10961        };
10962        assert!(matches!(
10963            inner2.kind,
10964            crate::TypeAnnotationKind::Name { .. }
10965        ));
10966    }
10967
10968    #[test]
10969    fn parse_object_type_annotation_simple() {
10970        let (ast, diags) = parse_str("let p: { x: number; y: number } = null;");
10971        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10972        let ty = type_of_let(single_stmt(&ast));
10973        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10974            panic!("expected Object, got {:?}", ty.kind);
10975        };
10976        assert_eq!(fields.len(), 2);
10977        assert_eq!(fields[0].name.name, "x");
10978        assert_eq!(fields[1].name.name, "y");
10979    }
10980
10981    #[test]
10982    fn readonly_is_rejected_on_methods_accessors_and_method_signatures() {
10983        let source = "class K {\n\
10984             readonly m(): number { return 1; }\n\
10985             public readonly n<T>(): number { return 1; }\n\
10986             readonly get g(): number { return 1; }\n\
10987             readonly p: number = 1;\n\
10988             }\n\
10989             type T = { readonly r(a: number): boolean; readonly f: () => void };\n\
10990             interface I { readonly s(): void; readonly q: number; }\n";
10991        let (_, diags) = parse_str(source);
10992        let lines: Vec<usize> = diags
10993            .iter()
10994            .map(|d| {
10995                assert_eq!(
10996                    d.message,
10997                    "`readonly` can only modify a property or index signature"
10998                );
10999                source[..d.span.start as usize].matches('\n').count() + 1
11000            })
11001            .collect();
11002        assert_eq!(lines, vec![2, 3, 4, 7, 8]);
11003    }
11004
11005    #[test]
11006    fn parse_object_type_method_members() {
11007        // `m(): T` is the same member as `m: () => T`, so it parses to a function-typed
11008        // field — including through the `?` modifier.
11009        let (ast, diags) =
11010            parse_str("let p: { m(): number; opt?(): string; r(a: number): boolean } = null;");
11011        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11012        let ty = type_of_let(single_stmt(&ast));
11013        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11014            panic!("expected Object, got {:?}", ty.kind);
11015        };
11016        assert_eq!(fields.len(), 3);
11017        for field in fields {
11018            assert!(
11019                matches!(field.ty.kind, crate::TypeAnnotationKind::Function { .. }),
11020                "member `{}` should be function-typed, got {:?}",
11021                field.name.name,
11022                field.ty.kind,
11023            );
11024        }
11025        assert!(fields[1].optional, "`opt?()` is an optional member");
11026        let crate::TypeAnnotationKind::Function { ref params, .. } = fields[2].ty.kind else {
11027            unreachable!("checked above");
11028        };
11029        assert_eq!(params.len(), 1);
11030        assert_eq!(params[0].name.name, "a");
11031    }
11032
11033    #[test]
11034    fn parse_object_type_annotation_with_commas() {
11035        let (ast, diags) = parse_str("let p: { x: number, y: number } = null;");
11036        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11037        let ty = type_of_let(single_stmt(&ast));
11038        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11039            panic!("expected Object");
11040        };
11041        assert_eq!(fields.len(), 2);
11042    }
11043
11044    #[test]
11045    fn parse_object_type_annotation_empty() {
11046        let (ast, diags) = parse_str("let p: {} = null;");
11047        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11048        let ty = type_of_let(single_stmt(&ast));
11049        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11050            panic!("expected Object");
11051        };
11052        assert!(fields.is_empty());
11053    }
11054
11055    #[test]
11056    fn parse_object_type_with_optional_field() {
11057        let (ast, diags) = parse_str("let p: { id: number; nick?: string } = null;");
11058        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11059        let ty = type_of_let(single_stmt(&ast));
11060        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11061            panic!("expected Object");
11062        };
11063        assert_eq!(fields.len(), 2);
11064        assert_eq!(fields[0].name.name, "id");
11065        assert!(!fields[0].optional);
11066        assert_eq!(fields[1].name.name, "nick");
11067        assert!(fields[1].optional);
11068    }
11069
11070    #[test]
11071    fn parse_object_type_with_readonly_fields() {
11072        let (ast, diags) = parse_str(
11073            "let issue: { readonly id: string; readonly title?: string; body: string } = null;",
11074        );
11075        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11076        let ty = type_of_let(single_stmt(&ast));
11077        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11078            panic!("expected Object");
11079        };
11080        let fields: Vec<(&str, bool, bool)> = fields
11081            .iter()
11082            .map(|field| (field.name.name.as_str(), field.optional, field.readonly))
11083            .collect();
11084        assert_eq!(
11085            fields,
11086            vec![
11087                ("id", false, true),
11088                ("title", true, true),
11089                ("body", false, false),
11090            ]
11091        );
11092    }
11093
11094    #[test]
11095    fn parse_object_type_mixed_optional_and_required() {
11096        let (ast, diags) = parse_str("let p: { a?: number; b: string; c?: boolean } = null;");
11097        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11098        let ty = type_of_let(single_stmt(&ast));
11099        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11100            panic!("expected Object");
11101        };
11102        let opt: Vec<(&str, bool)> = fields
11103            .iter()
11104            .map(|f| (f.name.name.as_str(), f.optional))
11105            .collect();
11106        assert_eq!(opt, vec![("a", true), ("b", false), ("c", true)]);
11107    }
11108
11109    #[test]
11110    fn parse_object_type_keyword_fields() {
11111        let (ast, diags) =
11112            parse_str("let p: { type: string; default?: number; null: boolean } = null;");
11113        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11114        let ty = type_of_let(single_stmt(&ast));
11115        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11116            panic!("expected Object");
11117        };
11118        let opt: Vec<(&str, bool)> = fields
11119            .iter()
11120            .map(|f| (f.name.name.as_str(), f.optional))
11121            .collect();
11122        assert_eq!(
11123            opt,
11124            vec![("type", false), ("default", true), ("null", false)]
11125        );
11126    }
11127
11128    #[test]
11129    fn parse_object_type_quoted_fields() {
11130        let (ast, diags) =
11131            parse_str(r#"let p: { "content-type": string; "x\u002drequest-id"?: string } = null;"#);
11132        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11133        let ty = type_of_let(single_stmt(&ast));
11134        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11135            panic!("expected Object");
11136        };
11137        let opt: Vec<(&str, bool)> = fields
11138            .iter()
11139            .map(|f| (f.name.name.as_str(), f.optional))
11140            .collect();
11141        assert_eq!(opt, vec![("content-type", false), ("x-request-id", true)]);
11142    }
11143
11144    #[test]
11145    fn parse_interface_with_optional_property() {
11146        let (ast, diags) = parse_str("interface User { id: number; deletedAt?: string; }");
11147        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11148        let stmt = single_stmt(&ast);
11149        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11150            panic!("expected interface decl");
11151        };
11152        let opt: Vec<(&str, bool)> = members
11153            .iter()
11154            .map(|m| match m {
11155                crate::InterfaceMember::Property { name, optional, .. } => {
11156                    (name.name.as_str(), *optional)
11157                }
11158                _ => panic!("expected property"),
11159            })
11160            .collect();
11161        assert_eq!(opt, vec![("id", false), ("deletedAt", true)]);
11162    }
11163
11164    #[test]
11165    fn parse_interface_with_readonly_properties() {
11166        let (ast, diags) = parse_str(
11167            "interface Issue { readonly id: string; readonly title?: string; body: string; }",
11168        );
11169        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11170        let stmt = single_stmt(&ast);
11171        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11172            panic!("expected interface decl");
11173        };
11174        let opt: Vec<(&str, bool, bool)> = members
11175            .iter()
11176            .map(|member| match member {
11177                crate::InterfaceMember::Property {
11178                    name,
11179                    optional,
11180                    readonly,
11181                    ..
11182                } => (name.name.as_str(), *optional, *readonly),
11183                _ => panic!("expected property"),
11184            })
11185            .collect();
11186        assert_eq!(
11187            opt,
11188            vec![
11189                ("id", false, true),
11190                ("title", true, true),
11191                ("body", false, false),
11192            ]
11193        );
11194    }
11195
11196    #[test]
11197    fn parse_readonly_property_name_without_modifier() {
11198        let (ast, diags) = parse_str(
11199            "interface Meta { readonly: boolean; }\nlet meta: { readonly: boolean } = null;",
11200        );
11201        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11202        let crate::StmtKind::InterfaceDecl { ref members, .. } =
11203            ast.try_stmt(ast.top_level[0]).unwrap().kind
11204        else {
11205            panic!("expected interface decl");
11206        };
11207        let crate::InterfaceMember::Property {
11208            ref name, readonly, ..
11209        } = members[0]
11210        else {
11211            panic!("expected property");
11212        };
11213        assert_eq!(name.name, "readonly");
11214        assert!(
11215            !readonly,
11216            "`readonly` here is the property name, not a modifier"
11217        );
11218
11219        let ty = type_of_let(ast.try_stmt(ast.top_level[1]).unwrap());
11220        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11221            panic!("expected Object");
11222        };
11223        assert_eq!(fields[0].name.name, "readonly");
11224        assert!(!fields[0].readonly);
11225    }
11226
11227    #[test]
11228    fn parse_interface_keyword_members() {
11229        let (ast, diags) =
11230            parse_str("interface IssueStatus { type: string; default(): string; null: boolean; }");
11231        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11232        let stmt = single_stmt(&ast);
11233        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11234            panic!("expected interface decl");
11235        };
11236        let names: Vec<&str> = members
11237            .iter()
11238            .map(|m| match m {
11239                crate::InterfaceMember::Method { name, .. }
11240                | crate::InterfaceMember::Property { name, .. } => name.name.as_str(),
11241                crate::InterfaceMember::IndexSignature(_) => panic!("unexpected index signature"),
11242            })
11243            .collect();
11244        assert_eq!(names, vec!["type", "default", "null"]);
11245    }
11246
11247    #[test]
11248    fn parse_interface_quoted_property_members() {
11249        let (ast, diags) = parse_str(
11250            r#"interface Headers { "content-type": string; "x\u002drequest-id"?: string; }"#,
11251        );
11252        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11253        let stmt = single_stmt(&ast);
11254        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11255            panic!("expected interface decl");
11256        };
11257        let names: Vec<(&str, bool)> = members
11258            .iter()
11259            .map(|m| match m {
11260                crate::InterfaceMember::Property { name, optional, .. } => {
11261                    (name.name.as_str(), *optional)
11262                }
11263                _ => panic!("expected property"),
11264            })
11265            .collect();
11266        assert_eq!(names, vec![("content-type", false), ("x-request-id", true)]);
11267    }
11268
11269    #[test]
11270    fn parse_param_optional_marker() {
11271        let (ast, diags) = parse_str("function f(x?: number): void {}");
11272        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11273        let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
11274            panic!("expected function")
11275        };
11276        assert!(params[0].optional);
11277        assert!(params[0].default.is_none());
11278    }
11279
11280    #[test]
11281    fn parse_interface_optional_method() {
11282        let (ast, diags) = parse_str("interface F { foo?(x?: number): void; }");
11283        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11284        let crate::StmtKind::InterfaceDecl { ref members, .. } = single_stmt(&ast).kind else {
11285            panic!("expected interface")
11286        };
11287        let crate::InterfaceMember::Property { optional, ty, .. } = &members[0] else {
11288            panic!("expected callable property")
11289        };
11290        assert!(*optional);
11291        let crate::TypeAnnotationKind::Function { params, .. } = &ty.kind else {
11292            panic!("expected function type")
11293        };
11294        assert!(params[0].optional);
11295    }
11296
11297    #[test]
11298    fn parse_interface_with_call_signature() {
11299        let (ast, diags) = parse_str("interface F { (s: string): number; }");
11300        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11301        let stmt = single_stmt(&ast);
11302        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11303            panic!("expected interface decl");
11304        };
11305        assert_eq!(members.len(), 1);
11306        match &members[0] {
11307            crate::InterfaceMember::Method { name, params, .. } => {
11308                assert_eq!(name.name, "@call");
11309                assert_eq!(params.len(), 1);
11310                assert_eq!(params[0].name.name, "s");
11311            }
11312            other => panic!("expected method (call signature), got {other:?}"),
11313        }
11314    }
11315
11316    #[test]
11317    fn parse_interface_mixed_call_signature_and_method() {
11318        let (ast, diags) = parse_str("interface F { greet(): string; (x: number): number; }");
11319        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11320        let stmt = single_stmt(&ast);
11321        let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11322            panic!("expected interface decl");
11323        };
11324        let names: Vec<&str> = members
11325            .iter()
11326            .map(|m| match m {
11327                crate::InterfaceMember::Method { name, .. } => name.name.as_str(),
11328                _ => "<property>",
11329            })
11330            .collect();
11331        assert_eq!(names, vec!["greet", "@call"]);
11332    }
11333
11334    #[test]
11335    fn parse_interface_rejects_generic_call_signature() {
11336        let (_ast, diags) = parse_str("interface F { <T>(x: T): T; }");
11337        assert!(
11338            diags
11339                .iter()
11340                .any(|d| d.message.contains("generic call signatures")),
11341            "expected generic-call-signature diagnostic, got {diags:?}"
11342        );
11343    }
11344
11345    #[test]
11346    fn parse_object_type_with_array_field() {
11347        let (ast, diags) = parse_str("let p: { xs: number[] } = null;");
11348        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11349        let ty = type_of_let(single_stmt(&ast));
11350        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11351            panic!("expected Object");
11352        };
11353        assert_eq!(fields[0].name.name, "xs");
11354        assert!(matches!(
11355            fields[0].ty.kind,
11356            crate::TypeAnnotationKind::Array(_)
11357        ));
11358    }
11359
11360    #[test]
11361    fn parse_array_of_objects() {
11362        let (ast, diags) = parse_str("let xs: { x: number }[] = null;");
11363        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11364        let ty = type_of_let(single_stmt(&ast));
11365        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11366            panic!("expected Array");
11367        };
11368        assert!(matches!(
11369            inner.kind,
11370            crate::TypeAnnotationKind::Object { .. }
11371        ));
11372    }
11373
11374    #[test]
11375    fn parse_tuple_type_annotation() {
11376        let (ast, diags) = parse_str("let p: [string, number] = null;");
11377        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11378        let ty = type_of_let(single_stmt(&ast));
11379        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11380            panic!("expected Tuple, got {:?}", ty.kind);
11381        };
11382        assert_eq!(elems.len(), 2);
11383        assert!(matches!(
11384            elems[0].kind,
11385            crate::TypeAnnotationKind::Name { .. }
11386        ));
11387        assert!(matches!(
11388            elems[1].kind,
11389            crate::TypeAnnotationKind::Name { .. }
11390        ));
11391    }
11392
11393    #[test]
11394    fn parse_singleton_tuple_type() {
11395        let (ast, diags) = parse_str("let p: [number] = null;");
11396        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11397        let ty = type_of_let(single_stmt(&ast));
11398        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11399            panic!("expected Tuple");
11400        };
11401        assert_eq!(elems.len(), 1);
11402    }
11403
11404    #[test]
11405    fn parse_nested_tuple_type() {
11406        let (ast, diags) = parse_str("let p: [[number, string], boolean] = null;");
11407        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11408        let ty = type_of_let(single_stmt(&ast));
11409        let crate::TypeAnnotationKind::Tuple(ref outer) = ty.kind else {
11410            panic!("expected outer Tuple");
11411        };
11412        assert_eq!(outer.len(), 2);
11413        let crate::TypeAnnotationKind::Tuple(ref inner) = outer[0].kind else {
11414            panic!("expected inner Tuple");
11415        };
11416        assert_eq!(inner.len(), 2);
11417    }
11418
11419    #[test]
11420    fn parse_array_of_tuple_type() {
11421        let (ast, diags) = parse_str("let xs: [string, number][] = null;");
11422        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11423        let ty = type_of_let(single_stmt(&ast));
11424        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11425            panic!("expected outer Array, got {:?}", ty.kind);
11426        };
11427        assert!(matches!(inner.kind, crate::TypeAnnotationKind::Tuple(_)));
11428    }
11429
11430    #[test]
11431    fn parse_trailing_comma_tuple_type() {
11432        let (ast, diags) = parse_str("let p: [number, string,] = null;");
11433        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11434        let ty = type_of_let(single_stmt(&ast));
11435        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11436            panic!("expected Tuple");
11437        };
11438        assert_eq!(elems.len(), 2);
11439    }
11440
11441    #[test]
11442    fn parse_empty_tuple_type_rejected() {
11443        let (_ast, diags) = parse_str("let p: [] = null;");
11444        assert!(
11445            diags.iter().any(|d| d
11446                .message
11447                .contains("tuple types must have at least one element")),
11448            "expected empty-tuple diagnostic, got: {diags:?}"
11449        );
11450    }
11451
11452    #[test]
11453    fn parse_labeled_tuple_drops_labels() {
11454        let (ast, diags) = parse_str("let p: [first: number, string] = null;");
11455        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11456        let ty = type_of_let(single_stmt(&ast));
11457        let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11458            panic!("expected Tuple, got {:?}", ty.kind);
11459        };
11460        assert_eq!(elems.len(), 2);
11461        assert!(
11462            elems
11463                .iter()
11464                .all(|e| matches!(e.kind, crate::TypeAnnotationKind::Name { .. }))
11465        );
11466    }
11467
11468    #[test]
11469    fn parse_labeled_optional_tuple_element() {
11470        let (ast, diags) = parse_str("let p: [first: number, second?: string] = null;");
11471        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11472        let ty = type_of_let(single_stmt(&ast));
11473        let crate::TypeAnnotationKind::Tuple(elements) = &ty.kind else {
11474            panic!("expected tuple")
11475        };
11476        assert!(matches!(
11477            elements[1].kind,
11478            crate::TypeAnnotationKind::Optional(_)
11479        ));
11480    }
11481
11482    #[test]
11483    fn parse_readonly_binds_to_the_postfix_type() {
11484        let (ast, diags) = parse_str("let p: readonly number[][] | null = null;");
11485        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11486        let ty = type_of_let(single_stmt(&ast));
11487        let crate::TypeAnnotationKind::Union(ref members) = ty.kind else {
11488            panic!("expected Union, got {:?}", ty.kind);
11489        };
11490        let crate::TypeAnnotationKind::Readonly(ref operand) = members[0].kind else {
11491            panic!("expected Readonly, got {:?}", members[0].kind);
11492        };
11493        let crate::TypeAnnotationKind::Array(ref inner) = operand.kind else {
11494            panic!("expected Array, got {:?}", operand.kind);
11495        };
11496        assert!(matches!(inner.kind, crate::TypeAnnotationKind::Array(_)));
11497    }
11498
11499    #[test]
11500    fn parse_readonly_as_a_type_name_is_not_the_operator() {
11501        let (ast, diags) = parse_str("let p: readonly = null;");
11502        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11503        let ty = type_of_let(single_stmt(&ast));
11504        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11505    }
11506
11507    #[test]
11508    fn parse_generic_one_arg() {
11509        let (ast, diags) = parse_str("let x: Foo<number> = null;");
11510        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11511        let ty = type_of_let(single_stmt(&ast));
11512        let crate::TypeAnnotationKind::Name { ref name, ref args } = ty.kind else {
11513            panic!("expected Name, got {:?}", ty.kind);
11514        };
11515        assert_eq!(name.span, crate::Span::new(F, 7, 10).unwrap());
11516        assert_eq!(ty.span, crate::Span::new(F, 7, 18).unwrap());
11517        assert_eq!(args.len(), 1);
11518        assert!(matches!(
11519            args[0].kind,
11520            crate::TypeAnnotationKind::Name { .. }
11521        ));
11522    }
11523
11524    #[test]
11525    fn parse_generic_two_args() {
11526        let (ast, diags) = parse_str("let m: Map<string, number> = null;");
11527        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11528        let ty = type_of_let(single_stmt(&ast));
11529        let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11530            panic!("expected Name");
11531        };
11532        assert_eq!(args.len(), 2);
11533    }
11534
11535    #[test]
11536    fn parse_generic_nested() {
11537        let (ast, diags) = parse_str("let b: Box<Box<T>> = null;");
11538        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11539        let ty = type_of_let(single_stmt(&ast));
11540        let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11541            panic!("expected outer Name");
11542        };
11543        assert_eq!(args.len(), 1);
11544        let crate::TypeAnnotationKind::Name {
11545            args: ref inner, ..
11546        } = args[0].kind
11547        else {
11548            panic!("expected inner Name");
11549        };
11550        assert_eq!(inner.len(), 1);
11551    }
11552
11553    #[test]
11554    fn parse_generic_array_postfix() {
11555        let (ast, diags) = parse_str("let xs: Foo<number>[] = null;");
11556        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11557        let ty = type_of_let(single_stmt(&ast));
11558        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11559            panic!("expected Array");
11560        };
11561        assert!(
11562            matches!(inner.kind, crate::TypeAnnotationKind::Name { ref args, .. } if args.len() == 1)
11563        );
11564    }
11565
11566    #[test]
11567    fn parse_generic_empty_diagnoses() {
11568        let (_ast, diags) = parse_str("let x: Foo<> = null;");
11569        assert!(
11570            diags
11571                .iter()
11572                .any(|d| d.message.contains("empty generic argument list")),
11573            "expected empty-generic-args diagnostic, got: {diags:?}"
11574        );
11575    }
11576
11577    #[test]
11578    fn parse_generic_trailing_comma_ok() {
11579        let (_ast, diags) = parse_str("let x: Foo<number,> = null;");
11580        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11581    }
11582
11583    #[test]
11584    fn parse_generic_missing_close_diagnoses() {
11585        let (_ast, diags) = parse_str("let x: Foo<number = null;");
11586        assert!(
11587            diags
11588                .iter()
11589                .any(|d| d.message.contains("expected `,` or `>`")),
11590            "expected unterminated-generic diagnostic, got: {diags:?}"
11591        );
11592    }
11593
11594    #[test]
11595    fn parse_qualified_two_segments() {
11596        let (ast, diags) = parse_str("let x: Foo.Bar = null;");
11597        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11598        let ty = type_of_let(single_stmt(&ast));
11599        let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11600            panic!("expected Qualified, got {:?}", ty.kind);
11601        };
11602        assert_eq!(path.len(), 2);
11603        // `Foo` at 7..10, `Bar` at 11..14.
11604        assert_eq!(path[0].span, crate::Span::new(F, 7, 10).unwrap());
11605        assert_eq!(path[1].span, crate::Span::new(F, 11, 14).unwrap());
11606        assert!(args.is_empty());
11607        assert_eq!(ty.span, crate::Span::new(F, 7, 14).unwrap());
11608    }
11609
11610    #[test]
11611    fn parse_qualified_three_segments() {
11612        let (ast, diags) = parse_str("let x: Foo.Bar.Baz = null;");
11613        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11614        let ty = type_of_let(single_stmt(&ast));
11615        let crate::TypeAnnotationKind::Qualified { ref path, .. } = ty.kind else {
11616            panic!("expected Qualified, got {:?}", ty.kind);
11617        };
11618        assert_eq!(path.len(), 3);
11619    }
11620
11621    #[test]
11622    fn parse_qualified_with_generic_args() {
11623        let (ast, diags) = parse_str("let x: Foo.Box<number> = null;");
11624        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11625        let ty = type_of_let(single_stmt(&ast));
11626        let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11627            panic!("expected Qualified, got {:?}", ty.kind);
11628        };
11629        assert_eq!(path.len(), 2);
11630        assert_eq!(args.len(), 1);
11631        assert!(matches!(
11632            args[0].kind,
11633            crate::TypeAnnotationKind::Name { .. }
11634        ));
11635    }
11636
11637    #[test]
11638    fn parse_qualified_with_array_postfix() {
11639        let (ast, diags) = parse_str("let xs: Foo.Bar[] = null;");
11640        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11641        let ty = type_of_let(single_stmt(&ast));
11642        let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11643            panic!("expected Array, got {:?}", ty.kind);
11644        };
11645        assert!(matches!(
11646            inner.kind,
11647            crate::TypeAnnotationKind::Qualified { .. }
11648        ));
11649    }
11650
11651    #[test]
11652    fn parse_qualified_trailing_dot_diagnoses() {
11653        let (_ast, diags) = parse_str("let x: Foo. = null;");
11654        assert!(
11655            diags.iter().any(|d| d
11656                .message
11657                .contains("expected identifier after `.` in type name")),
11658            "expected dotted-name diagnostic, got: {diags:?}"
11659        );
11660    }
11661
11662    #[test]
11663    fn parse_qualified_void_root_rejected_dot() {
11664        let (ast, diags) = parse_str("function f(): void.Bar { }");
11665        assert!(!diags.is_empty(), "expected a diagnostic for `void.Bar`");
11666        let _ = ast;
11667    }
11668
11669    fn union_members(ty: &crate::TypeAnnotation) -> &[crate::TypeAnnotation] {
11670        match &ty.kind {
11671            crate::TypeAnnotationKind::Union(members) => members,
11672            other => panic!("expected Union, got {other:?}"),
11673        }
11674    }
11675
11676    #[test]
11677    fn parse_union_two_members() {
11678        let (ast, diags) = parse_str("let x: number | string = null;");
11679        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11680        let ty = type_of_let(single_stmt(&ast));
11681        let members = union_members(ty);
11682        assert_eq!(members.len(), 2);
11683        assert!(matches!(
11684            members[0].kind,
11685            crate::TypeAnnotationKind::Name { .. }
11686        ));
11687        assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); // `number`
11688        assert!(matches!(
11689            members[1].kind,
11690            crate::TypeAnnotationKind::Name { .. }
11691        ));
11692        assert_eq!(members[1].span, crate::Span::new(F, 16, 22).unwrap()); // `string`
11693        assert_eq!(ty.span, crate::Span::new(F, 7, 22).unwrap());
11694    }
11695
11696    #[test]
11697    fn parse_union_three_members() {
11698        let (ast, diags) = parse_str("let x: A | B | C = null;");
11699        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11700        let ty = type_of_let(single_stmt(&ast));
11701        let members = union_members(ty);
11702        assert_eq!(members.len(), 3);
11703        for m in members {
11704            assert!(matches!(m.kind, crate::TypeAnnotationKind::Name { .. }));
11705        }
11706    }
11707
11708    #[test]
11709    fn parse_union_lower_than_array() {
11710        let (ast, diags) = parse_str("let x: number[] | string = null;");
11711        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11712        let ty = type_of_let(single_stmt(&ast));
11713        let members = union_members(ty);
11714        assert_eq!(members.len(), 2);
11715        assert!(
11716            matches!(members[0].kind, crate::TypeAnnotationKind::Array(_)),
11717            "expected first member to be Array, got {:?}",
11718            members[0].kind
11719        );
11720        assert!(matches!(
11721            members[1].kind,
11722            crate::TypeAnnotationKind::Name { .. }
11723        ));
11724    }
11725
11726    #[test]
11727    fn parse_union_with_null_member() {
11728        let (ast, diags) = parse_str("let x: number | null = null;");
11729        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11730        let ty = type_of_let(single_stmt(&ast));
11731        let members = union_members(ty);
11732        assert_eq!(members.len(), 2);
11733        assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); // `number`
11734        assert!(matches!(
11735            members[1].kind,
11736            crate::TypeAnnotationKind::Name { .. }
11737        ));
11738        assert_eq!(members[1].span, crate::Span::new(F, 16, 20).unwrap()); // `null`
11739    }
11740
11741    #[test]
11742    fn parse_union_in_function_return_type() {
11743        let (ast, diags) = parse_str("function f(): number | string { }");
11744        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11745        let stmt = single_stmt(&ast);
11746        match stmt.kind {
11747            crate::StmtKind::Function {
11748                ref return_type, ..
11749            } => {
11750                let return_type = return_type.as_ref().expect("plain return type");
11751                let members = union_members(return_type);
11752                assert_eq!(members.len(), 2);
11753            }
11754            _ => panic!("expected Function"),
11755        }
11756    }
11757
11758    #[test]
11759    fn parse_union_in_function_param_type() {
11760        let (ast, diags) = parse_str("function f(x: number | string): void { }");
11761        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11762        let stmt = single_stmt(&ast);
11763        match stmt.kind {
11764            crate::StmtKind::Function { ref params, .. } => {
11765                let p_ty = params[0].ty.as_ref().expect("param type");
11766                let members = union_members(p_ty);
11767                assert_eq!(members.len(), 2);
11768            }
11769            _ => panic!("expected Function"),
11770        }
11771    }
11772
11773    #[test]
11774    fn parse_union_in_object_field_type() {
11775        let (ast, diags) = parse_str("let p: { x: number | string } = null;");
11776        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11777        let ty = type_of_let(single_stmt(&ast));
11778        let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11779            panic!("expected Object, got {:?}", ty.kind);
11780        };
11781        assert_eq!(fields.len(), 1);
11782        let members = union_members(&fields[0].ty);
11783        assert_eq!(members.len(), 2);
11784    }
11785
11786    #[test]
11787    fn parse_union_leading_pipe_is_forgiven() {
11788        let (ast, diags) = parse_str("let x: | number | string = null;");
11789        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11790        let ty = type_of_let(single_stmt(&ast));
11791        let members = union_members(ty);
11792        assert_eq!(members.len(), 2);
11793    }
11794
11795    #[test]
11796    fn parse_union_leading_pipe_single_member_unwraps() {
11797        let (ast, diags) = parse_str("let x: | number = null;");
11798        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11799        let ty = type_of_let(single_stmt(&ast));
11800        assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11801    }
11802
11803    #[test]
11804    fn parse_union_trailing_pipe_diagnoses() {
11805        let (_, diags) = parse_str("let x: number | = null;");
11806        assert!(!diags.is_empty());
11807        assert_eq!(diags[0].message, "expected type");
11808    }
11809
11810    #[test]
11811    fn snapshot_union_type_mix() {
11812        let (ast, diags) = parse_str("let xs: number[] | string | null = null;");
11813        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11814        insta::assert_debug_snapshot!(ast);
11815    }
11816
11817    #[test]
11818    fn parse_object_type_duplicate_field_diagnoses() {
11819        let (_, diags) = parse_str("let p: { readonly x: number; x: string } = null;");
11820        assert!(!diags.is_empty());
11821        assert!(
11822            diags
11823                .iter()
11824                .any(|d| d.message == "duplicate field `x` in object type"),
11825            "expected duplicate-field diagnostic, got: {diags:?}"
11826        );
11827    }
11828
11829    #[test]
11830    fn snapshot_object_and_array_literals() {
11831        let (ast, diags) = parse_str(r#"let mix = [{ a: 1, b: "hi" }, {}];"#);
11832        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11833        insta::assert_debug_snapshot!(ast);
11834    }
11835
11836    #[test]
11837    fn snapshot_function_with_while() {
11838        let source = "function loop(n: number): void {\n  while (n > 0) {\n    log(n);\n  }\n}";
11839        let (ast, diags) = parse_str(source);
11840        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11841        insta::assert_debug_snapshot!(ast);
11842    }
11843
11844    fn parse_arrow_const(
11845        source: &str,
11846    ) -> (
11847        Ast,
11848        Vec<crate::ParamDecl>,
11849        Option<crate::TypeAnnotation>,
11850        crate::ArrowBody,
11851    ) {
11852        let (ast, diags) = parse_str(source);
11853        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11854        assert_eq!(ast.top_level.len(), 1);
11855        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11856        let value = match &stmt.kind {
11857            StmtKind::Const { value, .. } => *value,
11858            _ => panic!("expected const declaration"),
11859        };
11860        let value = match ast.try_expr(value).unwrap().kind {
11861            ExprKind::FunctionExpression { function, .. } => function,
11862            _ => value,
11863        };
11864        let expr = ast.try_expr(value).unwrap().clone();
11865        let (params, return_type, body) = match expr.kind {
11866            ExprKind::Arrow {
11867                params,
11868                return_type,
11869                body,
11870                ..
11871            } => (params, return_type, body),
11872            other => panic!("expected Arrow, got {other:?}"),
11873        };
11874        (ast, params, return_type, body)
11875    }
11876
11877    #[test]
11878    fn parses_anonymous_function_expression_as_arrow() {
11879        let (ast, params, return_type, body) =
11880            parse_arrow_const("const f = function (x: number): number { return x + 1; };");
11881        assert_eq!(params.len(), 1);
11882        assert_eq!(params[0].name.name, "x");
11883        assert!(return_type.is_some());
11884        assert!(matches!(body, crate::ArrowBody::Block(_)));
11885        let _ = ast;
11886    }
11887
11888    #[test]
11889    fn parses_function_expression_with_no_params() {
11890        let (_ast, params, _return_type, body) =
11891            parse_arrow_const("const f = function (): void {};");
11892        assert!(params.is_empty());
11893        assert!(matches!(body, crate::ArrowBody::Block(_)));
11894    }
11895
11896    /// The named wrapper preserves its body signature.
11897    #[test]
11898    fn parses_named_function_expression_signature() {
11899        let (_ast, params, _return_type, _body) =
11900            parse_arrow_const("const f = function named(x: number): number { return x + 1; };");
11901        assert_eq!(params.len(), 1);
11902    }
11903
11904    /// Self references are resolved in the function expression scope.
11905    #[test]
11906    fn accepts_a_named_function_expression_that_calls_itself() {
11907        let (_ast, diags) =
11908            parse_str("const f = function bar(x: number): number { return bar(x); };");
11909        assert!(diags.is_empty(), "{diags:?}");
11910    }
11911
11912    /// A member name after `.` is never a reference to the function itself.
11913    #[test]
11914    fn accepts_a_named_function_expression_using_the_name_as_a_property() {
11915        let (_ast, diags) = parse_str("const f = function bar(o: O): number { return o.bar; };");
11916        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11917    }
11918
11919    /// Nor is a key before `:`.
11920    #[test]
11921    fn accepts_a_named_function_expression_using_the_name_as_an_object_key() {
11922        let (_ast, diags) = parse_str(
11923            "const f = function bar(x: number): number { const o = { bar: 1 }; return o.bar + x; };",
11924        );
11925        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11926    }
11927
11928    /// A string that happens to spell the name is not a reference.
11929    #[test]
11930    fn accepts_a_named_function_expression_with_its_name_in_a_string() {
11931        let (_ast, diags) =
11932            parse_str("const f = function bar(x: number): string { return \"bar\"; };");
11933        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11934    }
11935
11936    /// A self reference in a template substitution remains a normal expression.
11937    #[test]
11938    fn accepts_a_self_call_inside_a_template_substitution() {
11939        let (_ast, diags) =
11940            parse_str("const f = function bar(x: number): string { return `${bar(0)}`; };");
11941        assert!(diags.is_empty(), "{diags:?}");
11942    }
11943
11944    #[test]
11945    fn rejects_a_generic_function_expression() {
11946        let (_ast, diags) = parse_str("const f = function <T>(x: T): T { return x; };");
11947        assert!(
11948            diags
11949                .iter()
11950                .any(|d| d.message.contains("generic function expressions")),
11951            "generic function expression should be rejected: {diags:?}"
11952        );
11953    }
11954
11955    #[test]
11956    fn accepts_this_inside_a_function_expression_in_a_class_method() {
11957        let (_ast, diags) = parse_str(
11958            "class C { x: number = 1; m(): number { \
11959             const f = function (this: { x: number }): number { return this.x; }; return f(); } }",
11960        );
11961        assert!(diags.is_empty(), "{diags:?}");
11962    }
11963
11964    #[test]
11965    fn retains_this_boundary_inside_method_shorthand() {
11966        let (ast, diags) = parse_str(
11967            "class C { x: number = 1; m(): number { \
11968             const o = { x: 2, g(): number { return this.x; } }; return o.g(); } }",
11969        );
11970        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11971        assert!((0..ast.exprs_len()).any(|index| matches!(
11972            ast.try_expr(crate::ExprId(index as u32)).unwrap().kind,
11973            ExprKind::ThisOutsideReceiver
11974        )));
11975    }
11976
11977    #[test]
11978    fn this_still_parses_in_an_ordinary_class_method() {
11979        let (_ast, diags) = parse_str("class C { x: number = 1; m(): number { return this.x; } }");
11980        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11981    }
11982
11983    /// `function` is a valid property name; the expression form must not shadow that.
11984    #[test]
11985    fn function_keyword_still_parses_as_an_object_key() {
11986        let (_ast, diags) = parse_str("let o = { function: 1 };");
11987        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11988    }
11989
11990    fn object_literal_member_values(source: &str) -> (Ast, Vec<crate::ExprId>) {
11991        let (ast, diags) = parse_str(source);
11992        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11993        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11994        let value = match &stmt.kind {
11995            StmtKind::Const { value, .. } => *value,
11996            other => panic!("expected a const declaration, got {other:?}"),
11997        };
11998        let members = match &ast.try_expr(value).unwrap().kind {
11999            ExprKind::ObjectLiteral { members } => members
12000                .iter()
12001                .map(crate::ObjectLiteralMember::value)
12002                .collect::<Vec<_>>(),
12003            other => panic!("expected ObjectLiteral, got {other:?}"),
12004        };
12005        (ast, members)
12006    }
12007
12008    #[test]
12009    fn parses_object_literal_method_shorthand_as_arrow() {
12010        let (ast, members) =
12011            object_literal_member_values("const o = { m(x: number): number { return x; } };");
12012        assert_eq!(members.len(), 1);
12013        match &ast.try_expr(members[0]).unwrap().kind {
12014            ExprKind::Arrow { params, body, .. } => {
12015                assert_eq!(params.len(), 1);
12016                assert_eq!(params[0].name.name, "x");
12017                assert!(matches!(body, crate::ArrowBody::Block(_)));
12018            }
12019            other => panic!("expected Arrow, got {other:?}"),
12020        }
12021    }
12022
12023    /// A keyword is a valid method name, just as it is a valid property name.
12024    #[test]
12025    fn parses_method_shorthand_with_a_keyword_name() {
12026        let (ast, members) =
12027            object_literal_member_values("const o = { if(x: number): number { return x; } };");
12028        assert!(matches!(
12029            ast.try_expr(members[0]).unwrap().kind,
12030            ExprKind::Arrow { .. }
12031        ));
12032    }
12033
12034    #[test]
12035    fn parses_method_shorthand_alongside_plain_and_shorthand_properties() {
12036        let (ast, members) =
12037            object_literal_member_values("const o = { a: 1, m(): void {}, b: 2 };");
12038        assert_eq!(members.len(), 3);
12039        assert!(matches!(
12040            ast.try_expr(members[1]).unwrap().kind,
12041            ExprKind::Arrow { .. }
12042        ));
12043    }
12044
12045    #[test]
12046    fn parses_bare_ident_arrow_expression_body() {
12047        let (_ast, params, return_type, body) = parse_arrow_const("const f = x => x + 1;");
12048        assert_eq!(params.len(), 1);
12049        assert_eq!(params[0].name.name, "x");
12050        assert!(params[0].ty.is_none());
12051        assert!(return_type.is_none());
12052        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12053    }
12054
12055    #[test]
12056    fn parses_zero_param_arrow() {
12057        let (_ast, params, return_type, body) = parse_arrow_const("const f = () => 0;");
12058        assert!(params.is_empty());
12059        assert!(return_type.is_none());
12060        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12061    }
12062
12063    #[test]
12064    fn parses_paren_unannotated_arrow() {
12065        let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x) => x;");
12066        assert_eq!(params.len(), 1);
12067        assert_eq!(params[0].name.name, "x");
12068        assert!(params[0].ty.is_none());
12069    }
12070
12071    #[test]
12072    fn parses_typed_param_arrow() {
12073        let (_ast, params, return_type, body) =
12074            parse_arrow_const("const f = (x: number) => x * 2;");
12075        assert_eq!(params.len(), 1);
12076        assert!(params[0].ty.is_some());
12077        assert!(return_type.is_none());
12078        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12079    }
12080
12081    #[test]
12082    fn parses_typed_param_and_return_arrow() {
12083        let (_ast, params, return_type, body) =
12084            parse_arrow_const("const f = (x: number): number => x * 2;");
12085        assert_eq!(params.len(), 1);
12086        assert!(params[0].ty.is_some());
12087        assert!(return_type.is_some());
12088        assert!(matches!(body, crate::ArrowBody::Expr(_)));
12089    }
12090
12091    #[test]
12092    fn parses_multi_param_unannotated_arrow() {
12093        let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x, y) => x + y;");
12094        assert_eq!(params.len(), 2);
12095        assert_eq!(params[0].name.name, "x");
12096        assert_eq!(params[1].name.name, "y");
12097        assert!(params[0].ty.is_none());
12098        assert!(params[1].ty.is_none());
12099    }
12100
12101    #[test]
12102    fn parses_multi_param_typed_arrow() {
12103        let (_ast, params, _return_type, _body) =
12104            parse_arrow_const("const f = (x: number, y: number) => x + y;");
12105        assert_eq!(params.len(), 2);
12106        assert!(params[0].ty.is_some());
12107        assert!(params[1].ty.is_some());
12108    }
12109
12110    #[test]
12111    fn parses_block_body_arrow() {
12112        let (_ast, params, _return_type, body) =
12113            parse_arrow_const("const f = (x: number) => { return x; };");
12114        assert_eq!(params.len(), 1);
12115        assert!(matches!(body, crate::ArrowBody::Block(_)));
12116    }
12117
12118    #[test]
12119    fn rejects_typed_bare_ident_arrow() {
12120        let (_ast, diags) = parse_str("const f = x: number => x;");
12121        assert!(
12122            !diags.is_empty(),
12123            "expected diagnostics for `x: number => …`",
12124        );
12125    }
12126
12127    #[test]
12128    fn arrow_inside_call_argument_parses() {
12129        let (ast, diags) = parse_str("f((x: number) => x + 1, 2);");
12130        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12131        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12132        let call = match &stmt.kind {
12133            StmtKind::Expr(eid) => ast.try_expr(*eid).unwrap(),
12134            _ => panic!("expected expression statement"),
12135        };
12136        let args = match &call.kind {
12137            ExprKind::Call { args, .. } => args.clone(),
12138            other => panic!("expected Call, got {other:?}"),
12139        };
12140        assert_eq!(args.len(), 2);
12141        assert!(matches!(
12142            ast.try_expr(args[0]).unwrap().kind,
12143            ExprKind::Arrow { .. }
12144        ));
12145    }
12146
12147    #[test]
12148    fn function_decl_param_still_requires_annotation() {
12149        let (_ast, diags) = parse_str("function f(x): number { return x; }");
12150        assert!(
12151            diags.iter().any(|d| d.message.contains("type annotation")),
12152            "expected `parameter requires a type annotation` diagnostic, got {diags:?}",
12153        );
12154    }
12155
12156    #[test]
12157    fn paren_expression_still_parses_when_no_arrow() {
12158        let (ast, diags) = parse_str("const a = (1 + 2);");
12159        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12160        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12161        let value = match &stmt.kind {
12162            StmtKind::Const { value, .. } => *value,
12163            _ => panic!("expected const declaration"),
12164        };
12165        assert!(matches!(
12166            ast.try_expr(value).unwrap().kind,
12167            ExprKind::Paren(_)
12168        ));
12169    }
12170
12171    #[test]
12172    fn parse_named_import_single() {
12173        let (ast, diags) = parse_str("import { v4 } from \"submilli:uuid\";");
12174        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12175        assert_eq!(ast.top_level.len(), 1);
12176        let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12177        let (module, kind) = match &stmt.kind {
12178            StmtKind::Import { module, kind, .. } => (module, kind),
12179            other => panic!("expected Import, got {other:?}"),
12180        };
12181        assert_eq!(module, "submilli:uuid");
12182        let specs = match kind {
12183            ImportKind::Named(specs) => specs,
12184            other => panic!("expected Named, got {other:?}"),
12185        };
12186        assert_eq!(specs.len(), 1);
12187        assert_eq!(specs[0].imported_name.name, "v4");
12188        assert_eq!(specs[0].local_name.name, "v4");
12189    }
12190
12191    #[test]
12192    fn parse_named_import_multi_and_alias() {
12193        let (ast, diags) =
12194            parse_str("import { v4, v7 as makeId, validate } from \"submilli:uuid\";");
12195        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12196        let specs = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12197            StmtKind::Import {
12198                kind: ImportKind::Named(s),
12199                ..
12200            } => s.clone(),
12201            other => panic!("expected named import, got {other:?}"),
12202        };
12203        assert_eq!(specs.len(), 3);
12204        assert_eq!(specs[0].imported_name.name, "v4");
12205        assert_eq!(specs[0].local_name.name, "v4");
12206        assert_eq!(specs[1].imported_name.name, "v7");
12207        assert_eq!(specs[1].local_name.name, "makeId");
12208        assert_eq!(specs[2].imported_name.name, "validate");
12209        assert_eq!(specs[2].local_name.name, "validate");
12210    }
12211
12212    #[test]
12213    fn parse_namespace_import() {
12214        let (ast, diags) = parse_str("import uuid from \"submilli:uuid\";");
12215        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12216        let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12217            StmtKind::Import { kind, .. } => kind.clone(),
12218            other => panic!("expected import, got {other:?}"),
12219        };
12220        let local = match kind {
12221            ImportKind::Namespace { local_name } => local_name,
12222            other => panic!("expected Namespace, got {other:?}"),
12223        };
12224        assert_eq!(local.name, "uuid");
12225    }
12226
12227    #[test]
12228    fn parse_wildcard_namespace_import_synonym() {
12229        let (ast, diags) = parse_str("import * as uuid from \"submilli:uuid\";");
12230        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12231        let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12232            StmtKind::Import { kind, .. } => kind.clone(),
12233            other => panic!("expected import, got {other:?}"),
12234        };
12235        let local = match kind {
12236            ImportKind::Namespace { local_name } => local_name,
12237            other => panic!("expected Namespace, got {other:?}"),
12238        };
12239        assert_eq!(local.name, "uuid");
12240    }
12241
12242    #[test]
12243    fn parse_wildcard_namespace_import_without_as_rejected() {
12244        let (_ast, diags) = parse_str("import * from \"submilli:uuid\";");
12245        assert!(
12246            diags
12247                .iter()
12248                .any(|d| d.message.contains("expected `as <name>`")),
12249            "expected missing-as diagnostic, got: {diags:?}",
12250        );
12251    }
12252
12253    #[test]
12254    fn parse_combined_default_and_named_rejected() {
12255        let (_ast, diags) = parse_str("import uuid, { v4 } from \"submilli:uuid\";");
12256        assert!(
12257            diags
12258                .iter()
12259                .any(|d| d.message.contains("combining default and named")),
12260            "expected combined-import diagnostic, got: {diags:?}",
12261        );
12262    }
12263
12264    #[test]
12265    fn parse_side_effect_import_rejected() {
12266        let (_ast, diags) = parse_str("import \"submilli:uuid\";");
12267        assert!(
12268            diags
12269                .iter()
12270                .any(|d| d.message.contains("side-effect-only imports")),
12271            "expected side-effect-import diagnostic, got: {diags:?}",
12272        );
12273    }
12274
12275    #[test]
12276    fn parse_empty_specifier_list_rejected() {
12277        let (_ast, diags) = parse_str("import {} from \"submilli:uuid\";");
12278        assert!(
12279            diags
12280                .iter()
12281                .any(|d| d.message.contains("empty import specifier list")),
12282            "expected empty-specifier diagnostic, got: {diags:?}",
12283        );
12284    }
12285
12286    #[test]
12287    fn parse_import_inside_function_body_rejected() {
12288        let (_ast, diags) =
12289            parse_str("function main(): void { import { v4 } from \"submilli:uuid\"; }");
12290        assert!(
12291            diags
12292                .iter()
12293                .any(|d| d.message.contains("must appear at the top of the file")),
12294            "expected top-level diagnostic, got: {diags:?}",
12295        );
12296    }
12297
12298    #[test]
12299    fn parse_missing_from_rejected() {
12300        let (_ast, diags) = parse_str("import { v4 } \"submilli:uuid\";");
12301        assert!(
12302            diags.iter().any(|d| d.message.contains("expected `from`")),
12303            "expected missing-from diagnostic, got: {diags:?}",
12304        );
12305    }
12306
12307    #[test]
12308    fn parse_missing_module_specifier_rejected() {
12309        let (_ast, diags) = parse_str("import { v4 } from;");
12310        assert!(
12311            diags
12312                .iter()
12313                .any(|d| d.message.contains("expected module specifier")),
12314            "expected missing-module diagnostic, got: {diags:?}",
12315        );
12316    }
12317
12318    #[test]
12319    fn parse_export_function_marks_exported() {
12320        // `export function main() {}` parses to a single function declaration in
12321        // `top_level`, plus an `exported_decls` entry pointing at it.
12322        let (ast, diags) = parse_str("export function main(): void {}");
12323        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12324        let stmt = single_stmt(&ast);
12325        assert!(
12326            matches!(stmt.kind, crate::StmtKind::Function { .. }),
12327            "expected Function decl, got {:?}",
12328            stmt.kind,
12329        );
12330        assert_eq!(ast.exported_decls.len(), 1, "one exported decl");
12331        assert_eq!(ast.exported_decls[0].stmt, ast.top_level[0]);
12332    }
12333
12334    #[test]
12335    fn parse_export_const_type_interface_enum_are_marked() {
12336        for src in [
12337            "export const X: number = 1;",
12338            "export type Id = string;",
12339            "export interface P { x: number; }",
12340            "export enum E { A, B }",
12341        ] {
12342            let (ast, diags) = parse_str(src);
12343            assert!(diags.is_empty(), "unexpected diags for {src:?}: {diags:?}");
12344            assert_eq!(ast.top_level.len(), 1, "one decl for {src:?}");
12345            assert_eq!(ast.exported_decls.len(), 1, "one exported decl for {src:?}");
12346        }
12347    }
12348
12349    #[test]
12350    fn parse_export_default_is_rejected_with_focused_message() {
12351        let (_ast, diags) = parse_str("export default function main(): void {}");
12352        assert!(
12353            diags
12354                .iter()
12355                .any(|d| d.message.contains("`export default` is not supported")),
12356            "expected focused export-default diagnostic, got: {diags:?}",
12357        );
12358        assert!(
12359            !diags.iter().any(|d| d.message.contains("expected `;`")),
12360            "the spurious missing-semicolon error must be gone: {diags:?}",
12361        );
12362    }
12363
12364    #[test]
12365    fn parse_export_list_parses_as_export_from() {
12366        // Form 2 (`export { x };`, no `from`) now parses cleanly — the
12367        // single-file gating happens in the typechecker, not the parser.
12368        let (ast, diags) = parse_str("function main(): void {}\nexport { main };");
12369        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12370        assert_eq!(ast.top_level.len(), 2, "function + export-from");
12371        let export = ast.try_stmt(ast.top_level[1]).unwrap();
12372        let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12373            panic!("expected ExportFrom, got {:?}", export.kind);
12374        };
12375        assert!(source.is_none(), "bare `export {{ x }}` has no source");
12376        assert_eq!(specs.len(), 1);
12377        assert_eq!(specs[0].imported_name.name, "main");
12378        assert_eq!(specs[0].local_name.name, "main");
12379    }
12380
12381    #[test]
12382    fn parse_export_from_with_source_and_alias_parses() {
12383        let (ast, diags) = parse_str("export { foo, bar as baz } from \"./util\";");
12384        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12385        let export = ast.try_stmt(ast.top_level[0]).unwrap();
12386        let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12387            panic!("expected ExportFrom, got {:?}", export.kind);
12388        };
12389        let Some((module, _)) = source else {
12390            panic!("expected a `from` source");
12391        };
12392        assert_eq!(module, "./util");
12393        assert_eq!(specs.len(), 2);
12394        assert_eq!(specs[1].imported_name.name, "bar");
12395        assert_eq!(specs[1].local_name.name, "baz");
12396    }
12397
12398    #[test]
12399    fn parse_export_inside_function_body_rejected() {
12400        let (_ast, diags) = parse_str("function main(): void { export const X: number = 1; }");
12401        assert!(
12402            diags
12403                .iter()
12404                .any(|d| d.message.contains("must appear at the top of the file")),
12405            "expected top-level diagnostic, got: {diags:?}",
12406        );
12407    }
12408
12409    #[test]
12410    fn parse_throw_with_expression() {
12411        let (ast, diags) = parse_str(r#"throw new Error("oops");"#);
12412        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12413        let stmt = single_stmt(&ast);
12414        let crate::StmtKind::Throw { value } = stmt.kind else {
12415            panic!("expected Throw, got {:?}", stmt.kind);
12416        };
12417        assert!(matches!(
12418            ast.try_expr(value).unwrap().kind,
12419            crate::ExprKind::New { .. }
12420        ));
12421    }
12422
12423    #[test]
12424    fn parse_new_with_dotted_callee() {
12425        let (ast, diags) = parse_str("new Temporal.Duration({});");
12426        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12427        let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12428            panic!("expected expression statement");
12429        };
12430        let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12431            panic!("expected New");
12432        };
12433        let crate::ExprKind::FieldAccess { name, .. } = &ast.try_expr(*callee).unwrap().kind else {
12434            panic!("expected FieldAccess callee");
12435        };
12436        assert_eq!(name.name, "Duration");
12437    }
12438
12439    #[test]
12440    fn parse_new_with_three_level_dotted_callee() {
12441        let (ast, diags) = parse_str("new Foo.Bar.Baz();");
12442        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12443        let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12444            panic!("expected expression statement");
12445        };
12446        let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12447            panic!("expected New");
12448        };
12449        let crate::ExprKind::FieldAccess { receiver, name } = &ast.try_expr(*callee).unwrap().kind
12450        else {
12451            panic!("expected outer FieldAccess");
12452        };
12453        assert_eq!(name.name, "Baz");
12454        let crate::ExprKind::FieldAccess { name: mid_name, .. } =
12455            &ast.try_expr(*receiver).unwrap().kind
12456        else {
12457            panic!("expected inner FieldAccess");
12458        };
12459        assert_eq!(mid_name.name, "Bar");
12460    }
12461
12462    #[test]
12463    fn parse_new_with_trailing_dot_diagnoses() {
12464        let (_ast, diags) = parse_str("new Foo.;");
12465        assert!(
12466            diags.iter().any(|d| d
12467                .message
12468                .contains("expected identifier after `.` in constructor name")),
12469            "expected trailing-dot diagnostic, got: {diags:?}",
12470        );
12471    }
12472
12473    #[test]
12474    fn throw_without_expression_diagnoses() {
12475        let (_ast, diags) = parse_str("throw;");
12476        assert!(
12477            diags
12478                .iter()
12479                .any(|d| d.message == "expected expression after `throw`"),
12480            "expected bare-throw diagnostic, got: {diags:?}",
12481        );
12482    }
12483
12484    #[test]
12485    fn parse_try_catch() {
12486        let (ast, diags) = parse_str("try { } catch (e: Error) { }");
12487        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12488        let stmt = single_stmt(&ast);
12489        let crate::StmtKind::Try {
12490            body,
12491            ref catches,
12492            ref finally,
12493        } = stmt.kind
12494        else {
12495            panic!("expected Try, got {:?}", stmt.kind);
12496        };
12497        assert!(matches!(
12498            ast.try_stmt(body).unwrap().kind,
12499            crate::StmtKind::Block(ref v) if v.is_empty()
12500        ));
12501        let [clause] = catches.as_slice() else {
12502            panic!("expected one catch clause, got {catches:?}");
12503        };
12504        assert_eq!(clause.binding.name, "e");
12505        assert!(clause.ty.is_some());
12506        assert!(finally.is_none());
12507    }
12508
12509    #[test]
12510    fn parse_try_finally() {
12511        let (ast, diags) = parse_str("try { } finally { }");
12512        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12513        let stmt = single_stmt(&ast);
12514        let crate::StmtKind::Try {
12515            ref catches,
12516            ref finally,
12517            ..
12518        } = stmt.kind
12519        else {
12520            panic!("expected Try");
12521        };
12522        assert!(catches.is_empty());
12523        let finally_id = finally.expect("finally block");
12524        assert!(matches!(
12525            ast.try_stmt(finally_id).unwrap().kind,
12526            crate::StmtKind::Block(_)
12527        ));
12528    }
12529
12530    #[test]
12531    fn parse_try_catch_finally() {
12532        let (ast, diags) = parse_str("try { } catch (e: Error) { } finally { }");
12533        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12534        let stmt = single_stmt(&ast);
12535        let crate::StmtKind::Try {
12536            ref catches,
12537            ref finally,
12538            ..
12539        } = stmt.kind
12540        else {
12541            panic!("expected Try");
12542        };
12543        assert_eq!(catches.len(), 1);
12544        assert!(finally.is_some());
12545    }
12546
12547    #[test]
12548    fn bare_try_diagnoses() {
12549        let (_ast, diags) = parse_str("try { }");
12550        assert!(
12551            diags
12552                .iter()
12553                .any(|d| d.message == "try requires at least one of `catch` or `finally`"),
12554            "expected bare-try diagnostic, got: {diags:?}",
12555        );
12556    }
12557
12558    #[test]
12559    fn parse_untyped_catch() {
12560        let (ast, diags) = parse_str("try { } catch (e) { }");
12561        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12562        let stmt = single_stmt(&ast);
12563        let crate::StmtKind::Try {
12564            ref catches,
12565            ref finally,
12566            ..
12567        } = stmt.kind
12568        else {
12569            panic!("expected Try");
12570        };
12571        let [clause] = catches.as_slice() else {
12572            panic!("expected one catch clause, got {catches:?}");
12573        };
12574        assert_eq!(clause.binding.name, "e");
12575        assert!(clause.ty.is_none());
12576        assert!(finally.is_none());
12577    }
12578
12579    #[test]
12580    fn parse_multiple_catch_clauses() {
12581        let (ast, diags) = parse_str("try { } catch (e: HttpError) { } catch (f) { }");
12582        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12583        let stmt = single_stmt(&ast);
12584        let crate::StmtKind::Try {
12585            ref catches,
12586            ref finally,
12587            ..
12588        } = stmt.kind
12589        else {
12590            panic!("expected Try");
12591        };
12592        let [first, second] = catches.as_slice() else {
12593            panic!("expected two catch clauses, got {catches:?}");
12594        };
12595        assert_eq!(first.binding.name, "e");
12596        assert!(first.ty.is_some());
12597        assert_eq!(second.binding.name, "f");
12598        assert!(second.ty.is_none());
12599        assert!(finally.is_none());
12600    }
12601
12602    #[test]
12603    fn catch_after_finally_diagnoses() {
12604        let (_ast, diags) = parse_str("try { } finally { } catch (e: Error) { }");
12605        assert!(
12606            !diags.is_empty(),
12607            "expected diagnostic for catch-after-finally"
12608        );
12609    }
12610
12611    #[test]
12612    fn catch_missing_open_paren_diagnoses() {
12613        let (_ast, diags) = parse_str("try { } catch e: Error) { }");
12614        assert!(
12615            diags
12616                .iter()
12617                .any(|d| d.message == "expected `(` after `catch`"),
12618            "expected open-paren diagnostic, got: {diags:?}",
12619        );
12620    }
12621
12622    fn class_members(ast: &Ast) -> &[crate::ClassMember] {
12623        match &single_stmt(ast).kind {
12624            StmtKind::ClassDecl { members, .. } => members,
12625            other => panic!("expected ClassDecl, got {other:?}"),
12626        }
12627    }
12628
12629    #[test]
12630    fn parse_animal_class() {
12631        let src = r#"
12632class Animal {
12633  name: string;
12634  private sound: string;
12635  readonly species: string;
12636
12637  constructor(name: string, sound: string, species: string) {
12638    this.name = name;
12639    this.sound = sound;
12640    this.species = species;
12641  }
12642
12643  speak(): string {
12644    return this.name + " says " + this.sound;
12645  }
12646}
12647"#;
12648        let (ast, diags) = parse_str(src);
12649        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12650        let StmtKind::ClassDecl {
12651            name,
12652            generics,
12653            extends,
12654            implements,
12655            members,
12656            ..
12657        } = &single_stmt(&ast).kind
12658        else {
12659            panic!("expected ClassDecl");
12660        };
12661        assert_eq!(name.name, "Animal");
12662        assert!(generics.is_empty());
12663        assert!(extends.is_none());
12664        assert!(implements.is_empty());
12665        assert_eq!(members.len(), 5);
12666
12667        let crate::ClassMember::Field {
12668            name, modifiers, ..
12669        } = &members[0]
12670        else {
12671            panic!("member 0 should be a field");
12672        };
12673        assert_eq!(name.name, "name");
12674        assert_eq!(modifiers.visibility, crate::Visibility::Public);
12675        assert!(modifiers.readonly.is_none());
12676
12677        let crate::ClassMember::Field { modifiers, .. } = &members[1] else {
12678            panic!("member 1 should be a field");
12679        };
12680        assert_eq!(modifiers.visibility, crate::Visibility::Private);
12681
12682        let crate::ClassMember::Field { modifiers, .. } = &members[2] else {
12683            panic!("member 2 should be a field");
12684        };
12685        assert!(modifiers.readonly.is_some());
12686        assert_eq!(modifiers.visibility, crate::Visibility::Public);
12687
12688        assert!(matches!(
12689            &members[3],
12690            crate::ClassMember::Constructor { .. }
12691        ));
12692
12693        let crate::ClassMember::Method { name, .. } = &members[4] else {
12694            panic!("member 4 should be a method");
12695        };
12696        assert_eq!(name.name, "speak");
12697    }
12698
12699    #[test]
12700    fn parse_dog_class_extends_super_and_void_method() {
12701        let src = r#"
12702class Dog extends Animal {
12703  private tricks: string[];
12704
12705  constructor(name: string) {
12706    super(name, "woof", "canis familiaris");
12707    this.tricks = [];
12708  }
12709
12710  learn(trick: string): void {
12711    this.tricks.push(trick);
12712  }
12713}
12714"#;
12715        let (ast, diags) = parse_str(src);
12716        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12717        let StmtKind::ClassDecl {
12718            extends, members, ..
12719        } = &single_stmt(&ast).kind
12720        else {
12721            panic!("expected ClassDecl");
12722        };
12723        assert!(extends.is_some(), "Dog should extend Animal");
12724        assert_eq!(members.len(), 3);
12725        assert!(matches!(&members[0], crate::ClassMember::Field { .. }));
12726        assert!(matches!(
12727            &members[1],
12728            crate::ClassMember::Constructor { .. }
12729        ));
12730        assert!(matches!(&members[2], crate::ClassMember::Method { .. }));
12731    }
12732
12733    #[test]
12734    fn parse_class_implements_list() {
12735        let (ast, diags) = parse_str("class C implements I, J { run(): void {} }");
12736        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12737        let StmtKind::ClassDecl { implements, .. } = &single_stmt(&ast).kind else {
12738            panic!("expected ClassDecl");
12739        };
12740        assert_eq!(implements.len(), 2);
12741    }
12742
12743    #[test]
12744    fn parse_class_extends_and_implements() {
12745        let (ast, diags) = parse_str("class C extends B implements I { run(): void {} }");
12746        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12747        let StmtKind::ClassDecl {
12748            extends,
12749            implements,
12750            ..
12751        } = &single_stmt(&ast).kind
12752        else {
12753            panic!("expected ClassDecl");
12754        };
12755        assert!(extends.is_some());
12756        assert_eq!(implements.len(), 1);
12757    }
12758
12759    #[test]
12760    fn parse_class_generics_optional_field_and_initializer() {
12761        let (ast, diags) = parse_str("class Box<T> { value?: T; items: T[] = []; }");
12762        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12763        let StmtKind::ClassDecl {
12764            generics, members, ..
12765        } = &single_stmt(&ast).kind
12766        else {
12767            panic!("expected ClassDecl");
12768        };
12769        assert_eq!(generics.len(), 1);
12770        assert_eq!(generics[0].name, "T");
12771
12772        let crate::ClassMember::Field {
12773            optional,
12774            initializer,
12775            ..
12776        } = &members[0]
12777        else {
12778            panic!("member 0 should be a field");
12779        };
12780        assert!(*optional);
12781        assert!(initializer.is_none());
12782
12783        let crate::ClassMember::Field { initializer, .. } = &members[1] else {
12784            panic!("member 1 should be a field");
12785        };
12786        assert!(initializer.is_some());
12787    }
12788
12789    #[test]
12790    fn export_class_is_marked() {
12791        let (ast, diags) = parse_str("export class Foo {}");
12792        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12793        assert_eq!(ast.top_level.len(), 1);
12794        assert_eq!(ast.exported_decls.len(), 1);
12795        assert!(matches!(
12796            &single_stmt(&ast).kind,
12797            StmtKind::ClassDecl { .. }
12798        ));
12799    }
12800
12801    #[test]
12802    fn parameterless_index_signature_is_reported_from_its_member_start() {
12803        for (source, expected) in [
12804            ("interface P { readonly []: number }", "readonly []"),
12805            ("let p: { readonly []: number } = {};", "readonly []"),
12806            ("let q: { []: number } = {};", "[]"),
12807        ] {
12808            let (_ast, diags) = parse_str(source);
12809            let diag = diags
12810                .iter()
12811                .find(|d| d.message.contains("must declare exactly one parameter"))
12812                .unwrap_or_else(|| panic!("no diagnostic for {source}"));
12813            let reported = &source[diag.span.start as usize..diag.span.end as usize];
12814            assert_eq!(reported, expected, "{source}");
12815        }
12816    }
12817
12818    #[test]
12819    fn unterminated_object_type_expects_closing_brace() {
12820        let (_ast, diags) = parse_str("let p: { a: number\n");
12821        assert!(
12822            diags.iter().any(|d| d.message == "expected `}`"),
12823            "{diags:?}"
12824        );
12825    }
12826
12827    #[test]
12828    fn conflicting_visibility_modifiers_diagnose() {
12829        let (_ast, diags) = parse_str("class C { public private x: number; }");
12830        assert!(
12831            diags
12832                .iter()
12833                .any(|d| d.message.contains("at most one visibility modifier")),
12834            "got: {diags:?}",
12835        );
12836    }
12837
12838    #[test]
12839    fn duplicate_readonly_modifier_diagnoses() {
12840        let (_ast, diags) = parse_str("class C { readonly readonly x: number; }");
12841        assert!(
12842            diags
12843                .iter()
12844                .any(|d| d.message.contains("duplicate `readonly`")),
12845            "got: {diags:?}",
12846        );
12847    }
12848
12849    #[test]
12850    fn field_named_like_a_modifier_is_allowed() {
12851        let (ast, diags) = parse_str("class C { private: number; }");
12852        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12853        let crate::ClassMember::Field {
12854            name, modifiers, ..
12855        } = &class_members(&ast)[0]
12856        else {
12857            panic!("expected a field");
12858        };
12859        assert_eq!(name.name, "private");
12860        assert_eq!(modifiers.visibility, crate::Visibility::Public);
12861    }
12862
12863    #[test]
12864    fn method_named_get_is_allowed() {
12865        let (ast, diags) = parse_str("class C { get(): number { return 1; } }");
12866        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12867        let crate::ClassMember::Method { name, .. } = &class_members(&ast)[0] else {
12868            panic!("expected a method");
12869        };
12870        assert_eq!(name.name, "get");
12871    }
12872
12873    #[test]
12874    fn static_method_parses() {
12875        let (ast, diags) = parse_str("class C { static f(): number { return 1; } }");
12876        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12877        let crate::ClassMember::Method {
12878            name, modifiers, ..
12879        } = &class_members(&ast)[0]
12880        else {
12881            panic!("expected a method");
12882        };
12883        assert_eq!(name.name, "f");
12884        assert!(modifiers.static_span.is_some());
12885    }
12886
12887    #[test]
12888    fn static_readonly_field_parses() {
12889        let (ast, diags) = parse_str("class C { static readonly x: number = 1; }");
12890        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12891        let crate::ClassMember::Field {
12892            name,
12893            modifiers,
12894            initializer,
12895            ..
12896        } = &class_members(&ast)[0]
12897        else {
12898            panic!("expected a field");
12899        };
12900        assert_eq!(name.name, "x");
12901        assert!(modifiers.static_span.is_some());
12902        assert!(modifiers.readonly.is_some());
12903        assert!(initializer.is_some());
12904    }
12905
12906    #[test]
12907    fn private_static_method_parses() {
12908        let (ast, diags) = parse_str("class C { private static f(): void {} }");
12909        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12910        let crate::ClassMember::Method { modifiers, .. } = &class_members(&ast)[0] else {
12911            panic!("expected a method");
12912        };
12913        assert_eq!(modifiers.visibility, crate::Visibility::Private);
12914        assert!(modifiers.static_span.is_some());
12915    }
12916
12917    #[test]
12918    fn method_named_static_is_allowed() {
12919        let (ast, diags) = parse_str("class C { static(): number { return 1; } }");
12920        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12921        let crate::ClassMember::Method {
12922            name, modifiers, ..
12923        } = &class_members(&ast)[0]
12924        else {
12925            panic!("expected a method");
12926        };
12927        assert_eq!(name.name, "static");
12928        assert!(modifiers.static_span.is_none());
12929    }
12930
12931    #[test]
12932    fn visibility_after_static_diagnoses() {
12933        let (_ast, diags) = parse_str("class C { static private f(): void {} }");
12934        assert!(
12935            diags
12936                .iter()
12937                .any(|d| d.message.contains("`private` must come before `static`")),
12938            "got: {diags:?}",
12939        );
12940    }
12941
12942    #[test]
12943    fn duplicate_static_modifier_diagnoses() {
12944        let (_ast, diags) = parse_str("class C { static static f(): void {} }");
12945        assert!(
12946            diags
12947                .iter()
12948                .any(|d| d.message.contains("duplicate `static` modifier")),
12949            "got: {diags:?}",
12950        );
12951    }
12952
12953    #[test]
12954    fn static_accessor_is_rejected() {
12955        let (_ast, diags) = parse_str("class C { static get x(): number { return 1; } }");
12956        assert!(
12957            diags
12958                .iter()
12959                .any(|d| d.message.contains("static accessors are not supported")),
12960            "got: {diags:?}",
12961        );
12962    }
12963
12964    #[test]
12965    fn static_constructor_is_rejected() {
12966        let (_ast, diags) = parse_str("class C { static constructor() {} }");
12967        assert!(
12968            diags
12969                .iter()
12970                .any(|d| d.message.contains("a constructor cannot be `static`")),
12971            "got: {diags:?}",
12972        );
12973    }
12974
12975    #[test]
12976    fn optional_static_field_is_rejected() {
12977        let (_ast, diags) = parse_str("class C { static x?: number; }");
12978        assert!(
12979            diags
12980                .iter()
12981                .any(|d| d.message.contains("a static field cannot be optional")),
12982            "got: {diags:?}",
12983        );
12984    }
12985
12986    #[test]
12987    fn async_function_names_the_synchronous_rewrite() {
12988        let (_ast, diags) = parse_str(
12989            "async function main(): Promise<string> {\n  const x = await f();\n  return x;\n}",
12990        );
12991        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
12992        assert_eq!(
12993            messages,
12994            [
12995                "`async` functions are not supported",
12996                "`await` is not supported"
12997            ],
12998            "{diags:?}"
12999        );
13000        assert_eq!(
13001            diags[0].help,
13002            [
13003                "calls return their value directly: remove `async`, write the return type `T` \
13004              instead of `Promise<T>`, and drop `await`"
13005            ]
13006        );
13007        assert_eq!(
13008            diags[1].help,
13009            ["calls return their value directly: remove `await`"]
13010        );
13011    }
13012
13013    #[test]
13014    fn async_is_rejected_on_every_function_form() {
13015        for source in [
13016            "export async function f(): void {}",
13017            "const f = async (): void => {};",
13018            "const f = async x => x;",
13019            "const f = async function (): void {};",
13020            "class C { async run(): void {} }",
13021            "class C { static async run(): void {} }",
13022        ] {
13023            let (_ast, diags) = parse_str(source);
13024            assert_eq!(
13025                diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13026                ["`async` functions are not supported"],
13027                "{source}: {diags:?}"
13028            );
13029        }
13030    }
13031
13032    #[test]
13033    fn await_before_a_template_is_reported() {
13034        for source in ["const a = await `hi`;", "const a = await `a${1}b`;"] {
13035            let (_ast, diags) = parse_str(source);
13036            let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13037            assert_eq!(
13038                messages,
13039                ["`await` is not supported"],
13040                "{source}: {diags:?}"
13041            );
13042        }
13043    }
13044
13045    #[test]
13046    fn async_and_await_stay_ordinary_names() {
13047        for source in [
13048            "const async = 1; const y = async + 1;",
13049            "function async(n: number): number { return n; } async(1);",
13050            "const await = 2; const z = await * 2;",
13051            "const await = (n: number): number => n; const a = await(1);",
13052            "const await = [1, 2]; const b = await[0];",
13053            "const async = { k: 1 }; const c = \"k\" in async; const d = async in ({ o: 1 });",
13054            "const async: unknown = 1; const n = async as (number);",
13055            "const await: unknown = 1; const m = await as number;",
13056            "const await = 1; const t = `${await}`;",
13057            "class C { async(): void {} async: number = 1; }",
13058        ] {
13059            let (_ast, diags) = parse_str(source);
13060            assert!(diags.is_empty(), "{source}: {diags:?}");
13061        }
13062    }
13063
13064    #[test]
13065    fn an_async_object_literal_method_names_the_rewrite_once() {
13066        assert_eq!(
13067            crate::typechecker::rules::test_util::compile_messages(
13068                "function main(): string {\n  const o = { async run(): string { return \"a\"; } };\n  return o.run();\n}\n"
13069            ),
13070            ["`async` functions are not supported"]
13071        );
13072        let (_ast, diags) = parse_str("const o = { async run(): string { return \"a\"; } };");
13073        assert_eq!(
13074            diags[0].help,
13075            [
13076                "calls return their value directly: remove `async`, write the return type `T` \
13077              instead of `Promise<T>`, and drop `await`"
13078            ]
13079        );
13080    }
13081
13082    #[test]
13083    fn await_in_an_async_object_literal_method_is_reported_too() {
13084        let (_ast, diags) =
13085            parse_str("const o = { async run(): string { const x = await f(); return x; } };");
13086        let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13087        assert_eq!(
13088            messages,
13089            [
13090                "`async` functions are not supported",
13091                "`await` is not supported"
13092            ],
13093            "{diags:?}"
13094        );
13095    }
13096
13097    #[test]
13098    fn every_async_member_form_reports_async() {
13099        for source in [
13100            "const o = { async *run(): void {} };",
13101            "class C { async *run(): void {} }",
13102            "const o = { async [k](): void {} };",
13103            "const o = { async in(): void {} };",
13104            "class C { async as(): void {} }",
13105        ] {
13106            let (_ast, diags) = parse_str(source);
13107            assert_eq!(
13108                diags[0].message, "`async` functions are not supported",
13109                "{source}: {diags:?}"
13110            );
13111        }
13112    }
13113
13114    #[test]
13115    fn async_string_key_methods_report_only_async() {
13116        for source in [
13117            "const o = { async \"s\"(): string { return \"a\"; } };",
13118            "class C { async \"s\"(): string { return \"a\"; } }",
13119        ] {
13120            let (_ast, diags) = parse_str(source);
13121            assert_eq!(
13122                diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13123                ["`async` functions are not supported"],
13124                "{source}: {diags:?}"
13125            );
13126        }
13127    }
13128
13129    #[test]
13130    fn async_before_a_line_break_is_a_member_of_its_own() {
13131        for source in [
13132            "class C {\n  async\n  run(): void {}\n}",
13133            "const o = { async\n run(): void {} };",
13134        ] {
13135            let (_ast, diags) = parse_str(source);
13136            assert!(
13137                diags
13138                    .iter()
13139                    .all(|d| d.message != "`async` functions are not supported"),
13140                "{source}: {diags:?}"
13141            );
13142        }
13143    }
13144
13145    #[test]
13146    fn async_stays_an_object_literal_key() {
13147        let messages = crate::typechecker::rules::test_util::compile_messages(
13148            "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",
13149        );
13150        assert!(messages.is_empty(), "{messages:?}");
13151    }
13152
13153    #[test]
13154    fn for_await_names_the_rewrite_once() {
13155        assert_eq!(
13156            crate::typechecker::rules::test_util::compile_messages(
13157                "import * as fs from \"submilli:fs\";\nfunction main(): string {\n  for await (const e of fs.list(\".\", false)) { }\n  return \"x\";\n}\n"
13158            ),
13159            ["`for await` is not supported"]
13160        );
13161        let (ast, diags) = parse_str("for await (const e of [1]) { }");
13162        assert_eq!(
13163            diags[0].help,
13164            ["calls return their values directly: remove `await` and write `for (const e of …)`"]
13165        );
13166        assert_eq!(ast.top_level.len(), 1, "the loop is still read");
13167    }
13168
13169    #[test]
13170    fn a_broken_switch_leaves_later_declarations_at_the_top_level() {
13171        assert_eq!(
13172            crate::typechecker::rules::test_util::compile_messages(
13173                "function g(): void { switch (1) { case 1 } }\nclass C {}\nfunction main() { return \"x\"; }\n"
13174            ),
13175            [
13176                "expected `:` after `case` label",
13177                "expected expression",
13178                "expected `:` and return type"
13179            ]
13180        );
13181    }
13182
13183    #[test]
13184    fn every_switch_arm_error_restores_the_nesting() {
13185        for switch in [
13186            "switch (1) { case 1 }",
13187            "switch (1) { case : break; }",
13188            "switch (1) { default: break; default: break; }",
13189            "switch (1) { default }",
13190            "switch (1) { x; }",
13191            "switch (1) { case 1: }",
13192        ] {
13193            let source = format!("function g(): void {{ {switch} }}\nclass C {{}}\n");
13194            let (_ast, diags) = parse_str(&source);
13195            assert!(
13196                diags.iter().all(|d| !d.message.contains("top level")),
13197                "{switch}: {diags:?}"
13198            );
13199        }
13200    }
13201
13202    #[test]
13203    fn protected_member_is_rejected() {
13204        let (_ast, diags) = parse_str("class C { protected x: number; }");
13205        assert!(
13206            diags
13207                .iter()
13208                .any(|d| d.message.contains("`protected` is not supported")),
13209            "got: {diags:?}",
13210        );
13211    }
13212
13213    #[test]
13214    fn abstract_member_is_rejected() {
13215        let (_ast, diags) = parse_str("class C { abstract run(): void; }");
13216        assert!(
13217            diags
13218                .iter()
13219                .any(|d| d.message.contains("abstract classes are not supported")),
13220            "got: {diags:?}",
13221        );
13222    }
13223
13224    #[test]
13225    fn getter_parses_as_accessor() {
13226        let (ast, diags) = parse_str("class C { get value(): number { return 1; } }");
13227        assert!(diags.is_empty(), "got: {diags:?}");
13228        let crate::ClassMember::Accessor { kind, name, .. } = &class_members(&ast)[0] else {
13229            panic!("expected an accessor member");
13230        };
13231        assert_eq!(*kind, crate::AccessorKind::Get);
13232        assert_eq!(name.name, "value");
13233    }
13234
13235    #[test]
13236    fn getter_without_return_type_is_rejected() {
13237        let (ast, diags) = parse_str("class C { get value() { return 1; } }");
13238        assert_eq!(diags.len(), 1, "got: {diags:?}");
13239        assert_eq!(diags[0].message, "expected `:` and return type");
13240        assert_eq!(diags[0].help, vec!["get name(): T { … }".to_string()]);
13241        assert!(
13242            matches!(
13243                &class_members(&ast)[0],
13244                crate::ClassMember::Accessor {
13245                    return_type: None,
13246                    ..
13247                }
13248            ),
13249            "the getter is still recovered as a member"
13250        );
13251    }
13252
13253    #[test]
13254    fn setter_parses_as_accessor() {
13255        let (ast, diags) = parse_str("class C { set value(v: number) {} }");
13256        assert!(diags.is_empty(), "got: {diags:?}");
13257        let crate::ClassMember::Accessor { kind, param, .. } = &class_members(&ast)[0] else {
13258            panic!("expected an accessor member");
13259        };
13260        assert_eq!(*kind, crate::AccessorKind::Set);
13261        assert!(param.is_some(), "setter should have a parameter");
13262    }
13263
13264    #[test]
13265    fn param_property_modifier_outside_constructor_is_rejected() {
13266        let (_ast, diags) = parse_str("function f(public x: number): void {}");
13267        assert!(
13268            diags.iter().any(|d| d
13269                .message
13270                .contains("parameter properties are only allowed in a constructor")),
13271            "got: {diags:?}",
13272        );
13273    }
13274
13275    #[test]
13276    fn constructor_return_type_is_rejected() {
13277        let (_ast, diags) = parse_str("class C { constructor(): void {} }");
13278        assert!(
13279            diags.iter().any(|d| d
13280                .message
13281                .contains("constructor cannot declare a return type")),
13282            "got: {diags:?}",
13283        );
13284    }
13285
13286    #[test]
13287    fn field_initializer_without_type_is_rejected() {
13288        let (_ast, diags) = parse_str("class C { x = 1; }");
13289        assert!(
13290            diags
13291                .iter()
13292                .any(|d| d.message.contains("class fields require a type annotation")),
13293            "got: {diags:?}",
13294        );
13295    }
13296
13297    #[test]
13298    fn this_outside_method_is_left_to_typechecker() {
13299        let (_ast, diags) = parse_str("function f(): string { return this.name; }");
13300        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13301    }
13302
13303    #[test]
13304    fn super_outside_method_is_rejected() {
13305        let (_ast, diags) = parse_str("function f(): void { super(1); }");
13306        assert!(
13307            diags.iter().any(|d| d
13308                .message
13309                .contains("`super` is only valid inside a class method or constructor body")),
13310            "got: {diags:?}",
13311        );
13312    }
13313
13314    #[test]
13315    fn this_inside_method_is_accepted_by_parser() {
13316        let (_ast, diags) = parse_str("class C { x: number; read(): number { return this.x; } }");
13317        assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13318    }
13319    #[test]
13320    fn parse_void_expression_preserves_operand_and_precedence() {
13321        let (ast, diags) = parse_str("void effect() === undefined;");
13322        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13323        let expression = expr_of_single_stmt(&ast);
13324        let crate::ExprKind::Binary { lhs, rhs, .. } = expression.kind else {
13325            panic!("expected equality")
13326        };
13327        let crate::ExprKind::Void { operand } = ast.try_expr(lhs).unwrap().kind else {
13328            panic!("expected void")
13329        };
13330        assert!(matches!(
13331            ast.try_expr(operand).unwrap().kind,
13332            crate::ExprKind::Call { .. }
13333        ));
13334        assert!(matches!(
13335            ast.try_expr(rhs).unwrap().kind,
13336            crate::ExprKind::Identifier(_)
13337        ));
13338    }
13339
13340    #[test]
13341    fn parse_void_newline_and_object_operands() {
13342        for source in [
13343            "void\n0;",
13344            "void { value: 1 };",
13345            "void\n{ value: 1 };",
13346            "void /x/;",
13347            "void void 0;",
13348        ] {
13349            let (_, diags) = parse_str(source);
13350            assert!(diags.is_empty(), "{source}: {diags:?}");
13351        }
13352        let (_, diags) = parse_str("void; 0;");
13353        assert!(
13354            !diags.is_empty(),
13355            "an explicit semicolon cannot split void from its operand"
13356        );
13357    }
13358
13359    #[test]
13360    fn parse_optional_parameters_and_defaults_in_callable_forms() {
13361        for source in [
13362            "function f(x = 1): number { return x; }",
13363            "function f(x: number = 1, y: number): number { return y; }",
13364            "function f({ x } = { x: 1 }): number { return x; }",
13365            "const f = (x?: number) => x;",
13366            "const f = (x: number = effect()) => x;",
13367            "const f = function(x: number = effect()) { return x; };",
13368            "const f = { m(x: number = effect()): number { return x; } };",
13369            "const f = ([x = 1]: number[] = values) => x;",
13370            "let f: (x?: number, ...rest: string[]) => void = value;",
13371            "let f: { method?(x?: number): void } = value;",
13372            "function f(x: number = 1, y?: number): void {}",
13373        ] {
13374            let (_, diags) = parse_str(source);
13375            assert!(diags.is_empty(), "{source}: {diags:?}");
13376        }
13377    }
13378
13379    #[test]
13380    fn parse_invalid_optional_parameters() {
13381        for source in [
13382            "function f(x?: number, y: number): void {}",
13383            "const f = (x?: number, y: number) => y;",
13384            "let f: (x?: number, y: number) => void = value;",
13385            "function f(...x?: number[]): void {}",
13386            "function f(x?: number = 1): void {}",
13387            "const f = (x?: number = 1) => x;",
13388            "function f({x}?: T): void {}",
13389        ] {
13390            let (_, diags) = parse_str(source);
13391            assert!(!diags.is_empty(), "expected diagnostic for {source}");
13392        }
13393    }
13394
13395    #[test]
13396    fn parse_optional_tuple_forms_and_order() {
13397        for source in [
13398            "let x: [number, string?] = value;",
13399            "let x: [x?: number, y?: string] = value;",
13400            "let x: readonly [(number | null)?, string?] = value;",
13401        ] {
13402            let (_, diags) = parse_str(source);
13403            assert!(diags.is_empty(), "{source}: {diags:?}");
13404        }
13405        for source in [
13406            "let x: [number?, string] = value;",
13407            "let x: [x: number?] = value;",
13408        ] {
13409            let (_, diags) = parse_str(source);
13410            assert!(!diags.is_empty(), "expected diagnostic for {source}");
13411        }
13412    }
13413
13414    #[test]
13415    fn parse_array_pattern_defaults_preserve_holes() {
13416        let (ast, diags) = parse_str("const [, a = effect(), b] = values;");
13417        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13418        let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
13419            panic!("expected pattern")
13420        };
13421        let crate::Binding::Array {
13422            elems, defaults, ..
13423        } = binding
13424        else {
13425            panic!("expected array")
13426        };
13427        assert_eq!(elems.len(), 3);
13428        assert_eq!(defaults.len(), elems.len());
13429        assert!(elems[0].is_none());
13430        assert!(defaults[0].is_none());
13431        assert!(defaults[1].is_some());
13432        assert!(defaults[2].is_none());
13433    }
13434
13435    #[test]
13436    fn parse_optional_class_method_forms() {
13437        let (ast, diags) =
13438            parse_str("class C { declared?(x?: number): void; implemented?(): void\n{} }");
13439        assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13440        let members = class_members(&ast);
13441        assert!(matches!(
13442            &members[0],
13443            crate::ClassMember::Field {
13444                optional: true,
13445                initializer: None,
13446                ty: crate::TypeAnnotation {
13447                    kind: crate::TypeAnnotationKind::Function { .. },
13448                    ..
13449                },
13450                ..
13451            }
13452        ));
13453        assert!(matches!(
13454            &members[1],
13455            crate::ClassMember::Method { optional: true, .. }
13456        ));
13457    }
13458    #[test]
13459    fn defaults_retain_their_callable_receiver_scope() {
13460        for source in [
13461            "class C { value: number = 1; method(value: number = this.value): number { return value; } }",
13462            "class C { constructor(value: number = this.value) {} value: number = 1; }",
13463            "const f = function(this: { value: number }, value: number = this.value): number { return value; };",
13464        ] {
13465            let (ast, diagnostics) = parse_str(source);
13466            assert!(diagnostics.is_empty(), "{source}: {diagnostics:?}");
13467            assert!(
13468                ast.source_expressions()
13469                    .iter()
13470                    .any(|expr| matches!(expr.kind, ExprKind::This))
13471            );
13472            assert!(
13473                !ast.source_expressions()
13474                    .iter()
13475                    .any(|expr| matches!(expr.kind, ExprKind::ThisOutsideReceiver))
13476            );
13477        }
13478    }
13479}