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;
11const MAX_PARSE_DEPTH: usize = 128;
14
15const 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#[derive(Clone, Copy, PartialEq)]
36enum TypePos {
37 Anywhere,
38 ArrowReturn,
39}
40
41pub fn parse(source: &str, tokens: Vec<Token>, file: FileId) -> (Ast, Vec<Diagnostic>) {
43 match parse_checked(source, tokens, file) {
44 Ok(result) => result,
45 Err(error) => (Ast::new(), error.into_diagnostics(file)),
46 }
47}
48
49pub fn parse_checked(
50 source: &str,
51 tokens: Vec<Token>,
52 file: FileId,
53) -> Result<(Ast, Vec<Diagnostic>), CompileError> {
54 crate::source::SourceError::check_source_len(source.len())
55 .map_err(|error| error.into_compiler_failure(CompilerStage::Parse))?;
56 validate_token_stream(source, &tokens, file).map_err(|message| CompilerFailure::Internal {
57 stage: CompilerStage::Parse,
58 span: None,
59 message: message.into(),
60 })?;
61 let eof = tokens
62 .last()
63 .cloned()
64 .unwrap_or_else(|| Token::new(TokenKind::Eof, Span::at(file)));
65 let mut p = Parser {
66 source,
67 tokens,
68 split_type_tokens: Vec::new(),
69 file,
70 pos: 0,
71 ast: Ast::new(),
72 diagnostics: Vec::new(),
73 block_depth: 0,
74 class_member_body_depth: 0,
75 function_expression_body_depth: 0,
76 recursion_depth: 0,
77 recursion_limit_span: None,
78 resume_at_next_member: false,
79 last_as_type_end: None,
80 trailing_declarators: Vec::new(),
81 eof,
82 fatal: None,
83 };
84 p.parse_program();
85 if p.fatal.is_none() {
86 p.reject_use_strict_with_non_simple_params();
87 }
88 if let Some(span) = p.recursion_limit_span {
89 p.fatal = Some(CompilerFailure::Limit {
90 stage: CompilerStage::Parse,
91 span: Some(span),
92 message: "parser recursion limit exceeded".into(),
93 help: vec!["simplify nested syntax or split it into separate declarations".into()],
94 });
95 }
96 if let Some(fatal) = p.fatal {
97 return Err(CompileError {
98 diagnostics: p.diagnostics,
99 fatal: Some(fatal),
100 });
101 }
102 p.ast
103 .validate_source(source, file)
104 .map_err(|error| CompileError {
105 diagnostics: p.diagnostics.clone(),
106 fatal: Some(error.into_compiler_failure(CompilerStage::Parse)),
107 })?;
108 crate::tree_height::check_syntax(&p.ast).map_err(|fatal| CompileError {
109 diagnostics: p.diagnostics.clone(),
110 fatal: Some(fatal),
111 })?;
112 Ok((p.ast, p.diagnostics))
113}
114
115fn validate_token_stream(source: &str, tokens: &[Token], file: FileId) -> Result<(), &'static str> {
116 if !matches!(tokens.last().map(|token| &token.kind), Some(TokenKind::Eof)) {
117 return Err("token stream must end with EOF");
118 }
119 let mut previous = 0;
120 for (index, token) in tokens.iter().enumerate() {
121 let span = token.span;
122 if span.file != file
123 || span.start < previous
124 || span.start > span.end
125 || source.get(span.start as usize..span.end as usize).is_none()
126 {
127 return Err("token span is outside its source or out of order");
128 }
129 if matches!(token.kind, TokenKind::Eof) && index != tokens.len() - 1 {
130 return Err("tokens follow EOF");
131 }
132 if let Some(doc) = &token.leading_doc {
133 let span = doc.span;
134 if span.file != file
135 || span.start > span.end
136 || span.end > token.span.start
137 || source.get(span.start as usize..span.end as usize) != Some(doc.text.as_str())
138 || !doc.text.starts_with("/**")
139 || !doc.text.ends_with("*/")
140 {
141 return Err("documentation metadata does not match its source");
142 }
143 }
144 previous = span.start;
145 }
146 Ok(())
147}
148
149pub(crate) struct Parser<'a> {
150 source: &'a str,
151 tokens: Vec<Token>,
152 split_type_tokens: Vec<(usize, Token)>,
153 file: FileId,
154 pos: usize,
155 ast: Ast,
156 diagnostics: Vec<Diagnostic>,
157 block_depth: u32,
159 class_member_body_depth: u32,
161 function_expression_body_depth: u32,
162 recursion_depth: usize,
163 recursion_limit_span: Option<Span>,
164 resume_at_next_member: bool,
168 last_as_type_end: Option<u32>,
172 trailing_declarators: Vec<StmtId>,
174 eof: Token,
175 fatal: Option<CompilerFailure>,
176}
177
178impl<'a> Parser<'a> {
179 fn span(&self, start: u32, end: u32) -> Span {
180 Span {
181 file: self.file,
182 start,
183 end,
184 }
185 }
186
187 fn type_name(&mut self, span: Span) -> Option<Ident> {
188 match span.text(self.source, self.file) {
189 Ok(name) => Some(Ident {
190 name: name.to_string(),
191 span,
192 }),
193 Err(error) => {
194 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
195 None
196 }
197 }
198 }
199
200 fn parse_program(&mut self) {
201 while !self.is_at_eof() {
202 if self.fatal.is_some() || self.error_count() >= MAX_ERRORS {
203 break;
204 }
205 if matches!(self.peek().kind, TokenKind::Semicolon) {
206 self.advance();
207 continue;
208 }
209 let pos_before = self.pos;
210 if let Some(id) = self.parse_statement() {
211 self.ast.top_level.push(id);
212 let trailing = self.take_trailing_declarators();
213 self.ast.top_level.extend(trailing);
214 } else {
215 if self.fatal.is_some() {
216 break;
217 }
218 if self.pos != pos_before && self.peek_starts_top_level_item() {
223 continue;
224 }
225 self.recover();
226 if self.pos != pos_before && matches!(self.peek().kind, TokenKind::RightBrace) {
227 self.advance();
228 continue;
229 }
230 if self.pos == pos_before && !self.is_at_eof() {
232 self.advance();
233 }
234 }
235 }
236 }
237
238 fn parse_statement(&mut self) -> Option<StmtId> {
239 self.trailing_declarators.clear();
240 self.with_recursion_limit(Self::parse_statement_inner)
241 }
242
243 fn parse_statement_inner(&mut self) -> Option<StmtId> {
244 self.reject_async_modifier();
245 match self.peek().kind {
246 TokenKind::Let => self.parse_let_or_const(false),
247 TokenKind::Const => self.parse_let_or_const(true),
248 TokenKind::Function => self.parse_function_decl(),
249 TokenKind::Class => self.parse_class_decl(),
250 TokenKind::Interface => self.parse_interface_decl(),
251 TokenKind::Enum => self.parse_enum_decl(),
252 TokenKind::Identifier if self.at_type_alias_head() => self.parse_type_alias_decl(),
253 TokenKind::Export => self.parse_export(),
254 TokenKind::Import => self.parse_import_decl(),
255 TokenKind::If => self.parse_if(),
256 TokenKind::While => self.parse_while(),
257 TokenKind::Do => self.parse_do_while(),
258 TokenKind::For => self.parse_for(),
259 TokenKind::Switch => self.parse_switch(),
260 TokenKind::Break => self.parse_break(),
261 TokenKind::Continue => self.parse_continue(),
262 TokenKind::Return => self.parse_return(),
263 TokenKind::Throw => self.parse_throw(),
264 TokenKind::Try => self.parse_try(),
265 TokenKind::LeftBrace => self.parse_block(),
266 _ => self.parse_expression_statement(),
267 }
268 }
269
270 fn parse_expression_statement(&mut self) -> Option<StmtId> {
271 let expr_id = self.parse_expression()?;
272 let expr_span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
273 if !self.at_statement_end() {
274 let what = if matches!(
275 parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.kind,
276 ExprKind::Assign { .. }
277 ) {
278 "assignment"
279 } else {
280 "expression"
281 };
282 self.error_at_peek(format!("expected `;` after {what}"));
283 return None;
284 }
285 let end = self.finish_statement();
286 let span = self.span(expr_span.start, end);
287 let kind = self.statement_kind_for(expr_id)?;
288 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
289 }
290
291 fn statement_kind_for(&mut self, expr_id: ExprId) -> Option<StmtKind> {
294 let ExprKind::Assign {
295 target,
296 op,
297 op_span,
298 value,
299 } = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?
300 .kind
301 .clone()
302 else {
303 return Some(StmtKind::Expr(expr_id));
304 };
305 Some(
306 match (
307 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?
308 .kind
309 .clone(),
310 op,
311 ) {
312 (ExprKind::Identifier(target), None) => StmtKind::Assign { target, value },
313 (ExprKind::Identifier(target), Some(op)) => StmtKind::CompoundAssign {
314 target,
315 op,
316 op_span,
317 value,
318 },
319 (ExprKind::FieldAccess { receiver, name }, None) => StmtKind::AssignField {
320 receiver,
321 field_name: name,
322 value,
323 },
324 (ExprKind::FieldAccess { receiver, name }, Some(op)) => {
325 StmtKind::CompoundAssignField {
326 receiver,
327 field_name: name,
328 op,
329 op_span,
330 value,
331 }
332 }
333 (ExprKind::IndexAccess { receiver, index }, None) => StmtKind::AssignIndex {
334 receiver,
335 index,
336 value,
337 },
338 (ExprKind::IndexAccess { receiver, index }, Some(op)) => {
339 StmtKind::CompoundAssignIndex {
340 receiver,
341 index,
342 op,
343 op_span,
344 value,
345 }
346 }
347 _ => return self.invariant_failure("invalid assignment target"),
348 },
349 )
350 }
351
352 fn parse_let_or_const(&mut self, is_const: bool) -> Option<StmtId> {
357 let mut doc = self.take_leading_doc();
358 let kw = self.advance();
359
360 let mut declarators = Vec::new();
361 let mut start = kw.span.start;
362 loop {
363 let Some(kind) = self.parse_declarator(is_const, doc.take()) else {
364 self.discard_detached_patterns(&declarators);
365 return None;
366 };
367 if !matches!(self.peek().kind, TokenKind::Comma) {
368 if !self.at_statement_end() {
369 self.error_at_peek("expected `;` after declaration");
370 self.discard_detached_patterns(&declarators);
371 return None;
372 }
373 let end = self.finish_statement();
374 declarators.push(self.push_declarator(kind, start, end)?);
375 break;
376 }
377 let end = self.prev_token_end();
378 declarators.push(self.push_declarator(kind, start, end)?);
379 self.advance();
380 start = self.peek().span.start;
381 }
382
383 let first = declarators.remove(0);
384 self.trailing_declarators = declarators;
385 Some(first)
386 }
387
388 fn push_declarator(&mut self, kind: StmtKind, start: u32, end: u32) -> Option<StmtId> {
389 let span = self.span(start, end);
390 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
391 }
392
393 fn take_trailing_declarators(&mut self) -> Vec<StmtId> {
397 std::mem::take(&mut self.trailing_declarators)
398 }
399
400 fn parse_declarator(
402 &mut self,
403 is_const: bool,
404 doc: Option<crate::DocComment>,
405 ) -> Option<StmtKind> {
406 let binding = match self.peek().kind {
407 TokenKind::LeftBrace | TokenKind::LeftBracket => Some(self.parse_binding()?),
408 _ => None,
409 };
410
411 let name = if binding.is_none() {
412 let name_tok =
413 self.expect_binding_identifier("expected identifier after `let`/`const`")?;
414 Some(self.ident_from_token(&name_tok))
415 } else {
416 None
417 };
418
419 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
420 self.advance();
421 Some(self.parse_type_annotation()?)
422 } else {
423 None
424 };
425
426 let implicit =
427 !is_const && matches!(self.peek().kind, TokenKind::Semicolon | TokenKind::Comma);
428 let value = if let (true, Some(name)) = (implicit, &name) {
429 let value = parse_arena_result(
431 self.ast.try_push_expr(Expr {
432 kind: ExprKind::Identifier(Ident {
433 name: "undefined".to_string(),
434 span: name.span,
435 }),
436 span: name.span,
437 }),
438 &mut self.fatal,
439 )?;
440 self.ast.implicit_initializers.insert(value);
441 self.ast.synthetic_undefined.insert(value);
442 value
443 } else {
444 if !matches!(self.peek().kind, TokenKind::Equals) {
445 let (msg, help) = if is_const {
446 (
447 "`const` declaration requires an initializer",
448 vec!["const x: T = expr;".to_string()],
449 )
450 } else {
451 (
452 "`let` declaration requires an initializer",
453 vec![
454 "let x: T = expr;".to_string(),
455 "or declare one that starts empty: `let x: T | undefined;`".to_string(),
456 ],
457 )
458 };
459 self.error_at_peek_with_help(msg, help);
460 return None;
461 }
462 self.advance();
463 self.parse_expression()?
464 };
465
466 Some(match (binding, name) {
467 (Some(binding), _) => {
468 if is_const {
469 StmtKind::ConstPattern {
470 binding,
471 ty,
472 value,
473 doc,
474 }
475 } else {
476 StmtKind::LetPattern {
477 binding,
478 ty,
479 value,
480 doc,
481 }
482 }
483 }
484 (None, Some(name)) => {
485 if is_const {
486 StmtKind::Const {
487 name,
488 ty,
489 value,
490 doc,
491 }
492 } else {
493 StmtKind::Let {
494 name,
495 ty,
496 value,
497 doc,
498 }
499 }
500 }
501 (None, None) => return self.invariant_failure("either binding or name must be Some"),
502 })
503 }
504
505 fn parse_binding(&mut self) -> Option<Binding> {
507 match self.peek().kind {
508 TokenKind::LeftBrace => self.parse_object_binding(),
509 TokenKind::LeftBracket => self.parse_array_binding(),
510 _ => {
511 self.error_at_peek("expected `{` or `[` to start a destructuring pattern");
512 None
513 }
514 }
515 }
516
517 fn parse_object_binding(&mut self) -> Option<Binding> {
518 let open = self.advance();
519 let mut fields: Vec<ObjectPatternField> = Vec::new();
520 let mut rest: Option<Ident> = None;
521
522 if matches!(self.peek().kind, TokenKind::RightBrace) {
523 self.error_at_peek("empty object destructuring pattern");
524 return None;
525 }
526
527 loop {
528 if matches!(self.peek().kind, TokenKind::DotDotDot) {
529 let dots = self.advance();
530 let name_tok = self.expect_binding_identifier("expected identifier after `...`")?;
531 let name = self.ident_from_token(&name_tok);
532 if !matches!(self.peek().kind, TokenKind::RightBrace) {
533 self.error_at_peek_with_help(
534 "rest element must be the last element in a destructuring pattern",
535 vec!["move `...` after every other binding".to_string()],
536 );
537 return None;
538 }
539 rest = Some(Ident {
540 name: name.name,
541 span: self.span(dots.span.start, name.span.end),
542 });
543 break;
544 }
545
546 let source_tok = self.expect_property_name("expected field name in object pattern")?;
547 let source = self.ident_from_token(&source_tok);
548 let field_span_start = source.span.start;
549
550 let (local, field_end) = if matches!(self.peek().kind, TokenKind::Colon) {
551 self.advance();
552 match self.peek().kind {
553 TokenKind::LeftBrace | TokenKind::LeftBracket => {
554 self.error_at_peek_with_help(
555 "nested destructuring is not supported",
556 vec![
557 "destructure once and access nested fields explicitly: const { a } = obj; const inner = a.b;"
558 .to_string(),
559 ],
560 );
561 return None;
562 }
563 _ => {
564 let local_tok = self.expect_binding_identifier(
565 "expected identifier after `:` in pattern",
566 )?;
567 let local = self.ident_from_token(&local_tok);
568 let end = local.span.end;
569 (local, end)
570 }
571 }
572 } else {
573 if !matches!(source_tok.kind, TokenKind::Identifier) {
574 self.error_at_peek("expected `:` after keyword field name in object pattern");
575 return None;
576 }
577 self.reject_strict_mode_reserved_word(&source_tok);
578 self.reject_eval_or_arguments(source_tok.span);
579 let end = source.span.end;
580 (source.clone(), end)
581 };
582
583 let default = if matches!(self.peek().kind, TokenKind::Equals) {
584 self.advance();
585 Some(self.parse_expression()?)
586 } else {
587 None
588 };
589 let field_end = match default {
590 Some(value) => {
591 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?
592 .span
593 .end
594 }
595 None => field_end,
596 };
597
598 fields.push(ObjectPatternField {
599 source,
600 local,
601 default,
602 span: self.span(field_span_start, field_end),
603 });
604
605 match self.peek().kind {
606 TokenKind::Comma => {
607 self.advance();
608 if matches!(self.peek().kind, TokenKind::RightBrace) {
609 break;
610 }
611 }
612 TokenKind::RightBrace => break,
613 _ => {
614 self.error_at_peek("expected `,` or `}` in object pattern");
615 return None;
616 }
617 }
618 }
619
620 if !matches!(self.peek().kind, TokenKind::RightBrace) {
621 self.error_at_peek("expected `}` to close object pattern");
622 return None;
623 }
624 let close = self.advance();
625
626 Some(Binding::Object {
627 fields,
628 rest,
629 span: self.span(open.span.start, close.span.end),
630 })
631 }
632
633 fn parse_array_binding(&mut self) -> Option<Binding> {
634 let open = self.advance();
635 let mut elems: Vec<Option<Ident>> = Vec::new();
636 let mut defaults = Vec::new();
637 let mut rest: Option<Ident> = None;
638
639 if matches!(self.peek().kind, TokenKind::RightBracket) {
640 self.error_at_peek("empty array destructuring pattern");
641 return None;
642 }
643
644 loop {
645 match self.peek().kind {
646 TokenKind::Comma => {
647 elems.push(None);
648 defaults.push(None);
649 self.advance();
650 if matches!(self.peek().kind, TokenKind::RightBracket) {
651 break;
652 }
653 continue;
654 }
655 TokenKind::RightBracket => break,
656 TokenKind::DotDotDot => {
657 let dots = self.advance();
658 let name_tok =
659 self.expect_binding_identifier("expected identifier after `...`")?;
660 let name = self.ident_from_token(&name_tok);
661 if !matches!(self.peek().kind, TokenKind::RightBracket) {
662 self.error_at_peek_with_help(
663 "rest element must be the last element in a destructuring pattern",
664 vec!["move `...` after every other binding".to_string()],
665 );
666 return None;
667 }
668 rest = Some(Ident {
669 name: name.name,
670 span: self.span(dots.span.start, name.span.end),
671 });
672 break;
673 }
674 TokenKind::LeftBrace | TokenKind::LeftBracket => {
675 self.error_at_peek_with_help(
676 "nested destructuring is not supported",
677 vec![
678 "destructure once and access nested elements explicitly: const [a] = arr; const inner = a[0];"
679 .to_string(),
680 ],
681 );
682 return None;
683 }
684 _ => {
685 let name_tok =
686 self.expect_binding_identifier("expected identifier in array pattern")?;
687 let name = self.ident_from_token(&name_tok);
688
689 let default = if matches!(self.peek().kind, TokenKind::Equals) {
690 self.advance();
691 Some(self.parse_expression()?)
692 } else {
693 None
694 };
695 defaults.push(default);
696 elems.push(Some(name));
697
698 match self.peek().kind {
699 TokenKind::Comma => {
700 self.advance();
701 if matches!(self.peek().kind, TokenKind::RightBracket) {
702 break;
703 }
704 }
705 TokenKind::RightBracket => break,
706 _ => {
707 self.error_at_peek("expected `,` or `]` in array pattern");
708 return None;
709 }
710 }
711 }
712 }
713 }
714
715 if !matches!(self.peek().kind, TokenKind::RightBracket) {
716 self.error_at_peek("expected `]` to close array pattern");
717 return None;
718 }
719 let close = self.advance();
720
721 Some(Binding::Array {
722 elems,
723 defaults,
724 rest,
725 span: self.span(open.span.start, close.span.end),
726 })
727 }
728
729 fn parse_function_decl(&mut self) -> Option<StmtId> {
730 let doc = self.take_leading_doc();
731 let kw = self.advance();
732
733 let name_tok = self.expect_binding_identifier("expected function name")?;
734 let name = self.ident_from_token(&name_tok);
735 let is_main = self.block_depth == 0 && name.name == "main";
736 let decl = self.parse_function_decl_after_name(doc, kw, name);
737 if decl.is_none() && is_main {
738 self.ast.unparsed_main = true;
739 }
740 decl
741 }
742
743 fn parse_function_decl_after_name(
744 &mut self,
745 doc: Option<crate::DocComment>,
746 kw: Token,
747 name: Ident,
748 ) -> Option<StmtId> {
749 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
750 self.parse_generic_param_list()?
751 } else {
752 Vec::new()
753 };
754
755 if !matches!(self.peek().kind, TokenKind::LeftParen) {
756 self.error_at_peek("expected `(` after function name");
757 return None;
758 }
759 self.advance();
760
761 let params = self.parse_param_list(false)?;
762 self.advance(); if !matches!(self.peek().kind, TokenKind::Colon) {
765 self.error_at_peek_with_help(
766 "expected `:` and return type",
767 vec![format!(
768 "write the return type: `function {}(…): T {{ … }}`, with `void` when it returns nothing",
769 name.name
770 )],
771 );
772 if matches!(self.peek().kind, TokenKind::LeftBrace) {
775 let _ = self.parse_outer_this_boundary(Self::parse_block);
776 }
777 return None;
778 }
779 self.advance();
780 let (return_type, type_predicate) =
781 self.parse_predicate_or_return_type(TypePos::Anywhere)?;
782
783 let body = self.parse_outer_this_boundary(Self::parse_block)?;
786 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
787 .span
788 .end;
789
790 parse_arena_result(
791 self.ast.try_push_stmt(Stmt {
792 kind: StmtKind::Function {
793 name,
794 generics,
795 params,
796 return_type,
797 type_predicate,
798 body,
799 doc,
800 },
801 span: self.span(kw.span.start, body_end),
802 }),
803 &mut self.fatal,
804 )
805 }
806
807 fn parse_class_decl(&mut self) -> Option<StmtId> {
808 let doc = self.take_leading_doc();
809 let kw = self.advance();
810 if self.block_depth > 0 {
813 self.error_at_with_help(
814 kw.span,
815 "a `class` must be declared at the top level of the module",
816 vec!["move this `class` outside any block or function body".to_string()],
817 );
818 }
819 let name = self.parse_type_decl_name("a", "class")?;
820
821 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
822 self.parse_generic_param_list()?
823 } else {
824 Vec::new()
825 };
826
827 let extends = if matches!(self.peek().kind, TokenKind::Extends) {
828 self.advance();
829 Some(self.parse_type_annotation()?)
830 } else {
831 None
832 };
833
834 let mut implements: Vec<crate::TypeAnnotation> = Vec::new();
835 if matches!(self.peek().kind, TokenKind::Implements) {
836 self.advance();
837 loop {
838 implements.push(self.parse_type_annotation()?);
839 if matches!(self.peek().kind, TokenKind::Comma) {
840 self.advance();
841 continue;
842 }
843 break;
844 }
845 }
846
847 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
848 self.error_at_peek("expected `{` after class header");
849 return None;
850 }
851 self.advance();
852
853 let mut members: Vec<crate::ClassMember> = Vec::new();
854 let mut member_failed = false;
855 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
859 && !(member_failed && self.peek_starts_top_level_item())
860 {
861 if matches!(self.peek().kind, TokenKind::Semicolon) {
863 self.advance();
864 continue;
865 }
866 match self.parse_class_member() {
867 Some(member) => members.push(member),
868 None if std::mem::take(&mut self.resume_at_next_member) => member_failed = true,
869 None => return None,
870 }
871 }
872
873 if !matches!(self.peek().kind, TokenKind::RightBrace) {
874 self.error_at_peek("expected `}` to close class");
875 return None;
876 }
877 let close = self.advance();
878 if member_failed {
879 return None;
880 }
881
882 parse_arena_result(
883 self.ast.try_push_stmt(Stmt {
884 kind: StmtKind::ClassDecl {
885 name,
886 generics,
887 extends,
888 implements,
889 members,
890 doc,
891 },
892 span: self.span(kw.span.start, close.span.end),
893 }),
894 &mut self.fatal,
895 )
896 }
897
898 fn parse_class_member(&mut self) -> Option<crate::ClassMember> {
899 let doc = self.take_leading_doc();
900 let member_start = self.peek().span.start;
901
902 if self.peek_identifier_text_is("protected") {
904 let tok = self.advance();
905 self.error_at_with_help(
906 tok.span,
907 "`protected` is not supported",
908 vec!["use `private` or `public`".to_string()],
909 );
910 return None;
911 }
912 if self.peek_identifier_text_is("abstract") {
913 let tok = self.advance();
914 self.error_at_with_help(
915 tok.span,
916 "abstract classes are not supported",
917 vec!["provide a concrete implementation instead of `abstract`".to_string()],
918 );
919 return None;
920 }
921
922 let modifiers = self.parse_class_modifiers();
923 self.reject_async_member_modifier();
924
925 for (kw, kind) in [
928 ("get", crate::AccessorKind::Get),
929 ("set", crate::AccessorKind::Set),
930 ] {
931 if self.peek_identifier_text_is(kw)
932 && is_property_name(&self.peek_at(1).kind)
933 && matches!(self.peek_at(2).kind, TokenKind::LeftParen)
934 {
935 if let Some(span) = modifiers.readonly {
936 self.reject_readonly_modifier(span);
937 }
938 if let Some(span) = modifiers.static_span {
939 self.error_at_with_help(
940 span,
941 "static accessors are not supported",
942 vec!["use a static method: `static name(): T { … }`".to_string()],
943 );
944 return None;
945 }
946 return self.parse_accessor(member_start, modifiers, kind, doc);
947 }
948 }
949
950 if self.peek_identifier_text_is("constructor")
952 && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
953 {
954 if let Some(span) = modifiers.static_span {
955 self.error_at_with_help(
956 span,
957 "a constructor cannot be `static`",
958 vec![
959 "remove `static` — the constructor already belongs to the class, not \
960 instances"
961 .to_string(),
962 ],
963 );
964 return None;
965 }
966 if let Some(span) = modifiers.readonly {
967 self.error_at_with_help(
968 span,
969 "a constructor cannot be `readonly`",
970 vec!["remove `readonly`; it applies to fields".to_string()],
971 );
972 return None;
973 }
974 return self.parse_constructor(member_start, modifiers.visibility, doc);
975 }
976
977 let name = self.expect_property_ident("expected class member name")?;
978 let optional_span = if matches!(self.peek().kind, TokenKind::Question) {
979 Some(self.advance().span)
980 } else {
981 None
982 };
983 let optional = optional_span.is_some();
984
985 if matches!(self.peek().kind, TokenKind::LessThan | TokenKind::LeftParen) {
987 if let Some(span) = modifiers.readonly {
988 self.reject_readonly_modifier(span);
989 }
990 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
991 self.parse_generic_param_list()?
992 } else {
993 Vec::new()
994 };
995 if !matches!(self.peek().kind, TokenKind::LeftParen) {
996 self.error_at_peek("expected `(` to start the method parameter list");
997 return None;
998 }
999 self.advance();
1000 let params = self.parse_class_member_params(false)?;
1001 self.advance(); if !matches!(self.peek().kind, TokenKind::Colon) {
1003 self.error_at_peek_with_help(
1004 "expected `:` and return type",
1005 vec![format!(
1006 "write the return type: `{}(…): T {{ … }}`, with `void` when it returns nothing",
1007 name.name
1008 )],
1009 );
1010 if matches!(self.peek().kind, TokenKind::LeftBrace)
1012 && self.parse_class_member_body().is_some()
1013 {
1014 self.resume_at_next_member = true;
1015 }
1016 return None;
1017 }
1018 self.advance();
1019 let return_type = self.parse_type_annotation()?;
1020 if self.at_inserted_semicolon() && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1021 {
1022 self.advance();
1023 }
1024 if optional
1025 && matches!(
1026 self.peek().kind,
1027 TokenKind::Semicolon | TokenKind::RightBrace
1028 )
1029 {
1030 if !generics.is_empty() {
1031 self.error_at(
1032 name.span,
1033 "generic optional method declarations are not supported",
1034 );
1035 return None;
1036 }
1037 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1038 self.advance().span.end
1039 } else {
1040 return_type.span.end
1041 };
1042 let ty = self.method_declaration_type(
1043 params,
1044 return_type,
1045 self.span(name.span.start, end),
1046 )?;
1047 return Some(crate::ClassMember::Field {
1048 name,
1049 modifiers,
1050 optional: true,
1051 method_signature: true,
1052 ty,
1053 initializer: None,
1054 span: self.span(member_start, end),
1055 doc,
1056 });
1057 }
1058 let body = self.parse_class_member_body()?;
1059 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1060 .span
1061 .end;
1062 return Some(crate::ClassMember::Method {
1063 name,
1064 optional,
1065 modifiers,
1066 generics,
1067 params,
1068 return_type,
1069 body,
1070 span: self.span(member_start, body_end),
1071 doc,
1072 });
1073 }
1074
1075 if let Some(span) = optional_span
1077 && modifiers.static_span.is_some()
1078 {
1079 self.error_at_with_help(
1080 span,
1081 "a static field cannot be optional",
1082 vec!["a static field must be initialized; give it a value with `= …`".to_string()],
1083 );
1084 return None;
1085 }
1086 if !matches!(self.peek().kind, TokenKind::Colon) {
1087 if matches!(self.peek().kind, TokenKind::Equals) {
1088 self.error_at_peek_with_help(
1089 "class fields require a type annotation",
1090 vec!["add `: T` before the initializer, e.g. `name: string = …`".to_string()],
1091 );
1092 } else {
1093 self.error_at_peek("expected `:` and a type for the class field");
1094 }
1095 return None;
1096 }
1097 self.advance();
1098 let ty = self.parse_type_annotation()?;
1099 let initializer = if matches!(self.peek().kind, TokenKind::Equals) {
1100 self.advance();
1101 self.class_member_body_depth += 1;
1104 let init = self.parse_expression();
1105 self.class_member_body_depth -= 1;
1106 Some(init?)
1107 } else {
1108 None
1109 };
1110 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1111 self.error_at_peek("expected `;` after class field");
1112 return None;
1113 }
1114 let semi = self.advance();
1115 Some(crate::ClassMember::Field {
1116 name,
1117 modifiers,
1118 optional,
1119 method_signature: false,
1120 ty,
1121 initializer,
1122 span: self.span(member_start, semi.span.end),
1123 doc,
1124 })
1125 }
1126
1127 fn parse_constructor(
1128 &mut self,
1129 member_start: u32,
1130 visibility: crate::Visibility,
1131 doc: Option<crate::DocComment>,
1132 ) -> Option<crate::ClassMember> {
1133 self.advance(); self.advance(); let params = self.parse_class_member_params(true)?;
1136 self.advance(); if matches!(self.peek().kind, TokenKind::Colon) {
1138 let colon = self.advance();
1139 let _ = self.parse_type_annotation();
1140 self.error_at_with_help(
1141 colon.span,
1142 "a constructor cannot declare a return type",
1143 vec![
1144 "drop the `: T` — a constructor always returns the class instance".to_string(),
1145 ],
1146 );
1147 }
1148 let body = self.parse_class_member_body()?;
1149 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1150 .span
1151 .end;
1152 Some(crate::ClassMember::Constructor {
1153 visibility,
1154 params,
1155 body,
1156 span: self.span(member_start, body_end),
1157 doc,
1158 })
1159 }
1160
1161 fn parse_accessor(
1164 &mut self,
1165 member_start: u32,
1166 modifiers: crate::ClassModifiers,
1167 kind: crate::AccessorKind,
1168 doc: Option<crate::DocComment>,
1169 ) -> Option<crate::ClassMember> {
1170 self.advance(); let name = self.expect_property_ident("expected accessor name")?;
1172 self.advance(); let mut params = self.parse_class_member_params(false)?;
1174 self.advance(); let param = match kind {
1176 crate::AccessorKind::Get => {
1177 if !params.is_empty() {
1178 self.error_at(name.span, "a getter cannot declare parameters");
1179 }
1180 None
1181 }
1182 crate::AccessorKind::Set => {
1183 if params.len() != 1 {
1184 self.error_at(name.span, "a setter must declare exactly one parameter");
1185 return None;
1186 }
1187 let param = params.remove(0);
1188 if param.rest {
1191 self.error_at(
1192 param.name.span,
1193 "a setter's parameter can't be a rest parameter",
1194 );
1195 } else if param.default.is_some() {
1196 self.error_at(name.span, "a setter's parameter can't have a default value");
1197 } else if param.optional {
1198 self.error_at(param.name.span, "a setter's parameter can't be optional");
1199 }
1200 Some(Box::new(param))
1201 }
1202 };
1203 let return_type = if matches!(self.peek().kind, TokenKind::Colon) {
1204 self.advance();
1205 Some(self.parse_type_annotation()?)
1206 } else {
1207 if kind == crate::AccessorKind::Get {
1211 self.error_at_peek_with_help(
1212 "expected `:` and return type",
1213 vec!["get name(): T { … }".to_string()],
1214 );
1215 }
1216 None
1217 };
1218 let body = self.parse_class_member_body()?;
1219 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1220 .span
1221 .end;
1222 Some(crate::ClassMember::Accessor {
1223 name,
1224 modifiers,
1225 kind,
1226 param,
1227 return_type,
1228 body,
1229 span: self.span(member_start, body_end),
1230 doc,
1231 })
1232 }
1233
1234 fn parse_class_modifiers(&mut self) -> crate::ClassModifiers {
1240 let mut visibility = crate::Visibility::Public;
1241 let mut visibility_span: Option<Span> = None;
1242 let mut readonly: Option<Span> = None;
1243 let mut static_span: Option<Span> = None;
1244 loop {
1245 if self.peek_word_is_class_modifier("public")
1246 || self.peek_word_is_class_modifier("private")
1247 {
1248 let (kind, word) = if self.peek_identifier_text_is("private") {
1249 (crate::Visibility::Private, "private")
1250 } else {
1251 (crate::Visibility::Public, "public")
1252 };
1253 let tok = self.advance();
1254 if visibility_span.is_some() {
1255 self.error_at_with_help(
1256 tok.span,
1257 "a class member may have at most one visibility modifier",
1258 vec!["keep a single `public` or `private`".to_string()],
1259 );
1260 continue;
1261 }
1262 let preceding = static_span
1265 .map(|_| "static")
1266 .or(readonly.map(|_| "readonly"));
1267 if let Some(preceding) = preceding {
1268 self.error_at_with_help(
1269 tok.span,
1270 format!("`{word}` must come before `{preceding}`"),
1271 vec![format!("write `{word} {preceding} <name>`")],
1272 );
1273 }
1274 visibility = kind;
1275 visibility_span = Some(tok.span);
1276 continue;
1277 }
1278 if self.peek_word_is_class_modifier("static") {
1279 let tok = self.advance();
1280 if static_span.is_some() {
1281 self.error_at(tok.span, "duplicate `static` modifier");
1282 } else {
1283 static_span = Some(tok.span);
1284 }
1285 continue;
1286 }
1287 if self.peek_word_is_class_modifier("readonly") {
1288 let tok = self.advance();
1289 if readonly.is_some() {
1290 self.error_at(tok.span, "duplicate `readonly` modifier");
1291 } else {
1292 readonly = Some(tok.span);
1293 }
1294 continue;
1295 }
1296 break;
1297 }
1298 crate::ClassModifiers {
1299 visibility,
1300 visibility_span,
1301 readonly,
1302 static_span,
1303 }
1304 }
1305
1306 fn peek_word_is_class_modifier(&self, word: &str) -> bool {
1312 if !self.peek_identifier_text_is(word) {
1313 return false;
1314 }
1315 if word != "static" && self.line_break_after_peek() {
1316 return false;
1317 }
1318 let next = &self.peek_at(1).kind;
1319 is_property_name(next) || matches!(next, TokenKind::StringLiteral(_))
1320 }
1321
1322 fn parse_class_member_params(
1323 &mut self,
1324 allow_param_properties: bool,
1325 ) -> Option<Vec<ParamDecl>> {
1326 let saved = self.class_member_body_depth;
1327 self.class_member_body_depth = 1;
1328 let params = self.parse_param_list(allow_param_properties);
1329 self.class_member_body_depth = saved;
1330 params
1331 }
1332
1333 fn parse_class_member_body(&mut self) -> Option<StmtId> {
1334 self.class_member_body_depth += 1;
1335 let body = self.parse_block();
1336 self.class_member_body_depth -= 1;
1337 body
1338 }
1339
1340 fn parse_interface_decl(&mut self) -> Option<StmtId> {
1341 let doc = self.take_leading_doc();
1342 let kw = self.advance();
1343 let name = self.parse_type_decl_name("an", "interface")?;
1344
1345 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1346 self.parse_generic_param_list()?
1347 } else {
1348 Vec::new()
1349 };
1350
1351 let mut extends = Vec::new();
1352 if matches!(self.peek().kind, TokenKind::Extends) {
1353 self.advance();
1354 loop {
1355 extends.push(self.parse_type_annotation()?);
1356 if !matches!(self.peek().kind, TokenKind::Comma) {
1357 break;
1358 }
1359 self.advance();
1360 }
1361 }
1362 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1363 self.error_at_peek("expected `{` after interface name");
1364 return None;
1365 }
1366 self.advance();
1367
1368 let mut members: Vec<crate::InterfaceMember> = Vec::new();
1369 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
1370 && !self.peek_starts_top_level_item()
1371 {
1372 let member_doc = self.take_leading_doc();
1376 if matches!(self.peek().kind, TokenKind::LeftParen) {
1377 let open = self.advance();
1378 let params = self.parse_param_list(false)?;
1379 self.advance();
1380 if !matches!(self.peek().kind, TokenKind::Colon) {
1381 self.error_at_peek("expected `:` and return type");
1382 return None;
1383 }
1384 self.advance();
1385 let return_type = self.parse_type_annotation()?;
1386 let end = self.finish_interface_member(return_type.span.end)?;
1387 let span = self.span(open.span.start, end);
1388 let sentinel = Ident {
1389 name: "@call".to_string(),
1390 span: open.span,
1391 };
1392 members.push(crate::InterfaceMember::Method {
1393 name: sentinel,
1394 optional: false,
1395 generics: Vec::new(),
1396 params,
1397 return_type,
1398 span,
1399 doc: member_doc,
1400 });
1401 continue;
1402 }
1403 if matches!(self.peek().kind, TokenKind::LessThan) {
1404 self.error_at_peek("generic call signatures are not yet supported");
1405 return None;
1406 }
1407 let member_start = self.peek().span.start;
1408 let readonly = self.eat_readonly_property_modifier();
1409 if self.peek_is_parameterless_index_signature() {
1410 self.skip_parameterless_index_signature(member_start)?;
1411 self.finish_interface_member(self.prev_token_end())?;
1412 continue;
1413 }
1414 if matches!(self.peek().kind, TokenKind::LeftBracket) {
1415 let signature = self.parse_index_signature(readonly)?;
1416 self.finish_interface_member(signature.span.end)?;
1417 members.push(crate::InterfaceMember::IndexSignature(signature));
1418 continue;
1419 }
1420 let member_name = self.expect_property_ident("expected interface member name")?;
1421 let optional = matches!(self.peek().kind, TokenKind::Question);
1422 if optional {
1423 self.advance();
1424 }
1425 if matches!(self.peek().kind, TokenKind::Colon) {
1426 self.advance();
1427 let ty = self.parse_type_annotation()?;
1428 let end = self.finish_interface_member(ty.span.end)?;
1429 let span = self.span(member_name.span.start, end);
1430 members.push(crate::InterfaceMember::Property {
1431 name: member_name,
1432 ty,
1433 optional,
1434 readonly,
1435 span,
1436 doc: member_doc,
1437 });
1438 continue;
1439 }
1440 if readonly {
1441 self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
1442 }
1443 let m_generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1444 self.parse_generic_param_list()?
1445 } else {
1446 Vec::new()
1447 };
1448 if !matches!(self.peek().kind, TokenKind::LeftParen) {
1449 self.error_at_peek(
1450 "expected `(` to start a method signature or `:` to start a property",
1451 );
1452 return None;
1453 }
1454 self.advance();
1455 let params = self.parse_param_list(false)?;
1456 self.advance();
1457 if !matches!(self.peek().kind, TokenKind::Colon) {
1458 self.error_at_peek("expected `:` and return type");
1459 return None;
1460 }
1461 self.advance();
1462 let return_type = self.parse_type_annotation()?;
1463 let end = self.finish_interface_member(return_type.span.end)?;
1464 let span = self.span(member_name.span.start, end);
1465 if optional {
1466 if !m_generics.is_empty() {
1467 self.error_at(
1468 member_name.span,
1469 "generic optional method declarations are not supported",
1470 );
1471 return None;
1472 }
1473 let ty = self.method_declaration_type(params, return_type, span)?;
1474 members.push(crate::InterfaceMember::Property {
1475 name: member_name,
1476 ty,
1477 optional: true,
1478 readonly,
1479 span,
1480 doc: member_doc,
1481 });
1482 } else {
1483 members.push(crate::InterfaceMember::Method {
1484 name: member_name,
1485 optional,
1486 generics: m_generics,
1487 params,
1488 return_type,
1489 span,
1490 doc: member_doc,
1491 });
1492 }
1493 }
1494
1495 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1496 self.error_at_peek("expected `}` to close interface");
1497 return None;
1498 }
1499 let close = self.advance();
1500
1501 parse_arena_result(
1502 self.ast.try_push_stmt(Stmt {
1503 kind: StmtKind::InterfaceDecl {
1504 name,
1505 generics,
1506 extends,
1507 members,
1508 doc,
1509 },
1510 span: self.span(kw.span.start, close.span.end),
1511 }),
1512 &mut self.fatal,
1513 )
1514 }
1515
1516 fn method_declaration_type(
1517 &mut self,
1518 params: Vec<ParamDecl>,
1519 return_type: TypeAnnotation,
1520 span: Span,
1521 ) -> Option<TypeAnnotation> {
1522 let mut fields = Vec::new();
1523 for param in params {
1524 if param.pattern.is_some() || param.default.is_some() {
1525 self.error_at(
1526 param.name.span,
1527 "a method declaration cannot contain a parameter pattern or default value",
1528 );
1529 return None;
1530 }
1531 let Some(ty) = param.ty else {
1532 return self.invariant_failure("method parameter annotation was not retained");
1533 };
1534 fields.push(TypeAnnotationField {
1535 name: param.name,
1536 ty,
1537 optional: param.optional,
1538 readonly: false,
1539 rest: param.rest,
1540 method: false,
1541 });
1542 }
1543 Some(TypeAnnotation {
1544 kind: TypeAnnotationKind::Function {
1545 params: fields,
1546 return_type: Box::new(return_type),
1547 },
1548 span,
1549 })
1550 }
1551
1552 fn finish_interface_member(&mut self, body_end: u32) -> Option<u32> {
1553 self.finish_type_member(body_end, "expected `;`, `,`, or `}` after interface member")
1554 }
1555
1556 fn finish_type_member(&mut self, body_end: u32, message: &str) -> Option<u32> {
1562 match self.peek().kind {
1563 TokenKind::Semicolon | TokenKind::Comma => Some(self.advance().span.end),
1564 TokenKind::RightBrace => Some(body_end),
1565 _ if self.line_break_before_peek() => Some(body_end),
1566 _ => {
1567 self.error_at_peek(message);
1568 None
1569 }
1570 }
1571 }
1572
1573 fn parse_type_alias_decl(&mut self) -> Option<StmtId> {
1574 let doc = self.take_leading_doc();
1575 let kw = self.advance();
1576 let name = self.parse_type_decl_name("a", "type alias")?;
1577
1578 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1579 self.parse_generic_param_list()?
1580 } else {
1581 Vec::new()
1582 };
1583
1584 if !matches!(self.peek().kind, TokenKind::Equals) {
1585 self.error_at_peek("expected `=` after type alias name");
1586 return None;
1587 }
1588 self.advance();
1589
1590 let ty = self.parse_type_annotation()?;
1591
1592 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1595 self.advance().span.end
1596 } else if self.line_break_before_peek() {
1597 self.prev_token_end()
1598 } else {
1599 self.error_at_peek("expected `;` after type alias body");
1600 return None;
1601 };
1602
1603 parse_arena_result(
1604 self.ast.try_push_stmt(Stmt {
1605 kind: StmtKind::TypeAliasDecl {
1606 name,
1607 generics,
1608 ty,
1609 doc,
1610 },
1611 span: self.span(kw.span.start, end),
1612 }),
1613 &mut self.fatal,
1614 )
1615 }
1616
1617 fn parse_enum_decl(&mut self) -> Option<StmtId> {
1619 #[derive(Copy, Clone)]
1620 enum Kind {
1621 Number,
1622 String,
1623 }
1624 let doc = self.take_leading_doc();
1625 let kw = self.advance();
1626 let name = self.parse_type_decl_name("an", "enum")?;
1627
1628 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1629 self.error_at_peek("expected `{` after enum name");
1630 return None;
1631 }
1632 self.advance();
1633
1634 let mut members: Vec<EnumMember> = Vec::new();
1635 let mut first_explicit: Option<(Kind, Span)> = None;
1636 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1637 if matches!(self.peek().kind, TokenKind::Semicolon) {
1639 self.advance();
1640 continue;
1641 }
1642 let member_doc = self.take_leading_doc();
1643 let member_name_tok = self.expect_identifier_name("expected enum member name")?;
1644 let member_name = self.ident_from_token(&member_name_tok);
1645
1646 let value = if matches!(self.peek().kind, TokenKind::Equals) {
1647 self.advance();
1648 let init = self.parse_enum_initializer()?;
1649 let (this_kind, this_span) = match &init {
1650 EnumInitializer::Number { span, .. } => (Kind::Number, *span),
1651 EnumInitializer::String { span, .. } => (Kind::String, *span),
1652 };
1653 match first_explicit {
1654 None => first_explicit = Some((this_kind, this_span)),
1655 Some((prev, _)) => {
1656 let mismatched = matches!(
1657 (prev, this_kind),
1658 (Kind::Number, Kind::String) | (Kind::String, Kind::Number)
1659 );
1660 if mismatched {
1661 self.error_at(
1662 this_span,
1663 "mixed numeric and string enum members are not allowed",
1664 );
1665 }
1666 }
1667 }
1668 Some(init)
1669 } else {
1670 None
1671 };
1672
1673 let end_span = value
1674 .as_ref()
1675 .map_or(member_name.span, super::ast::EnumInitializer::span);
1676 let member_span = self.span(member_name.span.start, end_span.end);
1677 members.push(EnumMember {
1678 name: member_name,
1679 value,
1680 span: member_span,
1681 doc: member_doc,
1682 });
1683
1684 match self.peek().kind {
1685 TokenKind::Comma | TokenKind::Semicolon => {
1688 self.advance();
1689 }
1690 TokenKind::RightBrace | TokenKind::Eof => {}
1691 _ => {
1692 self.error_at_peek("expected `,` or `}` after enum member");
1693 return None;
1694 }
1695 }
1696 }
1697
1698 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1699 self.error_at_peek("expected `}` to close enum");
1700 return None;
1701 }
1702 let close = self.advance();
1703
1704 parse_arena_result(
1705 self.ast.try_push_stmt(Stmt {
1706 kind: StmtKind::EnumDecl { name, members, doc },
1707 span: self.span(kw.span.start, close.span.end),
1708 }),
1709 &mut self.fatal,
1710 )
1711 }
1712
1713 fn parse_export(&mut self) -> Option<StmtId> {
1720 let export_tok = self.advance();
1721 let export_span = export_tok.span;
1722
1723 let nested = self.block_depth > 0;
1724 if nested {
1725 self.error_at_with_help(
1726 export_span,
1727 "`export` statements must appear at the top of the file",
1728 vec!["move this `export` to the top level of the module".to_string()],
1729 );
1730 }
1733
1734 if self.peek_identifier_text_is("type")
1737 && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1738 {
1739 self.advance();
1740 }
1741
1742 self.reject_async_modifier();
1743 let declares = matches!(
1744 self.peek().kind,
1745 TokenKind::Function
1746 | TokenKind::Class
1747 | TokenKind::Const
1748 | TokenKind::Let
1749 | TokenKind::Interface
1750 | TokenKind::Enum
1751 ) || self.at_type_alias_head();
1752 if declares {
1753 if export_tok.leading_doc.is_some() && self.peek().leading_doc.is_none() {
1756 if let Some(token) = self.tokens.get_mut(self.pos) {
1757 token.leading_doc = export_tok.leading_doc;
1758 } else {
1759 return self.invariant_failure("export cursor is outside token stream");
1760 }
1761 }
1762 let id = self.parse_statement()?;
1763 if nested {
1764 let detached: Vec<StmtId> = std::iter::once(id)
1765 .chain(self.take_trailing_declarators())
1766 .collect();
1767 self.discard_detached_patterns(&detached);
1768 return None;
1769 }
1770 for stmt in std::iter::once(id).chain(self.trailing_declarators.clone()) {
1772 self.ast
1773 .exported_decls
1774 .push(ExportedDecl { stmt, export_span });
1775 }
1776 return Some(id);
1777 }
1778
1779 match self.peek().kind {
1780 TokenKind::LeftBrace => {
1781 let doc = export_tok
1784 .leading_doc
1785 .as_ref()
1786 .and_then(|raw| self.parse_doc(raw))
1787 .or_else(|| self.take_leading_doc());
1788 self.parse_export_from(export_span, nested, doc)
1789 }
1790 TokenKind::Default => {
1791 self.error_at_peek_with_help(
1792 "`export default` is not supported",
1793 vec![
1794 "default exports are out of scope; drop `default` — a named \
1795 `export` marks the declaration visible to sibling modules"
1796 .to_string(),
1797 ],
1798 );
1799 None
1800 }
1801 _ => {
1802 self.error_at_peek_with_help(
1803 "expected a declaration after `export`",
1804 vec![
1805 "`export` may precede `function`, `class`, `const`, `let`, `type`, \
1806 `interface`, or `enum`, or take the form `export { … }`"
1807 .to_string(),
1808 ],
1809 );
1810 None
1811 }
1812 }
1813 }
1814
1815 fn parse_export_from(
1818 &mut self,
1819 export_span: Span,
1820 nested: bool,
1821 doc: Option<crate::DocComment>,
1822 ) -> Option<StmtId> {
1823 let (specs, open_span) = self.parse_specifier_list(SpecifierList::Export)?;
1824 if specs.is_empty() {
1825 self.error_at_with_help(
1826 open_span,
1827 "empty export specifier list",
1828 vec!["name what you re-export: `export { name } from \"./util\";`".to_string()],
1829 );
1830 return None;
1831 }
1832
1833 let source = if self.peek_identifier_text_is("from") {
1834 self.advance();
1835 let module_tok = self.peek().clone();
1836 let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1837 s.clone()
1838 } else {
1839 self.error_at_peek("expected module specifier string after `from`");
1840 return None;
1841 };
1842 self.advance();
1843 Some((module, module_tok.span))
1844 } else {
1845 None
1846 };
1847
1848 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1849 self.error_at_peek("expected `;` after export statement");
1850 return None;
1851 }
1852 let semi = self.advance();
1853
1854 if nested {
1855 return None;
1856 }
1857
1858 parse_arena_result(
1859 self.ast.try_push_stmt(Stmt {
1860 kind: StmtKind::ExportFrom { specs, source, doc },
1861 span: self.span(export_span.start, semi.span.end),
1862 }),
1863 &mut self.fatal,
1864 )
1865 }
1866
1867 fn parse_import_decl(&mut self) -> Option<StmtId> {
1868 let doc = self.take_leading_doc();
1869 let kw = self.advance();
1870 let kw_span = kw.span;
1871
1872 let nested = self.block_depth > 0;
1873 if nested {
1874 self.error_at_with_help(
1875 kw_span,
1876 "`import` statements must appear at the top of the file",
1877 vec!["move this `import` outside any block / function body".to_string()],
1878 );
1879 }
1881
1882 let kind = self.parse_import_kind()?;
1883
1884 if !self.peek_identifier_text_is("from") {
1885 self.error_at_peek("expected `from` after import specifier list");
1886 return None;
1887 }
1888 self.advance();
1889
1890 let module_tok = self.peek().clone();
1891 let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1892 s.clone()
1893 } else {
1894 self.error_at_peek("expected module specifier string after `from`");
1895 return None;
1896 };
1897 let module_span = module_tok.span;
1898 self.advance();
1899
1900 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1901 self.error_at_peek("expected `;` after import statement");
1902 return None;
1903 }
1904 let semi = self.advance();
1905
1906 if nested {
1907 return None;
1908 }
1909
1910 parse_arena_result(
1911 self.ast.try_push_stmt(Stmt {
1912 kind: StmtKind::Import {
1913 module,
1914 module_span,
1915 kind,
1916 doc,
1917 },
1918 span: self.span(kw_span.start, semi.span.end),
1919 }),
1920 &mut self.fatal,
1921 )
1922 }
1923
1924 fn parse_import_kind(&mut self) -> Option<ImportKind> {
1925 if self.peek_identifier_text_is("type")
1930 && match self.peek_at(1).kind {
1931 TokenKind::LeftBrace | TokenKind::Star => true,
1932 TokenKind::Identifier => {
1933 !(self.peek_at_is_word(1, "from")
1934 && matches!(self.peek_at(2).kind, TokenKind::StringLiteral(_)))
1935 }
1936 _ => false,
1937 }
1938 {
1939 self.advance();
1940 }
1941
1942 match self.peek().kind {
1943 TokenKind::LeftBrace => self.parse_named_imports(),
1944 TokenKind::Identifier => {
1945 let local_tok = self.advance();
1946 let local_name = self.ident_from_token(&local_tok);
1947 if matches!(self.peek().kind, TokenKind::Comma) {
1948 self.error_at_peek_with_help(
1949 "combining default and named imports is not supported",
1950 vec!["use two separate `import` statements".to_string()],
1951 );
1952 return None;
1953 }
1954 Some(ImportKind::Namespace { local_name })
1955 }
1956 TokenKind::Star => {
1959 self.advance();
1960 if !self.peek_identifier_text_is("as") {
1961 self.error_at_peek_with_help(
1962 "expected `as <name>` after `import *`",
1963 vec![
1964 "name the namespace binding: \
1965 `import * as ns from \"pkg\";` (or use the \
1966 short form: `import ns from \"pkg\";`)"
1967 .to_string(),
1968 ],
1969 );
1970 return None;
1971 }
1972 self.advance();
1973 let local_tok = self.expect_identifier("expected namespace name after `as`")?;
1974 let local_name = self.ident_from_token(&local_tok);
1975 Some(ImportKind::Namespace { local_name })
1976 }
1977 TokenKind::StringLiteral(_) => {
1978 self.error_at_peek_with_help(
1979 "side-effect-only imports are not supported",
1980 vec![
1981 "name what you import: `import { name } from \"pkg\";` \
1982 or `import ns from \"pkg\";`"
1983 .to_string(),
1984 ],
1985 );
1986 None
1987 }
1988 _ => {
1989 self.error_at_peek("expected `{` or namespace name after `import`");
1990 None
1991 }
1992 }
1993 }
1994
1995 fn parse_named_imports(&mut self) -> Option<ImportKind> {
1996 let (specs, open_span) = self.parse_specifier_list(SpecifierList::Import)?;
1997 if specs.is_empty() {
1998 self.error_at_with_help(
1999 open_span,
2000 "empty import specifier list",
2001 vec!["name what you import: `import { name } from \"pkg\";`".to_string()],
2002 );
2003 return None;
2004 }
2005 Some(ImportKind::Named(specs))
2006 }
2007
2008 fn parse_specifier_list(
2014 &mut self,
2015 list: SpecifierList,
2016 ) -> Option<(Vec<ImportSpecifier>, Span)> {
2017 let what = list.noun();
2018 let open = self.advance();
2019 let mut specs: Vec<ImportSpecifier> = Vec::new();
2020 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2021 if self.peek_identifier_text_is("type")
2024 && matches!(self.peek_at(1).kind, TokenKind::Identifier)
2025 && !self.peek_at_is_word(1, "as")
2026 {
2027 self.advance();
2028 }
2029 let imported_tok =
2030 self.expect_identifier_name(&format!("expected {what} specifier name"))?;
2031 let imported_name = self.ident_from_token(&imported_tok);
2032
2033 let binds_local = list == SpecifierList::Import;
2034 let local_name = if self.peek_identifier_text_is("as") {
2035 self.advance();
2036 let local_tok = self.expect_identifier_name("expected local name after `as`")?;
2037 if binds_local {
2038 self.reject_strict_mode_reserved_word(&local_tok);
2039 }
2040 self.ident_from_token(&local_tok)
2041 } else {
2042 if binds_local {
2043 self.reject_strict_mode_reserved_word(&imported_tok);
2044 }
2045 imported_name.clone()
2046 };
2047
2048 specs.push(ImportSpecifier {
2049 imported_name,
2050 local_name,
2051 });
2052
2053 match self.peek().kind {
2054 TokenKind::Comma => {
2055 self.advance();
2056 }
2057 TokenKind::RightBrace | TokenKind::Eof => {}
2058 _ => {
2059 self.error_at_peek(format!("expected `,` or `}}` after {what} specifier"));
2060 return None;
2061 }
2062 }
2063 }
2064
2065 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2066 self.error_at_peek(format!("expected `}}` to close {what} specifier list"));
2067 return None;
2068 }
2069 self.advance();
2070
2071 Some((specs, open.span))
2072 }
2073
2074 fn parse_enum_initializer(&mut self) -> Option<EnumInitializer> {
2075 let neg = if matches!(self.peek().kind, TokenKind::Minus) {
2076 Some(self.advance())
2077 } else {
2078 None
2079 };
2080 let tok = self.advance();
2081 match &tok.kind {
2082 TokenKind::NumberLiteral(n) => {
2083 let value = if neg.is_some() { -n } else { *n };
2084 let value = if value == 0.0 { 0.0 } else { value };
2086 let start = neg.as_ref().map_or(tok.span.start, |t| t.span.start);
2087 Some(EnumInitializer::Number {
2088 value,
2089 span: self.span(start, tok.span.end),
2090 })
2091 }
2092 TokenKind::StringLiteral(s) => {
2093 if let Some(neg_tok) = &neg {
2094 self.error_at(
2095 self.span(neg_tok.span.start, tok.span.end),
2096 "cannot negate a string enum initializer",
2097 );
2098 return None;
2099 }
2100 Some(EnumInitializer::String {
2101 value: s.clone(),
2102 span: tok.span,
2103 })
2104 }
2105 _ => {
2106 let span = neg.map_or(tok.span, |t| self.span(t.span.start, tok.span.end));
2107 self.error_at(
2108 span,
2109 "expected a number or string literal for enum initializer",
2110 );
2111 None
2112 }
2113 }
2114 }
2115
2116 fn is_contextual_type_operator(&self, index: usize, operator: &str) -> bool {
2124 let Some(tok) = self.tokens.get(index) else {
2125 return false;
2126 };
2127 if !self.token_is_word(tok, operator) {
2128 return false;
2129 }
2130 matches!(
2131 self.tokens.get(index + 1).map(|t| &t.kind),
2132 Some(
2133 TokenKind::Identifier
2134 | TokenKind::Void
2135 | TokenKind::LeftBrace
2136 | TokenKind::LeftParen
2137 | TokenKind::LeftBracket
2138 | TokenKind::StringLiteral(_)
2139 | TokenKind::NumberLiteral(_)
2140 | TokenKind::BooleanLiteral(_)
2141 | TokenKind::NullLiteral
2142 )
2143 )
2144 }
2145
2146 fn consume_type_close(&mut self) -> Option<Token> {
2149 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2150 return Some(self.advance());
2151 }
2152 let original = self.peek().clone();
2153 if !matches!(original.kind, TokenKind::GreaterEquals) {
2154 return self.invariant_failure("type closing token must start with `>`");
2155 }
2156 let middle = original.span.start + 1;
2157 let close = Token::new(
2158 TokenKind::GreaterThan,
2159 self.span(original.span.start, middle),
2160 );
2161 let remaining = Token::new(TokenKind::Equals, self.span(middle, original.span.end));
2162 self.split_type_tokens.push((self.pos, original));
2163 *self.tokens.get_mut(self.pos)? = remaining;
2164 Some(close)
2165 }
2166
2167 fn restore_type_tokens(&mut self, checkpoint: usize) {
2168 for (index, original) in self.split_type_tokens.drain(checkpoint..).rev() {
2169 self.tokens[index] = original;
2171 }
2172 }
2173
2174 fn parse_type_argument_list(&mut self) -> Option<(Vec<TypeAnnotation>, u32)> {
2175 let lt = self.advance();
2176 if matches!(
2177 self.peek().kind,
2178 TokenKind::GreaterThan | TokenKind::GreaterEquals
2179 ) {
2180 self.error_at(lt.span, "empty generic argument list `<>` is not allowed");
2181 return None;
2182 }
2183 let mut args = Vec::new();
2184 loop {
2185 args.push(self.parse_type_annotation()?);
2186 match self.peek().kind {
2187 TokenKind::Comma => {
2188 self.advance();
2189 if matches!(
2190 self.peek().kind,
2191 TokenKind::GreaterThan | TokenKind::GreaterEquals
2192 ) {
2193 let gt = self.consume_type_close()?;
2194 return Some((args, gt.span.end));
2195 }
2196 }
2197 TokenKind::GreaterThan | TokenKind::GreaterEquals => {
2198 let gt = self.consume_type_close()?;
2199 return Some((args, gt.span.end));
2200 }
2201 _ => {
2202 self.error_at(
2203 lt.span,
2204 "expected `,` or `>` to close generic argument list",
2205 );
2206 return None;
2207 }
2208 }
2209 }
2210 }
2211
2212 fn parse_type_decl_name(&mut self, article: &str, noun: &str) -> Option<Ident> {
2216 let name_tok = self.expect_identifier(&format!("expected {noun} name"))?;
2217 let name = self.ident_from_token(&name_tok);
2218 if BUILT_IN_TYPE_NAMES.contains(&name.name.as_str()) {
2219 self.error_at_with_help(
2220 name.span,
2221 format!(
2222 "`{}` is a built-in type and can't be used as {article} {noun} name",
2223 name.name
2224 ),
2225 vec![format!("rename the {noun}")],
2226 );
2227 }
2228 Some(name)
2229 }
2230
2231 fn parse_generic_param_list(&mut self) -> Option<Vec<crate::TypeParameterAnnotation>> {
2232 let lt = self.advance();
2233 if matches!(
2234 self.peek().kind,
2235 TokenKind::GreaterThan | TokenKind::GreaterEquals
2236 ) {
2237 self.error_at(lt.span, "empty generic parameter list `<>` is not allowed");
2238 return None;
2239 }
2240 let mut generics = Vec::new();
2241 loop {
2242 let name_tok = self.expect_identifier("expected generic parameter name")?;
2243 let name = self.ident_from_token(&name_tok);
2244 let constraint = if matches!(self.peek().kind, TokenKind::Extends) {
2245 self.advance();
2246 Some(self.parse_type_annotation()?)
2247 } else {
2248 None
2249 };
2250 if matches!(self.peek().kind, TokenKind::Equals) {
2251 let equals = self.advance();
2252 self.error_at_with_help(equals.span, "generic type parameter defaults are not supported", vec!["remove the default and supply a type argument explicitly when inference cannot determine it".into()]);
2253 self.parse_type_annotation()?;
2254 }
2255 if generics
2259 .iter()
2260 .any(|g: &crate::TypeParameterAnnotation| g.name == name.name)
2261 {
2262 self.error_at_with_help(
2263 name.span,
2264 format!("duplicate type parameter `{}`", name.name),
2265 vec!["give each type parameter its own name".to_string()],
2266 );
2267 } else {
2268 generics.push(crate::TypeParameterAnnotation {
2269 ident: name,
2270 constraint,
2271 });
2272 }
2273 match self.peek().kind {
2274 TokenKind::Comma => {
2275 self.advance();
2276 if matches!(
2277 self.peek().kind,
2278 TokenKind::GreaterThan | TokenKind::GreaterEquals
2279 ) {
2280 self.consume_type_close()?;
2281 return Some(generics);
2282 }
2283 }
2284 TokenKind::GreaterThan | TokenKind::GreaterEquals => {
2285 self.consume_type_close()?;
2286 return Some(generics);
2287 }
2288 _ => {
2289 self.error_at_peek("expected `,` or `>`");
2290 return None;
2291 }
2292 }
2293 }
2294 }
2295
2296 fn parse_param_list(&mut self, allow_param_properties: bool) -> Option<Vec<ParamDecl>> {
2297 if matches!(self.peek().kind, TokenKind::RightParen) {
2298 return Some(Vec::new());
2299 }
2300 let mut params = Vec::new();
2301 loop {
2302 let param = self.parse_param(allow_param_properties)?;
2303 let is_rest = param.rest;
2304 params.push(param);
2305 match self.peek().kind {
2306 TokenKind::Comma => {
2307 if is_rest {
2308 let comma = self.advance();
2309 self.error_at(comma.span, "rest parameter must be the last parameter");
2310 return None;
2311 }
2312 self.advance();
2313 if matches!(self.peek().kind, TokenKind::RightParen) {
2314 break;
2315 }
2316 }
2317 TokenKind::RightParen => break,
2318 _ => {
2319 self.error_at_peek("expected `,` or `)`");
2320 return None;
2321 }
2322 }
2323 }
2324 self.check_parameter_order(params.iter().map(ParamOrder::of_param));
2325 Some(params)
2326 }
2327
2328 fn parse_param(&mut self, allow_param_properties: bool) -> Option<ParamDecl> {
2329 let modifiers = if self.peek_word_is_class_modifier("public")
2333 || self.peek_word_is_class_modifier("private")
2334 || self.peek_word_is_class_modifier("readonly")
2335 {
2336 let span = self.peek().span;
2337 let parsed = self.parse_class_modifiers();
2338 if allow_param_properties {
2339 Some(parsed)
2340 } else {
2341 self.error_at_with_help(
2342 span,
2343 "parameter properties are only allowed in a constructor",
2344 vec![
2345 "move the `public`/`private`/`readonly` modifier to a constructor parameter"
2346 .to_string(),
2347 ],
2348 );
2349 None
2350 }
2351 } else {
2352 None
2353 };
2354
2355 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
2356 self.advance();
2357 true
2358 } else {
2359 false
2360 };
2361 if modifiers.is_some() && rest {
2362 self.error_at_peek("a parameter property cannot be a rest parameter");
2363 return None;
2364 }
2365
2366 let (name, pattern) = match self.peek().kind {
2369 TokenKind::LeftBrace | TokenKind::LeftBracket => {
2370 if rest {
2371 self.error_at_peek("rest parameter cannot be destructured");
2372 return None;
2373 }
2374 if modifiers.is_some() {
2375 self.error_at_peek("a parameter property cannot be destructured");
2376 return None;
2377 }
2378 let binding = self.parse_binding()?;
2379 let placeholder = Ident {
2380 name: String::new(),
2381 span: binding.span(),
2382 };
2383 (placeholder, Some(binding))
2384 }
2385 _ => {
2386 let name_tok = self.expect_binding_identifier(if rest {
2387 "expected identifier after `...`"
2388 } else {
2389 "expected parameter name"
2390 })?;
2391 let name = self.ident_from_token(&name_tok);
2392 (name, None)
2393 }
2394 };
2395
2396 let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
2397 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2398 self.advance();
2399 Some(self.parse_type_annotation()?)
2400 } else if !rest && matches!(self.peek().kind, TokenKind::Equals) {
2401 None
2402 } else {
2403 if rest {
2404 self.error_at_peek_with_help(
2405 "rest parameter requires a type annotation",
2406 vec!["function name(...args: T[]): R { … }".to_string()],
2407 );
2408 } else {
2409 self.error_at_peek_with_help(
2410 "parameter requires a type annotation",
2411 vec!["add a type (`x: T`) or a default initializer (`x = value`)".to_string()],
2412 );
2413 }
2414 return None;
2415 };
2416 let default = self.parse_parameter_default(rest, optional)?;
2417 Some(ParamDecl {
2418 name,
2419 ty,
2420 default,
2421 optional,
2422 pattern,
2423 rest,
2424 modifiers,
2425 })
2426 }
2427
2428 fn parse_parameter_optional(&mut self, rest: bool, pattern: bool) -> Option<bool> {
2429 if !matches!(self.peek().kind, TokenKind::Question) {
2430 return Some(false);
2431 }
2432 let question = self.advance();
2433 if rest || pattern {
2434 self.error_at(
2435 question.span,
2436 if rest {
2437 "a rest parameter cannot be optional"
2438 } else {
2439 "a destructured parameter cannot be optional; use a default value"
2440 },
2441 );
2442 return None;
2443 }
2444 Some(true)
2445 }
2446
2447 fn parse_parameter_default(&mut self, rest: bool, optional: bool) -> Option<Option<ExprId>> {
2450 if !matches!(self.peek().kind, TokenKind::Equals) {
2451 return Some(None);
2452 }
2453 if rest {
2454 self.error_rest_parameter_default();
2455 return None;
2456 }
2457 if optional {
2458 self.error_at_peek_with_help(
2459 "an optional parameter cannot have a default value",
2460 vec!["drop the `?`: a parameter with a default is already optional".to_string()],
2461 );
2462 }
2463 self.advance();
2464 Some(Some(self.parse_expression()?))
2465 }
2466
2467 fn error_rest_parameter_default(&mut self) {
2469 self.error_at_peek_with_help(
2470 "rest parameter cannot have a default value",
2471 vec!["omit the `= …`; an unspecified rest defaults to `[]`".to_string()],
2472 );
2473 }
2474
2475 fn check_parameter_order<'p>(&mut self, params: impl IntoIterator<Item = ParamOrder<'p>>) {
2479 let mut follows_optional = false;
2480 for param in params {
2481 if param.optional {
2482 follows_optional = true;
2483 continue;
2484 }
2485 if !follows_optional || !param.required {
2486 continue;
2487 }
2488 let label = match param.name {
2489 Some(name) => format!("parameter `{name}`"),
2490 None => "destructured parameter".to_string(),
2491 };
2492 self.error_at_with_help(
2493 param.span,
2494 format!("required {label} cannot follow an optional parameter"),
2495 vec!["make it optional too (`x?: T`), give it a default (`x: T = …`), or move it before the optional parameters".to_string()],
2496 );
2497 return;
2498 }
2499 }
2500
2501 fn eat_asi_semicolon_before(&mut self, next: TokenKind) {
2506 if matches!(self.peek().kind, TokenKind::Semicolon) && self.peek_at(1).kind == next {
2507 self.advance();
2508 }
2509 }
2510
2511 fn parse_block(&mut self) -> Option<StmtId> {
2512 self.eat_asi_semicolon_before(TokenKind::LeftBrace);
2513 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2514 self.error_at_peek("expected `{`");
2515 return None;
2516 }
2517 let open = self.advance();
2518 self.block_depth += 1;
2519
2520 let mut stmts = Vec::new();
2521 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2522 if matches!(self.peek().kind, TokenKind::Semicolon) {
2523 self.advance();
2524 continue;
2525 }
2526 let pos_before = self.pos;
2527 if let Some(id) = self.parse_statement() {
2528 stmts.push(id);
2529 stmts.extend(self.take_trailing_declarators());
2530 } else {
2531 self.recover();
2532 if self.pos == pos_before && !self.is_at_eof() {
2533 self.advance();
2534 }
2535 }
2536 }
2537
2538 self.block_depth -= 1;
2539 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2540 self.error_at_peek("expected `}`");
2541 return None;
2542 }
2543 let close = self.advance();
2544
2545 let span = self.span(open.span.start, close.span.end);
2546 parse_arena_result(
2547 self.ast.try_push_stmt(Stmt {
2548 kind: StmtKind::Block(stmts),
2549 span,
2550 }),
2551 &mut self.fatal,
2552 )
2553 }
2554
2555 fn parse_control_body(&mut self) -> Option<StmtId> {
2560 if matches!(self.peek().kind, TokenKind::LeftBrace) {
2561 return self.parse_block();
2562 }
2563 let keyword = match self.peek().kind {
2568 TokenKind::Let => Some(("a", "let")),
2569 TokenKind::Const => Some(("a", "const")),
2570 TokenKind::Function => Some(("a", "function")),
2571 TokenKind::Interface => Some(("an", "interface")),
2573 TokenKind::Enum => Some(("an", "enum")),
2574 _ if self.peek_identifier_text_is("type") => Some(("a", "type")),
2575 _ => None,
2576 };
2577 let declares = match self.peek_at(1).kind {
2581 TokenKind::Identifier => true,
2582 TokenKind::LeftBracket | TokenKind::LeftBrace => !matches!(keyword, Some((_, "type"))),
2583 _ => false,
2584 };
2585 if let Some((article, keyword)) = keyword
2586 && declares
2587 {
2588 self.error_at_peek_with_help(
2589 format!(
2590 "{article} `{keyword}` declaration can't be the body of a statement without braces"
2591 ),
2592 vec![format!("wrap it in braces: `{{ {keyword} … }}`")],
2593 );
2594 }
2595 self.block_depth += 1;
2596 let stmt = self.parse_statement();
2597 self.block_depth -= 1;
2598 let stmt = stmt?;
2599 let mut stmts = vec![stmt];
2600 stmts.extend(self.take_trailing_declarators());
2601 let first = parse_arena_result(self.ast.try_stmt(stmt), &mut self.fatal)?.span;
2602 let last_id = stmts.last().copied().unwrap_or(stmt);
2603 let last = parse_arena_result(self.ast.try_stmt(last_id), &mut self.fatal)?.span;
2604 let span = self.span(first.start, last.end);
2605 parse_arena_result(
2606 self.ast.try_push_stmt(Stmt {
2607 kind: StmtKind::Block(stmts),
2608 span,
2609 }),
2610 &mut self.fatal,
2611 )
2612 }
2613
2614 fn parse_loop_body(&mut self) -> Option<StmtId> {
2617 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2618 return self.parse_control_body();
2619 }
2620 self.empty_body_at_semicolon()
2621 }
2622
2623 fn parse_branch_body(&mut self, keyword: &str) -> Option<StmtId> {
2627 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2628 return self.parse_control_body();
2629 }
2630 self.error_at_peek_with_help(
2631 format!("`{keyword}` has an empty body"),
2632 vec![format!("remove the `;` after `{keyword}`")],
2633 );
2634 self.empty_body_at_semicolon()
2635 }
2636
2637 fn empty_body_at_semicolon(&mut self) -> Option<StmtId> {
2639 let semi = self.advance();
2640 parse_arena_result(
2641 self.ast.try_push_stmt(Stmt {
2642 kind: StmtKind::Block(vec![]),
2643 span: semi.span,
2644 }),
2645 &mut self.fatal,
2646 )
2647 }
2648
2649 fn parse_if(&mut self) -> Option<StmtId> {
2650 self.with_recursion_limit(Self::parse_if_inner)
2651 }
2652
2653 fn parse_if_inner(&mut self) -> Option<StmtId> {
2654 let kw = self.advance();
2655 let condition = self.parse_paren_condition()?;
2656 let then_block = self.parse_branch_body("if")?;
2657
2658 let else_block = if matches!(self.peek().kind, TokenKind::Else) {
2659 self.advance();
2660 if matches!(self.peek().kind, TokenKind::If) {
2661 Some(self.parse_if()?)
2662 } else {
2663 Some(self.parse_branch_body("else")?)
2664 }
2665 } else {
2666 None
2667 };
2668
2669 let end = match else_block {
2670 Some(id) => {
2671 parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
2672 .span
2673 .end
2674 }
2675 None => {
2676 parse_arena_result(self.ast.try_stmt(then_block), &mut self.fatal)?
2677 .span
2678 .end
2679 }
2680 };
2681
2682 parse_arena_result(
2683 self.ast.try_push_stmt(Stmt {
2684 kind: StmtKind::If {
2685 condition,
2686 then_block,
2687 else_block,
2688 },
2689 span: self.span(kw.span.start, end),
2690 }),
2691 &mut self.fatal,
2692 )
2693 }
2694
2695 fn parse_while(&mut self) -> Option<StmtId> {
2696 let kw = self.advance();
2697 let condition = self.parse_paren_condition()?;
2698 let body = self.parse_loop_body()?;
2699 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2700 .span
2701 .end;
2702
2703 parse_arena_result(
2704 self.ast.try_push_stmt(Stmt {
2705 kind: StmtKind::While { condition, body },
2706 span: self.span(kw.span.start, body_end),
2707 }),
2708 &mut self.fatal,
2709 )
2710 }
2711
2712 fn parse_for(&mut self) -> Option<StmtId> {
2714 let kw = self.advance();
2715 if self.peek_identifier_text_is("await")
2718 && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
2719 {
2720 let tok = self.advance();
2721 self.error_at_with_help(
2722 tok.span,
2723 "`for await` is not supported",
2724 vec![
2725 "calls return their values directly: remove `await` and write \
2726 `for (const e of …)`"
2727 .to_string(),
2728 ],
2729 );
2730 }
2731 if !matches!(self.peek().kind, TokenKind::LeftParen) {
2732 self.error_at_peek("expected `(` after `for`");
2733 return None;
2734 }
2735 self.advance();
2736
2737 if let Some(in_at) = self.scan_for_in_head() {
2738 return self.parse_for_in_tail(kw, in_at);
2739 }
2740 if self.scan_for_of_shape() {
2741 self.parse_for_of_tail(kw)
2742 } else {
2743 self.parse_c_for_tail(kw)
2744 }
2745 }
2746
2747 fn scan_for_in_head(&self) -> Option<usize> {
2750 let binding_at = self.pos
2751 + usize::from(matches!(
2752 self.peek().kind,
2753 TokenKind::Let | TokenKind::Const
2754 ));
2755 let in_at = match self.tokens.get(binding_at)?.kind {
2756 TokenKind::Identifier => binding_at + 1,
2757 TokenKind::LeftBracket => self.scan_past_balanced(
2758 binding_at,
2759 |k| matches!(k, TokenKind::LeftBracket),
2760 |k| matches!(k, TokenKind::RightBracket),
2761 )?,
2762 TokenKind::LeftBrace => self.scan_past_balanced(
2763 binding_at,
2764 |k| matches!(k, TokenKind::LeftBrace),
2765 |k| matches!(k, TokenKind::RightBrace),
2766 )?,
2767 _ => return None,
2768 };
2769 matches!(self.tokens.get(in_at)?.kind, TokenKind::In).then_some(in_at)
2770 }
2771
2772 fn parse_for_in_tail(&mut self, kw: Token, in_at: usize) -> Option<StmtId> {
2777 let binding_kind = match self.peek().kind {
2778 TokenKind::Let => Some(crate::BindingKind::Let),
2779 TokenKind::Const => Some(crate::BindingKind::Const),
2780 _ => None,
2781 };
2782 if binding_kind.is_some() {
2783 self.advance();
2784 }
2785 let name = if matches!(
2786 self.peek().kind,
2787 TokenKind::LeftBracket | TokenKind::LeftBrace
2788 ) {
2789 self.error_at_peek_with_help(
2790 "the left-hand side of `for…in` cannot be a destructuring pattern",
2791 vec![
2792 "use `for (const k in obj)` and read each value with `obj[k]`".to_string(),
2793 "or use `for (const [k, v] of Object.entries(obj))` for key-value pairs"
2794 .to_string(),
2795 ],
2796 );
2797 self.pos = in_at;
2798 None
2799 } else {
2800 let name_tok = self.advance();
2801 Some(self.ident_from_token(&name_tok))
2802 };
2803 self.advance();
2804
2805 let object = self.parse_expression()?;
2806 if !matches!(self.peek().kind, TokenKind::RightParen) {
2807 self.error_at_peek("expected `)` after `for…in` object");
2808 return None;
2809 }
2810 self.advance();
2811 let body = self.parse_loop_body()?;
2812 let body_span = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
2813 let loop_span = self.span(kw.span.start, body_span.end);
2814
2815 let Some(name) = name else {
2816 return parse_arena_result(
2821 self.ast.try_push_stmt(Stmt {
2822 kind: StmtKind::Block(Vec::new()),
2823 span: loop_span,
2824 }),
2825 &mut self.fatal,
2826 );
2827 };
2828
2829 let held = self.hold_for_in_object(object)?;
2830 let keys = self.for_in_keys(&held)?;
2831 let (binding_kind, binding, body) = if let Some(kind) = binding_kind {
2832 (kind, name, body)
2833 } else {
2834 let (key, body) = self.copy_key_to_outer(name, body, body_span)?;
2835 (crate::BindingKind::Const, key, body)
2836 };
2837 let for_of = parse_arena_result(
2838 self.ast.try_push_stmt(Stmt {
2839 kind: StmtKind::ForOf {
2840 binding_kind,
2841 name: binding,
2842 ty: None,
2843 iter: keys,
2844 body,
2845 },
2846 span: loop_span,
2847 }),
2848 &mut self.fatal,
2849 )?;
2850 parse_arena_result(
2851 self.ast.try_push_stmt(Stmt {
2852 kind: StmtKind::Block(vec![held.statement, for_of]),
2853 span: loop_span,
2854 }),
2855 &mut self.fatal,
2856 )
2857 }
2858
2859 fn copy_key_to_outer(
2863 &mut self,
2864 outer: Ident,
2865 body: StmtId,
2866 body_span: Span,
2867 ) -> Option<(Ident, StmtId)> {
2868 let span = outer.span;
2869 let key = Ident {
2870 name: FOR_IN_KEY.to_string(),
2871 span,
2872 };
2873 let key_expr = self.synthetic_expr(span, ExprKind::Identifier(key.clone()))?;
2874 let copy = parse_arena_result(
2875 self.ast.try_push_stmt(Stmt {
2876 kind: StmtKind::Assign {
2877 target: outer,
2878 value: key_expr,
2879 },
2880 span,
2881 }),
2882 &mut self.fatal,
2883 )?;
2884 let body = parse_arena_result(
2885 self.ast.try_push_stmt(Stmt {
2886 kind: StmtKind::Block(vec![copy, body]),
2887 span: body_span,
2888 }),
2889 &mut self.fatal,
2890 )?;
2891 Some((key, body))
2892 }
2893
2894 fn hold_for_in_object(&mut self, object: ExprId) -> Option<HeldForInObject> {
2899 let span = parse_arena_result(self.ast.try_expr(object), &mut self.fatal)?.span;
2900 let name = Ident {
2901 name: FOR_IN_OBJECT.to_string(),
2902 span,
2903 };
2904 let statement = parse_arena_result(
2905 self.ast.try_push_stmt(Stmt {
2906 kind: StmtKind::Let {
2907 name: name.clone(),
2908 ty: None,
2909 value: object,
2910 doc: None,
2911 },
2912 span,
2913 }),
2914 &mut self.fatal,
2915 )?;
2916 Some(HeldForInObject { name, statement })
2917 }
2918
2919 fn for_in_keys(&mut self, held: &HeldForInObject) -> Option<ExprId> {
2923 let span = held.name.span;
2924 let undefined = self.synthetic_expr(
2925 span,
2926 ExprKind::Identifier(Ident {
2927 name: "undefined".to_string(),
2928 span,
2929 }),
2930 )?;
2931 self.ast.synthetic_undefined.insert(undefined);
2932 let null = self.synthetic_expr(span, ExprKind::Null)?;
2933 let is_undefined = self.held_equals(held, undefined)?;
2934 let is_null = self.held_equals(held, null)?;
2935 let cond = self.synthetic_expr(
2936 span,
2937 ExprKind::Binary {
2938 op: BinOp::Or,
2939 lhs: is_undefined,
2940 rhs: is_null,
2941 },
2942 )?;
2943 let none = self.synthetic_expr(span, ExprKind::ArrayLiteral { elements: vec![] })?;
2944 let namespace = self.synthetic_expr(
2945 span,
2946 ExprKind::Identifier(Ident {
2947 name: "Object".to_string(),
2948 span,
2949 }),
2950 )?;
2951 let callee = self.synthetic_expr(
2952 span,
2953 ExprKind::FieldAccess {
2954 receiver: namespace,
2955 name: Ident {
2956 name: "keys".to_string(),
2957 span,
2958 },
2959 },
2960 )?;
2961 let argument = self.synthetic_expr(span, ExprKind::Identifier(held.name.clone()))?;
2962 let keys = self.synthetic_expr(
2963 span,
2964 ExprKind::Call {
2965 callee,
2966 type_args: None,
2967 args: vec![argument],
2968 },
2969 )?;
2970 self.synthetic_expr(
2971 span,
2972 ExprKind::Ternary {
2973 cond,
2974 then_: none,
2975 else_: keys,
2976 },
2977 )
2978 }
2979
2980 fn held_equals(&mut self, held: &HeldForInObject, other: ExprId) -> Option<ExprId> {
2981 let object =
2982 self.synthetic_expr(held.name.span, ExprKind::Identifier(held.name.clone()))?;
2983 self.synthetic_expr(
2984 held.name.span,
2985 ExprKind::Binary {
2986 op: BinOp::Eq,
2987 lhs: object,
2988 rhs: other,
2989 },
2990 )
2991 }
2992
2993 fn synthetic_expr(&mut self, span: Span, kind: ExprKind) -> Option<ExprId> {
2994 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
2995 }
2996
2997 fn scan_for_of_shape(&self) -> bool {
2999 if !matches!(self.peek().kind, TokenKind::Let | TokenKind::Const) {
3000 return false;
3001 }
3002 let (start, mut depth) = match self.peek_at(1).kind {
3004 TokenKind::Identifier => match self.peek_at(2).kind {
3005 TokenKind::Identifier if self.peek_at_is_word(2, "of") => return true,
3006 TokenKind::Colon => (3, 0usize),
3007 _ => return false,
3008 },
3009 TokenKind::LeftBracket | TokenKind::LeftBrace => (2, 1usize),
3010 _ => return false,
3011 };
3012 let mut i = start;
3013 loop {
3014 match self.peek_at(i).kind {
3015 TokenKind::Eof => return false,
3016 TokenKind::LessThan
3017 | TokenKind::LeftParen
3018 | TokenKind::LeftBracket
3019 | TokenKind::LeftBrace => {
3020 depth += 1;
3021 }
3022 TokenKind::GreaterThan
3023 | TokenKind::RightParen
3024 | TokenKind::RightBracket
3025 | TokenKind::RightBrace => {
3026 if depth == 0 {
3027 return false;
3028 }
3029 depth -= 1;
3030 }
3031 TokenKind::Identifier if depth == 0 && self.peek_at_is_word(i, "of") => {
3034 return true;
3035 }
3036 TokenKind::Equals | TokenKind::Semicolon if depth == 0 => return false,
3037 _ => {}
3038 }
3039 i += 1;
3040 }
3041 }
3042
3043 fn parse_c_for_tail(&mut self, kw: Token) -> Option<StmtId> {
3045 let init = match self.peek().kind {
3046 TokenKind::Semicolon => {
3047 self.advance();
3048 Vec::new()
3049 }
3050 TokenKind::Let | TokenKind::Const => {
3051 self.trailing_declarators.clear();
3052 let is_const = matches!(self.peek().kind, TokenKind::Const);
3053 let mut init = vec![self.parse_let_or_const(is_const)?];
3054 init.extend(self.take_trailing_declarators());
3055 init
3056 }
3057 _ => self.parse_for_init_expressions()?,
3058 };
3059 let for_stmt = self.finish_c_for(kw, init.clone());
3060 if for_stmt.is_none() {
3061 self.discard_detached_patterns(&init);
3062 }
3063 for_stmt
3064 }
3065
3066 fn finish_c_for(&mut self, kw: Token, init: Vec<StmtId>) -> Option<StmtId> {
3067 let condition = if matches!(self.peek().kind, TokenKind::Semicolon) {
3068 None
3069 } else {
3070 Some(self.parse_expression()?)
3071 };
3072 if !matches!(self.peek().kind, TokenKind::Semicolon) {
3073 self.error_at_peek("expected `;` after `for` condition");
3074 return None;
3075 }
3076 self.advance();
3077
3078 let update = if matches!(self.peek().kind, TokenKind::RightParen) {
3079 None
3080 } else {
3081 Some(self.parse_for_update_stmt()?)
3082 };
3083 if !matches!(self.peek().kind, TokenKind::RightParen) {
3084 self.error_at_peek("expected `)` after `for` update");
3085 return None;
3086 }
3087 self.advance();
3088
3089 let body = self.parse_loop_body()?;
3090 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3091 .span
3092 .end;
3093 parse_arena_result(
3094 self.ast.try_push_stmt(Stmt {
3095 kind: StmtKind::For {
3096 init,
3097 condition,
3098 update,
3099 body,
3100 },
3101 span: self.span(kw.span.start, body_end),
3102 }),
3103 &mut self.fatal,
3104 )
3105 }
3106
3107 fn discard_detached_patterns(&mut self, stmts: &[StmtId]) {
3112 for &id in stmts {
3113 let Some(stmt) = parse_arena_result(self.ast.try_stmt_mut(id), &mut self.fatal) else {
3114 return;
3115 };
3116 if matches!(
3117 stmt.kind,
3118 StmtKind::LetPattern { .. } | StmtKind::ConstPattern { .. }
3119 ) {
3120 stmt.kind = StmtKind::Block(Vec::new());
3121 }
3122 }
3123 }
3124
3125 fn parse_for_init_expressions(&mut self) -> Option<Vec<StmtId>> {
3128 let mut init = vec![self.parse_for_clause_expr()?];
3129 while matches!(self.peek().kind, TokenKind::Comma) {
3130 self.advance();
3131 init.push(self.parse_for_clause_expr()?);
3132 }
3133 if !self.at_statement_end() {
3134 let last = parse_arena_result(self.ast.try_stmt(*init.last()?), &mut self.fatal)?;
3135 let what = if matches!(last.kind, StmtKind::Expr(_)) {
3136 "expression"
3137 } else {
3138 "assignment"
3139 };
3140 self.error_at_peek(format!("expected `;` after {what}"));
3141 return None;
3142 }
3143 self.finish_statement();
3144 Some(init)
3145 }
3146
3147 fn parse_for_update_stmt(&mut self) -> Option<StmtId> {
3150 let first = self.parse_for_clause_expr()?;
3151 if !matches!(self.peek().kind, TokenKind::Comma) {
3152 return Some(first);
3153 }
3154 let mut stmts = vec![first];
3155 while matches!(self.peek().kind, TokenKind::Comma) {
3156 self.advance();
3157 stmts.push(self.parse_for_clause_expr()?);
3158 }
3159 let start = parse_arena_result(self.ast.try_stmt(first), &mut self.fatal)?
3160 .span
3161 .start;
3162 let span = self.span(start, self.prev_token_end());
3163 parse_arena_result(
3164 self.ast.try_push_stmt(Stmt {
3165 kind: StmtKind::Block(stmts),
3166 span,
3167 }),
3168 &mut self.fatal,
3169 )
3170 }
3171
3172 fn parse_for_clause_expr(&mut self) -> Option<StmtId> {
3173 let expr_id = self.parse_expression()?;
3174 let span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
3175 let kind = self.statement_kind_for(expr_id)?;
3176 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
3177 }
3178
3179 fn parse_for_of_tail(&mut self, kw: Token) -> Option<StmtId> {
3180 let binding_tok = self.advance();
3181 let binding_kind = match binding_tok.kind {
3182 TokenKind::Let => crate::BindingKind::Let,
3183 TokenKind::Const => crate::BindingKind::Const,
3184 _ => return self.invariant_failure("scan_for_of_shape gated this"),
3185 };
3186 let (name, binding) = match self.peek().kind {
3187 TokenKind::LeftBracket => {
3188 let b = self.parse_binding()?;
3189 (None, Some(b))
3190 }
3191 TokenKind::LeftBrace => {
3192 let b = self.parse_binding()?;
3193 self.error_at(
3194 b.span(),
3195 "object destructuring is not supported in `for-of`; use array destructuring or unpack inside the loop body",
3196 );
3197 return None;
3198 }
3199 _ => {
3200 let name_tok = self.expect_binding_identifier(
3201 "expected identifier after `let`/`const` in `for-of`",
3202 )?;
3203 (Some(self.ident_from_token(&name_tok)), None)
3204 }
3205 };
3206 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3207 self.advance();
3208 Some(self.parse_type_annotation()?)
3209 } else {
3210 None
3211 };
3212 if !self.peek_identifier_text_is("of") {
3213 self.error_at_peek("expected `of` in `for-of`");
3214 return None;
3215 }
3216 self.advance();
3217
3218 let iter = self.parse_expression()?;
3219 if !matches!(self.peek().kind, TokenKind::RightParen) {
3220 self.error_at_peek("expected `)` after `for-of` iterable");
3221 return None;
3222 }
3223 self.advance();
3224
3225 let body = self.parse_loop_body()?;
3226 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3227 .span
3228 .end;
3229 let kind = match (binding, name) {
3230 (Some(binding), _) => StmtKind::ForOfPattern {
3231 binding_kind,
3232 binding,
3233 ty,
3234 iter,
3235 body,
3236 },
3237 (None, Some(name)) => StmtKind::ForOf {
3238 binding_kind,
3239 name,
3240 ty,
3241 iter,
3242 body,
3243 },
3244 (None, None) => return self.invariant_failure("either binding or name must be Some"),
3245 };
3246 parse_arena_result(
3247 self.ast.try_push_stmt(Stmt {
3248 kind,
3249 span: self.span(kw.span.start, body_end),
3250 }),
3251 &mut self.fatal,
3252 )
3253 }
3254
3255 fn parse_do_while(&mut self) -> Option<StmtId> {
3256 let kw = self.advance();
3257 let body = self.parse_loop_body()?;
3258 self.eat_asi_semicolon_before(TokenKind::While);
3259 if !matches!(self.peek().kind, TokenKind::While) {
3260 self.error_at_peek("expected `while` after `do` body");
3261 return None;
3262 }
3263 self.advance();
3264 let condition = self.parse_paren_condition()?;
3265 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
3269 self.advance().span.end
3270 } else {
3271 parse_arena_result(self.ast.try_expr(condition), &mut self.fatal)?
3272 .span
3273 .end
3274 };
3275 parse_arena_result(
3276 self.ast.try_push_stmt(Stmt {
3277 kind: StmtKind::DoWhile { body, condition },
3278 span: self.span(kw.span.start, end),
3279 }),
3280 &mut self.fatal,
3281 )
3282 }
3283
3284 fn parse_switch(&mut self) -> Option<StmtId> {
3286 let kw = self.advance();
3287 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3288 self.error_at_peek("expected `(` after `switch`");
3289 return None;
3290 }
3291 self.advance();
3292 let discriminant = self.parse_expression()?;
3293 if !matches!(self.peek().kind, TokenKind::RightParen) {
3294 self.error_at_peek("expected `)` after `switch` discriminant");
3295 return None;
3296 }
3297 self.advance();
3298
3299 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
3300 self.error_at_peek("expected `{` to start `switch` body");
3301 return None;
3302 }
3303 self.advance();
3304 self.block_depth += 1;
3305 let arms = self.parse_switch_arms();
3306 self.block_depth -= 1;
3307 let (cases, default) = arms?;
3308
3309 if !matches!(self.peek().kind, TokenKind::RightBrace) {
3310 self.error_at_peek("expected `}` to close `switch` body");
3311 return None;
3312 }
3313 let close = self.advance();
3314
3315 parse_arena_result(
3316 self.ast.try_push_stmt(Stmt {
3317 kind: StmtKind::Switch {
3318 discriminant,
3319 cases,
3320 default,
3321 },
3322 span: self.span(kw.span.start, close.span.end),
3323 }),
3324 &mut self.fatal,
3325 )
3326 }
3327
3328 fn parse_switch_arms(&mut self) -> Option<(Vec<SwitchCase>, Option<SwitchDefault>)> {
3330 let mut cases: Vec<SwitchCase> = Vec::new();
3331 let mut default: Option<SwitchDefault> = None;
3332 let mut pending_values: Vec<ExprId> = Vec::new();
3333 let mut pending_label_start: Option<u32> = None;
3334 let mut pending_default: Option<Span> = None;
3335
3336 loop {
3337 match self.peek().kind {
3338 TokenKind::Eof => break,
3339 TokenKind::Semicolon => {
3341 self.advance();
3342 }
3343 TokenKind::RightBrace => {
3344 if pending_label_start.is_some() {
3346 self.error_at_peek(
3347 "expected case body before `}` — case labels must be followed by statements",
3348 );
3349 return None;
3350 }
3351 break;
3352 }
3353 TokenKind::Case => {
3354 let case_kw = self.advance();
3355 if pending_label_start.is_none() {
3356 pending_label_start = Some(case_kw.span.start);
3357 }
3358 let value = self.parse_expression()?;
3359 if !matches!(self.peek().kind, TokenKind::Colon) {
3360 self.error_at_peek("expected `:` after `case` label");
3361 return None;
3362 }
3363 self.advance();
3364 pending_values.push(value);
3365 }
3366 TokenKind::Default => {
3367 let kw_tok = self.advance();
3368 if default.is_some() {
3369 self.error_at(kw_tok.span, "duplicate `default` clause in `switch`");
3370 return None;
3371 }
3372 if !matches!(self.peek().kind, TokenKind::Colon) {
3373 self.error_at_peek("expected `:` after `default`");
3374 return None;
3375 }
3376 self.advance();
3377 if pending_label_start.is_none() {
3378 pending_label_start = Some(kw_tok.span.start);
3379 }
3380 pending_default = Some(kw_tok.span);
3381 }
3382 _ => {
3383 if pending_label_start.is_none() {
3384 self.error_at_peek(
3385 "expected `case` or `default` at the start of a `switch` body",
3386 );
3387 return None;
3388 }
3389 let Some(label_start) = pending_label_start else {
3390 return self.invariant_failure("switch arm has no label");
3391 };
3392 let body = self.parse_switch_arm_body(label_start)?;
3393 let arm_span =
3394 parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
3395 if !pending_values.is_empty() {
3396 let first = parse_arena_result(
3397 self.ast.try_expr(pending_values[0]),
3398 &mut self.fatal,
3399 )?
3400 .span
3401 .start;
3402 let case_span = self.span(first, arm_span.end);
3403 cases.push(SwitchCase {
3404 values: std::mem::take(&mut pending_values),
3405 body,
3406 span: case_span,
3407 });
3408 }
3409 if let Some(def_kw_span) = pending_default.take() {
3410 default = Some(SwitchDefault {
3411 body,
3412 span: self.span(def_kw_span.start, arm_span.end),
3413 });
3414 }
3415 pending_label_start = None;
3416 }
3417 }
3418 }
3419 Some((cases, default))
3420 }
3421
3422 fn parse_switch_arm_body(&mut self, body_start: u32) -> Option<StmtId> {
3424 let mut stmts: Vec<StmtId> = Vec::new();
3425 let mut end_offset = body_start;
3426 loop {
3427 if matches!(
3428 self.peek().kind,
3429 TokenKind::Case | TokenKind::Default | TokenKind::RightBrace | TokenKind::Eof
3430 ) {
3431 break;
3432 }
3433 if matches!(self.peek().kind, TokenKind::Semicolon) {
3434 end_offset = self.peek().span.end;
3435 self.advance();
3436 continue;
3437 }
3438 let pos_before = self.pos;
3439 if let Some(id) = self.parse_statement() {
3440 stmts.push(id);
3441 stmts.extend(self.take_trailing_declarators());
3442 let last = stmts.last().copied().unwrap_or(id);
3443 end_offset = parse_arena_result(self.ast.try_stmt(last), &mut self.fatal)?
3444 .span
3445 .end;
3446 } else {
3447 self.recover();
3448 if self.pos == pos_before && !self.is_at_eof() {
3449 self.advance();
3450 }
3451 end_offset = end_offset.max(self.prev_token_end());
3454 }
3455 }
3456 let span = self.span(body_start, end_offset);
3457 parse_arena_result(
3458 self.ast.try_push_stmt(Stmt {
3459 kind: StmtKind::Block(stmts),
3460 span,
3461 }),
3462 &mut self.fatal,
3463 )
3464 }
3465
3466 fn parse_break(&mut self) -> Option<StmtId> {
3467 let kw = self.advance();
3468 if !matches!(self.peek().kind, TokenKind::Semicolon) {
3469 self.error_at_peek("expected `;` after `break`");
3470 return None;
3471 }
3472 let semi = self.advance();
3473 parse_arena_result(
3474 self.ast.try_push_stmt(Stmt {
3475 kind: StmtKind::Break,
3476 span: self.span(kw.span.start, semi.span.end),
3477 }),
3478 &mut self.fatal,
3479 )
3480 }
3481
3482 fn parse_continue(&mut self) -> Option<StmtId> {
3483 let kw = self.advance();
3484 if !matches!(self.peek().kind, TokenKind::Semicolon) {
3485 self.error_at_peek("expected `;` after `continue`");
3486 return None;
3487 }
3488 let semi = self.advance();
3489 parse_arena_result(
3490 self.ast.try_push_stmt(Stmt {
3491 kind: StmtKind::Continue,
3492 span: self.span(kw.span.start, semi.span.end),
3493 }),
3494 &mut self.fatal,
3495 )
3496 }
3497
3498 fn parse_return(&mut self) -> Option<StmtId> {
3499 let kw = self.advance();
3500
3501 let value = if matches!(self.peek().kind, TokenKind::Semicolon) {
3502 None
3503 } else {
3504 Some(self.parse_expression()?)
3505 };
3506
3507 if !self.at_statement_end() {
3508 self.error_at_peek("expected `;` after return");
3509 return None;
3510 }
3511 let end = self.finish_statement();
3512
3513 parse_arena_result(
3514 self.ast.try_push_stmt(Stmt {
3515 kind: StmtKind::Return(value),
3516 span: self.span(kw.span.start, end),
3517 }),
3518 &mut self.fatal,
3519 )
3520 }
3521
3522 fn parse_throw(&mut self) -> Option<StmtId> {
3523 let kw = self.advance();
3524
3525 if matches!(self.peek().kind, TokenKind::Semicolon) {
3526 self.error_at_peek_with_help(
3527 "expected expression after `throw`",
3528 vec!["throw new Error(\"message\");".to_string()],
3529 );
3530 return None;
3531 }
3532
3533 let value = self.parse_expression()?;
3534
3535 if !self.at_statement_end() {
3536 self.error_at_peek("expected `;` after `throw`");
3537 return None;
3538 }
3539 let end = self.finish_statement();
3540
3541 parse_arena_result(
3542 self.ast.try_push_stmt(Stmt {
3543 kind: StmtKind::Throw { value },
3544 span: self.span(kw.span.start, end),
3545 }),
3546 &mut self.fatal,
3547 )
3548 }
3549
3550 fn parse_try(&mut self) -> Option<StmtId> {
3551 let kw = self.advance();
3552
3553 let body = self.parse_block()?;
3554
3555 let mut catches: Vec<CatchClause> = Vec::new();
3556 let mut finally: Option<StmtId> = None;
3557 let mut tail_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3558 .span
3559 .end;
3560
3561 loop {
3562 match self.peek().kind {
3563 TokenKind::Catch => {
3564 if finally.is_some() {
3565 self.error_at_peek("`catch` clause must appear before `finally`");
3566 return None;
3567 }
3568 let clause = self.parse_catch_clause()?;
3569 tail_end = clause.span.end;
3570 catches.push(clause);
3571 }
3572 TokenKind::Finally => {
3573 if finally.is_some() {
3574 self.error_at_peek("duplicate `finally` clause");
3575 return None;
3576 }
3577 self.advance();
3578 let block = self.parse_block()?;
3579 tail_end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
3580 .span
3581 .end;
3582 finally = Some(block);
3583 }
3584 _ => break,
3585 }
3586 }
3587
3588 if catches.is_empty() && finally.is_none() {
3589 self.error_at(
3590 self.span(kw.span.start, tail_end),
3591 "try requires at least one of `catch` or `finally`",
3592 );
3593 return None;
3594 }
3595
3596 parse_arena_result(
3597 self.ast.try_push_stmt(Stmt {
3598 kind: StmtKind::Try {
3599 body,
3600 catches,
3601 finally,
3602 },
3603 span: self.span(kw.span.start, tail_end),
3604 }),
3605 &mut self.fatal,
3606 )
3607 }
3608
3609 fn parse_catch_clause(&mut self) -> Option<CatchClause> {
3610 let kw = self.advance();
3611
3612 let (binding, ty) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
3613 (
3615 Ident {
3616 name: "#catch".into(),
3617 span: kw.span,
3618 },
3619 None,
3620 )
3621 } else {
3622 self.parse_catch_binding()?
3623 };
3624
3625 let body = self.parse_block()?;
3626 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3627 .span
3628 .end;
3629
3630 Some(CatchClause {
3631 binding,
3632 ty,
3633 body,
3634 span: self.span(kw.span.start, body_end),
3635 })
3636 }
3637
3638 fn parse_catch_binding(&mut self) -> Option<(Ident, Option<TypeAnnotation>)> {
3639 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3640 self.error_at_peek("expected `(` after `catch`");
3641 return None;
3642 }
3643 self.advance();
3644 let name_tok = self.expect_binding_identifier("expected catch binding name")?;
3645 let binding = self.ident_from_token(&name_tok);
3646 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3647 self.advance();
3648 if self.reject_any_catch_annotation() {
3649 None
3651 } else {
3652 Some(self.parse_type_annotation()?)
3653 }
3654 } else {
3655 None
3656 };
3657 if !matches!(self.peek().kind, TokenKind::RightParen) {
3658 self.error_at_peek("expected `)` after catch binding");
3659 return None;
3660 }
3661 self.advance();
3662 Some((binding, ty))
3663 }
3664
3665 fn reject_any_catch_annotation(&mut self) -> bool {
3668 let is_bare_any = self.peek_identifier_text_is("any")
3669 && matches!(self.peek_at(1).kind, TokenKind::RightParen);
3670 if !is_bare_any {
3671 return false;
3672 }
3673 let any_tok = self.advance();
3674 self.error_at_with_help(
3676 any_tok.span,
3677 "`any` is not supported",
3678 vec![
3679 "write `catch (e)` to catch every thrown error, or name an error class, \
3680 as in `catch (e: RangeError)`, to catch only that type"
3681 .to_string(),
3682 ],
3683 );
3684 true
3685 }
3686
3687 fn parse_paren_condition(&mut self) -> Option<ExprId> {
3688 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3689 self.error_at_peek("expected `(`");
3690 return None;
3691 }
3692 self.advance();
3693 let expr = self.parse_expression()?;
3694 if !matches!(self.peek().kind, TokenKind::RightParen) {
3695 self.error_at_peek("expected `)`");
3696 return None;
3697 }
3698 self.advance();
3699 Some(expr)
3700 }
3701
3702 fn parse_predicate_or_return_type(
3703 &mut self,
3704 type_pos: TypePos,
3705 ) -> Option<(
3706 Option<TypeAnnotation>,
3707 Option<crate::TypePredicateAnnotation>,
3708 )> {
3709 if matches!(self.peek().kind, TokenKind::Identifier) && self.peek_at_is_word(1, "is") {
3710 let param_tok = self.advance();
3711 let param = self.ident_from_token(¶m_tok);
3712 self.advance();
3713 let asserted = self.parse_type_annotation_in(type_pos)?;
3714 let span = self.span(param.span.start, asserted.span.end);
3715 return Some((
3716 None,
3717 Some(crate::TypePredicateAnnotation {
3718 param,
3719 asserted,
3720 span,
3721 }),
3722 ));
3723 }
3724 Some((Some(self.parse_type_annotation_in(type_pos)?), None))
3725 }
3726
3727 fn parse_type_annotation(&mut self) -> Option<TypeAnnotation> {
3728 self.parse_type_annotation_in(TypePos::Anywhere)
3729 }
3730
3731 fn parse_type_annotation_in(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3732 let leading_pipe = matches!(self.peek().kind, TokenKind::Pipe);
3734 if leading_pipe {
3735 self.advance();
3736 }
3737 let first = self.parse_type_array(type_pos)?;
3738 if !matches!(self.peek().kind, TokenKind::Pipe) {
3739 return Some(first);
3740 }
3741 let start = first.span.start;
3742 let mut end = first.span.end;
3743 let mut members = vec![first];
3744 while matches!(self.peek().kind, TokenKind::Pipe) {
3745 self.advance();
3746 let member = self.parse_type_array(type_pos)?;
3747 end = member.span.end;
3748 members.push(member);
3749 }
3750 Some(TypeAnnotation {
3751 kind: TypeAnnotationKind::Union(members),
3752 span: self.span(start, end),
3753 })
3754 }
3755
3756 fn parse_type_array(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3757 self.with_recursion_limit(|parser| parser.parse_type_array_inner(type_pos))
3758 }
3759
3760 fn parse_type_array_inner(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3761 if self.is_contextual_type_operator(self.pos, "keyof") {
3765 let kw = self.advance();
3766 let operand = self.parse_type_array(type_pos)?;
3767 let span = self.span(kw.span.start, operand.span.end);
3768 return Some(TypeAnnotation {
3769 kind: TypeAnnotationKind::KeyOf(Box::new(operand)),
3770 span,
3771 });
3772 }
3773 if self.is_contextual_type_operator(self.pos, "readonly") {
3774 return self.parse_readonly_type(type_pos);
3775 }
3776
3777 let mut ty = match self.peek().kind {
3778 TokenKind::TemplateHead(ref text) => self.parse_template_type(text.clone())?,
3779 TokenKind::TemplateNoSubstitution(ref text) => {
3780 let text = text.clone();
3781 let token = self.advance();
3782 TypeAnnotation {
3783 kind: TypeAnnotationKind::StringLiteral(text),
3784 span: token.span,
3785 }
3786 }
3787
3788 TokenKind::Typeof => {
3794 let kw = self.advance();
3795 let first = self.expect_identifier("expected a value name after `typeof`")?;
3796 let mut path = vec![self.type_name(first.span)?];
3797 let mut end = first.span.end;
3798 while matches!(self.peek().kind, TokenKind::Dot) {
3799 self.advance();
3800 let seg = self.expect_identifier_name("expected a property name after `.`")?;
3801 end = seg.span.end;
3802 path.push(self.type_name(seg.span)?);
3803 }
3804 TypeAnnotation {
3805 kind: TypeAnnotationKind::TypeOf { path },
3806 span: self.span(kw.span.start, end),
3807 }
3808 }
3809 TokenKind::Identifier | TokenKind::Void => {
3810 let leading_is_identifier = matches!(self.peek().kind, TokenKind::Identifier);
3812 let tok = self.advance();
3813 let first_span = tok.span;
3814 let mut path_end = first_span.end;
3815 let first_name = self.type_name(first_span)?;
3816 let mut path = vec![first_name.clone()];
3817 if leading_is_identifier {
3818 while matches!(self.peek().kind, TokenKind::Dot) {
3819 self.advance();
3820 if !matches!(self.peek().kind, TokenKind::Identifier) {
3821 self.error_at_peek("expected identifier after `.` in type name");
3822 return None;
3823 }
3824 let seg = self.advance();
3825 path_end = seg.span.end;
3826 path.push(self.type_name(seg.span)?);
3827 }
3828 }
3829 let (args, end) = if matches!(self.peek().kind, TokenKind::LessThan)
3831 && !self.line_break_before_peek()
3832 {
3833 self.parse_type_argument_list()?
3834 } else {
3835 (Vec::new(), path_end)
3836 };
3837 let outer_span = self.span(first_span.start, end);
3838 let kind = if path.len() == 1 {
3839 if first_name.name == "any" {
3840 self.error_at_with_help(
3841 first_span,
3842 "`any` is not supported",
3843 vec![
3844 "annotate a concrete type instead — an object shape \
3845 like `{ id: string }`, a named `interface`, or \
3846 `unknown` (a safe dynamic type you narrow before use)"
3847 .to_string(),
3848 ],
3849 );
3850 }
3851 TypeAnnotationKind::Name {
3852 name: first_name,
3853 args,
3854 }
3855 } else {
3856 TypeAnnotationKind::Qualified { path, args }
3857 };
3858 TypeAnnotation {
3859 kind,
3860 span: outer_span,
3861 }
3862 }
3863 TokenKind::NullLiteral => {
3865 let tok = self.advance();
3866 TypeAnnotation {
3867 kind: TypeAnnotationKind::Name {
3868 name: self.type_name(tok.span)?,
3869 args: Vec::new(),
3870 },
3871 span: tok.span,
3872 }
3873 }
3874 TokenKind::StringLiteral(_) => {
3875 let tok = self.advance();
3876 let span = tok.span;
3877 let TokenKind::StringLiteral(s) = tok.kind else {
3878 unreachable!("advance preserves the matched StringLiteral variant");
3880 };
3881 TypeAnnotation {
3882 kind: TypeAnnotationKind::StringLiteral(s),
3883 span,
3884 }
3885 }
3886 TokenKind::NumberLiteral(_) => {
3887 let tok = self.advance();
3888 let span = tok.span;
3889 let TokenKind::NumberLiteral(v) = tok.kind else {
3890 unreachable!("advance preserves the matched NumberLiteral variant");
3892 };
3893 let canonical = if v == 0.0 { 0.0 } else { v };
3895 TypeAnnotation {
3896 kind: TypeAnnotationKind::NumberLiteral(crate::types::LiteralF64(canonical)),
3897 span,
3898 }
3899 }
3900 TokenKind::BigIntLiteral(_) => {
3901 let tok = self.advance();
3902 let TokenKind::BigIntLiteral(digits) = tok.kind else {
3903 unreachable!("advance preserves the matched BigIntLiteral variant");
3905 };
3906 TypeAnnotation {
3907 kind: TypeAnnotationKind::BigIntLiteral(digits),
3908 span: tok.span,
3909 }
3910 }
3911 TokenKind::Minus
3912 if matches!(
3913 self.peek_at(1).kind,
3914 TokenKind::NumberLiteral(_) | TokenKind::BigIntLiteral(_)
3915 ) =>
3916 {
3917 let minus = self.advance().span;
3918 let tok = self.advance();
3919 let span = self.span(minus.start, tok.span.end);
3920 let kind =
3921 match tok.kind {
3922 TokenKind::NumberLiteral(v) => TypeAnnotationKind::NumberLiteral(
3923 crate::types::LiteralF64(if v == 0.0 { 0.0 } else { -v }),
3924 ),
3925 TokenKind::BigIntLiteral(digits) => TypeAnnotationKind::BigIntLiteral(
3926 crate::types::negate_bigint_digits(&digits),
3927 ),
3928 _ => unreachable!("guarded to a numeric literal after `-`"),
3930 };
3931 TypeAnnotation { kind, span }
3932 }
3933 TokenKind::BooleanLiteral(value) => {
3934 let span = self.advance().span;
3935 TypeAnnotation {
3936 kind: TypeAnnotationKind::BooleanLiteral(value),
3937 span,
3938 }
3939 }
3940 TokenKind::LeftBrace => self.parse_object_type_annotation()?,
3941 TokenKind::LeftParen if self.paren_at_opens_function_type(self.pos, type_pos) => {
3942 self.parse_function_type_annotation()?
3943 }
3944 TokenKind::LeftParen => self.parse_grouped_or_function_type()?,
3945 TokenKind::LeftBracket => self.parse_tuple_type_annotation()?,
3947 _ => {
3948 self.error_at_peek("expected type");
3949 return None;
3950 }
3951 };
3952 let mut array_suffixes = 0usize;
3955 while matches!(self.peek().kind, TokenKind::LeftBracket) && !self.line_break_before_peek() {
3958 array_suffixes += 1;
3959 if self.recursion_depth.saturating_add(array_suffixes) >= MAX_PARSE_DEPTH {
3960 self.stop_at_recursion_limit();
3961 return None;
3962 }
3963 let open = self.advance();
3964 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3965 let index = self.parse_type_annotation()?;
3968 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3969 self.error_at(open.span, "expected `]` to close indexed access type");
3970 return None;
3971 }
3972 let close = self.advance();
3973 let span = self.span(ty.span.start, close.span.end);
3974 ty = TypeAnnotation {
3975 kind: TypeAnnotationKind::IndexedAccess {
3976 object: Box::new(ty),
3977 index: Box::new(index),
3978 },
3979 span,
3980 };
3981 continue;
3982 }
3983 let close = self.advance();
3984 let span = self.span(ty.span.start, close.span.end);
3985 ty = TypeAnnotation {
3986 kind: TypeAnnotationKind::Array(Box::new(ty)),
3987 span,
3988 };
3989 }
3990 Some(ty)
3991 }
3992
3993 fn parse_readonly_type(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3997 let kw = self.advance();
3998 let opens_group = matches!(self.peek().kind, TokenKind::LeftParen);
3999 let operand = self.parse_type_array(type_pos)?;
4000 let grouped = opens_group
4003 && matches!(
4004 self.pos
4005 .checked_sub(1)
4006 .and_then(|index| self.tokens.get(index))
4007 .map(|token| &token.kind),
4008 Some(TokenKind::RightParen)
4009 );
4010 if grouped
4011 || !matches!(
4012 operand.kind,
4013 TypeAnnotationKind::Array(_) | TypeAnnotationKind::Tuple(_)
4014 )
4015 {
4016 self.error_at_with_help(
4017 kw.span,
4018 "`readonly` only applies to array and tuple types",
4019 vec![
4020 "write `readonly T[]` or `readonly [A, B]`; to protect an object's \
4021 properties, mark each one `readonly` instead"
4022 .to_string(),
4023 ],
4024 );
4025 return Some(operand);
4026 }
4027 let span = self.span(kw.span.start, operand.span.end);
4028 Some(TypeAnnotation {
4029 kind: TypeAnnotationKind::Readonly(Box::new(operand)),
4030 span,
4031 })
4032 }
4033
4034 fn paren_at_opens_function_type(&self, index: usize, type_pos: TypePos) -> bool {
4045 let after = |offset: usize| self.tokens.get(index + offset).map(|t| &t.kind);
4046 match (after(1), after(2)) {
4047 (Some(TokenKind::RightParen | TokenKind::DotDotDot), _) => true,
4049 (Some(TokenKind::Identifier), Some(TokenKind::Colon | TokenKind::Question)) => true,
4051 (Some(TokenKind::Identifier), Some(TokenKind::Comma | TokenKind::RightParen)) => {
4060 type_pos == TypePos::Anywhere
4061 && self.index_after_matching_paren(index).is_some_and(|i| {
4062 matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow))
4063 })
4064 }
4065 _ => false,
4067 }
4068 }
4069
4070 fn parse_grouped_or_function_type(&mut self) -> Option<TypeAnnotation> {
4075 let open = self.pos;
4076 let diagnostics_before = self.diagnostics.len();
4077 if let Some(ty) = self.parse_grouped_type_annotation() {
4078 return Some(ty);
4079 }
4080 let arrow_follows = self
4081 .index_after_matching_paren(open)
4082 .is_some_and(|i| matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)));
4083 if !arrow_follows {
4084 return None;
4085 }
4086 self.pos = open;
4087 self.diagnostics.truncate(diagnostics_before);
4088 self.parse_function_type_annotation()
4089 }
4090
4091 fn parse_grouped_type_annotation(&mut self) -> Option<TypeAnnotation> {
4094 let open = self.advance();
4095 let inner = self.parse_type_annotation()?;
4096 if !matches!(self.peek().kind, TokenKind::RightParen) {
4097 self.error_at_peek("expected `)` to close a parenthesized type");
4098 return None;
4099 }
4100 let close = self.advance();
4101 Some(TypeAnnotation {
4102 span: self.span(open.span.start, close.span.end),
4103 ..inner
4104 })
4105 }
4106
4107 fn parse_function_type_annotation(&mut self) -> Option<TypeAnnotation> {
4109 let open = self.advance();
4110 let params = self.parse_function_type_params()?;
4111 if !matches!(self.peek().kind, TokenKind::Arrow) {
4112 self.error_at_peek("expected `=>` in function-type annotation");
4113 return None;
4114 }
4115 self.advance();
4116 let return_type = self.parse_type_annotation()?;
4117 let end = return_type.span.end;
4118 Some(TypeAnnotation {
4119 kind: TypeAnnotationKind::Function {
4120 params,
4121 return_type: Box::new(return_type),
4122 },
4123 span: self.span(open.span.start, end),
4124 })
4125 }
4126
4127 fn parse_function_type_params(&mut self) -> Option<Vec<TypeAnnotationField>> {
4131 let mut params: Vec<TypeAnnotationField> = Vec::new();
4132 if !matches!(self.peek().kind, TokenKind::RightParen) {
4133 loop {
4134 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
4135 self.advance();
4136 true
4137 } else {
4138 false
4139 };
4140 let name_tok = self.expect_binding_identifier(if rest {
4141 "expected identifier after `...` in function-type annotation"
4142 } else {
4143 "expected parameter name in function-type annotation"
4144 })?;
4145 let name = self.ident_from_token(&name_tok);
4146 let optional = self.parse_parameter_optional(rest, false)?;
4147 if !matches!(self.peek().kind, TokenKind::Colon) {
4148 if rest {
4149 self.error_at_peek_with_help(
4150 "rest parameter requires a type annotation",
4151 vec!["(...args: T[]) => R".to_string()],
4152 );
4153 } else {
4154 self.error_at_peek(
4155 "expected `:` after parameter name (function-type params \
4156 require named annotations in v1)",
4157 );
4158 }
4159 return None;
4160 }
4161 self.advance();
4162 let ty = self.parse_type_annotation()?;
4163 if rest && !is_rest_array_annotation(&ty) {
4164 self.error_at(ty.span, "rest parameter type must be an array");
4165 return None;
4166 }
4167 if matches!(self.peek().kind, TokenKind::Equals) {
4168 if rest {
4169 self.error_rest_parameter_default();
4170 } else {
4171 self.error_at_peek(
4172 "default values in function-type annotations are not yet supported",
4173 );
4174 }
4175 return None;
4176 }
4177 params.push(TypeAnnotationField {
4178 name,
4179 ty,
4180 optional,
4181 readonly: false,
4182 rest,
4183 method: false,
4184 });
4185 match self.peek().kind {
4186 TokenKind::Comma => {
4187 if rest {
4188 let comma = self.advance();
4189 self.error_at(comma.span, "rest parameter must be the last parameter");
4190 return None;
4191 }
4192 self.advance();
4193 if matches!(self.peek().kind, TokenKind::RightParen) {
4194 break;
4195 }
4196 }
4197 TokenKind::RightParen => break,
4198 _ => {
4199 self.error_at_peek("expected `,` or `)`");
4200 return None;
4201 }
4202 }
4203 }
4204 self.check_parameter_order(params.iter().map(ParamOrder::of_field));
4205 }
4206 if !matches!(self.peek().kind, TokenKind::RightParen) {
4207 self.error_at_peek("expected `)`");
4208 return None;
4209 }
4210 self.advance();
4211 Some(params)
4212 }
4213
4214 fn parse_tuple_type_annotation(&mut self) -> Option<TypeAnnotation> {
4215 let open = self.advance();
4216 if matches!(self.peek().kind, TokenKind::RightBracket) {
4217 let close = self.peek().span;
4218 self.error_at(
4219 self.span(open.span.start, close.end),
4220 "tuple types must have at least one element",
4221 );
4222 return None;
4223 }
4224 let mut elements: Vec<TypeAnnotation> = Vec::new();
4225 loop {
4226 if matches!(self.peek().kind, TokenKind::DotDotDot) {
4227 self.error_at_with_help(
4228 self.peek().span,
4229 "rest elements in tuple types are not supported",
4230 vec!["use an array type `T[]` for a list of varying length".to_string()],
4231 );
4232 return None;
4233 }
4234 let label = self.skip_tuple_element_label();
4237 let mut ty = self.parse_type_annotation()?;
4238 let suffix_optional = matches!(self.peek().kind, TokenKind::Question);
4239 if suffix_optional && label != TupleElementLabel::None {
4240 self.error_at_peek_with_help(
4241 "put the optional marker after the tuple element label",
4242 vec!["write `name?: T` instead of `name: T?`".to_string()],
4243 );
4244 return None;
4245 }
4246 let optional = label == TupleElementLabel::Optional || suffix_optional;
4247 if optional {
4248 let end = if suffix_optional {
4249 self.advance().span.end
4250 } else {
4251 ty.span.end
4252 };
4253 let span = self.span(ty.span.start, end);
4254 ty = TypeAnnotation {
4255 kind: TypeAnnotationKind::Optional(Box::new(ty)),
4256 span,
4257 };
4258 } else if elements
4259 .iter()
4260 .any(|element| matches!(element.kind, TypeAnnotationKind::Optional(_)))
4261 {
4262 self.error_at(
4263 ty.span,
4264 "a required tuple element cannot follow an optional element",
4265 );
4266 return None;
4267 }
4268 elements.push(ty);
4269 match self.peek().kind {
4270 TokenKind::Comma => {
4271 self.advance();
4272 if matches!(self.peek().kind, TokenKind::RightBracket) {
4273 break;
4274 }
4275 }
4276 TokenKind::RightBracket => break,
4277 _ => {
4278 self.error_at_peek("expected `,` or `]`");
4279 return None;
4280 }
4281 }
4282 }
4283 if !matches!(self.peek().kind, TokenKind::RightBracket) {
4284 self.error_at_peek("expected `]`");
4285 return None;
4286 }
4287 let close = self.advance();
4288 Some(TypeAnnotation {
4289 kind: TypeAnnotationKind::Tuple(elements),
4290 span: self.span(open.span.start, close.span.end),
4291 })
4292 }
4293
4294 fn skip_tuple_element_label(&mut self) -> TupleElementLabel {
4296 if !is_property_name(&self.peek().kind) {
4297 return TupleElementLabel::None;
4298 }
4299 match self.peek_at(1).kind {
4300 TokenKind::Colon => {
4301 self.advance();
4302 self.advance();
4303 TupleElementLabel::Required
4304 }
4305 TokenKind::Question if matches!(self.peek_at(2).kind, TokenKind::Colon) => {
4306 self.advance();
4307 self.advance();
4308 self.advance();
4309 TupleElementLabel::Optional
4310 }
4311 _ => TupleElementLabel::None,
4312 }
4313 }
4314
4315 fn peek_is_parameterless_index_signature(&self) -> bool {
4318 matches!(self.peek().kind, TokenKind::LeftBracket)
4319 && matches!(self.peek_at(1).kind, TokenKind::RightBracket)
4320 }
4321
4322 fn skip_parameterless_index_signature(&mut self, member_start: u32) -> Option<()> {
4326 self.advance();
4327 let close = self.advance();
4328 self.error_at_with_help(
4329 self.span(member_start, close.span.end),
4330 "an index signature must declare exactly one parameter",
4331 vec!["write `[key: string]: V`".to_string()],
4332 );
4333 if matches!(self.peek().kind, TokenKind::Colon) {
4334 self.advance();
4335 self.parse_type_annotation()?;
4336 }
4337 Some(())
4338 }
4339
4340 fn parse_template_type(&mut self, text: String) -> Option<TypeAnnotation> {
4341 let head = self.advance();
4342 let mut parts = vec![text];
4343 let mut substitutions = Vec::new();
4344 loop {
4345 substitutions.push(self.parse_type_annotation()?);
4346 let token = self.advance();
4347 match token.kind {
4348 TokenKind::TemplateMiddle(text) => parts.push(text),
4349 TokenKind::TemplateTail(text) => {
4350 parts.push(text);
4351 return Some(TypeAnnotation {
4352 kind: TypeAnnotationKind::TemplateLiteral {
4353 parts,
4354 substitutions,
4355 },
4356 span: self.span(head.span.start, token.span.end),
4357 });
4358 }
4359 _ => {
4360 self.error_at_peek("expected template type continuation");
4361 return None;
4362 }
4363 }
4364 }
4365 }
4366
4367 fn parse_index_signature(&mut self, readonly: bool) -> Option<crate::IndexSignatureAnnotation> {
4368 let open = self.advance();
4369 let parameter = self.expect_property_ident("expected index parameter name")?;
4370 self.reject_eval_or_arguments(parameter.span);
4371 if !matches!(self.peek().kind, TokenKind::Colon) {
4372 self.error_at_peek("expected `:` after index parameter name");
4373 return None;
4374 }
4375 self.advance();
4376 let key = self.parse_type_annotation()?;
4377 if !matches!(self.peek().kind, TokenKind::RightBracket) {
4378 self.error_at_peek("expected `]` after string index key type");
4379 return None;
4380 }
4381 self.advance();
4382 if !matches!(self.peek().kind, TokenKind::Colon) {
4383 self.error_at_peek("expected `:` before index value type");
4384 return None;
4385 }
4386 self.advance();
4387 let value = self.parse_type_annotation()?;
4388 Some(crate::IndexSignatureAnnotation {
4389 key,
4390 span: self.span(open.span.start, value.span.end),
4391 value,
4392 readonly,
4393 })
4394 }
4395
4396 fn parse_object_type_annotation(&mut self) -> Option<TypeAnnotation> {
4397 let open = self.advance();
4398 let mut fields: Vec<TypeAnnotationField> = Vec::new();
4399 let mut index = None;
4400 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
4401 && !self.peek_starts_top_level_item()
4402 {
4403 let member_start = self.peek().span.start;
4404 let readonly = self.eat_readonly_property_modifier();
4405 if self.peek_is_parameterless_index_signature() {
4406 self.skip_parameterless_index_signature(member_start)?;
4407 } else if self.peek_is_construct_signature() {
4408 self.reject_construct_signature()?;
4409 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
4410 let signature = self.parse_index_signature(readonly)?;
4411 if index.is_some() {
4412 self.error_at_with_help(
4413 signature.span,
4414 "duplicate string index signature",
4415 vec![],
4416 );
4417 }
4418 index = Some(Box::new(signature));
4419 } else {
4420 let field = self.parse_object_type_field(readonly, member_start)?;
4421 if let Some(existing) = fields.iter().find(|f| f.name.name == field.name.name) {
4422 self.diagnostics.push(Diagnostic {
4423 severity: Severity::Error,
4424 span: field.name.span,
4425 message: format!("duplicate field `{}` in object type", field.name.name),
4426 help: vec![],
4427 notes: vec![(existing.name.span, "first defined here".into())],
4428 });
4429 }
4430 fields.push(field);
4431 }
4432 self.finish_type_member(self.prev_token_end(), "expected `;`, `,`, or `}`")?;
4433 }
4434 if !matches!(self.peek().kind, TokenKind::RightBrace) {
4435 self.error_at_peek("expected `}`");
4436 return None;
4437 }
4438 let close = self.advance();
4439 Some(TypeAnnotation {
4440 kind: TypeAnnotationKind::Object { index, fields },
4441 span: self.span(open.span.start, close.span.end),
4442 })
4443 }
4444
4445 fn peek_starts_top_level_item(&self) -> bool {
4448 let import_statement = matches!(self.peek().kind, TokenKind::Import)
4449 && matches!(
4450 self.peek_at(1).kind,
4451 TokenKind::LeftBrace
4452 | TokenKind::Star
4453 | TokenKind::Identifier
4454 | TokenKind::StringLiteral(_)
4455 );
4456 import_statement || self.peek_starts_declaration()
4457 }
4458
4459 fn peek_starts_declaration(&self) -> bool {
4466 if matches!(self.peek().kind, TokenKind::Export)
4467 && matches!(
4468 self.peek_at(1).kind,
4469 TokenKind::Default | TokenKind::LeftBrace | TokenKind::Star
4470 )
4471 {
4472 return true;
4473 }
4474 let mut offset = 0;
4475 while self.peek_at_is_declaration_modifier(offset) {
4476 offset += 1;
4477 }
4478 let declaration_keyword = matches!(
4479 self.peek_at(offset).kind,
4480 TokenKind::Function
4481 | TokenKind::Class
4482 | TokenKind::Let
4483 | TokenKind::Const
4484 | TokenKind::Enum
4485 | TokenKind::Interface
4486 ) || self.peek_at_is_word(offset, "type");
4487 declaration_keyword && matches!(self.peek_at(offset + 1).kind, TokenKind::Identifier)
4488 }
4489
4490 fn peek_at_is_declaration_modifier(&self, offset: usize) -> bool {
4492 matches!(self.peek_at(offset).kind, TokenKind::Export)
4493 || ["declare", "async", "abstract"]
4494 .iter()
4495 .any(|word| self.peek_at_is_word(offset, word))
4496 }
4497
4498 fn peek_is_construct_signature(&self) -> bool {
4503 matches!(self.peek().kind, TokenKind::New)
4504 && matches!(
4505 self.peek_at(1).kind,
4506 TokenKind::LeftParen | TokenKind::LessThan
4507 )
4508 }
4509
4510 fn reject_construct_signature(&mut self) -> Option<()> {
4514 let keyword = self.advance();
4515 self.error_at_with_help(
4516 keyword.span,
4517 "construct signatures are not supported in object types",
4518 vec![
4519 "declare an `interface` with the `new (…)` signature, or pass a factory \
4520 function such as `() => T`"
4521 .to_string(),
4522 ],
4523 );
4524 if matches!(self.peek().kind, TokenKind::LessThan) {
4525 self.parse_generic_param_list()?;
4526 }
4527 if !matches!(self.peek().kind, TokenKind::LeftParen) {
4528 self.error_at_peek("expected `(` after construct signature type parameters");
4529 return None;
4530 }
4531 self.parse_object_type_method_signature(keyword.span.start)?;
4532 Some(())
4533 }
4534
4535 fn parse_object_type_field(
4537 &mut self,
4538 readonly: bool,
4539 member_start: u32,
4540 ) -> Option<TypeAnnotationField> {
4541 let name = self.expect_property_ident("expected field name in object type")?;
4542 let optional = matches!(self.peek().kind, TokenKind::Question);
4544 if optional {
4545 self.advance();
4546 }
4547 let method = matches!(self.peek().kind, TokenKind::LeftParen);
4548 let ty = if method {
4549 if readonly {
4550 self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
4551 }
4552 self.parse_object_type_method_signature(name.span.start)?
4553 } else {
4554 if !matches!(self.peek().kind, TokenKind::Colon) {
4555 self.error_at_peek("expected `:` after field name");
4556 return None;
4557 }
4558 self.advance();
4559 self.parse_type_annotation()?
4560 };
4561 Some(TypeAnnotationField {
4562 name,
4563 ty,
4564 optional,
4565 readonly,
4566 rest: false,
4567 method,
4568 })
4569 }
4570
4571 fn parse_object_type_method_signature(&mut self, start: u32) -> Option<TypeAnnotation> {
4576 self.advance();
4577 let params = self.parse_function_type_params()?;
4578 if !matches!(self.peek().kind, TokenKind::Colon) {
4579 self.error_at_peek("expected `:` and return type after method parameters");
4580 return None;
4581 }
4582 self.advance();
4583 let return_type = self.parse_type_annotation()?;
4584 let end = return_type.span.end;
4585 Some(TypeAnnotation {
4586 kind: TypeAnnotationKind::Function {
4587 params,
4588 return_type: Box::new(return_type),
4589 },
4590 span: self.span(start, end),
4591 })
4592 }
4593
4594 fn expect_identifier(&mut self, message: &str) -> Option<Token> {
4597 let tok = self.expect_identifier_name(message)?;
4598 self.reject_strict_mode_reserved_word(&tok);
4599 Some(tok)
4600 }
4601
4602 fn expect_binding_identifier(&mut self, message: &str) -> Option<Token> {
4606 let tok = self.expect_identifier(message)?;
4607 self.reject_eval_or_arguments(tok.span);
4608 Some(tok)
4609 }
4610
4611 fn expect_identifier_name(&mut self, message: &str) -> Option<Token> {
4614 if matches!(self.peek().kind, TokenKind::Identifier) {
4615 return Some(self.advance());
4616 }
4617 if is_reserved_identifier_word(&self.peek().kind) {
4618 let tok = self.advance();
4619 let keyword = &self.source[tok.span.start as usize..tok.span.end as usize];
4620 self.error_at_with_help(
4621 tok.span,
4622 format!("`{keyword}` is a reserved keyword and can't be used as a name"),
4623 vec![format!(
4624 "rename it, for example: `{}`",
4625 reserved_keyword_rename_example(keyword)
4626 )],
4627 );
4628 return None;
4629 }
4630 self.error_at_peek(message.to_string());
4631 None
4632 }
4633
4634 fn reject_strict_mode_reserved_word(&mut self, tok: &Token) {
4637 let word = &self.source[tok.span.start as usize..tok.span.end as usize];
4638 if !STRICT_MODE_RESERVED_WORDS.contains(&word) {
4639 return;
4640 }
4641 self.error_at_with_help(
4642 tok.span,
4643 format!("`{word}` is a reserved word in strict mode and can't be used as a name"),
4644 vec![format!(
4645 "rename it, for example: `{}`",
4646 reserved_keyword_rename_example(word)
4647 )],
4648 );
4649 }
4650
4651 fn reject_eval_or_arguments(&mut self, span: Span) {
4654 let word = &self.source[span.start as usize..span.end as usize];
4655 if !matches!(word, "eval" | "arguments") {
4656 return;
4657 }
4658 self.error_at_with_help(
4659 span,
4660 format!("`{word}` can't name a binding in strict mode"),
4661 vec![format!(
4662 "rename it, for example: `{}`",
4663 reserved_keyword_rename_example(word)
4664 )],
4665 );
4666 }
4667
4668 fn ident_from_token(&self, tok: &Token) -> Ident {
4669 Ident {
4670 name: self.source[tok.span.start as usize..tok.span.end as usize].to_string(),
4671 span: tok.span,
4672 }
4673 }
4674
4675 fn property_ident_from_token(&self, tok: &Token) -> Ident {
4676 if let TokenKind::StringLiteral(s) = &tok.kind {
4677 Ident {
4678 name: s.clone(),
4679 span: tok.span,
4680 }
4681 } else if let TokenKind::NumberLiteral(value) = &tok.kind {
4682 Ident {
4683 name: crate::runtime::number::format_number_js(*value),
4684 span: tok.span,
4685 }
4686 } else {
4687 self.ident_from_token(tok)
4688 }
4689 }
4690
4691 fn expect_property_name(&mut self, message: &str) -> Option<Token> {
4692 if !is_property_name(&self.peek().kind) {
4693 self.error_at_peek(message.to_string());
4694 return None;
4695 }
4696 Some(self.advance())
4697 }
4698
4699 fn expect_property_ident(&mut self, message: &str) -> Option<Ident> {
4700 if !is_property_name(&self.peek().kind)
4701 && !matches!(
4702 self.peek().kind,
4703 TokenKind::StringLiteral(_) | TokenKind::NumberLiteral(_)
4704 )
4705 {
4706 self.error_at_peek(message.to_string());
4707 return None;
4708 }
4709 let tok = self.advance();
4710 Some(self.property_ident_from_token(&tok))
4711 }
4712
4713 fn readonly_modifier_span(&self, member_start: u32) -> Span {
4716 self.span(member_start, member_start + "readonly".len() as u32)
4717 }
4718
4719 fn reject_readonly_modifier(&mut self, span: Span) {
4722 self.error_at_with_help(
4723 span,
4724 "`readonly` can only modify a property or index signature",
4725 vec!["remove `readonly`".to_string()],
4726 );
4727 }
4728
4729 fn eat_readonly_property_modifier(&mut self) -> bool {
4730 if self.peek_identifier_text_is("readonly")
4731 && !self.line_break_after_peek()
4732 && (self.peek_starts_property_after_readonly()
4733 || matches!(self.peek_at(1).kind, TokenKind::LeftBracket))
4734 {
4735 self.advance();
4736 return true;
4737 }
4738 false
4739 }
4740
4741 fn peek_starts_property_after_readonly(&self) -> bool {
4742 let name = &self.peek_at(1).kind;
4743 let after_name = &self.peek_at(2).kind;
4744 let after_optional = &self.peek_at(3).kind;
4745 let starts_member_type =
4748 |kind: &TokenKind| matches!(kind, TokenKind::Colon | TokenKind::LeftParen);
4749 (is_property_name(name) || matches!(name, TokenKind::StringLiteral(_)))
4750 && (starts_member_type(after_name)
4751 || matches!(after_name, TokenKind::Question) && starts_member_type(after_optional))
4752 }
4753
4754 fn peek_at_is_binary_word(&self, offset: usize) -> bool {
4756 self.peek_at_is_word(offset, "as") || self.peek_at_is_word(offset, "satisfies")
4757 }
4758
4759 fn peek_identifier_text_is(&self, expected: &str) -> bool {
4760 self.token_is_word(self.peek(), expected)
4761 }
4762
4763 fn peek_at_is_word(&self, offset: usize, expected: &str) -> bool {
4764 self.token_is_word(self.peek_at(offset), expected)
4765 }
4766
4767 fn token_is_word(&self, tok: &Token, expected: &str) -> bool {
4768 matches!(tok.kind, TokenKind::Identifier)
4769 && &self.source[tok.span.start as usize..tok.span.end as usize] == expected
4770 }
4771
4772 fn at_delete_operator(&self) -> bool {
4779 if !self.peek_identifier_text_is("delete") {
4780 return false;
4781 }
4782 if !matches!(
4783 self.peek_at(1).kind,
4784 TokenKind::Identifier | TokenKind::This
4785 ) {
4786 return false;
4787 }
4788 let next_is_cast_keyword =
4789 self.peek_at_is_word(1, "as") || self.peek_at_is_word(1, "satisfies");
4790 !next_is_cast_keyword || cannot_start_type(&self.peek_at(2).kind)
4791 }
4792
4793 fn at_type_alias_head(&self) -> bool {
4797 self.peek_identifier_text_is("type")
4798 && matches!(self.peek_at(1).kind, TokenKind::Identifier)
4799 }
4800
4801 fn parse_expression(&mut self) -> Option<ExprId> {
4804 self.with_recursion_limit(Self::parse_expression_inner)
4805 }
4806
4807 fn parse_expression_inner(&mut self) -> Option<ExprId> {
4808 let written = self.parse_conditional()?;
4809 let shift_assignment = self.peek_shift().filter(|shift| shift.assignment);
4810 if !is_assign_lookahead(&self.peek().kind) && shift_assignment.is_none() {
4811 return Some(written);
4812 }
4813 let target_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4814 let target = self.assignment_target(written)?;
4815 let op_tok = self.advance();
4816 let mut op_span = op_tok.span;
4817 let op = if let Some(ShiftToken { op, tokens, .. }) = shift_assignment {
4818 for _ in 1..tokens {
4819 op_span.end = self.advance().span.end;
4820 }
4821 Some(op)
4822 } else {
4823 compound_op_for_token(&op_tok.kind)
4824 };
4825 let value = self.parse_expression()?;
4826 let value_span = parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
4827 parse_arena_result(
4828 self.ast.try_push_expr(Expr {
4829 kind: ExprKind::Assign {
4830 target,
4831 op,
4832 op_span,
4833 value,
4834 },
4835 span: self.span(target_span.start, value_span.end),
4836 }),
4837 &mut self.fatal,
4838 )
4839 }
4840
4841 fn assignment_target(&mut self, written: ExprId) -> Option<ExprId> {
4844 let mut target = written;
4845 while let ExprKind::Paren(inner) =
4846 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind
4847 {
4848 target = inner;
4849 }
4850 if matches!(
4851 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind,
4852 ExprKind::Identifier(_) | ExprKind::FieldAccess { .. } | ExprKind::IndexAccess { .. }
4853 ) {
4854 return Some(target);
4855 }
4856 let error_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4857 self.error_at_with_help(
4858 error_span,
4859 "invalid assignment target",
4860 vec![
4861 "assign to a variable, a field (`o.f = …`), or an element (`a[i] = …`)".to_string(),
4862 ],
4863 );
4864 None
4865 }
4866
4867 fn parse_conditional(&mut self) -> Option<ExprId> {
4868 self.reject_async_modifier();
4869 if self.is_arrow_start() {
4870 return self.parse_arrow();
4871 }
4872 let cond = self.parse_binary(0)?;
4873 if !matches!(self.peek().kind, TokenKind::Question) {
4875 return Some(cond);
4876 }
4877 self.advance();
4878 let then_ = self.parse_expression()?;
4879 if !matches!(self.peek().kind, TokenKind::Colon) {
4880 self.error_at_peek_with_help(
4881 "expected `:` to complete ternary expression",
4882 vec!["const x = cond ? then : else;".to_string()],
4883 );
4884 return None;
4885 }
4886 self.advance();
4887 let else_ = self.parse_expression()?;
4888 let cond_span = parse_arena_result(self.ast.try_expr(cond), &mut self.fatal)?.span;
4889 let else_span = parse_arena_result(self.ast.try_expr(else_), &mut self.fatal)?.span;
4890 parse_arena_result(
4891 self.ast.try_push_expr(Expr {
4892 kind: ExprKind::Ternary { cond, then_, else_ },
4893 span: self.span(cond_span.start, else_span.end),
4894 }),
4895 &mut self.fatal,
4896 )
4897 }
4898
4899 fn is_arrow_start(&self) -> bool {
4900 match self.peek().kind {
4901 TokenKind::Identifier => matches!(self.peek_at(1).kind, TokenKind::Arrow),
4902 TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos),
4903 _ => false,
4904 }
4905 }
4906
4907 fn reject_async_modifier(&mut self) {
4910 if !self.peek_identifier_text_is("async") {
4911 return;
4912 }
4913 let modifies = match self.peek_at(1).kind {
4914 TokenKind::Function => true,
4915 TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos + 1),
4917 ref name
4921 if is_property_name(name)
4922 && !matches!(name, TokenKind::In)
4923 && !self.peek_at_is_binary_word(1) =>
4924 {
4925 matches!(
4926 self.peek_at(2).kind,
4927 TokenKind::Arrow | TokenKind::LeftParen | TokenKind::LessThan
4928 )
4929 }
4930 _ => false,
4931 };
4932 if modifies {
4933 self.report_async_modifier();
4934 }
4935 }
4936
4937 fn reject_async_member_modifier(&mut self) {
4942 if !self.peek_identifier_text_is("async") || self.line_break_after_peek() {
4943 return;
4944 }
4945 let modifies = match self.peek_at(1).kind {
4946 TokenKind::Star
4947 | TokenKind::LeftBracket
4948 | TokenKind::StringLiteral(_)
4949 | TokenKind::NumberLiteral(_) => true,
4950 ref name if is_property_name(name) => {
4951 matches!(
4952 self.peek_at(2).kind,
4953 TokenKind::LeftParen | TokenKind::LessThan
4954 )
4955 }
4956 _ => false,
4957 };
4958 if modifies {
4959 self.report_async_modifier();
4960 }
4961 }
4962
4963 fn report_async_modifier(&mut self) {
4967 let tok = self.advance();
4968 self.error_at_with_help(
4969 tok.span,
4970 "`async` functions are not supported",
4971 vec![
4972 "calls return their value directly: remove `async`, write the return type \
4973 `T` instead of `Promise<T>`, and drop `await`"
4974 .to_string(),
4975 ],
4976 );
4977 }
4978
4979 fn reject_await_operator(&mut self) -> bool {
4985 if !self.peek_identifier_text_is("await") {
4986 return false;
4987 }
4988 let operand_starts = matches!(
4989 self.peek_at(1).kind,
4990 TokenKind::Identifier
4991 | TokenKind::This
4992 | TokenKind::Super
4993 | TokenKind::New
4994 | TokenKind::Function
4995 | TokenKind::StringLiteral(_)
4996 | TokenKind::NumberLiteral(_)
4997 | TokenKind::NullLiteral
4998 | TokenKind::BooleanLiteral(_)
4999 | TokenKind::TemplateHead(_)
5002 | TokenKind::TemplateNoSubstitution(_)
5003 ) && !self.peek_at_is_binary_word(1);
5004 if !operand_starts || self.line_break_after_peek() {
5005 return false;
5006 }
5007 let tok = self.advance();
5008 self.error_at_with_help(
5009 tok.span,
5010 "`await` is not supported",
5011 vec!["calls return their value directly: remove `await`".to_string()],
5012 );
5013 true
5014 }
5015
5016 fn index_after_matching_paren(&self, index: usize) -> Option<usize> {
5019 if !matches!(
5020 self.tokens.get(index).map(|t| &t.kind),
5021 Some(TokenKind::LeftParen)
5022 ) {
5023 return None;
5024 }
5025 self.scan_past_balanced(
5026 index,
5027 |k| matches!(k, TokenKind::LeftParen),
5028 |k| matches!(k, TokenKind::RightParen),
5029 )
5030 }
5031
5032 fn find_arrow_after_matching_paren(&self, open: usize) -> bool {
5033 let Some(i) = self.index_after_matching_paren(open) else {
5034 return false;
5035 };
5036 let after_type = if matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Colon)) {
5037 match self.scan_past_type_annotation(i + 1, TypePos::ArrowReturn) {
5038 Some(p) => p,
5039 None => return false,
5040 }
5041 } else {
5042 i
5043 };
5044 matches!(
5045 self.tokens.get(after_type).map(|t| &t.kind),
5046 Some(TokenKind::Arrow),
5047 )
5048 }
5049
5050 fn scan_past_type_annotation(&self, start: usize, type_pos: TypePos) -> Option<usize> {
5056 self.scan_past_type_annotation_at_depth(start, type_pos, 0)
5057 }
5058
5059 fn scan_past_type_annotation_at_depth(
5060 &self,
5061 start: usize,
5062 type_pos: TypePos,
5063 depth: usize,
5064 ) -> Option<usize> {
5065 if depth >= MAX_PARSE_DEPTH {
5066 return None;
5067 }
5068 if matches!(
5069 self.tokens.get(start).map(|t| &t.kind),
5070 Some(TokenKind::Identifier)
5071 ) && self
5072 .tokens
5073 .get(start + 1)
5074 .is_some_and(|t| self.token_is_word(t, "is"))
5075 {
5076 return self.scan_past_type_annotation_at_depth(start + 2, type_pos, depth + 1);
5077 }
5078 let start = if matches!(
5080 self.tokens.get(start).map(|t| &t.kind),
5081 Some(TokenKind::Pipe)
5082 ) {
5083 start + 1
5084 } else {
5085 start
5086 };
5087 let mut i = self.scan_past_single_type_member(start, type_pos, depth + 1)?;
5088 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Pipe)) {
5089 i = self.scan_past_single_type_member(i + 1, type_pos, depth + 1)?;
5090 }
5091 Some(i)
5092 }
5093
5094 fn scan_past_single_type_member(
5095 &self,
5096 start: usize,
5097 type_pos: TypePos,
5098 depth: usize,
5099 ) -> Option<usize> {
5100 if depth >= MAX_PARSE_DEPTH {
5101 return None;
5102 }
5103 if self.is_contextual_type_operator(start, "keyof")
5104 || self.is_contextual_type_operator(start, "readonly")
5105 {
5106 return self.scan_past_single_type_member(start + 1, type_pos, depth + 1);
5107 }
5108 let mut i = start;
5109 match self.tokens.get(i)?.kind {
5110 TokenKind::Identifier | TokenKind::Void => {
5111 i += 1;
5112 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Dot)) {
5113 if !matches!(
5114 self.tokens.get(i + 1).map(|t| &t.kind),
5115 Some(TokenKind::Identifier)
5116 ) {
5117 return None;
5118 }
5119 i += 2;
5120 }
5121 if matches!(
5122 self.tokens.get(i).map(|t| &t.kind),
5123 Some(TokenKind::LessThan)
5124 ) {
5125 i = self.scan_past_balanced(
5126 i,
5127 |k| matches!(k, TokenKind::LessThan),
5128 |k| matches!(k, TokenKind::GreaterThan),
5129 )?;
5130 }
5131 }
5132 TokenKind::NullLiteral
5133 | TokenKind::StringLiteral(_)
5134 | TokenKind::NumberLiteral(_)
5135 | TokenKind::BooleanLiteral(_) => {
5136 i += 1;
5137 }
5138 TokenKind::LeftBrace => {
5139 i = self.scan_past_balanced(
5140 i,
5141 |k| matches!(k, TokenKind::LeftBrace),
5142 |k| matches!(k, TokenKind::RightBrace),
5143 )?;
5144 }
5145 TokenKind::LeftBracket => {
5146 i = self.scan_past_balanced(
5147 i,
5148 |k| matches!(k, TokenKind::LeftBracket),
5149 |k| matches!(k, TokenKind::RightBracket),
5150 )?;
5151 }
5152 TokenKind::LeftParen => {
5157 let is_function_type = self.paren_at_opens_function_type(i, type_pos);
5158 i = self.scan_past_balanced(
5159 i,
5160 |k| matches!(k, TokenKind::LeftParen),
5161 |k| matches!(k, TokenKind::RightParen),
5162 )?;
5163 if is_function_type {
5164 if !matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)) {
5165 return None;
5166 }
5167 return self.scan_past_type_annotation_at_depth(
5168 i + 1,
5169 TypePos::Anywhere,
5170 depth + 1,
5171 );
5172 }
5173 }
5174 _ => return None,
5175 }
5176 while matches!(
5177 self.tokens.get(i).map(|t| &t.kind),
5178 Some(TokenKind::LeftBracket)
5179 ) {
5180 if matches!(
5181 self.tokens.get(i + 1).map(|t| &t.kind),
5182 Some(TokenKind::RightBracket)
5183 ) {
5184 i += 2;
5185 continue;
5186 }
5187 i = self.scan_past_type_annotation_at_depth(i + 1, TypePos::Anywhere, depth + 1)?;
5189 if !matches!(
5190 self.tokens.get(i).map(|t| &t.kind),
5191 Some(TokenKind::RightBracket)
5192 ) {
5193 return None;
5194 }
5195 i += 1;
5196 }
5197 Some(i)
5198 }
5199
5200 fn scan_past_balanced(
5203 &self,
5204 start: usize,
5205 is_open: fn(&TokenKind) -> bool,
5206 is_close: fn(&TokenKind) -> bool,
5207 ) -> Option<usize> {
5208 let mut depth: usize = 1;
5211 let mut i = start + 1;
5212 while i < self.tokens.len() && depth > 0 {
5213 let token = self.tokens.get(i)?;
5214 let kind = &token.kind;
5215 if is_open(kind) {
5216 depth += 1;
5217 } else if is_close(kind) {
5218 depth -= 1;
5219 } else if matches!(kind, TokenKind::Eof) {
5220 return None;
5221 }
5222 i += 1;
5223 }
5224 if depth == 0 { Some(i) } else { None }
5225 }
5226
5227 fn parse_arrow(&mut self) -> Option<ExprId> {
5228 let start_span = self.peek().span;
5229
5230 let params = match self.peek().kind {
5231 TokenKind::Identifier => {
5232 let name_tok = self.advance();
5233 self.reject_eval_or_arguments(name_tok.span);
5234 let name = self.ident_from_token(&name_tok);
5235 vec![ParamDecl {
5236 name,
5237 ty: None,
5238 default: None,
5239 optional: false,
5240 pattern: None,
5241 rest: false,
5242 modifiers: None,
5243 }]
5244 }
5245 TokenKind::LeftParen => {
5246 self.advance();
5247 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
5248 Vec::new()
5249 } else {
5250 self.parse_arrow_param_list()?
5251 };
5252 if !matches!(self.peek().kind, TokenKind::RightParen) {
5253 self.error_at_peek("expected `)`");
5254 return None;
5255 }
5256 self.advance();
5257 params
5258 }
5259 _ => return self.invariant_failure("is_arrow_start guarded the entry"),
5260 };
5261
5262 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
5263 self.advance();
5264 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
5265 } else {
5266 (None, None)
5267 };
5268
5269 if !matches!(self.peek().kind, TokenKind::Arrow) {
5270 self.error_at_peek("expected `=>` in arrow function");
5271 return None;
5272 }
5273 if self.line_break_before_peek() {
5275 let arrow_span = self.peek().span;
5276 self.error_at(arrow_span, "a line break is not allowed before `=>`");
5277 }
5278 self.advance();
5279
5280 let (body, end_pos) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
5281 let block = self.parse_block()?;
5282 let span = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?.span;
5283 (ArrowBody::Block(block), span.end)
5284 } else {
5285 let expr = self.parse_expression()?;
5286 let span = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?.span;
5287 (ArrowBody::Expr(expr), span.end)
5288 };
5289
5290 parse_arena_result(
5291 self.ast.try_push_expr(Expr {
5292 kind: ExprKind::Arrow {
5293 params,
5294 return_type,
5295 type_predicate,
5296 body,
5297 },
5298 span: self.span(start_span.start, end_pos),
5299 }),
5300 &mut self.fatal,
5301 )
5302 }
5303
5304 fn parse_function_expression(&mut self) -> Option<ExprId> {
5307 let kw = self.advance();
5308
5309 if matches!(self.peek().kind, TokenKind::LessThan) {
5310 self.error_at_peek("generic function expressions are not supported");
5311 return None;
5312 }
5313
5314 let self_name = if matches!(self.peek().kind, TokenKind::Identifier) {
5315 let name_tok = self.advance();
5316 self.reject_eval_or_arguments(name_tok.span);
5317 Some(self.ident_from_token(&name_tok))
5318 } else {
5319 None
5320 };
5321
5322 if !matches!(self.peek().kind, TokenKind::LeftParen) {
5323 self.error_at_peek("expected `(` after `function`");
5324 return None;
5325 }
5326 self.advance();
5327
5328 let this_type = if matches!(self.peek().kind, TokenKind::This) {
5329 self.advance();
5330 if !matches!(self.peek().kind, TokenKind::Colon) {
5331 self.error_at_peek("expected a type annotation after `this`");
5332 return None;
5333 }
5334 self.advance();
5335 let ty = self.parse_type_annotation()?;
5336 if matches!(self.peek().kind, TokenKind::Comma) {
5337 self.advance();
5338 } else if !matches!(self.peek().kind, TokenKind::RightParen) {
5339 self.error_at_peek("expected `,` or `)` after the `this` parameter");
5340 return None;
5341 }
5342 Some(ty)
5343 } else {
5344 None
5345 };
5346 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
5347 Vec::new()
5348 } else {
5349 let saved_class = self.class_member_body_depth;
5350 let saved_function = self.function_expression_body_depth;
5351 self.class_member_body_depth = 0;
5352 self.function_expression_body_depth = 1;
5353 let params = self.parse_arrow_param_list();
5354 self.class_member_body_depth = saved_class;
5355 self.function_expression_body_depth = saved_function;
5356 params?
5357 };
5358 if !matches!(self.peek().kind, TokenKind::RightParen) {
5359 self.error_at_peek("expected `)`");
5360 return None;
5361 }
5362 self.advance();
5363
5364 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
5365 self.advance();
5366 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
5367 } else {
5368 (None, None)
5369 };
5370
5371 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
5372 self.error_at_peek("expected `{` to open the function body");
5373 return None;
5374 }
5375 let saved = self.class_member_body_depth;
5376 self.class_member_body_depth = 0;
5377 self.function_expression_body_depth += 1;
5378 let block = self.parse_block();
5379 self.function_expression_body_depth -= 1;
5380 self.class_member_body_depth = saved;
5381 let block = block?;
5382 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
5383 .span
5384 .end;
5385
5386 let function = parse_arena_result(
5387 self.ast.try_push_expr(Expr {
5388 kind: ExprKind::Arrow {
5389 params,
5390 return_type,
5391 type_predicate,
5392 body: ArrowBody::Block(block),
5393 },
5394 span: self.span(kw.span.start, end),
5395 }),
5396 &mut self.fatal,
5397 )?;
5398 parse_arena_result(
5399 self.ast.try_push_expr(Expr {
5400 kind: ExprKind::FunctionExpression {
5401 name: self_name,
5402 this_type,
5403 function,
5404 },
5405 span: self.span(kw.span.start, end),
5406 }),
5407 &mut self.fatal,
5408 )
5409 }
5410
5411 fn parse_outer_this_boundary<T>(
5414 &mut self,
5415 parse: impl FnOnce(&mut Self) -> Option<T>,
5416 ) -> Option<T> {
5417 let saved = self.class_member_body_depth;
5418 self.class_member_body_depth = 0;
5419 let saved_function = self.function_expression_body_depth;
5420 self.function_expression_body_depth = 0;
5421 let parsed = parse(self);
5422 self.class_member_body_depth = saved;
5423 self.function_expression_body_depth = saved_function;
5424 parsed
5425 }
5426
5427 fn parse_arrow_param_list(&mut self) -> Option<Vec<ParamDecl>> {
5428 let mut params = Vec::new();
5429 loop {
5430 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
5431 self.advance();
5432 true
5433 } else {
5434 false
5435 };
5436 let (name, pattern) = match self.peek().kind {
5439 TokenKind::LeftBrace | TokenKind::LeftBracket => {
5440 if rest {
5441 self.error_at_peek("rest parameter cannot be destructured");
5442 return None;
5443 }
5444 let binding = self.parse_binding()?;
5445 let placeholder = Ident {
5446 name: String::new(),
5447 span: binding.span(),
5448 };
5449 (placeholder, Some(binding))
5450 }
5451 _ => {
5452 let name_tok = self.expect_binding_identifier(if rest {
5453 "expected identifier after `...`"
5454 } else {
5455 "expected parameter name"
5456 })?;
5457 let name = self.ident_from_token(&name_tok);
5458 (name, None)
5459 }
5460 };
5461 let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
5462 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
5463 self.advance();
5464 Some(self.parse_type_annotation()?)
5465 } else {
5466 None
5469 };
5470 let default = self.parse_parameter_default(rest, optional)?;
5471 params.push(ParamDecl {
5472 name,
5473 ty,
5474 default,
5475 optional,
5476 pattern,
5477 rest,
5478 modifiers: None,
5479 });
5480 match self.peek().kind {
5481 TokenKind::Comma => {
5482 if rest {
5483 let comma = self.advance();
5484 self.error_at(comma.span, "rest parameter must be the last parameter");
5485 return None;
5486 }
5487 self.advance();
5488 if matches!(self.peek().kind, TokenKind::RightParen) {
5489 break;
5490 }
5491 }
5492 TokenKind::RightParen => break,
5493 _ => {
5494 self.error_at_peek("expected `,` or `)`");
5495 return None;
5496 }
5497 }
5498 }
5499 self.check_parameter_order(params.iter().map(ParamOrder::of_param));
5500 Some(params)
5501 }
5502
5503 fn parse_binary(&mut self, min_prec: u8) -> Option<ExprId> {
5504 self.with_recursion_limit(|parser| parser.parse_binary_inner(min_prec))
5505 }
5506
5507 fn peek_shift(&self) -> Option<ShiftToken> {
5510 let first = self.peek();
5511 let second = self.peek_at(1);
5512 if first.span.end != second.span.start {
5513 return None;
5514 }
5515 match (&first.kind, &second.kind) {
5516 (TokenKind::LessThan, TokenKind::LessThan) => {
5517 Some(ShiftToken::new(BinOp::Shl, 2, false))
5518 }
5519 (TokenKind::LessThan, TokenKind::LessEquals) => {
5520 Some(ShiftToken::new(BinOp::Shl, 2, true))
5521 }
5522 (TokenKind::GreaterThan, TokenKind::GreaterEquals) => {
5523 Some(ShiftToken::new(BinOp::Shr, 2, true))
5524 }
5525 (TokenKind::GreaterThan, TokenKind::GreaterThan) => {
5526 let third = self.peek_at(2);
5527 if second.span.end == third.span.start {
5528 match third.kind {
5529 TokenKind::GreaterThan => {
5530 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, false));
5531 }
5532 TokenKind::GreaterEquals => {
5533 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, true));
5534 }
5535 _ => {}
5536 }
5537 }
5538 Some(ShiftToken::new(BinOp::Shr, 2, false))
5539 }
5540 _ => None,
5541 }
5542 }
5543
5544 fn peek_binary(&self) -> Option<BinaryToken> {
5545 if let Some(shift) = self.peek_shift() {
5546 return (!shift.assignment).then_some(BinaryToken {
5547 op: shift.op,
5548 precedence: SHIFT_PRECEDENCE,
5549 tokens: shift.tokens,
5550 });
5551 }
5552 peek_binop(&self.peek().kind).map(|(op, precedence)| BinaryToken {
5553 op,
5554 precedence,
5555 tokens: 1,
5556 })
5557 }
5558
5559 fn parse_binary_inner(&mut self, min_prec: u8) -> Option<ExprId> {
5560 let mut lhs = self.parse_cast()?;
5561 let mut last_op: Option<BinOp> = None;
5565 let mut reported_mixing = false;
5566 while let Some(BinaryToken {
5567 op,
5568 precedence: prec,
5569 tokens: count,
5570 }) = self.peek_binary()
5571 {
5572 if prec < min_prec {
5573 break;
5574 }
5575 if !reported_mixing
5576 && let Some(last) = last_op
5577 && mixed_logical(last, op)
5578 {
5579 reported_mixing = true;
5580 let op_span = self.peek().span;
5581 self.error_at_with_help(
5585 op_span,
5586 "mixing `??` with `||` / `&&` requires parentheses",
5587 vec![mixed_logical_help(last, op)],
5588 );
5589 }
5590 if op == BinOp::Pow
5591 && matches!(
5592 parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.kind,
5593 ExprKind::Unary { .. }
5594 | ExprKind::Typeof { .. }
5595 | ExprKind::Delete { .. }
5596 | ExprKind::Void { .. }
5597 )
5598 {
5599 let error_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5600 self.error_at_with_help(
5601 error_span,
5602 "an unparenthesized unary expression cannot be the left operand of `**`",
5603 vec!["choose the grouping explicitly: `(-x) ** 2` or `-(x ** 2)`".into()],
5604 );
5605 return None;
5606 }
5607 let loose = matches!(self.peek().kind, TokenKind::EqEq | TokenKind::BangEq);
5608 for _ in 0..count {
5609 self.advance();
5610 }
5611 let rhs = self.parse_binary(right_operand_min_prec(op, prec))?;
5612 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5613 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
5614 if loose {
5615 self.reject_loose_nullish_comparison(op, lhs, rhs)?;
5616 }
5617 lhs = parse_arena_result(
5618 self.ast.try_push_expr(Expr {
5619 kind: ExprKind::Binary { op, lhs, rhs },
5620 span: self.span(lhs_span.start, rhs_span.end),
5621 }),
5622 &mut self.fatal,
5623 )?;
5624 last_op = Some(op);
5625 }
5626 Some(lhs)
5627 }
5628
5629 fn parse_cast(&mut self) -> Option<ExprId> {
5631 let mut expr = self.parse_unary()?;
5632 while matches!(self.peek().kind, TokenKind::Instanceof)
5633 || self.peek_identifier_text_is("as")
5634 {
5635 let is_instanceof = matches!(self.peek().kind, TokenKind::Instanceof);
5636 self.advance();
5637 let ty = self.parse_type_annotation()?;
5638 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5639 .span
5640 .start;
5641 let end = ty.span.end;
5642 let kind = if is_instanceof {
5643 ExprKind::InstanceOf { value: expr, ty }
5644 } else {
5645 self.last_as_type_end = Some(end);
5646 ExprKind::As { expr, ty }
5647 };
5648 expr = parse_arena_result(
5649 self.ast.try_push_expr(Expr {
5650 kind,
5651 span: self.span(start, end),
5652 }),
5653 &mut self.fatal,
5654 )?;
5655 }
5656 Some(expr)
5657 }
5658
5659 fn parse_unary(&mut self) -> Option<ExprId> {
5660 self.with_recursion_limit(Self::parse_unary_inner)
5661 }
5662
5663 fn parse_unary_inner(&mut self) -> Option<ExprId> {
5664 if self.reject_await_operator() {
5665 return self.parse_unary();
5666 }
5667 if matches!(self.peek().kind, TokenKind::Typeof) {
5670 return self.parse_prefix_expr(|operand| ExprKind::Typeof { operand });
5671 }
5672 if matches!(self.peek().kind, TokenKind::Void) {
5673 return self.parse_prefix_expr(|operand| ExprKind::Void { operand });
5674 }
5675 if self.at_delete_operator() {
5676 return self.parse_prefix_expr(|operand| ExprKind::Delete { operand });
5677 }
5678 if matches!(self.peek().kind, TokenKind::New) {
5679 let op_tok = self.advance();
5680 let mut callee = self.parse_atom()?;
5681 while matches!(self.peek().kind, TokenKind::Dot) {
5682 self.advance();
5683 if !matches!(self.peek().kind, TokenKind::Identifier) {
5684 self.error_at_peek("expected identifier after `.` in constructor name");
5685 return None;
5686 }
5687 let ident_tok = self.advance();
5688 let name = self.ident_from_token(&ident_tok);
5689 let receiver_span =
5690 parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5691 let combined_span = self.span(receiver_span.start, ident_tok.span.end);
5692 callee = parse_arena_result(
5693 self.ast.try_push_expr(Expr {
5694 kind: ExprKind::FieldAccess {
5695 receiver: callee,
5696 name,
5697 },
5698 span: combined_span,
5699 }),
5700 &mut self.fatal,
5701 )?;
5702 }
5703 let type_args = if matches!(self.peek().kind, TokenKind::LessThan) {
5704 let saved_pos = self.pos;
5705 let saved_splits = self.split_type_tokens.len();
5706 let saved_diag_len = self.diagnostics.len();
5707 if let Some(args) = self.try_parse_type_args_only() {
5708 Some(args)
5709 } else {
5710 self.restore_type_tokens(saved_splits);
5711 self.pos = saved_pos;
5712 self.diagnostics.truncate(saved_diag_len);
5713 None
5714 }
5715 } else {
5716 None
5717 };
5718 if !matches!(self.peek().kind, TokenKind::LeftParen) {
5719 self.error_at_peek("expected `(` after constructor name in `new` expression");
5720 return None;
5721 }
5722 self.advance();
5723 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5724 Vec::new()
5725 } else {
5726 self.parse_call_args()?
5727 };
5728 if !matches!(self.peek().kind, TokenKind::RightParen) {
5729 self.error_at_peek("expected `)`");
5730 return None;
5731 }
5732 let close = self.advance();
5733 let new_expr = parse_arena_result(
5734 self.ast.try_push_expr(Expr {
5735 kind: ExprKind::New {
5736 callee,
5737 type_args,
5738 args,
5739 },
5740 span: self.span(op_tok.span.start, close.span.end),
5741 }),
5742 &mut self.fatal,
5743 )?;
5744 return self.parse_postfix_from(new_expr);
5747 }
5748 let op = match self.peek().kind {
5749 TokenKind::Bang => UnOp::Not,
5750 TokenKind::Tilde => UnOp::BitNot,
5751 TokenKind::Minus => UnOp::Neg,
5752 TokenKind::Plus => UnOp::Pos,
5753 _ => return self.parse_postfix(),
5754 };
5755 self.parse_prefix_expr(|operand| ExprKind::Unary { op, operand })
5756 }
5757
5758 fn parse_prefix_expr(&mut self, kind: impl FnOnce(ExprId) -> ExprKind) -> Option<ExprId> {
5760 let op_tok = self.advance();
5761 if matches!(op_tok.kind, TokenKind::Void) && self.at_inserted_semicolon() {
5764 self.advance();
5765 }
5766 let operand = self.parse_unary()?;
5767 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5768 parse_arena_result(
5769 self.ast.try_push_expr(Expr {
5770 kind: kind(operand),
5771 span: self.span(op_tok.span.start, operand_span.end),
5772 }),
5773 &mut self.fatal,
5774 )
5775 }
5776
5777 fn parse_postfix(&mut self) -> Option<ExprId> {
5778 let expr = self.parse_atom()?;
5779 self.parse_postfix_from(expr)
5780 }
5781
5782 fn parse_postfix_from(&mut self, mut expr: ExprId) -> Option<ExprId> {
5783 let mut chain_parts: Option<(ExprId, Vec<crate::ChainPart>)> = None;
5787 loop {
5788 match self.peek().kind {
5789 TokenKind::LeftParen => {
5790 if let Some((_, parts)) = &mut chain_parts {
5791 let part = self.parse_chain_call_tail(false)?;
5792 parts.push(part);
5793 } else {
5794 expr = self.parse_call_tail(expr, None)?;
5795 }
5796 }
5797 TokenKind::Dot => {
5798 if let Some((_, parts)) = &mut chain_parts {
5799 let part = self.parse_chain_field_tail(false)?;
5800 parts.push(part);
5801 } else {
5802 expr = self.parse_field_access_tail(expr)?;
5803 }
5804 }
5805 TokenKind::LeftBracket => {
5806 if let Some((_, parts)) = &mut chain_parts {
5807 let part = self.parse_chain_index_tail(false)?;
5808 parts.push(part);
5809 } else {
5810 expr = self.parse_index_access_tail(expr)?;
5811 }
5812 }
5813 TokenKind::QuestionDot => {
5814 if chain_parts.is_none() {
5815 chain_parts = Some((expr, Vec::new()));
5816 }
5817 self.advance();
5818 let part = match self.peek().kind {
5819 TokenKind::LeftParen => self.parse_chain_call_tail(true)?,
5820 TokenKind::LeftBracket => self.parse_chain_index_tail(true)?,
5821 _ if is_property_name(&self.peek().kind) => {
5822 self.parse_chain_field_tail(true)?
5823 }
5824 _ => {
5825 self.error_at_peek(
5826 "expected field name, `(` for call, or `[` for index after `?.`",
5827 );
5828 return None;
5829 }
5830 };
5831 if let Some((_, parts)) = &mut chain_parts {
5832 parts.push(part);
5833 }
5834 }
5835 TokenKind::LessThan => {
5839 if chain_parts.is_some() {
5840 break;
5842 }
5843 let saved_pos = self.pos;
5844 let saved_splits = self.split_type_tokens.len();
5845 let saved_diag_len = self.diagnostics.len();
5846 if let Some(call_id) = self.try_parse_generic_call_tail(expr) {
5847 expr = call_id;
5848 } else {
5849 self.restore_type_tokens(saved_splits);
5850 self.pos = saved_pos;
5851 self.diagnostics.truncate(saved_diag_len);
5852 break;
5853 }
5854 }
5855 TokenKind::Bang => {
5856 if let Some((_, parts)) = &mut chain_parts {
5857 let tok = self.advance();
5858 parts.push(crate::ChainPart::NonNull { span: tok.span });
5859 continue;
5860 }
5861 let tok = self.advance();
5862 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5863 .span
5864 .start;
5865 expr = parse_arena_result(
5866 self.ast.try_push_expr(Expr {
5867 kind: ExprKind::PostfixUnary {
5868 op: PostfixOp::NonNullAssert,
5869 operand: expr,
5870 },
5871 span: self.span(start, tok.span.end),
5872 }),
5873 &mut self.fatal,
5874 )?;
5875 }
5876 TokenKind::PlusPlus | TokenKind::MinusMinus => {
5878 if chain_parts.is_some() {
5879 break;
5880 }
5881 let tok = self.advance();
5882 let op = if matches!(tok.kind, TokenKind::PlusPlus) {
5883 PostfixOp::Inc
5884 } else {
5885 PostfixOp::Dec
5886 };
5887 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5888 .span
5889 .start;
5890 expr = parse_arena_result(
5891 self.ast.try_push_expr(Expr {
5892 kind: ExprKind::PostfixUnary { op, operand: expr },
5893 span: self.span(start, tok.span.end),
5894 }),
5895 &mut self.fatal,
5896 )?;
5897 break;
5898 }
5899 _ => break,
5900 }
5901 }
5902 if let Some((base, parts)) = chain_parts {
5903 let base_span = parse_arena_result(self.ast.try_expr(base), &mut self.fatal)?.span;
5904 let end = parts.last().map_or(base_span.end, |p| p.span().end);
5905 Some(parse_arena_result(
5906 self.ast.try_push_expr(Expr {
5907 kind: ExprKind::OptionalChain { base, parts },
5908 span: self.span(base_span.start, end),
5909 }),
5910 &mut self.fatal,
5911 )?)
5912 } else {
5913 Some(expr)
5914 }
5915 }
5916
5917 fn parse_chain_field_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5919 let start_pos = self.peek().span.start;
5920 if !optional {
5921 self.advance();
5922 }
5923 let name_tok = if is_property_name(&self.peek().kind) {
5924 self.advance()
5925 } else {
5926 let what = if optional { "?." } else { "." };
5927 self.error_at_peek(format!("expected field name after `{what}`"));
5928 return None;
5929 };
5930 let name = self.ident_from_token(&name_tok);
5931 let end = name.span.end;
5932 Some(crate::ChainPart::Field {
5933 name,
5934 optional,
5935 span: self.span(start_pos, end),
5936 })
5937 }
5938
5939 fn parse_chain_index_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5940 let open = self.advance();
5941 let idx = self.parse_expression()?;
5942 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5943 self.error_at_peek("expected `]`");
5944 return None;
5945 }
5946 let close = self.advance();
5947 Some(crate::ChainPart::Index {
5948 idx,
5949 optional,
5950 span: self.span(open.span.start, close.span.end),
5951 })
5952 }
5953
5954 fn parse_chain_call_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5955 let open = self.advance();
5956 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5957 Vec::new()
5958 } else {
5959 self.parse_call_args()?
5960 };
5961 if !matches!(self.peek().kind, TokenKind::RightParen) {
5962 self.error_at_peek("expected `)`");
5963 return None;
5964 }
5965 let close = self.advance();
5966 Some(crate::ChainPart::Call {
5967 args,
5968 type_args: None,
5969 optional,
5970 span: self.span(open.span.start, close.span.end),
5971 })
5972 }
5973
5974 fn parse_field_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5975 self.advance();
5976 let name_tok = if is_property_name(&self.peek().kind) {
5978 self.advance()
5979 } else {
5980 self.error_at_peek("expected field name after `.`");
5981 return None;
5982 };
5983 let name = self.ident_from_token(&name_tok);
5984 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5985 parse_arena_result(
5986 self.ast.try_push_expr(Expr {
5987 kind: ExprKind::FieldAccess {
5988 receiver,
5989 name: name.clone(),
5990 },
5991 span: self.span(receiver_span.start, name.span.end),
5992 }),
5993 &mut self.fatal,
5994 )
5995 }
5996
5997 fn parse_index_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5998 self.advance();
5999 let index = self.parse_expression()?;
6000 if !matches!(self.peek().kind, TokenKind::RightBracket) {
6001 self.error_at_peek("expected `]`");
6002 return None;
6003 }
6004 let close = self.advance();
6005 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
6006 parse_arena_result(
6007 self.ast.try_push_expr(Expr {
6008 kind: ExprKind::IndexAccess { receiver, index },
6009 span: self.span(receiver_span.start, close.span.end),
6010 }),
6011 &mut self.fatal,
6012 )
6013 }
6014
6015 fn parse_call_tail(
6016 &mut self,
6017 callee: ExprId,
6018 type_args: Option<Vec<TypeAnnotation>>,
6019 ) -> Option<ExprId> {
6020 self.advance();
6021 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
6022 Vec::new()
6023 } else {
6024 self.parse_call_args()?
6025 };
6026 if !matches!(self.peek().kind, TokenKind::RightParen) {
6027 self.error_at_peek("expected `)`");
6028 return None;
6029 }
6030 let close = self.advance();
6031
6032 let callee_span = parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
6033 parse_arena_result(
6034 self.ast.try_push_expr(Expr {
6035 kind: ExprKind::Call {
6036 callee,
6037 type_args,
6038 args,
6039 },
6040 span: self.span(callee_span.start, close.span.end),
6041 }),
6042 &mut self.fatal,
6043 )
6044 }
6045
6046 fn try_parse_generic_call_tail(&mut self, callee: ExprId) -> Option<ExprId> {
6048 let type_args = self.try_parse_type_args_only()?;
6049 if !matches!(self.peek().kind, TokenKind::LeftParen) {
6050 return None;
6051 }
6052 self.parse_call_tail(callee, Some(type_args))
6053 }
6054
6055 fn try_parse_type_args_only(&mut self) -> Option<Vec<TypeAnnotation>> {
6056 if !matches!(self.peek().kind, TokenKind::LessThan) {
6057 return self.invariant_failure("type argument parser expected `<`");
6058 }
6059 self.advance();
6060
6061 if matches!(
6062 self.peek().kind,
6063 TokenKind::GreaterThan | TokenKind::GreaterEquals
6064 ) {
6065 return None;
6066 }
6067
6068 let mut type_args = Vec::new();
6069 loop {
6070 let ta = self.parse_type_annotation()?;
6071 type_args.push(ta);
6072 match self.peek().kind {
6073 TokenKind::Comma => {
6074 self.advance();
6075 if matches!(
6076 self.peek().kind,
6077 TokenKind::GreaterThan | TokenKind::GreaterEquals
6078 ) {
6079 self.consume_type_close()?;
6080 break;
6081 }
6082 }
6083 TokenKind::GreaterThan | TokenKind::GreaterEquals => {
6084 self.consume_type_close()?;
6085 break;
6086 }
6087 _ => return None,
6088 }
6089 }
6090 Some(type_args)
6091 }
6092
6093 fn parse_call_args(&mut self) -> Option<Vec<ExprId>> {
6094 let mut args = Vec::new();
6095 loop {
6096 let spread = matches!(self.peek().kind, TokenKind::DotDotDot);
6097 if spread {
6098 self.advance();
6099 }
6100 let arg = self.parse_expression()?;
6101 if spread {
6102 self.ast.call_spread_args.insert(arg);
6103 }
6104 args.push(arg);
6105 match self.peek().kind {
6106 TokenKind::Comma => {
6107 self.advance();
6108 if matches!(self.peek().kind, TokenKind::RightParen) {
6109 return Some(args);
6110 }
6111 }
6112 TokenKind::RightParen => return Some(args),
6113 _ => {
6114 self.error_at_peek("expected `,` or `)`");
6115 return None;
6116 }
6117 }
6118 }
6119 }
6120
6121 fn parse_angle_cast(&mut self) -> Option<ExprId> {
6128 let open = self.advance();
6129 let ty = self.parse_type_annotation()?;
6130 if !matches!(self.peek().kind, TokenKind::GreaterThan) {
6131 self.error_at_peek("expected `>` to close a type assertion");
6132 return None;
6133 }
6134 self.advance();
6135 let expr = self.parse_unary()?;
6136 let end = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
6137 .span
6138 .end;
6139 parse_arena_result(
6140 self.ast.try_push_expr(Expr {
6141 kind: ExprKind::As { expr, ty },
6142 span: self.span(open.span.start, end),
6143 }),
6144 &mut self.fatal,
6145 )
6146 }
6147
6148 fn parse_atom(&mut self) -> Option<ExprId> {
6149 match self.peek().kind {
6150 TokenKind::LessThan => return self.parse_angle_cast(),
6155 TokenKind::LeftParen => return self.parse_paren(),
6156 TokenKind::LeftBrace => return self.parse_object_literal(),
6159 TokenKind::LeftBracket => return self.parse_array_literal(),
6160 TokenKind::TemplateHead(_) => return self.parse_template_literal(),
6161 TokenKind::This => return self.parse_this_or_super(true),
6162 TokenKind::Super => return self.parse_this_or_super(false),
6163 TokenKind::Function => return self.parse_function_expression(),
6164 _ => {}
6165 }
6166 if !matches!(
6167 self.peek().kind,
6168 TokenKind::NumberLiteral(_)
6169 | TokenKind::BigIntLiteral(_)
6170 | TokenKind::StringLiteral(_)
6171 | TokenKind::TemplateNoSubstitution(_)
6172 | TokenKind::BooleanLiteral(_)
6173 | TokenKind::NullLiteral
6174 | TokenKind::Identifier
6175 | TokenKind::RegexLiteral { .. }
6176 ) {
6177 self.error_at_peek("expected expression");
6178 return None;
6179 }
6180 let tok = self.advance();
6181 let span = tok.span;
6182 let kind = match tok.kind {
6183 TokenKind::NumberLiteral(v) => ExprKind::Number(v),
6184 TokenKind::BigIntLiteral(s) => ExprKind::BigInt(s),
6185 TokenKind::StringLiteral(s) => ExprKind::String(s),
6186 TokenKind::TemplateNoSubstitution(s) => ExprKind::String(s),
6187 TokenKind::BooleanLiteral(b) => ExprKind::Boolean(b),
6188 TokenKind::NullLiteral => ExprKind::Null,
6189 TokenKind::Identifier => ExprKind::Identifier(Ident {
6190 name: self.source[span.start as usize..span.end as usize].to_string(),
6191 span,
6192 }),
6193 TokenKind::RegexLiteral { source, flags } => ExprKind::Regex { source, flags },
6194 _ => unreachable!("advance preserves the accepted expression token"),
6196 };
6197 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
6198 }
6199
6200 fn parse_this_or_super(&mut self, is_this: bool) -> Option<ExprId> {
6203 let tok = self.advance();
6204 let span = tok.span;
6205 if !is_this && self.class_member_body_depth == 0 {
6206 self.error_at_with_help(
6207 span,
6208 "`super` is only valid inside a class method or constructor body",
6209 vec![
6210 "call `super(...)` in a subclass constructor or `super.method(...)` \
6211 in a subclass method"
6212 .to_string(),
6213 ],
6214 );
6215 }
6216 let kind = if is_this
6217 && self.class_member_body_depth == 0
6218 && self.function_expression_body_depth == 0
6219 {
6220 ExprKind::ThisOutsideReceiver
6221 } else if is_this {
6222 ExprKind::This
6223 } else {
6224 ExprKind::Super
6225 };
6226 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
6227 }
6228
6229 fn parse_template_literal(&mut self) -> Option<ExprId> {
6232 let head_tok = self.advance();
6233 let TokenKind::TemplateHead(head) = head_tok.kind else {
6234 unreachable!("template dispatch requires TemplateHead");
6235 };
6236 let start = head_tok.span.start;
6237 let mut parts: Vec<String> = vec![head];
6238 let mut exprs: Vec<ExprId> = Vec::new();
6239 let mut substitution_spans: Vec<Span> = Vec::new();
6240 let mut substitution_start = head_tok.span.end.saturating_sub(2);
6243 loop {
6244 let expr = self.parse_expression()?;
6245 exprs.push(expr);
6246 let closing = self.peek().span.start;
6247 let substitution_span = self.span(substitution_start, closing.saturating_add(1));
6248 match self.peek().kind.clone() {
6249 TokenKind::TemplateMiddle(s) => {
6250 let tok = self.advance();
6251 substitution_spans.push(substitution_span);
6252 substitution_start = tok.span.end.saturating_sub(2);
6253 parts.push(s);
6254 }
6255 TokenKind::TemplateTail(s) => {
6256 let tok = self.advance();
6257 substitution_spans.push(substitution_span);
6258 parts.push(s);
6259 return parse_arena_result(
6260 self.ast.try_push_expr(Expr {
6261 kind: ExprKind::TemplateLiteral {
6262 parts,
6263 exprs,
6264 substitution_spans,
6265 },
6266 span: self.span(start, tok.span.end),
6267 }),
6268 &mut self.fatal,
6269 );
6270 }
6271 _ => {
6272 self.error_at_peek("expected `}` to close template interpolation");
6273 return None;
6274 }
6275 }
6276 }
6277 }
6278
6279 fn parse_object_literal(&mut self) -> Option<ExprId> {
6280 let open = self.advance();
6281
6282 let mut members: Vec<ObjectLiteralMember> = Vec::new();
6283 if !matches!(self.peek().kind, TokenKind::RightBrace) {
6284 loop {
6285 if matches!(self.peek().kind, TokenKind::DotDotDot) {
6286 let dots = self.advance();
6287 let value = self.parse_expression()?;
6288 let value_span =
6289 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
6290 members.push(ObjectLiteralMember::Spread {
6291 value,
6292 span: self.span(dots.span.start, value_span.end),
6293 });
6294 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
6295 self.advance();
6296 let key = self.parse_expression()?;
6297 if !matches!(self.peek().kind, TokenKind::RightBracket) {
6298 self.error_at_peek("expected `]` after computed property key");
6299 return None;
6300 }
6301 self.advance();
6302 if !matches!(self.peek().kind, TokenKind::Colon) {
6303 self.error_at_peek("expected `:` after computed property key; computed methods are not supported");
6304 return None;
6305 }
6306 self.advance();
6307 let value = self.parse_expression()?;
6308 members.push(ObjectLiteralMember::Computed { key, value });
6309 } else {
6310 let field = self.parse_object_literal_field()?;
6311 if let Some(existing) = members.iter().find_map(|m| match m {
6314 ObjectLiteralMember::Field(f) if f.name.name == field.name.name => Some(f),
6315 _ => None,
6316 }) {
6317 self.diagnostics.push(Diagnostic {
6318 severity: Severity::Error,
6319 span: field.name.span,
6320 message: format!(
6321 "duplicate field `{}` in object literal",
6322 field.name.name
6323 ),
6324 help: vec![],
6325 notes: vec![(existing.name.span, "first defined here".into())],
6326 });
6327 }
6329 members.push(ObjectLiteralMember::Field(field));
6330 }
6331 if self.at_inserted_semicolon()
6332 && matches!(self.peek_at(1).kind, TokenKind::RightBrace)
6333 {
6334 self.advance();
6335 }
6336 match self.peek().kind {
6337 TokenKind::Comma => {
6338 self.advance();
6339 if matches!(self.peek().kind, TokenKind::RightBrace) {
6340 break;
6341 }
6342 }
6343 TokenKind::RightBrace => break,
6344 _ => {
6345 self.error_at_peek("expected `,` or `}`");
6346 return None;
6347 }
6348 }
6349 }
6350 }
6351
6352 if !matches!(self.peek().kind, TokenKind::RightBrace) {
6353 self.error_at_peek("expected `}`");
6354 return None;
6355 }
6356 let close = self.advance();
6357
6358 parse_arena_result(
6359 self.ast.try_push_expr(Expr {
6360 kind: ExprKind::ObjectLiteral { members },
6361 span: self.span(open.span.start, close.span.end),
6362 }),
6363 &mut self.fatal,
6364 )
6365 }
6366
6367 fn parse_object_literal_field(&mut self) -> Option<ObjectLiteralField> {
6368 self.reject_async_member_modifier();
6369 let (name, shorthandable) = match self.peek().kind.clone() {
6370 TokenKind::Identifier => {
6371 let tok = self.advance();
6372 (self.ident_from_token(&tok), true)
6373 }
6374 kind if is_property_name(&kind) => {
6375 let tok = self.advance();
6376 (self.ident_from_token(&tok), false)
6377 }
6378 TokenKind::StringLiteral(_) | TokenKind::NumberLiteral(_) => {
6379 let tok = self.advance();
6380 (self.property_ident_from_token(&tok), false)
6381 }
6382 _ => {
6383 self.error_at_peek("expected field name");
6384 return None;
6385 }
6386 };
6387
6388 if matches!(self.peek().kind, TokenKind::LeftParen) {
6394 let value = self.parse_method_shorthand_body(name.span)?;
6395 return Some(ObjectLiteralField { name, value });
6396 }
6397
6398 if !matches!(self.peek().kind, TokenKind::Colon) {
6399 if shorthandable {
6402 let value = parse_arena_result(
6403 self.ast.try_push_expr(Expr {
6404 kind: ExprKind::Identifier(name.clone()),
6405 span: name.span,
6406 }),
6407 &mut self.fatal,
6408 )?;
6409 return Some(ObjectLiteralField { name, value });
6410 }
6411 self.error_at_peek("expected `:` after field name");
6412 return None;
6413 }
6414 self.advance();
6415
6416 let value = self.parse_expression()?;
6417 Some(ObjectLiteralField { name, value })
6418 }
6419
6420 fn parse_method_shorthand_body(&mut self, name_span: Span) -> Option<ExprId> {
6423 self.advance();
6424
6425 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
6426 Vec::new()
6427 } else {
6428 self.parse_arrow_param_list()?
6429 };
6430 if !matches!(self.peek().kind, TokenKind::RightParen) {
6431 self.error_at_peek("expected `)`");
6432 return None;
6433 }
6434 self.advance();
6435
6436 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
6437 self.advance();
6438 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
6439 } else {
6440 (None, None)
6441 };
6442
6443 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
6444 self.error_at_peek("expected `{` to open the method body");
6445 return None;
6446 }
6447 let block = self.parse_outer_this_boundary(Self::parse_block)?;
6448 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
6449 .span
6450 .end;
6451
6452 parse_arena_result(
6453 self.ast.try_push_expr(Expr {
6454 kind: ExprKind::Arrow {
6455 params,
6456 return_type,
6457 type_predicate,
6458 body: ArrowBody::Block(block),
6459 },
6460 span: self.span(name_span.start, end),
6461 }),
6462 &mut self.fatal,
6463 )
6464 }
6465
6466 fn parse_array_literal(&mut self) -> Option<ExprId> {
6467 let open = self.advance();
6468
6469 let mut elements: Vec<ArrayLiteralElement> = Vec::new();
6470 if !matches!(self.peek().kind, TokenKind::RightBracket) {
6471 loop {
6472 let element = if matches!(self.peek().kind, TokenKind::DotDotDot) {
6473 let dots = self.advance();
6474 let value = self.parse_expression()?;
6475 let value_span =
6476 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
6477 ArrayLiteralElement::Spread {
6478 value,
6479 span: self.span(dots.span.start, value_span.end),
6480 }
6481 } else {
6482 ArrayLiteralElement::Value(self.parse_expression()?)
6483 };
6484 elements.push(element);
6485 match self.peek().kind {
6486 TokenKind::Comma => {
6487 self.advance();
6488 if matches!(self.peek().kind, TokenKind::RightBracket) {
6489 break;
6490 }
6491 }
6492 TokenKind::RightBracket => break,
6493 _ => {
6494 self.error_at_peek("expected `,` or `]`");
6495 return None;
6496 }
6497 }
6498 }
6499 }
6500
6501 if !matches!(self.peek().kind, TokenKind::RightBracket) {
6502 self.error_at_peek("expected `]`");
6503 return None;
6504 }
6505 let close = self.advance();
6506
6507 parse_arena_result(
6508 self.ast.try_push_expr(Expr {
6509 kind: ExprKind::ArrayLiteral { elements },
6510 span: self.span(open.span.start, close.span.end),
6511 }),
6512 &mut self.fatal,
6513 )
6514 }
6515
6516 fn parse_paren(&mut self) -> Option<ExprId> {
6517 let open = self.advance();
6518 let inner = self.parse_expression()?;
6519 if !matches!(self.peek().kind, TokenKind::RightParen) {
6520 self.error_at_peek("expected `)`");
6521 return None;
6522 }
6523 let close = self.advance();
6524 let span = self.span(open.span.start, close.span.end);
6525 parse_arena_result(
6526 self.ast.try_push_expr(Expr {
6527 kind: ExprKind::Paren(inner),
6528 span,
6529 }),
6530 &mut self.fatal,
6531 )
6532 }
6533
6534 fn with_recursion_limit<T>(&mut self, parse: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
6535 if self.fatal.is_some() {
6536 return None;
6537 }
6538 if self.recursion_limit_span.is_some() || self.recursion_depth >= MAX_PARSE_DEPTH {
6539 self.stop_at_recursion_limit();
6540 return None;
6541 }
6542 self.recursion_depth += 1;
6543 let result = parse(self);
6544 self.recursion_depth -= 1;
6545 result
6546 }
6547
6548 fn stop_at_recursion_limit(&mut self) {
6549 self.recursion_limit_span.get_or_insert(self.peek().span);
6550 self.pos = self.tokens.len().saturating_sub(1);
6552 }
6553
6554 fn recover(&mut self) {
6555 while !self.is_at_eof() {
6556 match self.peek().kind {
6557 TokenKind::Semicolon => {
6558 self.advance();
6559 return;
6560 }
6561 TokenKind::RightBrace
6562 | TokenKind::Let
6563 | TokenKind::Const
6564 | TokenKind::Function
6565 | TokenKind::Export
6566 | TokenKind::If
6567 | TokenKind::While
6568 | TokenKind::Return
6569 | TokenKind::Try
6570 | TokenKind::Throw => return,
6571 _ => {
6572 self.advance();
6573 }
6574 }
6575 }
6576 }
6577
6578 fn peek(&self) -> &Token {
6579 self.tokens.get(self.pos).unwrap_or(&self.eof)
6580 }
6581
6582 fn peek_at(&self, offset: usize) -> &Token {
6583 self.pos
6584 .checked_add(offset)
6585 .and_then(|index| self.tokens.get(index))
6586 .unwrap_or(&self.eof)
6587 }
6588
6589 fn at_inserted_semicolon(&self) -> bool {
6591 let token = self.peek();
6592 matches!(token.kind, TokenKind::Semicolon) && token.span.start == token.span.end
6593 }
6594
6595 fn advance(&mut self) -> Token {
6596 let tok = self.peek().clone();
6597 if self.pos < self.tokens.len().saturating_sub(1) {
6598 self.pos += 1;
6599 }
6600 tok
6601 }
6602
6603 fn is_at_eof(&self) -> bool {
6604 self.fatal.is_some() || matches!(self.peek().kind, TokenKind::Eof)
6605 }
6606
6607 fn line_break_before_peek(&self) -> bool {
6609 self.source_has_line_break(self.prev_token_end(), self.peek().span.start)
6610 }
6611
6612 fn line_break_after_peek(&self) -> bool {
6614 self.source_has_line_break(self.peek().span.end, self.peek_at(1).span.start)
6615 }
6616
6617 fn source_has_line_break(&self, start: u32, end: u32) -> bool {
6620 self.source
6621 .get(start as usize..end as usize)
6622 .is_some_and(|gap| gap.contains(['\n', '\r']))
6623 }
6624
6625 fn at_statement_end(&self) -> bool {
6630 matches!(self.peek().kind, TokenKind::Semicolon)
6631 || (self.last_as_type_end == Some(self.prev_token_end())
6632 && self.line_break_before_peek())
6633 }
6634
6635 fn finish_statement(&mut self) -> u32 {
6638 if matches!(self.peek().kind, TokenKind::Semicolon) {
6639 return self.advance().span.end;
6640 }
6641 self.prev_token_end()
6642 }
6643
6644 fn prev_token_end(&self) -> u32 {
6647 self.tokens
6648 .get(self.pos.saturating_sub(1))
6649 .unwrap_or(&self.eof)
6650 .span
6651 .end
6652 }
6653
6654 fn take_leading_doc(&mut self) -> Option<crate::DocComment> {
6655 let parsed = self
6656 .peek()
6657 .leading_doc
6658 .as_ref()
6659 .map(crate::parse_doc_comment)
6660 .transpose();
6661 match parsed {
6662 Ok(doc) => doc,
6663 Err(error) => {
6664 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6665 None
6666 }
6667 }
6668 }
6669
6670 fn parse_doc(&mut self, raw: &crate::RawDoc) -> Option<crate::DocComment> {
6671 match crate::parse_doc_comment(raw) {
6672 Ok(doc) => Some(doc),
6673 Err(error) => {
6674 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6675 None
6676 }
6677 }
6678 }
6679
6680 fn reject_use_strict_with_non_simple_params(&mut self) {
6684 let mut functions: Vec<(Vec<Span>, StmtId)> = Vec::new();
6685 let non_simple = |param: &ParamDecl| {
6686 (param.default.is_some() || param.pattern.is_some() || param.rest).then(|| {
6687 param
6688 .pattern
6689 .as_ref()
6690 .map_or(param.name.span, Binding::span)
6691 })
6692 };
6693 for expr in self.ast.source_expressions() {
6694 if let ExprKind::Arrow {
6695 params,
6696 body: ArrowBody::Block(body),
6697 ..
6698 } = &expr.kind
6699 {
6700 functions.push((params.iter().filter_map(non_simple).collect(), *body));
6701 }
6702 }
6703 for stmt in self.ast.source_statements() {
6704 match &stmt.kind {
6705 StmtKind::Function { params, body, .. } => {
6706 functions.push((params.iter().filter_map(non_simple).collect(), *body));
6707 }
6708 StmtKind::ClassDecl { members, .. } => {
6709 for member in members {
6710 match member {
6711 ClassMember::Method { params, body, .. }
6712 | ClassMember::Constructor { params, body, .. } => {
6713 functions
6714 .push((params.iter().filter_map(non_simple).collect(), *body));
6715 }
6716 ClassMember::Accessor { param, body, .. } => {
6717 let params = param.as_deref().and_then(non_simple);
6718 functions.push((params.into_iter().collect(), *body));
6719 }
6720 _ => {}
6721 }
6722 }
6723 }
6724 _ => {}
6725 }
6726 }
6727 for (params, body) in functions {
6728 if params.is_empty() {
6729 continue;
6730 }
6731 let Some(directive) = self.use_strict_directive(body) else {
6732 continue;
6733 };
6734 for span in params {
6735 self.error_at(
6736 span,
6737 "this parameter is not allowed with a `\"use strict\"` directive",
6738 );
6739 }
6740 self.error_at_with_help(
6741 directive,
6742 "a `\"use strict\"` directive can't be used with a parameter list that has defaults, patterns, or a rest parameter",
6743 vec!["remove the directive; Submilli code is always strict".into()],
6744 );
6745 }
6746 }
6747
6748 fn use_strict_directive(&self, body: StmtId) -> Option<Span> {
6752 let StmtKind::Block(stmts) = &self.ast.try_stmt(body).ok()?.kind else {
6753 return None;
6754 };
6755 for stmt in stmts {
6756 let stmt = self.ast.try_stmt(*stmt).ok()?;
6757 let StmtKind::Expr(expr) = stmt.kind else {
6758 return None;
6759 };
6760 let expr = self.ast.try_expr(expr).ok()?;
6761 let ExprKind::String(value) = &expr.kind else {
6762 return None;
6763 };
6764 if self.source.as_bytes().get(expr.span.start as usize) == Some(&b'`') {
6765 return None;
6766 }
6767 if value == "use strict" {
6768 return Some(stmt.span);
6769 }
6770 }
6771 None
6772 }
6773
6774 fn error_at(&mut self, span: Span, message: impl Into<String>) {
6775 self.diagnostics.push(Diagnostic {
6776 severity: Severity::Error,
6777 span,
6778 message: message.into(),
6779 help: vec![],
6780 notes: vec![],
6781 });
6782 }
6783
6784 fn reject_loose_nullish_comparison(
6787 &mut self,
6788 op: BinOp,
6789 lhs: ExprId,
6790 rhs: ExprId,
6791 ) -> Option<()> {
6792 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
6793 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
6794 let (nullish, operand, nullish_first) =
6795 match (self.nullish_operand(lhs)?, self.nullish_operand(rhs)?) {
6796 (Some(left), Some(right)) => {
6797 self.reject_nullish_against_nullish(op, lhs_span, rhs_span, [left, right]);
6798 return Some(());
6799 }
6800 (None, Some(nullish)) => (nullish, lhs, false),
6801 (Some(nullish), None) => (nullish, rhs, true),
6802 (None, None) => return Some(()),
6803 };
6804 let comparison = LooseComparison {
6805 nullish,
6806 nullish_first,
6807 negated: op == BinOp::NotEq,
6808 };
6809 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
6810 let x = self.single_line_source(operand_span).unwrap_or("x");
6811 let strict = self.strict_suggestion(&comparison, x);
6812 let either = self.either_suggestion(&comparison, operand, x)?;
6813 let literal = comparison.nullish.literal.text();
6814 let written = self
6816 .single_line_source(self.span(lhs_span.start, rhs_span.end))
6817 .map_or_else(
6818 || {
6819 let written_op = if comparison.negated { "!=" } else { "==" };
6820 comparison.ordered(x, written_op, literal)
6821 },
6822 str::to_string,
6823 );
6824 self.error_at_with_help(
6825 self.span(lhs_span.start, rhs_span.end),
6826 format!(
6827 "`{written}` does not also match `{}`: `==` compares like `===`",
6828 comparison.nullish.literal.other().text()
6829 ),
6830 vec![
6831 either,
6832 format!("to test for `{literal}` alone, write `{strict}`"),
6833 ],
6834 );
6835 Some(())
6836 }
6837
6838 fn reject_nullish_against_nullish(
6842 &mut self,
6843 op: BinOp,
6844 lhs: Span,
6845 rhs: Span,
6846 sides: [NullishOperand; 2],
6847 ) {
6848 let span = self.span(lhs.start, rhs.end);
6849 let subject = self.single_line_source(span).map_or_else(
6852 || "this comparison".to_string(),
6853 |written| format!("`{written}`"),
6854 );
6855 let value = if op == BinOp::NotEq { "false" } else { "true" };
6856 let effects: Vec<String> = sides
6857 .iter()
6858 .filter_map(|side| side.effect)
6859 .map(|effect| {
6860 self.single_line_source(effect.operand).map_or_else(
6861 || "the `void` operand's expression".to_string(),
6862 |statement| format!("`{statement};`"),
6863 )
6864 })
6865 .collect();
6866 let help = if effects.is_empty() {
6867 format!("in TypeScript this is always `{value}`; write `{value}`")
6868 } else {
6869 format!(
6870 "in TypeScript this is always `{value}`; run {} {}, then use `{value}`",
6871 effects.join(" and "),
6872 if effects.len() > 1 {
6873 "each as its own statement"
6874 } else {
6875 "as its own statement"
6876 }
6877 )
6878 };
6879 self.error_at_with_help(
6880 span,
6881 format!("{subject} compares two nullish values: `==` compares like `===`"),
6882 vec![help],
6883 );
6884 }
6885
6886 fn strict_suggestion(&self, comparison: &LooseComparison, x: &str) -> String {
6889 let eq = comparison.strict_op();
6890 match comparison.nullish.effect {
6891 Some(effect) => {
6892 let void = self.single_line_source(effect.void).unwrap_or("void expr");
6893 comparison.ordered(x, eq, void)
6894 }
6895 None => format!("{x} {eq} {}", comparison.nullish.literal.text()),
6896 }
6897 }
6898
6899 fn either_suggestion(
6903 &mut self,
6904 comparison: &LooseComparison,
6905 operand: ExprId,
6906 x: &str,
6907 ) -> Option<String> {
6908 let mut steps = Vec::new();
6909 let tested = if self.repeats_without_effect(operand)? {
6910 x
6911 } else {
6912 steps.push(format!("bind `{x}` to a `const` (`const value = {x};`)"));
6913 "value"
6914 };
6915 if let Some(effect) = comparison.nullish.effect {
6916 let statement = self.single_line_source(effect.operand).unwrap_or("expr");
6917 let step = format!("run `{statement};` as its own statement");
6918 if comparison.nullish_first {
6919 steps.insert(0, step);
6920 } else {
6921 steps.push(step);
6922 }
6923 }
6924 let eq = comparison.strict_op();
6925 let joiner = if comparison.negated { "&&" } else { "||" };
6926 let both = format!("{tested} {eq} null {joiner} {tested} {eq} undefined");
6927 Some(if steps.is_empty() {
6928 format!("to test for either, write `{both}`")
6929 } else {
6930 format!(
6931 "to test for either, first {}, then write `{both}`",
6932 steps.join(", then ")
6933 )
6934 })
6935 }
6936
6937 fn repeats_without_effect(&mut self, id: ExprId) -> Option<bool> {
6940 let unwrapped = self.unparenthesized(id)?;
6941 let expr = parse_arena_result(self.ast.try_expr(unwrapped), &mut self.fatal)?;
6942 if is_literal(&expr.kind) {
6943 return Some(true);
6944 }
6945 if self
6946 .nullish_operand(unwrapped)?
6947 .is_some_and(|nullish| nullish.effect.is_none())
6948 {
6949 return Some(true);
6950 }
6951 self.is_reference_path(id)
6952 }
6953
6954 fn nullish_operand(&mut self, id: ExprId) -> Option<Option<NullishOperand>> {
6956 let id = self.unparenthesized(id)?;
6957 let expr = parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?;
6958 let span = expr.span;
6959 Some(match &expr.kind {
6960 ExprKind::Null => Some(NullishOperand {
6961 literal: Nullish::Null,
6962 effect: None,
6963 }),
6964 ExprKind::Identifier(ident) if ident.name == "undefined" => Some(NullishOperand {
6965 literal: Nullish::Undefined,
6966 effect: None,
6967 }),
6968 ExprKind::Void { operand } => {
6969 let operand_id = self.unparenthesized(*operand)?;
6970 let operand =
6971 parse_arena_result(self.ast.try_expr(operand_id), &mut self.fatal)?.clone();
6972 let effect_free =
6974 is_literal(&operand.kind) || self.is_reference_path(operand_id)?;
6975 let effect = (!effect_free).then_some(VoidEffect {
6976 void: span,
6977 operand: operand.span,
6978 });
6979 Some(NullishOperand {
6980 literal: Nullish::Undefined,
6981 effect,
6982 })
6983 }
6984 _ => None,
6985 })
6986 }
6987
6988 fn is_reference_path(&mut self, id: ExprId) -> Option<bool> {
6991 let mut id = id;
6992 loop {
6993 match &parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6994 ExprKind::Identifier(_) | ExprKind::This => return Some(true),
6995 ExprKind::FieldAccess { receiver, .. } => id = *receiver,
6996 ExprKind::Paren(inner) => id = *inner,
6997 _ => return Some(false),
6998 }
6999 }
7000 }
7001
7002 fn unparenthesized(&mut self, id: ExprId) -> Option<ExprId> {
7003 let mut id = id;
7004 loop {
7005 match parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
7006 ExprKind::Paren(inner) => id = inner,
7007 _ => return Some(id),
7008 }
7009 }
7010 }
7011
7012 fn single_line_source(&self, span: Span) -> Option<&'a str> {
7013 self.source
7014 .get(span.start as usize..span.end as usize)
7015 .filter(|text| !text.contains('\n'))
7016 }
7017
7018 fn error_at_with_help(&mut self, span: Span, message: impl Into<String>, help: Vec<String>) {
7019 self.diagnostics.push(Diagnostic {
7020 severity: Severity::Error,
7021 span,
7022 message: message.into(),
7023 help,
7024 notes: vec![],
7025 });
7026 }
7027
7028 fn invariant_failure<T>(&mut self, message: &str) -> Option<T> {
7029 self.fatal.get_or_insert_with(|| CompilerFailure::Internal {
7030 stage: CompilerStage::Parse,
7031 span: None,
7032 message: message.into(),
7033 });
7034 self.pos = self.tokens.len();
7035 None
7036 }
7037
7038 fn error_at_peek(&mut self, message: impl Into<String>) {
7039 let span = self.peek().span;
7040 self.error_at(span, message);
7041 }
7042
7043 fn error_at_peek_with_help(&mut self, message: impl Into<String>, help: Vec<String>) {
7044 let span = self.peek().span;
7045 self.error_at_with_help(span, message, help);
7046 }
7047
7048 fn error_count(&self) -> usize {
7049 self.diagnostics
7050 .iter()
7051 .filter(|d| d.severity == Severity::Error)
7052 .count()
7053 }
7054}
7055
7056pub(crate) fn is_property_name(kind: &TokenKind) -> bool {
7057 matches!(
7058 kind,
7059 TokenKind::Identifier
7060 | TokenKind::BooleanLiteral(_)
7061 | TokenKind::NullLiteral
7062 | TokenKind::Let
7063 | TokenKind::Const
7064 | TokenKind::Function
7065 | TokenKind::If
7066 | TokenKind::Else
7067 | TokenKind::While
7068 | TokenKind::Do
7069 | TokenKind::For
7070 | TokenKind::Break
7071 | TokenKind::Continue
7072 | TokenKind::Return
7073 | TokenKind::Switch
7074 | TokenKind::Case
7075 | TokenKind::Default
7076 | TokenKind::Void
7077 | TokenKind::Interface
7078 | TokenKind::Enum
7079 | TokenKind::In
7080 | TokenKind::Typeof
7081 | TokenKind::Import
7082 | TokenKind::Export
7083 | TokenKind::New
7084 | TokenKind::Try
7085 | TokenKind::Catch
7086 | TokenKind::Finally
7087 | TokenKind::Throw
7088 | TokenKind::Class
7089 | TokenKind::Extends
7090 | TokenKind::Implements
7091 | TokenKind::Super
7092 | TokenKind::This
7093 )
7094}
7095
7096fn is_reserved_identifier_word(kind: &TokenKind) -> bool {
7097 matches!(
7098 kind,
7099 TokenKind::BooleanLiteral(_)
7100 | TokenKind::NullLiteral
7101 | TokenKind::Let
7102 | TokenKind::Const
7103 | TokenKind::Function
7104 | TokenKind::If
7105 | TokenKind::Else
7106 | TokenKind::While
7107 | TokenKind::Do
7108 | TokenKind::For
7109 | TokenKind::Break
7110 | TokenKind::Continue
7111 | TokenKind::Return
7112 | TokenKind::Switch
7113 | TokenKind::Case
7114 | TokenKind::Default
7115 | TokenKind::Export
7116 | TokenKind::Void
7117 | TokenKind::Interface
7118 | TokenKind::Enum
7119 | TokenKind::In
7120 | TokenKind::Typeof
7121 | TokenKind::Import
7122 | TokenKind::New
7123 | TokenKind::Try
7124 | TokenKind::Catch
7125 | TokenKind::Finally
7126 | TokenKind::Throw
7127 | TokenKind::Class
7128 | TokenKind::Extends
7129 | TokenKind::Implements
7130 | TokenKind::Super
7131 | TokenKind::This
7132 )
7133}
7134
7135#[derive(Clone, Copy, PartialEq, Eq)]
7139enum SpecifierList {
7140 Import,
7141 Export,
7142}
7143
7144impl SpecifierList {
7145 fn noun(self) -> &'static str {
7146 match self {
7147 SpecifierList::Import => "import",
7148 SpecifierList::Export => "export",
7149 }
7150 }
7151}
7152
7153const STRICT_MODE_RESERVED_WORDS: [&str; 6] = [
7157 "package",
7158 "private",
7159 "protected",
7160 "public",
7161 "static",
7162 "yield",
7163];
7164
7165fn reserved_keyword_rename_example(keyword: &str) -> &'static str {
7166 match keyword {
7167 "default" => "defaultValue",
7168 _ => "valueName",
7169 }
7170}
7171
7172fn mixed_logical(prev: BinOp, next: BinOp) -> bool {
7173 let prev_logical = matches!(prev, BinOp::Or | BinOp::And);
7174 let next_logical = matches!(next, BinOp::Or | BinOp::And);
7175 let prev_nullish = matches!(prev, BinOp::NullishCoalesce);
7176 let next_nullish = matches!(next, BinOp::NullishCoalesce);
7177 (prev_logical && next_nullish) || (prev_nullish && next_logical)
7178}
7179
7180fn mixed_logical_help(prev: BinOp, next: BinOp) -> String {
7183 let symbol = |op: BinOp| if op == BinOp::And { "&&" } else { "||" };
7184 let (first, second) = if prev == BinOp::NullishCoalesce {
7185 ("??", symbol(next))
7186 } else {
7187 (symbol(prev), "??")
7188 };
7189 format!(
7190 "add parentheses to choose the grouping: `(a {first} b) {second} c` or `a {first} (b {second} c)`"
7191 )
7192}
7193
7194const LOGICAL_AND_PREC: u8 = 2;
7195
7196const FOR_IN_OBJECT: &str = "#for_in_object";
7199const FOR_IN_KEY: &str = "#for_in_key";
7200
7201struct HeldForInObject {
7203 name: Ident,
7204 statement: StmtId,
7205}
7206
7207#[derive(Clone, Copy)]
7209struct NullishOperand {
7210 literal: Nullish,
7211 effect: Option<VoidEffect>,
7213}
7214
7215#[derive(Clone, Copy, PartialEq, Eq)]
7216enum Nullish {
7217 Null,
7218 Undefined,
7219}
7220
7221impl Nullish {
7222 fn text(self) -> &'static str {
7223 match self {
7224 Nullish::Null => "null",
7225 Nullish::Undefined => "undefined",
7226 }
7227 }
7228
7229 fn other(self) -> Self {
7231 match self {
7232 Nullish::Null => Nullish::Undefined,
7233 Nullish::Undefined => Nullish::Null,
7234 }
7235 }
7236}
7237
7238struct LooseComparison {
7240 nullish: NullishOperand,
7241 nullish_first: bool,
7243 negated: bool,
7245}
7246
7247impl LooseComparison {
7248 fn strict_op(&self) -> &'static str {
7249 if self.negated { "!==" } else { "===" }
7250 }
7251
7252 fn ordered(&self, x: &str, op: &str, nullish: &str) -> String {
7254 if self.nullish_first {
7255 format!("{nullish} {op} {x}")
7256 } else {
7257 format!("{x} {op} {nullish}")
7258 }
7259 }
7260}
7261
7262#[derive(Clone, Copy)]
7263struct VoidEffect {
7264 void: Span,
7266 operand: Span,
7268}
7269
7270fn is_literal(kind: &ExprKind) -> bool {
7271 matches!(
7272 kind,
7273 ExprKind::Number(_)
7274 | ExprKind::BigInt(_)
7275 | ExprKind::String(_)
7276 | ExprKind::Boolean(_)
7277 | ExprKind::Null
7278 )
7279}
7280
7281struct ParamOrder<'p> {
7283 optional: bool,
7284 required: bool,
7286 name: Option<&'p str>,
7288 span: Span,
7289}
7290
7291impl<'p> ParamOrder<'p> {
7292 fn of_param(param: &'p ParamDecl) -> Self {
7293 let (name, span) = match ¶m.pattern {
7294 Some(binding) => (None, binding.span()),
7295 None => (Some(param.name.name.as_str()), param.name.span),
7296 };
7297 Self {
7298 optional: param.optional,
7299 required: !param.is_omittable() && !param.rest,
7300 name,
7301 span,
7302 }
7303 }
7304
7305 fn of_field(field: &'p TypeAnnotationField) -> Self {
7306 Self {
7307 optional: field.optional,
7308 required: !field.optional && !field.rest,
7309 name: Some(field.name.name.as_str()),
7310 span: field.name.span,
7311 }
7312 }
7313}
7314
7315struct BinaryToken {
7317 op: BinOp,
7318 precedence: u8,
7319 tokens: usize,
7320}
7321
7322struct ShiftToken {
7324 op: BinOp,
7325 tokens: usize,
7326 assignment: bool,
7328}
7329
7330impl ShiftToken {
7331 fn new(op: BinOp, tokens: usize, assignment: bool) -> Self {
7332 Self {
7333 op,
7334 tokens,
7335 assignment,
7336 }
7337 }
7338}
7339
7340const SHIFT_PRECEDENCE: u8 = 8;
7342
7343#[derive(Clone, Copy, PartialEq)]
7344enum TupleElementLabel {
7345 None,
7346 Required,
7348 Optional,
7350}
7351
7352fn peek_binop(kind: &TokenKind) -> Option<(BinOp, u8)> {
7353 Some(match kind {
7354 TokenKind::QuestionQuestion => (BinOp::NullishCoalesce, 0),
7355 TokenKind::PipePipe => (BinOp::Or, 1),
7356 TokenKind::AmpAmp => (BinOp::And, LOGICAL_AND_PREC),
7357 TokenKind::Pipe => (BinOp::BitOr, 3),
7358 TokenKind::Caret => (BinOp::BitXor, 4),
7359 TokenKind::Amp => (BinOp::BitAnd, 5),
7360 TokenKind::EqEqEq | TokenKind::EqEq => (BinOp::Eq, 6),
7361 TokenKind::BangEqEq | TokenKind::BangEq => (BinOp::NotEq, 6),
7362 TokenKind::LessThan => (BinOp::Lt, 7),
7363 TokenKind::GreaterThan => (BinOp::Gt, 7),
7364 TokenKind::LessEquals => (BinOp::Le, 7),
7365 TokenKind::GreaterEquals => (BinOp::Ge, 7),
7366 TokenKind::In => (BinOp::In, 7),
7367 TokenKind::Plus => (BinOp::Add, 9),
7370 TokenKind::Minus => (BinOp::Sub, 9),
7371 TokenKind::Star => (BinOp::Mul, 10),
7372 TokenKind::Slash => (BinOp::Div, 10),
7373 TokenKind::Percent => (BinOp::Rem, 10),
7374 TokenKind::StarStar => (BinOp::Pow, 11),
7376 _ => return None,
7377 })
7378}
7379
7380fn cannot_start_type(kind: &TokenKind) -> bool {
7384 matches!(
7385 kind,
7386 TokenKind::Dot
7387 | TokenKind::QuestionDot
7388 | TokenKind::LeftBracket
7389 | TokenKind::Bang
7390 | TokenKind::Semicolon
7391 | TokenKind::RightParen
7392 | TokenKind::RightBrace
7393 | TokenKind::Comma
7394 | TokenKind::Eof
7395 )
7396}
7397
7398fn right_operand_min_prec(op: BinOp, prec: u8) -> u8 {
7402 match op {
7403 BinOp::Pow => prec,
7404 BinOp::NullishCoalesce => LOGICAL_AND_PREC + 1,
7405 _ => prec + 1,
7406 }
7407}
7408
7409fn compound_op_for_token(kind: &TokenKind) -> Option<BinOp> {
7410 Some(match kind {
7411 TokenKind::PlusEquals => BinOp::Add,
7412 TokenKind::MinusEquals => BinOp::Sub,
7413 TokenKind::StarEquals => BinOp::Mul,
7414 TokenKind::SlashEquals => BinOp::Div,
7415 TokenKind::PercentEquals => BinOp::Rem,
7416 TokenKind::StarStarEquals => BinOp::Pow,
7417 TokenKind::AmpEquals => BinOp::BitAnd,
7418 TokenKind::PipeEquals => BinOp::BitOr,
7419 TokenKind::CaretEquals => BinOp::BitXor,
7420 _ => return None,
7421 })
7422}
7423
7424fn is_assign_lookahead(kind: &TokenKind) -> bool {
7425 matches!(kind, TokenKind::Equals) || compound_op_for_token(kind).is_some()
7426}
7427
7428fn parse_arena_result<T>(
7431 result: Result<T, crate::arena::ArenaError>,
7432 fatal: &mut Option<CompilerFailure>,
7433) -> Option<T> {
7434 match result {
7435 Ok(value) => Some(value),
7436 Err(error) => {
7437 fatal.get_or_insert_with(|| error.into_compiler_failure(CompilerStage::Parse));
7438 None
7439 }
7440 }
7441}
7442
7443fn is_rest_array_annotation(ty: &crate::TypeAnnotation) -> bool {
7445 use crate::ast::TypeAnnotationKind;
7446 match &ty.kind {
7447 TypeAnnotationKind::Array(_) => true,
7448 TypeAnnotationKind::Readonly(inner) => matches!(inner.kind, TypeAnnotationKind::Array(_)),
7449 _ => false,
7450 }
7451}
7452
7453#[cfg(test)]
7454mod tests {
7455 use super::{MAX_ERRORS, MAX_PARSE_DEPTH, Parser, parse};
7456 use crate::source::Sources;
7457 use crate::{Asi, Ast, ExprKind, FileId, ImportKind, StmtKind, Token, TokenKind, diagnostics};
7458
7459 const F: FileId = FileId(0);
7460
7461 #[test]
7462 fn for_in_destructuring_reports_the_invalid_head() {
7463 for declaration in ["let ", "const ", ""] {
7464 for pattern in [
7465 "[a, b]",
7466 "{a, b}",
7467 "[a, ...rest]",
7468 "{a: [b]}",
7469 "[a = 'x' in {}]",
7470 "{a = (1 < 2)}",
7471 "{['a']: a}",
7472 ] {
7473 let source = format!("for ({declaration}{pattern} in []) {{}}");
7474 let (_, diagnostics) = parse_str(&source);
7475 assert_eq!(diagnostics.len(), 1, "{source}: {diagnostics:?}");
7476 let diagnostic = diagnostics.first().expect("a pattern is invalid in for…in");
7477 assert_eq!(
7478 diagnostic.message,
7479 "the left-hand side of `for…in` cannot be a destructuring pattern",
7480 "{source}: {diagnostics:?}"
7481 );
7482 assert_eq!(
7483 diagnostic.span.start as usize,
7484 source.find(pattern).unwrap()
7485 );
7486 assert!(diagnostic.help.iter().any(|h| h.contains("obj[k]")));
7487 assert!(
7488 diagnostic
7489 .help
7490 .iter()
7491 .any(|h| h.contains("Object.entries(obj)"))
7492 );
7493 assert!(
7494 !diagnostics
7495 .iter()
7496 .any(|d| d.message.contains("initializer"))
7497 );
7498 }
7499 }
7500 }
7501
7502 #[test]
7503 fn unfinished_for_in_patterns_report_syntax_errors() {
7504 for source in ["for (let [a in []", "for (const {a in []", "for ([a"] {
7505 let (_, diagnostics) = parse_str(source);
7506 assert!(!diagnostics.is_empty(), "{source}");
7507 }
7508 }
7509
7510 #[test]
7511 fn for_in_destructuring_recovery_preserves_loop_boundaries() {
7512 for body in ["{}", "{ const [local] = [1]; break; }", ";"] {
7513 let source = format!(
7514 "function main(): void {{
7515 for (let [a] in []) {{ for (const [b] in {{}}) {body} }}
7516 for (const {{a}} in {{}}) {body}
7517 for (let [a] in []) for (const [b] in {{}}) {body}
7518 const after = 1;
7519 }}"
7520 );
7521 let (ast, diagnostics) = parse_str(&source);
7522 assert_eq!(diagnostics.len(), 5, "{source}: {diagnostics:?}");
7523 assert!(
7524 diagnostics.iter().all(|d| d.message
7525 == "the left-hand side of `for…in` cannot be a destructuring pattern")
7526 );
7527 let main = ast.try_stmt(ast.top_level[0]).unwrap();
7528 let StmtKind::Function { body, .. } = main.kind else {
7529 panic!("expected main function");
7530 };
7531 let StmtKind::Block(ref statements) = ast.try_stmt(body).unwrap().kind else {
7532 panic!("expected function body");
7533 };
7534 assert_eq!(statements.len(), 4, "all sibling statements are preserved");
7535 assert!(matches!(
7536 ast.try_stmt(statements[3]).unwrap().kind,
7537 StmtKind::Const { .. }
7538 ));
7539 }
7540 }
7541
7542 #[test]
7543 fn for_in_head_scanning_handles_deep_and_unfinished_patterns_iteratively() {
7544 for opening in ['[', '{'] {
7545 let closing = if opening == '[' { ']' } else { '}' };
7546 let pattern = opening.to_string().repeat(4096) + &closing.to_string().repeat(4096);
7547 let source = format!("{pattern} in obj");
7548 let parser = parser_from_source(&source);
7549 let in_at = parser.scan_for_in_head().expect("balanced pattern head");
7550 assert!(matches!(parser.tokens[in_at].kind, TokenKind::In));
7551 let source = format!("let {pattern} of obj");
7552 assert!(parser_from_source(&source).scan_for_of_shape());
7553
7554 let source = opening.to_string().repeat(4096);
7555 assert!(parser_from_source(&source).scan_for_in_head().is_none());
7556 let source = format!("let {source}");
7557 assert!(!parser_from_source(&source).scan_for_of_shape());
7558 }
7559 }
7560
7561 #[test]
7562 fn index_key_type_does_not_disrupt_initializer_parsing() {
7563 let (ast, diagnostics) =
7564 parse_str("const byId: { [id: boolean]: string } = {}; const next = 1;");
7565 assert!(
7566 diagnostics.is_empty(),
7567 "key-domain errors belong to typechecking: {diagnostics:?}"
7568 );
7569 assert_eq!(ast.top_level.len(), 2);
7570 }
7571
7572 #[test]
7573 fn malformed_direct_token_streams_return_typed_failures() {
7574 use crate::compiler_error::CompilerFailure;
7575 for tokens in [
7576 Vec::new(),
7577 vec![Token::new(
7578 TokenKind::Identifier,
7579 crate::Span::new(F, 0, 2).unwrap(),
7580 )],
7581 vec![
7582 Token::new(
7583 TokenKind::Identifier,
7584 crate::Span {
7585 file: F,
7586 start: 1,
7587 end: 2,
7588 },
7589 ),
7590 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
7591 ],
7592 vec![
7593 Token::new(TokenKind::Eof, crate::Span::at(F)),
7594 Token::new(TokenKind::Eof, crate::Span::at(F)),
7595 ],
7596 vec![
7597 Token::new(
7598 TokenKind::Identifier,
7599 crate::Span {
7600 file: F,
7601 start: 2,
7602 end: 0,
7603 },
7604 ),
7605 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
7606 ],
7607 vec![Token::new(TokenKind::Eof, crate::Span::at(FileId(9)))],
7608 ] {
7609 let error = super::parse_checked("é", tokens, F).expect_err("invalid token metadata");
7610 assert!(matches!(
7611 error.fatal,
7612 Some(CompilerFailure::Internal { .. })
7613 ));
7614 }
7615 }
7616
7617 #[test]
7618 fn malformed_documentation_metadata_returns_a_fatal_error() {
7619 let source = "/** hello */ function main(): void {}";
7620 let valid = tokens_of(source);
7621 for doc in [
7622 crate::RawDoc {
7623 text: "/** hi */".into(),
7624 span: crate::Span {
7625 file: F,
7626 start: u32::MAX,
7627 end: u32::MAX,
7628 },
7629 },
7630 crate::RawDoc {
7631 text: "/** different */".into(),
7632 span: crate::Span::new(F, 0, 12).unwrap(),
7633 },
7634 crate::RawDoc {
7635 text: "/** hello */".into(),
7636 span: crate::Span::new(FileId(9), 0, 12).unwrap(),
7637 },
7638 ] {
7639 let mut tokens = valid.clone();
7640 tokens[0].leading_doc = Some(doc);
7641 let error = super::parse_checked(source, tokens, F).expect_err("invalid doc metadata");
7642 assert!(error.to_string().contains("documentation metadata"));
7643 }
7644 assert!(super::parse_checked(source, valid, F).is_ok());
7645 }
7646
7647 #[test]
7648 fn invalid_parser_dispatch_survives_diagnostic_rollback() {
7649 let mut parser = parser_from_source("let x = 1;");
7650 assert!(parser.try_parse_type_args_only().is_none());
7651 parser.diagnostics.clear();
7652 assert!(parser.fatal.is_some());
7653 assert!(parser.parse_expression().is_none());
7654 }
7655
7656 #[test]
7657 fn invalid_arena_read_stops_parser_and_survives_diagnostic_rollback() {
7658 use crate::{
7659 ExprId,
7660 compiler_error::{CompilerFailure, CompilerStage},
7661 };
7662 let mut parser = parser_from_source("let x = 1;");
7663 assert!(parser.statement_kind_for(ExprId(u32::MAX)).is_none());
7664 assert!(matches!(
7665 parser.fatal,
7666 Some(CompilerFailure::Internal {
7667 stage: CompilerStage::Parse,
7668 span: None,
7669 ..
7670 })
7671 ));
7672 parser.diagnostics.clear();
7673 let position = parser.pos;
7674 assert!(parser.parse_statement().is_none());
7675 parser.recover();
7676 assert_eq!(parser.pos, position);
7677 assert!(parser.ast.expr_ids().unwrap().next().is_none());
7678 }
7679
7680 fn tokens_of(source: &str) -> Vec<Token> {
7681 let mut asi = Asi::new(source, crate::FileId(0));
7682 let mut tokens = Vec::new();
7683 loop {
7684 let tok = asi.next_token();
7685 let is_eof = matches!(tok.kind, TokenKind::Eof);
7686 tokens.push(tok);
7687 if is_eof {
7688 break;
7689 }
7690 }
7691 let diags = asi.into_diagnostics();
7692 assert!(diags.is_empty(), "unexpected lexer diagnostics: {diags:?}");
7693 tokens
7694 }
7695
7696 fn parse_str(source: &str) -> (Ast, Vec<crate::Diagnostic>) {
7697 parse(source, tokens_of(source), crate::FileId(0))
7698 }
7699
7700 fn parser_from_source(source: &str) -> Parser<'_> {
7701 Parser {
7702 source,
7703 tokens: tokens_of(source),
7704 split_type_tokens: Vec::new(),
7705 file: F,
7706 pos: 0,
7707 ast: Ast::new(),
7708 diagnostics: Vec::new(),
7709 block_depth: 0,
7710 class_member_body_depth: 0,
7711 function_expression_body_depth: 0,
7712 recursion_depth: 0,
7713 recursion_limit_span: None,
7714 resume_at_next_member: false,
7715 last_as_type_end: None,
7716 trailing_declarators: Vec::new(),
7717 eof: Token::new(
7718 TokenKind::Eof,
7719 crate::Span::new(F, source.len() as u32, source.len() as u32).unwrap(),
7720 ),
7721 fatal: None,
7722 }
7723 }
7724
7725 #[test]
7726 fn recursion_budget_accepts_boundary_and_restores_depth() {
7727 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH - 1));
7728 let mut parser = parser_from_source(&source);
7729 assert!(parser.parse_unary().is_some());
7730 assert_eq!(parser.recursion_depth, 0);
7731 assert!(parser.recursion_limit_span.is_none());
7732
7733 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH));
7734 let mut parser = parser_from_source(&source);
7735 assert!(parser.parse_unary().is_none());
7736 assert_eq!(parser.recursion_depth, 0);
7737 assert!(parser.recursion_limit_span.is_some());
7738 }
7739
7740 #[test]
7741 fn parse_empty_input() {
7742 let (ast, diags) = parse_str("");
7743 assert!(diags.is_empty());
7744 assert!(ast.top_level.is_empty());
7745 }
7746
7747 #[test]
7748 fn any_type_is_rejected() {
7749 for src in [
7752 "function main(): void { const x: any = 1; }",
7753 "function main(): void { const x = 1 as any; }",
7754 "function main(): void { const x: any[] = []; }",
7755 ] {
7756 let (_ast, diags) = parse_str(src);
7757 assert!(
7758 diags
7759 .iter()
7760 .any(|d| d.message.contains("`any` is not supported")),
7761 "expected an `any` rejection for {src:?}, got: {diags:?}",
7762 );
7763 assert!(
7764 diags
7765 .iter()
7766 .any(|d| d.help.iter().any(|h| h.contains("`unknown`"))),
7767 "expected the fix-shape help for {src:?}, got: {diags:?}",
7768 );
7769 }
7770 }
7771
7772 #[test]
7773 fn any_catch_binding_help_suggests_catch_forms() {
7774 let (_ast, diags) = parse_str("function main(): void { try { } catch (e: any) { } }");
7777 let any_diags: Vec<_> = diags
7778 .iter()
7779 .filter(|d| d.message.contains("`any` is not supported"))
7780 .collect();
7781 assert_eq!(any_diags.len(), 1, "got: {diags:?}");
7782 assert!(
7783 any_diags[0].help.iter().any(|h| h.contains("`catch (e)`")),
7784 "got: {diags:?}"
7785 );
7786 assert!(
7787 !any_diags[0].help.iter().any(|h| h.contains("`unknown`")),
7788 "got: {diags:?}"
7789 );
7790 }
7791
7792 #[test]
7793 fn arrow_return_type_scanner_matches_type_grammar() {
7794 for ty in [
7799 "number",
7800 "void",
7801 "null",
7802 "ns.Foo",
7803 "Map<string, number>",
7804 "ns.Foo<string>[]",
7805 "number[]",
7806 "[number, number]",
7807 "[number, [string, boolean]]",
7808 "[number, string][]",
7809 "{ a: number }",
7810 "{ a: number }[]",
7811 "\"on\" | \"off\"",
7812 "1 | 2",
7813 "Point | null",
7814 "| number | string",
7815 "(n: number) => number",
7816 "(n: number) => [number, number]",
7817 "readonly number[]",
7818 "readonly number[][]",
7819 "readonly [number, string]",
7820 "readonly number[] | null",
7821 "[first: number, second: string]",
7822 "[new: number, string]",
7823 "keyof { a: number }",
7824 ] {
7825 let annotation = format!("function f(): void {{ const a: {ty} = x; }}");
7826 let (_ast, diags) = parse_str(&annotation);
7827 assert!(
7828 diags.is_empty(),
7829 "annotation grammar rejected {ty:?}: {diags:?}",
7830 );
7831
7832 let arrow = format!("function f(): void {{ const g = (): {ty} => x; }}");
7833 let (_ast, diags) = parse_str(&arrow);
7834 assert!(
7835 diags.is_empty(),
7836 "arrow lookahead rejected return type {ty:?}: {diags:?}",
7837 );
7838 }
7839
7840 let (_ast, diags) =
7842 parse_str("function f(): void { const g = (v: unknown): v is string => x; }");
7843 assert!(
7844 diags.is_empty(),
7845 "arrow lookahead rejected predicate return: {diags:?}",
7846 );
7847 }
7848
7849 #[test]
7850 fn bare_semicolons_are_silent_empty_statements() {
7851 let (ast, diags) = parse_str(";");
7852 assert!(diags.is_empty());
7853 assert!(ast.top_level.is_empty());
7854 }
7855
7856 #[test]
7857 fn parse_single_non_expression_token_errors() {
7858 let (ast, diags) = parse_str("}");
7859 assert_eq!(diags.len(), 1);
7860 assert_eq!(diags[0].message, "expected expression");
7861 assert!(ast.top_level.is_empty());
7862 }
7863
7864 #[test]
7865 fn recovery_across_many_bad_starts() {
7866 let (ast, diags) = parse_str("} } } x;");
7867 assert_eq!(diags.len(), 3);
7868 for d in &diags {
7869 assert_eq!(d.message, "expected expression");
7870 }
7871 assert_eq!(ast.top_level.len(), 1);
7872 }
7873
7874 #[test]
7875 fn recovery_consumes_semicolon() {
7876 let mut p = parser_from_source("foo ;");
7877 p.error_at_peek("test");
7878 p.recover();
7879 assert!(matches!(
7880 p.peek().kind,
7881 TokenKind::Semicolon | TokenKind::Eof
7882 ));
7883 assert!(matches!(p.peek().kind, TokenKind::Eof));
7884 }
7885
7886 #[test]
7887 fn recovery_stops_at_right_brace() {
7888 let mut p = parser_from_source("foo }");
7889 p.error_at_peek("test");
7890 p.recover();
7891 assert!(matches!(p.peek().kind, TokenKind::RightBrace));
7892 }
7893
7894 #[test]
7895 fn recovery_stops_at_let_keyword() {
7896 let mut p = parser_from_source("foo let x");
7897 p.error_at_peek("test");
7898 p.recover();
7899 assert!(matches!(p.peek().kind, TokenKind::Let));
7900 }
7901
7902 #[test]
7903 fn recovery_stops_at_function_keyword() {
7904 let mut p = parser_from_source("foo function bar");
7905 p.error_at_peek("test");
7906 p.recover();
7907 assert!(matches!(p.peek().kind, TokenKind::Function));
7908 }
7909
7910 #[test]
7911 fn error_cap_at_20() {
7912 let source = "} ".repeat(25);
7913 let (_, diags) = parse_str(&source);
7914 assert_eq!(diags.len(), MAX_ERRORS);
7915 }
7916
7917 #[test]
7918 fn driver_makes_progress_past_unmatched_brace() {
7919 let (ast, diags) = parse_str("} x;");
7920 assert_eq!(diags.len(), 1);
7921 assert_eq!(diags[0].message, "expected expression");
7922 assert_eq!(ast.top_level.len(), 1);
7923 }
7924
7925 #[test]
7926 fn snapshot_multi_error_render() {
7927 let source = "foo bar;\nbaz qux;\nhello world;";
7928 let (_, diags) = parse_str(source);
7929 let (sources, _) = Sources::single("script.subm", source).unwrap();
7930 let rendered: String = diags
7931 .iter()
7932 .map(|d| diagnostics::render(d, &sources))
7933 .collect::<Vec<_>>()
7934 .join("\n");
7935 insta::assert_snapshot!(rendered);
7936 }
7937
7938 fn single_stmt(ast: &Ast) -> &crate::Stmt {
7939 assert_eq!(ast.top_level.len(), 1);
7940 ast.try_stmt(ast.top_level[0]).unwrap()
7941 }
7942
7943 fn expr_of_single_stmt(ast: &Ast) -> &crate::Expr {
7944 let stmt = single_stmt(ast);
7945 match stmt.kind {
7946 crate::StmtKind::Expr(id) => ast.try_expr(id).unwrap(),
7947 _ => panic!("expected expression statement, got {:?}", stmt.kind),
7948 }
7949 }
7950
7951 #[test]
7952 fn parse_number_literal() {
7953 let (ast, diags) = parse_str("42;");
7954 assert!(diags.is_empty());
7955 let expr = expr_of_single_stmt(&ast);
7956 assert_eq!(expr.kind, crate::ExprKind::Number(42.0));
7957 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
7958 }
7959
7960 #[test]
7961 fn parse_string_literal() {
7962 let (ast, diags) = parse_str(r#""s";"#);
7963 assert!(diags.is_empty());
7964 let expr = expr_of_single_stmt(&ast);
7965 assert_eq!(expr.kind, crate::ExprKind::String("s".to_string()));
7966 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7967 }
7968
7969 #[test]
7970 fn parse_boolean_true() {
7971 let (ast, diags) = parse_str("true;");
7972 assert!(diags.is_empty());
7973 let expr = expr_of_single_stmt(&ast);
7974 assert_eq!(expr.kind, crate::ExprKind::Boolean(true));
7975 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7976 }
7977
7978 #[test]
7979 fn parse_boolean_false() {
7980 let (ast, diags) = parse_str("false;");
7981 assert!(diags.is_empty());
7982 let expr = expr_of_single_stmt(&ast);
7983 assert_eq!(expr.kind, crate::ExprKind::Boolean(false));
7984 assert_eq!(expr.span, crate::Span::new(F, 0, 5).unwrap());
7985 }
7986
7987 #[test]
7988 fn parse_null_literal() {
7989 let (ast, diags) = parse_str("null;");
7990 assert!(diags.is_empty());
7991 let expr = expr_of_single_stmt(&ast);
7992 assert_eq!(expr.kind, crate::ExprKind::Null);
7993 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7994 }
7995
7996 #[test]
7997 fn parse_identifier() {
7998 let (ast, diags) = parse_str("x;");
7999 assert!(diags.is_empty());
8000 let expr = expr_of_single_stmt(&ast);
8001 assert!(matches!(expr.kind, crate::ExprKind::Identifier(_)));
8002 assert_eq!(expr.span, crate::Span::new(F, 0, 1).unwrap());
8003 }
8004
8005 #[test]
8006 fn reserved_keywords_remain_invalid_declaration_names() {
8007 let (_ast, diags) = parse_str("function default(): void {}");
8008 assert_eq!(diags.len(), 1, "{diags:#?}");
8009 assert_eq!(
8010 diags[0].message,
8011 "`default` is a reserved keyword and can't be used as a name"
8012 );
8013 assert!(
8014 diags[0]
8015 .help
8016 .iter()
8017 .any(|h| h == "rename it, for example: `defaultValue`"),
8018 "expected rename help, got: {diags:?}"
8019 );
8020 }
8021
8022 #[test]
8023 fn eval_and_arguments_cannot_name_bindings() {
8024 let bindings = [
8025 ("let arguments = 1;", "arguments"),
8026 ("const [eval] = [1];", "eval"),
8027 ("const { a: eval } = { a: 1 };", "eval"),
8028 ("const { eval } = { eval: 1 };", "eval"),
8029 ("const [...arguments] = [1];", "arguments"),
8030 ("function eval(): void {}", "eval"),
8031 ("function f(arguments: number): void {}", "arguments"),
8032 ("const f = arguments => arguments;", "arguments"),
8033 ("const f = (...eval: number[]) => 1;", "eval"),
8034 ("const f = function eval(): void {};", "eval"),
8035 ("for (const eval of [1]) {}", "eval"),
8036 ("try {} catch (arguments) {}", "arguments"),
8037 ("type F = (eval: number) => void;", "eval"),
8038 ("type G = (...arguments: number[]) => void;", "arguments"),
8039 ("type H = { [eval: string]: number };", "eval"),
8040 ];
8041 for (source, word) in bindings {
8042 let (_ast, diags) = parse_str(source);
8043 assert!(
8044 diags
8045 .iter()
8046 .any(|d| d.message == format!("`{word}` can't name a binding in strict mode")),
8047 "{source}: {diags:#?}"
8048 );
8049 }
8050 }
8051
8052 #[test]
8053 fn eval_and_arguments_still_name_types_and_properties() {
8054 for source in [
8055 "class eval {}",
8056 "interface arguments { eval: number }",
8057 "type eval = number;",
8058 "enum arguments { eval }",
8059 "const o = { eval: 1, arguments: 2 };",
8060 "import { eval } from \"./m\";",
8061 ] {
8062 let (_ast, diags) = parse_str(source);
8063 assert!(diags.is_empty(), "{source}: {diags:#?}");
8064 }
8065 }
8066
8067 #[test]
8068 fn reserved_keywords_remain_invalid_parameter_names() {
8069 let (_ast, diags) = parse_str("function f(default: string): void {}");
8070 assert_eq!(diags.len(), 1, "{diags:#?}");
8071 assert_eq!(
8072 diags[0].message,
8073 "`default` is a reserved keyword and can't be used as a name"
8074 );
8075 assert!(
8076 diags[0]
8077 .help
8078 .iter()
8079 .any(|h| h == "rename it, for example: `defaultValue`"),
8080 "expected rename help, got: {diags:?}"
8081 );
8082 }
8083
8084 #[test]
8085 fn contextual_keywords_are_valid_names() {
8086 for src in [
8087 "const type = \"x\";",
8088 "const from = 1;",
8089 "let of = 2;",
8090 "const as = 3;",
8091 "const is = 4;",
8092 "function f(from: string): void {}",
8093 ] {
8094 let (_ast, diags) = parse_str(src);
8095 assert!(
8096 diags.is_empty(),
8097 "unexpected diagnostics for {src:?}: {diags:#?}"
8098 );
8099 }
8100 }
8101
8102 #[test]
8103 fn type_alias_dispatch_table() {
8104 let (ast, diags) = parse_str("type X = number;");
8105 assert!(diags.is_empty(), "{diags:#?}");
8106 assert!(matches!(
8107 ast.try_stmt(ast.top_level[0]).unwrap().kind,
8108 crate::StmtKind::TypeAliasDecl { .. }
8109 ));
8110
8111 for src in ["type = 5;", "type;", "type(1);", "type < 3;", "type.foo;"] {
8113 let (ast, diags) = parse_str(src);
8114 assert!(
8115 diags.is_empty(),
8116 "unexpected diagnostics for {src:?}: {diags:#?}"
8117 );
8118 assert!(
8119 !matches!(
8120 ast.try_stmt(ast.top_level[0]).unwrap().kind,
8121 crate::StmtKind::TypeAliasDecl { .. }
8122 ),
8123 "{src:?} must not parse as a type alias"
8124 );
8125 }
8126
8127 let (_ast, diags) = parse_str("type X;");
8128 assert!(
8129 diags
8130 .iter()
8131 .any(|d| d.message == "expected `=` after type alias name"),
8132 "bare `type X` should commit to the alias diagnostic, got: {diags:#?}"
8133 );
8134 }
8135
8136 #[test]
8137 fn export_type_alias_dispatch() {
8138 let (ast, diags) = parse_str("export type X = number;");
8139 assert!(diags.is_empty(), "{diags:#?}");
8140 assert_eq!(ast.exported_decls.len(), 1);
8141 assert!(matches!(
8142 ast.try_stmt(ast.exported_decls[0].stmt).unwrap().kind,
8143 crate::StmtKind::TypeAliasDecl { .. }
8144 ));
8145
8146 let (_ast, diags) = parse_str("export type = 5;");
8147 assert!(
8148 diags
8149 .iter()
8150 .any(|d| d.message == "expected a declaration after `export`"),
8151 "`export type = 5` must not become an exported assignment, got: {diags:#?}"
8152 );
8153 }
8154
8155 #[test]
8156 fn parse_paren_simple() {
8157 let (ast, diags) = parse_str("(x);");
8158 assert!(diags.is_empty());
8159 let expr = expr_of_single_stmt(&ast);
8160 let crate::ExprKind::Paren(inner_id) = expr.kind else {
8161 panic!("expected Paren, got {:?}", expr.kind);
8162 };
8163 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
8164 let inner = ast.try_expr(inner_id).unwrap();
8165 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
8166 assert_eq!(inner.span, crate::Span::new(F, 1, 2).unwrap());
8167 }
8168
8169 #[test]
8170 fn parse_paren_nested() {
8171 let (ast, diags) = parse_str("((x));");
8172 assert!(diags.is_empty());
8173 let outer = expr_of_single_stmt(&ast);
8174 let crate::ExprKind::Paren(mid_id) = outer.kind else {
8175 panic!("expected outer Paren");
8176 };
8177 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
8178 let mid = ast.try_expr(mid_id).unwrap();
8179 let crate::ExprKind::Paren(inner_id) = mid.kind else {
8180 panic!("expected inner Paren");
8181 };
8182 assert_eq!(mid.span, crate::Span::new(F, 1, 4).unwrap());
8183 let inner = ast.try_expr(inner_id).unwrap();
8184 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
8185 assert_eq!(inner.span, crate::Span::new(F, 2, 3).unwrap());
8186 }
8187
8188 #[test]
8189 fn parse_sequence_of_atoms() {
8190 let (ast, diags) = parse_str(r#"42; "hi"; true; null; x; (x);"#);
8191 assert!(diags.is_empty());
8192 assert_eq!(ast.top_level.len(), 6);
8193 insta::assert_debug_snapshot!(ast);
8194 }
8195
8196 #[test]
8197 fn missing_semicolon_after_expression() {
8198 let (ast, diags) = parse_str("42 43");
8199 assert_eq!(diags.len(), 1);
8200 assert_eq!(diags[0].message, "expected `;` after expression");
8201 assert!(ast.top_level.is_empty());
8202 }
8203
8204 #[test]
8205 fn empty_paren_diagnoses() {
8206 let (ast, diags) = parse_str("();");
8207 assert_eq!(diags.len(), 1);
8208 assert_eq!(diags[0].message, "expected expression");
8209 assert!(ast.top_level.is_empty());
8210 }
8211
8212 #[test]
8213 fn unterminated_paren_diagnoses() {
8214 let (ast, diags) = parse_str("(x;");
8215 assert_eq!(diags.len(), 1);
8216 assert_eq!(diags[0].message, "expected `)`");
8217 assert!(ast.top_level.is_empty());
8218 }
8219
8220 fn binary(e: &crate::Expr) -> (crate::BinOp, crate::ExprId, crate::ExprId) {
8221 match e.kind {
8222 crate::ExprKind::Binary { op, lhs, rhs } => (op, lhs, rhs),
8223 _ => panic!("expected Binary, got {:?}", e.kind),
8224 }
8225 }
8226
8227 #[test]
8228 fn parse_binary_add() {
8229 let (ast, diags) = parse_str("a + b;");
8230 assert!(diags.is_empty());
8231 let outer = expr_of_single_stmt(&ast);
8232 let (op, lhs, rhs) = binary(outer);
8233 assert_eq!(op, crate::BinOp::Add);
8234 assert!(matches!(
8235 ast.try_expr(lhs).unwrap().kind,
8236 crate::ExprKind::Identifier(_)
8237 ));
8238 assert!(matches!(
8239 ast.try_expr(rhs).unwrap().kind,
8240 crate::ExprKind::Identifier(_)
8241 ));
8242 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
8243 }
8244
8245 #[test]
8246 fn bitwise_precedence_and_shift_adjacency() {
8247 let (ast, diags) = parse_str("a | b ^ c & d == e >> f + g;");
8248 assert!(diags.is_empty(), "{diags:?}");
8249 let (op, _, mut rhs) = binary(expr_of_single_stmt(&ast));
8250 assert_eq!(op, crate::BinOp::BitOr);
8251 for expected in [
8252 crate::BinOp::BitXor,
8253 crate::BinOp::BitAnd,
8254 crate::BinOp::Eq,
8255 crate::BinOp::Shr,
8256 crate::BinOp::Add,
8257 ] {
8258 let (op, _, next) = binary(ast.try_expr(rhs).unwrap());
8259 assert_eq!(op, expected);
8260 rhs = next;
8261 }
8262 for source in ["a > > b;", "a >/*gap*/> b;", "a < < b;"] {
8263 let (_, diags) = parse_str(source);
8264 assert!(!diags.is_empty(), "{source}");
8265 }
8266 }
8267
8268 #[test]
8269 fn bitwise_shift_assignment_span() {
8270 let (ast, diags) = parse_str("a >>>= b;");
8271 assert!(diags.is_empty(), "{diags:?}");
8272 let crate::StmtKind::CompoundAssign { op, op_span, .. } = single_stmt(&ast).kind else {
8273 panic!("expected assignment");
8274 };
8275 assert_eq!(op, crate::BinOp::UnsignedShr);
8276 assert_eq!(op_span, crate::Span::new(F, 2, 6).unwrap());
8277 }
8278
8279 #[test]
8280 fn precedence_mul_binds_tighter_than_add() {
8281 let (ast, diags) = parse_str("a + b * c;");
8282 assert!(diags.is_empty());
8283 let outer = expr_of_single_stmt(&ast);
8284 let (op, lhs, rhs) = binary(outer);
8285 assert_eq!(op, crate::BinOp::Add);
8286 assert!(matches!(
8287 ast.try_expr(lhs).unwrap().kind,
8288 crate::ExprKind::Identifier(_)
8289 ));
8290 let (rop, rlhs, rrhs) = binary(ast.try_expr(rhs).unwrap());
8291 assert_eq!(rop, crate::BinOp::Mul);
8292 assert!(matches!(
8293 ast.try_expr(rlhs).unwrap().kind,
8294 crate::ExprKind::Identifier(_)
8295 ));
8296 assert!(matches!(
8297 ast.try_expr(rrhs).unwrap().kind,
8298 crate::ExprKind::Identifier(_)
8299 ));
8300 }
8301
8302 #[test]
8303 fn precedence_mul_left_of_add() {
8304 let (ast, diags) = parse_str("a * b + c;");
8305 assert!(diags.is_empty());
8306 let outer = expr_of_single_stmt(&ast);
8307 let (op, lhs, rhs) = binary(outer);
8308 assert_eq!(op, crate::BinOp::Add);
8309 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
8310 assert_eq!(lop, crate::BinOp::Mul);
8311 assert!(matches!(
8312 ast.try_expr(rhs).unwrap().kind,
8313 crate::ExprKind::Identifier(_)
8314 ));
8315 }
8316
8317 #[test]
8318 fn left_associative_same_precedence() {
8319 let (ast, diags) = parse_str("a - b - c;");
8320 assert!(diags.is_empty());
8321 let outer = expr_of_single_stmt(&ast);
8322 let (op, lhs, rhs) = binary(outer);
8323 assert_eq!(op, crate::BinOp::Sub);
8324 let (lop, llhs, lrhs) = binary(ast.try_expr(lhs).unwrap());
8325 assert_eq!(lop, crate::BinOp::Sub);
8326 assert!(matches!(
8327 ast.try_expr(llhs).unwrap().kind,
8328 crate::ExprKind::Identifier(_)
8329 ));
8330 assert!(matches!(
8331 ast.try_expr(lrhs).unwrap().kind,
8332 crate::ExprKind::Identifier(_)
8333 ));
8334 assert!(matches!(
8335 ast.try_expr(rhs).unwrap().kind,
8336 crate::ExprKind::Identifier(_)
8337 ));
8338 }
8339
8340 #[test]
8341 fn equality_inside_logical() {
8342 let (ast, diags) = parse_str("a === b && c === d;");
8343 assert!(diags.is_empty());
8344 let outer = expr_of_single_stmt(&ast);
8345 let (op, lhs, rhs) = binary(outer);
8346 assert_eq!(op, crate::BinOp::And);
8347 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
8348 assert_eq!(lop, crate::BinOp::Eq);
8349 let (rop, ..) = binary(ast.try_expr(rhs).unwrap());
8350 assert_eq!(rop, crate::BinOp::Eq);
8351 }
8352
8353 #[test]
8354 fn double_equal_is_same_variant_as_triple() {
8355 let (ast, _) = parse_str("a == b;");
8356 let outer = expr_of_single_stmt(&ast);
8357 let (op, ..) = binary(outer);
8358 assert_eq!(op, crate::BinOp::Eq);
8359 }
8360
8361 #[test]
8362 fn bang_eq_is_not_eq() {
8363 let (ast, _) = parse_str("a != b;");
8364 let outer = expr_of_single_stmt(&ast);
8365 let (op, ..) = binary(outer);
8366 assert_eq!(op, crate::BinOp::NotEq);
8367 }
8368
8369 #[test]
8370 fn comparison_has_higher_prec_than_logical() {
8371 let (ast, _) = parse_str("a < b && c;");
8372 let outer = expr_of_single_stmt(&ast);
8373 let (op, lhs, _) = binary(outer);
8374 assert_eq!(op, crate::BinOp::And);
8375 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
8376 assert_eq!(lop, crate::BinOp::Lt);
8377 }
8378
8379 #[test]
8380 fn full_precedence_chain_snapshot() {
8381 let (ast, diags) = parse_str("a || b && c === d < e + f * g;");
8382 assert!(diags.is_empty());
8383 insta::assert_debug_snapshot!(ast);
8384 }
8385
8386 #[test]
8387 fn paren_overrides_precedence() {
8388 let (ast, _) = parse_str("(a + b) * c;");
8389 let outer = expr_of_single_stmt(&ast);
8390 let (op, lhs, rhs) = binary(outer);
8391 assert_eq!(op, crate::BinOp::Mul);
8392 let crate::ExprKind::Paren(inner_id) = ast.try_expr(lhs).unwrap().kind else {
8393 panic!("expected Paren on lhs");
8394 };
8395 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
8396 assert_eq!(inner_op, crate::BinOp::Add);
8397 assert!(matches!(
8398 ast.try_expr(rhs).unwrap().kind,
8399 crate::ExprKind::Identifier(_)
8400 ));
8401 }
8402
8403 #[test]
8404 fn binary_missing_rhs_diagnoses() {
8405 let (ast, diags) = parse_str("a +;");
8406 assert_eq!(diags.len(), 1);
8407 assert_eq!(diags[0].message, "expected expression");
8408 assert!(ast.top_level.is_empty());
8409 }
8410
8411 fn unary(e: &crate::Expr) -> (crate::UnOp, crate::ExprId) {
8412 match e.kind {
8413 crate::ExprKind::Unary { op, operand } => (op, operand),
8414 _ => panic!("expected Unary, got {:?}", e.kind),
8415 }
8416 }
8417
8418 #[test]
8419 fn parse_unary_not() {
8420 let (ast, _) = parse_str("!x;");
8421 let outer = expr_of_single_stmt(&ast);
8422 let (op, operand) = unary(outer);
8423 assert_eq!(op, crate::UnOp::Not);
8424 assert!(matches!(
8425 ast.try_expr(operand).unwrap().kind,
8426 crate::ExprKind::Identifier(_)
8427 ));
8428 assert_eq!(outer.span, crate::Span::new(F, 0, 2).unwrap());
8429 }
8430
8431 #[test]
8432 fn parse_unary_neg() {
8433 let (ast, _) = parse_str("-x;");
8434 let (op, _) = unary(expr_of_single_stmt(&ast));
8435 assert_eq!(op, crate::UnOp::Neg);
8436 }
8437
8438 #[test]
8439 fn parse_unary_pos() {
8440 let (ast, _) = parse_str("+x;");
8441 let (op, _) = unary(expr_of_single_stmt(&ast));
8442 assert_eq!(op, crate::UnOp::Pos);
8443 }
8444
8445 #[test]
8446 fn repeated_unary_not() {
8447 let (ast, _) = parse_str("!!x;");
8448 let outer = expr_of_single_stmt(&ast);
8449 let (op, operand) = unary(outer);
8450 assert_eq!(op, crate::UnOp::Not);
8451 let (inner_op, inner_operand) = unary(ast.try_expr(operand).unwrap());
8452 assert_eq!(inner_op, crate::UnOp::Not);
8453 assert!(matches!(
8454 ast.try_expr(inner_operand).unwrap().kind,
8455 crate::ExprKind::Identifier(_)
8456 ));
8457 }
8458
8459 #[test]
8460 fn unary_binds_tighter_than_binary() {
8461 let (ast, _) = parse_str("-x + 1;");
8462 let outer = expr_of_single_stmt(&ast);
8463 let (op, lhs, rhs) = binary(outer);
8464 assert_eq!(op, crate::BinOp::Add);
8465 let (unop, _) = unary(ast.try_expr(lhs).unwrap());
8466 assert_eq!(unop, crate::UnOp::Neg);
8467 assert_eq!(
8468 ast.try_expr(rhs).unwrap().kind,
8469 crate::ExprKind::Number(1.0)
8470 );
8471 }
8472
8473 #[test]
8474 fn unary_over_paren() {
8475 let (ast, _) = parse_str("-(x + 1);");
8476 let outer = expr_of_single_stmt(&ast);
8477 let (op, operand) = unary(outer);
8478 assert_eq!(op, crate::UnOp::Neg);
8479 let crate::ExprKind::Paren(inner_id) = ast.try_expr(operand).unwrap().kind else {
8480 panic!("expected Paren");
8481 };
8482 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
8483 assert_eq!(inner_op, crate::BinOp::Add);
8484 }
8485
8486 #[test]
8487 fn parse_is_as_expression_rejected() {
8488 let (_, diags) = parse_str("x is number;");
8489 assert!(!diags.is_empty(), "expected parse error for `x is T`");
8490 }
8491
8492 fn as_cast(e: &crate::Expr) -> (crate::ExprId, &crate::TypeAnnotation) {
8493 match &e.kind {
8494 crate::ExprKind::As { expr, ty } => (*expr, ty),
8495 other => panic!("expected ExprKind::As, got {other:?}"),
8496 }
8497 }
8498
8499 #[test]
8500 fn parse_instanceof_class() {
8501 let (ast, diags) = parse_str("x instanceof Foo;");
8502 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8503 let outer = expr_of_single_stmt(&ast);
8504 let crate::ExprKind::InstanceOf { value, ty } = &outer.kind else {
8505 panic!("expected ExprKind::InstanceOf, got {:?}", outer.kind);
8506 };
8507 assert!(matches!(
8508 ast.try_expr(*value).unwrap().kind,
8509 crate::ExprKind::Identifier(_)
8510 ));
8511 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8512 }
8513
8514 #[test]
8515 fn parse_instanceof_binds_tighter_than_logical_and() {
8516 let (ast, diags) = parse_str("a instanceof B && c;");
8518 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8519 let outer = expr_of_single_stmt(&ast);
8520 let (op, lhs, rhs) = binary(outer);
8521 assert_eq!(op, crate::BinOp::And);
8522 assert!(matches!(
8523 ast.try_expr(lhs).unwrap().kind,
8524 crate::ExprKind::InstanceOf { .. }
8525 ));
8526 assert!(matches!(
8527 ast.try_expr(rhs).unwrap().kind,
8528 crate::ExprKind::Identifier(_)
8529 ));
8530 }
8531
8532 #[test]
8535 fn parse_angle_cast_lowers_to_the_as_node() {
8536 let (ast, diags) = parse_str("<number>x;");
8537 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8538 let outer = expr_of_single_stmt(&ast);
8539 let (inner, ty) = as_cast(outer);
8540 assert!(matches!(
8541 ast.try_expr(inner).unwrap().kind,
8542 crate::ExprKind::Identifier(_)
8543 ));
8544 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8545 }
8546
8547 #[test]
8551 fn a_less_than_comparison_is_not_read_as_a_cast() {
8552 let (ast, diags) = parse_str("a < b;");
8553 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8554 let e = expr_of_single_stmt(&ast);
8555 assert!(matches!(e.kind, crate::ExprKind::Binary { .. }));
8556 }
8557
8558 #[test]
8559 fn parse_typeof_in_type_position() {
8560 let (_ast, diags) = parse_str("let a: typeof b = 1;");
8561 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8562 }
8563
8564 #[test]
8565 fn parse_typeof_with_a_dotted_path() {
8566 let (_ast, diags) = parse_str("let a: typeof b.c.d = 1;");
8567 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8568 }
8569
8570 #[test]
8571 fn parse_as_primitive() {
8572 let (ast, diags) = parse_str("x as number;");
8573 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8574 let outer = expr_of_single_stmt(&ast);
8575 let (inner, ty) = as_cast(outer);
8576 assert!(matches!(
8577 ast.try_expr(inner).unwrap().kind,
8578 crate::ExprKind::Identifier(_)
8579 ));
8580 match &ty.kind {
8581 crate::TypeAnnotationKind::Name { args, .. } => {
8582 assert!(args.is_empty());
8583 }
8584 other => panic!("expected Name target, got {other:?}"),
8585 }
8586 assert_eq!(outer.span, crate::Span::new(F, 0, 11).unwrap());
8587 }
8588
8589 #[test]
8590 fn parse_as_object_literal_type() {
8591 let (ast, diags) = parse_str("x as { a: number };");
8592 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8593 let outer = expr_of_single_stmt(&ast);
8594 let (_, ty) = as_cast(outer);
8595 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Object { .. }));
8596 }
8597
8598 #[test]
8599 fn parse_as_array_short() {
8600 let (ast, diags) = parse_str("x as string[];");
8601 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8602 let outer = expr_of_single_stmt(&ast);
8603 let (_, ty) = as_cast(outer);
8604 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
8605 }
8606
8607 #[test]
8608 fn parse_as_array_generic() {
8609 let (ast, diags) = parse_str("x as T[];");
8610 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8611 let outer = expr_of_single_stmt(&ast);
8612 let (_, ty) = as_cast(outer);
8613 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
8614 }
8615
8616 #[test]
8617 fn parse_as_generic_instantiation() {
8618 let (ast, diags) = parse_str("x as Foo<Bar>;");
8619 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8620 let outer = expr_of_single_stmt(&ast);
8621 let (_, ty) = as_cast(outer);
8622 match &ty.kind {
8623 crate::TypeAnnotationKind::Name { args, .. } => {
8624 assert_eq!(args.len(), 1, "Foo<Bar> has one type arg");
8625 }
8626 other => panic!("expected Name with type args, got {other:?}"),
8627 }
8628 }
8629
8630 #[test]
8631 fn parse_as_precedence_higher_than_additive() {
8632 let (ast, diags) = parse_str("x as number + 1;");
8633 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8634 let outer = expr_of_single_stmt(&ast);
8635 let (op, lhs, rhs) = binary(outer);
8636 assert_eq!(op, crate::BinOp::Add);
8637 assert!(matches!(
8638 ast.try_expr(lhs).unwrap().kind,
8639 crate::ExprKind::As { .. }
8640 ));
8641 assert!(matches!(
8642 ast.try_expr(rhs).unwrap().kind,
8643 crate::ExprKind::Number(_)
8644 ));
8645 }
8646
8647 #[test]
8648 fn parse_as_precedence_lower_than_unary() {
8649 let (ast, diags) = parse_str("!x as boolean;");
8650 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8651 let outer = expr_of_single_stmt(&ast);
8652 let (inner, _) = as_cast(outer);
8653 assert!(matches!(
8654 ast.try_expr(inner).unwrap().kind,
8655 crate::ExprKind::Unary {
8656 op: crate::UnOp::Not,
8657 ..
8658 }
8659 ));
8660 }
8661
8662 #[test]
8663 fn parse_as_left_associative_chain() {
8664 let (ast, diags) = parse_str("x as A as B;");
8665 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8666 let outer = expr_of_single_stmt(&ast);
8667 let (inner, _outer_ty) = as_cast(outer);
8668 let (innermost, _inner_ty) = as_cast(ast.try_expr(inner).unwrap());
8669 assert!(matches!(
8670 ast.try_expr(innermost).unwrap().kind,
8671 crate::ExprKind::Identifier(_)
8672 ));
8673 }
8674
8675 #[test]
8676 fn parse_as_inside_call_arg() {
8677 let (ast, diags) = parse_str("f(x as T);");
8678 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8679 let outer = expr_of_single_stmt(&ast);
8680 match &outer.kind {
8681 crate::ExprKind::Call { args, .. } => {
8682 assert_eq!(args.len(), 1);
8683 assert!(matches!(
8684 ast.try_expr(args[0]).unwrap().kind,
8685 crate::ExprKind::As { .. }
8686 ));
8687 }
8688 other => panic!("expected Call, got {other:?}"),
8689 }
8690 }
8691
8692 #[test]
8693 fn parse_as_after_field_access() {
8694 let (ast, diags) = parse_str("obj.field as T;");
8695 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8696 let outer = expr_of_single_stmt(&ast);
8697 let (inner, _) = as_cast(outer);
8698 assert!(matches!(
8699 ast.try_expr(inner).unwrap().kind,
8700 crate::ExprKind::FieldAccess { .. }
8701 ));
8702 }
8703
8704 #[test]
8705 fn parse_type_guard_return_annotation_on_function() {
8706 let src = "function isCircle(s: Shape): s is Circle { return true; }";
8707 let (ast, diags) = parse_str(src);
8708 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8709 let stmt = single_stmt(&ast);
8710 match stmt.kind {
8711 crate::StmtKind::Function {
8712 ref return_type,
8713 ref type_predicate,
8714 ..
8715 } => {
8716 assert!(
8717 return_type.is_none(),
8718 "predicate form leaves return_type empty",
8719 );
8720 let tp = type_predicate.as_ref().expect("predicate parsed");
8721 assert_eq!(tp.param.name, "s");
8722 assert!(matches!(
8723 tp.asserted.kind,
8724 crate::TypeAnnotationKind::Name { .. }
8725 ));
8726 assert_eq!(tp.span, crate::Span::new(F, 29, 40).unwrap());
8727 }
8728 _ => panic!("expected Function"),
8729 }
8730 }
8731
8732 #[test]
8733 fn parse_type_guard_return_annotation_on_arrow() {
8734 let src = "const f = (s: Shape): s is Circle => true;";
8735 let (ast, diags) = parse_str(src);
8736 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8737 let value = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
8738 crate::StmtKind::Const { value, .. } => *value,
8739 _ => panic!("expected const"),
8740 };
8741 match &ast.try_expr(value).unwrap().kind {
8742 crate::ExprKind::Arrow {
8743 return_type,
8744 type_predicate,
8745 ..
8746 } => {
8747 assert!(return_type.is_none());
8748 let tp = type_predicate.as_ref().expect("predicate parsed");
8749 assert_eq!(tp.param.name, "s");
8750 }
8751 other => panic!("expected Arrow, got {other:?}"),
8752 }
8753 }
8754
8755 #[test]
8756 fn parse_plain_return_type_unaffected_by_predicate_parser() {
8757 let src = "function f(s: Shape): boolean { return true; }";
8758 let (ast, diags) = parse_str(src);
8759 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8760 let stmt = single_stmt(&ast);
8761 match stmt.kind {
8762 crate::StmtKind::Function {
8763 ref return_type,
8764 ref type_predicate,
8765 ..
8766 } => {
8767 assert!(return_type.is_some(), "plain form has return_type");
8768 assert!(type_predicate.is_none(), "plain form has no type_predicate",);
8769 }
8770 _ => panic!("expected Function"),
8771 }
8772 }
8773
8774 #[test]
8775 fn parse_type_guard_rejects_non_return_position() {
8776 let (_, diags) = parse_str("let x: y is Foo = null;");
8777 assert!(
8778 !diags.is_empty(),
8779 "expected parse error for `is` outside return-type position",
8780 );
8781 }
8782
8783 #[test]
8784 fn parse_typeof_prefix() {
8785 let (ast, diags) = parse_str("typeof x;");
8786 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8787 let outer = expr_of_single_stmt(&ast);
8788 let crate::ExprKind::Typeof { operand } = outer.kind else {
8789 panic!("expected Typeof, got {:?}", outer.kind);
8790 };
8791 assert!(matches!(
8792 ast.try_expr(operand).unwrap().kind,
8793 crate::ExprKind::Identifier(_)
8794 ));
8795 assert_eq!(outer.span, crate::Span::new(F, 0, 8).unwrap());
8796 }
8797
8798 #[test]
8799 fn parse_typeof_in_equality() {
8800 let (ast, diags) = parse_str(r#"typeof x === "number";"#);
8801 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8802 let outer = expr_of_single_stmt(&ast);
8803 let (op, lhs, rhs) = binary(outer);
8804 assert_eq!(op, crate::BinOp::Eq);
8805 assert!(matches!(
8806 ast.try_expr(lhs).unwrap().kind,
8807 crate::ExprKind::Typeof { .. }
8808 ));
8809 assert!(matches!(
8810 ast.try_expr(rhs).unwrap().kind,
8811 crate::ExprKind::String(ref s) if s == "number"
8812 ));
8813 }
8814
8815 #[test]
8816 fn parse_let_without_type() {
8817 let (ast, diags) = parse_str("let x = 1;");
8818 assert!(diags.is_empty());
8819 let stmt = single_stmt(&ast);
8820 assert_eq!(stmt.span, crate::Span::new(F, 0, 10).unwrap());
8821 match stmt.kind {
8822 crate::StmtKind::Let {
8823 ref name,
8824 ref ty,
8825 value,
8826 ..
8827 } => {
8828 assert_eq!(name.name, "x");
8829 assert_eq!(name.span, crate::Span::new(F, 4, 5).unwrap());
8830 assert!(ty.is_none());
8831 assert_eq!(
8832 ast.try_expr(value).unwrap().kind,
8833 crate::ExprKind::Number(1.0)
8834 );
8835 }
8836 _ => panic!("expected Let"),
8837 }
8838 }
8839
8840 #[test]
8841 fn parse_const_with_type_and_string() {
8842 let (ast, diags) = parse_str(r#"const y: string = "hi";"#);
8843 assert!(diags.is_empty());
8844 let stmt = single_stmt(&ast);
8845 match stmt.kind {
8846 crate::StmtKind::Const {
8847 ref name,
8848 ref ty,
8849 value,
8850 ..
8851 } => {
8852 assert_eq!(name.name, "y");
8853 assert_eq!(name.span, crate::Span::new(F, 6, 7).unwrap());
8854 let ty = ty.as_ref().expect("expected type annotation");
8855 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8856 assert_eq!(ty.span, crate::Span::new(F, 9, 15).unwrap());
8857 assert_eq!(
8858 ast.try_expr(value).unwrap().kind,
8859 crate::ExprKind::String("hi".to_string())
8860 );
8861 }
8862 _ => panic!("expected Const"),
8863 }
8864 }
8865
8866 #[test]
8867 fn parse_let_with_type_and_binary_initializer() {
8868 let (ast, diags) = parse_str("let z: number = 1 + 2;");
8869 assert!(diags.is_empty());
8870 let stmt = single_stmt(&ast);
8871 match stmt.kind {
8872 crate::StmtKind::Let { ref ty, value, .. } => {
8873 assert!(ty.is_some());
8874 let (op, ..) = binary(ast.try_expr(value).unwrap());
8875 assert_eq!(op, crate::BinOp::Add);
8876 }
8877 _ => panic!("expected Let"),
8878 }
8879 }
8880
8881 #[test]
8882 fn void_type_annotation_is_accepted_syntactically() {
8883 let (ast, diags) = parse_str("let v: void = null;");
8884 assert!(diags.is_empty());
8885 let stmt = single_stmt(&ast);
8886 match stmt.kind {
8887 crate::StmtKind::Let { ref ty, .. } => {
8888 let ty = ty.as_ref().unwrap();
8889 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8890 assert_eq!(ty.span, crate::Span::new(F, 7, 11).unwrap());
8891 }
8892 _ => panic!("expected Let"),
8893 }
8894 }
8895
8896 #[test]
8897 fn const_missing_initializer_diagnoses() {
8898 let (ast, diags) = parse_str("const x;");
8899 assert_eq!(diags.len(), 1);
8900 assert_eq!(
8901 diags[0].message,
8902 "`const` declaration requires an initializer"
8903 );
8904 assert!(ast.top_level.is_empty());
8905 }
8906
8907 #[test]
8908 fn parse_const_object_shorthand_pattern() {
8909 let (ast, diags) = parse_str("const { a, b } = obj;");
8910 assert!(diags.is_empty(), "unexpected: {diags:?}");
8911 let stmt = single_stmt(&ast);
8912 match stmt.kind {
8913 crate::StmtKind::ConstPattern {
8914 binding:
8915 crate::Binding::Object {
8916 ref fields,
8917 ref rest,
8918 ..
8919 },
8920 ..
8921 } => {
8922 assert_eq!(fields.len(), 2);
8923 assert_eq!(fields[0].source.name, "a");
8924 assert_eq!(fields[0].local.name, "a");
8925 assert_eq!(fields[1].source.name, "b");
8926 assert_eq!(fields[1].local.name, "b");
8927 assert!(rest.is_none());
8928 }
8929 _ => panic!("expected ConstPattern Object, got {:?}", stmt.kind),
8930 }
8931 }
8932
8933 #[test]
8934 fn parse_const_object_pattern_keyword_source_key() {
8935 let (ast, diags) = parse_str("const { type: statusType, default: fallback } = obj;");
8936 assert!(diags.is_empty(), "unexpected: {diags:?}");
8937 let stmt = single_stmt(&ast);
8938 let crate::StmtKind::ConstPattern {
8939 binding: crate::Binding::Object { ref fields, .. },
8940 ..
8941 } = stmt.kind
8942 else {
8943 panic!("expected object const pattern");
8944 };
8945
8946 let names: Vec<(&str, &str)> = fields
8947 .iter()
8948 .map(|f| (f.source.name.as_str(), f.local.name.as_str()))
8949 .collect();
8950 assert_eq!(names, vec![("type", "statusType"), ("default", "fallback")]);
8951 }
8952
8953 #[test]
8954 fn parse_const_object_pattern_keyword_shorthand_rejected() {
8955 let (_ast, diags) = parse_str("const { default } = obj;");
8956 assert!(
8957 diags
8958 .iter()
8959 .any(|d| d.message == "expected `:` after keyword field name in object pattern"),
8960 "expected keyword-shorthand diagnostic, got: {diags:?}"
8961 );
8962 }
8963
8964 #[test]
8965 fn parse_const_object_renamed_pattern() {
8966 let (ast, diags) = parse_str("const { a: x, b: y } = obj;");
8967 assert!(diags.is_empty(), "unexpected: {diags:?}");
8968 match single_stmt(&ast).kind {
8969 crate::StmtKind::ConstPattern {
8970 binding: crate::Binding::Object { ref fields, .. },
8971 ..
8972 } => {
8973 assert_eq!(fields[0].source.name, "a");
8974 assert_eq!(fields[0].local.name, "x");
8975 assert_eq!(fields[1].source.name, "b");
8976 assert_eq!(fields[1].local.name, "y");
8977 }
8978 ref other => panic!("expected ConstPattern Object, got {other:?}"),
8979 }
8980 }
8981
8982 #[test]
8983 fn parse_const_object_with_rest() {
8984 let (ast, diags) = parse_str("const { a, ...rest } = obj;");
8985 assert!(diags.is_empty(), "unexpected: {diags:?}");
8986 match single_stmt(&ast).kind {
8987 crate::StmtKind::ConstPattern {
8988 binding:
8989 crate::Binding::Object {
8990 ref fields,
8991 ref rest,
8992 ..
8993 },
8994 ..
8995 } => {
8996 assert_eq!(fields.len(), 1);
8997 assert_eq!(fields[0].source.name, "a");
8998 let r = rest.as_ref().expect("expected rest");
8999 assert_eq!(r.name, "rest");
9000 }
9001 ref other => panic!("expected ConstPattern Object, got {other:?}"),
9002 }
9003 }
9004
9005 #[test]
9006 fn parse_const_array_pattern() {
9007 let (ast, diags) = parse_str("const [x, y] = arr;");
9008 assert!(diags.is_empty(), "unexpected: {diags:?}");
9009 match single_stmt(&ast).kind {
9010 crate::StmtKind::ConstPattern {
9011 binding:
9012 crate::Binding::Array {
9013 ref elems,
9014 ref rest,
9015 ..
9016 },
9017 ..
9018 } => {
9019 assert_eq!(elems.len(), 2);
9020 assert_eq!(elems[0].as_ref().unwrap().name, "x");
9021 assert_eq!(elems[1].as_ref().unwrap().name, "y");
9022 assert!(rest.is_none());
9023 }
9024 ref other => panic!("expected ConstPattern Array, got {other:?}"),
9025 }
9026 }
9027
9028 #[test]
9029 fn parse_const_array_with_hole_and_rest() {
9030 let (ast, diags) = parse_str("const [, x, ...rest] = arr;");
9031 assert!(diags.is_empty(), "unexpected: {diags:?}");
9032 match single_stmt(&ast).kind {
9033 crate::StmtKind::ConstPattern {
9034 binding:
9035 crate::Binding::Array {
9036 ref elems,
9037 ref rest,
9038 ..
9039 },
9040 ..
9041 } => {
9042 assert_eq!(elems.len(), 2);
9043 assert!(elems[0].is_none(), "first slot should be a hole");
9044 assert_eq!(elems[1].as_ref().unwrap().name, "x");
9045 assert_eq!(rest.as_ref().unwrap().name, "rest");
9046 }
9047 ref other => panic!("expected ConstPattern Array, got {other:?}"),
9048 }
9049 }
9050
9051 #[test]
9052 fn parse_let_object_pattern() {
9053 let (ast, diags) = parse_str("let { a, b } = obj;");
9054 assert!(diags.is_empty(), "unexpected: {diags:?}");
9055 assert!(matches!(
9056 single_stmt(&ast).kind,
9057 crate::StmtKind::LetPattern {
9058 binding: crate::Binding::Object { .. },
9059 ..
9060 }
9061 ));
9062 }
9063
9064 #[test]
9065 fn parse_function_param_object_pattern() {
9066 let src = "function f({ a, b }: T): void { return; }";
9067 let (ast, diags) = parse_str(src);
9068 assert!(diags.is_empty(), "unexpected: {diags:?}");
9069 match single_stmt(&ast).kind {
9070 crate::StmtKind::Function { ref params, .. } => {
9071 assert_eq!(params.len(), 1);
9072 let p = ¶ms[0];
9073 assert!(p.pattern.is_some(), "expected pattern param");
9074 assert_eq!(p.name.name, "", "pattern placeholder uses empty name");
9075 match p.pattern.as_ref().unwrap() {
9076 crate::Binding::Object { fields, .. } => {
9077 assert_eq!(fields.len(), 2);
9078 assert_eq!(fields[0].source.name, "a");
9079 }
9080 other => panic!("expected Object binding, got {other:?}"),
9081 }
9082 }
9083 ref other => panic!("expected Function, got {other:?}"),
9084 }
9085 }
9086
9087 #[test]
9088 fn parse_arrow_param_array_pattern() {
9089 let src = "const g = ([x, y]: number[]): number => x + y;";
9090 let (_ast, diags) = parse_str(src);
9091 assert!(diags.is_empty(), "unexpected: {diags:?}");
9092 }
9093
9094 #[test]
9095 fn parse_destructure_nested_rejected() {
9096 let (_ast, diags) = parse_str("const { a: { b } } = obj;");
9097 assert!(
9098 diags
9099 .iter()
9100 .any(|d| d.message.contains("nested destructuring")),
9101 "expected nested-destructuring diagnostic, got: {diags:?}",
9102 );
9103 }
9104
9105 #[test]
9106 fn parse_destructure_array_nested_rejected() {
9107 let (_ast, diags) = parse_str("const [[a]] = arr;");
9108 assert!(
9109 diags
9110 .iter()
9111 .any(|d| d.message.contains("nested destructuring")),
9112 "expected nested-destructuring diagnostic, got: {diags:?}",
9113 );
9114 }
9115
9116 #[test]
9117 fn parse_destructure_pattern_defaults() {
9118 let (ast, diags) = parse_str("const { a = 1, b: c = 2 } = obj;");
9119 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9120 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
9121 panic!("expected pattern")
9122 };
9123 let crate::Binding::Object { fields, .. } = binding else {
9124 panic!("expected object")
9125 };
9126 assert_eq!(fields.len(), 2);
9127 assert!(fields.iter().all(|field| field.default.is_some()));
9128 assert_eq!(fields[1].local.name, "c");
9129 }
9130
9131 #[test]
9132 fn parse_destructure_param_default() {
9133 let (ast, diags) = parse_str("function f({ a = 1 }: T = obj): void { return; }");
9134 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9135 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
9136 panic!("expected function")
9137 };
9138 assert!(params[0].default.is_some());
9139 assert!(params[0].pattern.is_some());
9140 }
9141
9142 #[test]
9143 fn parse_destructure_rest_not_last_rejected() {
9144 let (_ast, diags) = parse_str("const { ...rest, a } = obj;");
9145 assert!(
9146 diags
9147 .iter()
9148 .any(|d| d.message.contains("rest element must be the last element")),
9149 "expected rest-not-last diagnostic, got: {diags:?}",
9150 );
9151 }
9152
9153 #[test]
9154 fn parse_destructure_empty_object_rejected() {
9155 let (_ast, diags) = parse_str("const { } = obj;");
9156 assert!(
9157 diags
9158 .iter()
9159 .any(|d| d.message.contains("empty object destructuring pattern")),
9160 "expected empty-pattern diagnostic, got: {diags:?}",
9161 );
9162 }
9163
9164 #[test]
9165 fn parse_ternary_simple() {
9166 let (ast, diags) = parse_str("const x: number = a ? 1 : 2;");
9167 assert!(diags.is_empty(), "unexpected: {diags:?}");
9168 let stmt = single_stmt(&ast);
9169 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9170 panic!("expected Const, got {:?}", stmt.kind);
9171 };
9172 assert!(matches!(
9173 ast.try_expr(value).unwrap().kind,
9174 crate::ExprKind::Ternary { .. }
9175 ));
9176 }
9177
9178 #[test]
9179 fn parse_ternary_nested_right_assoc() {
9180 let (ast, diags) = parse_str("const x: number = a ? 1 : b ? 2 : 3;");
9181 assert!(diags.is_empty(), "unexpected: {diags:?}");
9182 let stmt = single_stmt(&ast);
9183 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9184 panic!("expected Const");
9185 };
9186 let crate::ExprKind::Ternary { else_, .. } = ast.try_expr(value).unwrap().kind else {
9187 panic!("expected outer Ternary");
9188 };
9189 assert!(matches!(
9190 ast.try_expr(else_).unwrap().kind,
9191 crate::ExprKind::Ternary { .. }
9192 ));
9193 }
9194
9195 #[test]
9196 fn parse_ternary_missing_colon_diagnoses() {
9197 let (_, diags) = parse_str("const x: number = a ? 1;");
9198 assert!(
9199 diags
9200 .iter()
9201 .any(|d| d.message.contains("expected `:` to complete ternary")),
9202 "expected colon diagnostic, got: {diags:?}",
9203 );
9204 }
9205
9206 #[test]
9207 fn parse_nullish_coalesce_binop() {
9208 let (ast, diags) = parse_str("const x: number = a ?? 1;");
9209 assert!(diags.is_empty(), "unexpected: {diags:?}");
9210 let stmt = single_stmt(&ast);
9211 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9212 panic!("expected Const");
9213 };
9214 match ast.try_expr(value).unwrap().kind {
9215 crate::ExprKind::Binary { op, .. } => {
9216 assert_eq!(op, crate::BinOp::NullishCoalesce);
9217 }
9218 ref other => panic!("expected Binary NullishCoalesce, got {other:?}"),
9219 }
9220 }
9221
9222 #[test]
9223 fn parse_nullish_left_associative() {
9224 let (ast, diags) = parse_str("const x: number = a ?? b ?? c;");
9225 assert!(diags.is_empty(), "unexpected: {diags:?}");
9226 let stmt = single_stmt(&ast);
9227 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9228 panic!("expected Const");
9229 };
9230 let crate::ExprKind::Binary { lhs, .. } = ast.try_expr(value).unwrap().kind else {
9231 panic!("expected outer Binary");
9232 };
9233 match ast.try_expr(lhs).unwrap().kind {
9234 crate::ExprKind::Binary { op, .. } => {
9235 assert_eq!(op, crate::BinOp::NullishCoalesce);
9236 }
9237 ref other => panic!("expected inner Binary, got {other:?}"),
9238 }
9239 }
9240
9241 #[test]
9242 fn parse_pow_is_right_associative() {
9243 let (ast, diags) = parse_str("const x: number = 2 ** 3 ** 2;");
9244 assert!(diags.is_empty(), "unexpected: {diags:?}");
9245 let stmt = single_stmt(&ast);
9246 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9247 panic!("expected Const");
9248 };
9249 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
9250 panic!("expected outer Binary");
9251 };
9252 assert_eq!(op, crate::BinOp::Pow);
9253 match ast.try_expr(rhs).unwrap().kind {
9254 crate::ExprKind::Binary { op: inner_op, .. } => {
9255 assert_eq!(inner_op, crate::BinOp::Pow);
9256 }
9257 ref other => panic!("expected inner Pow Binary on RHS, got {other:?}"),
9258 }
9259 }
9260
9261 #[test]
9262 fn parse_pow_higher_than_multiply() {
9263 let (ast, diags) = parse_str("const x: number = 2 * 3 ** 2;");
9264 assert!(diags.is_empty(), "unexpected: {diags:?}");
9265 let stmt = single_stmt(&ast);
9266 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9267 panic!("expected Const");
9268 };
9269 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
9270 panic!("expected outer Binary");
9271 };
9272 assert_eq!(op, crate::BinOp::Mul);
9273 match ast.try_expr(rhs).unwrap().kind {
9274 crate::ExprKind::Binary { op: inner_op, .. } => {
9275 assert_eq!(inner_op, crate::BinOp::Pow);
9276 }
9277 ref other => panic!("expected Pow on the RHS of *, got {other:?}"),
9278 }
9279 }
9280
9281 #[test]
9282 fn parse_compound_assign_ident() {
9283 let (ast, diags) = parse_str("function main(): void { let x: number = 0; x += 5; }");
9284 assert!(diags.is_empty(), "unexpected: {diags:?}");
9285 let func = ast.top_level.iter().find_map(|sid| {
9286 if let crate::StmtKind::Function { body, .. } = &ast.try_stmt(*sid).unwrap().kind {
9287 Some(*body)
9288 } else {
9289 None
9290 }
9291 });
9292 let crate::StmtKind::Block(stmts) = &ast.try_stmt(func.expect("main")).unwrap().kind else {
9293 panic!("expected function body to be a block");
9294 };
9295 let target = stmts
9296 .iter()
9297 .find_map(|sid| match &ast.try_stmt(*sid).unwrap().kind {
9298 crate::StmtKind::CompoundAssign { target, op, .. } => {
9299 Some((target.name.clone(), *op))
9300 }
9301 _ => None,
9302 });
9303 let (name, op) = target.expect("expected a CompoundAssign in the body");
9304 assert_eq!(name, "x");
9305 assert_eq!(op, crate::BinOp::Add);
9306 }
9307
9308 #[test]
9309 fn parse_nullish_mixed_with_or_rejected() {
9310 let (_, diags) = parse_str("const x: number = a || b ?? c;");
9311 assert!(
9312 diags
9313 .iter()
9314 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
9315 "expected mixing diagnostic, got: {diags:?}",
9316 );
9317 }
9318
9319 #[test]
9320 fn parse_nullish_mixed_with_and_rejected() {
9321 let (_, diags) = parse_str("const x: number = a && b ?? c;");
9322 assert!(
9323 diags
9324 .iter()
9325 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
9326 "expected mixing diagnostic, got: {diags:?}",
9327 );
9328 }
9329
9330 #[test]
9331 fn parse_nullish_followed_by_logical_rejected() {
9332 for (source, help) in [
9333 (
9334 "const x: number = a ?? b || c;",
9335 "`(a ?? b) || c` or `a ?? (b || c)`",
9336 ),
9337 (
9338 "const x: number = a ?? b && c;",
9339 "`(a ?? b) && c` or `a ?? (b && c)`",
9340 ),
9341 (
9342 "const x: number = a ?? b ?? c || d;",
9343 "`(a ?? b) || c` or `a ?? (b || c)`",
9344 ),
9345 (
9347 "const x: number = a || b ?? c || d;",
9348 "`(a || b) ?? c` or `a || (b ?? c)`",
9349 ),
9350 ] {
9351 let (_, diags) = parse_str(source);
9352 assert_eq!(diags.len(), 1, "diags for {source:?}: {diags:?}");
9353 assert!(
9354 diags[0].message.contains("mixing `??` with `||` / `&&`"),
9355 "message for {source:?}: {diags:?}",
9356 );
9357 assert!(
9358 diags[0].help.iter().any(|h| h.contains(help)),
9359 "help for {source:?}: {diags:?}",
9360 );
9361 }
9362 }
9363
9364 #[test]
9365 fn parse_nullish_operand_keeps_tighter_operators() {
9366 let (ast, diags) = parse_str("const x: boolean = a ?? b == c;");
9368 assert!(diags.is_empty(), "unexpected: {diags:?}");
9369 let stmt = single_stmt(&ast);
9370 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9371 panic!("expected Const");
9372 };
9373 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
9374 panic!("expected outer Binary");
9375 };
9376 assert_eq!(op, crate::BinOp::NullishCoalesce);
9377 assert!(matches!(
9378 ast.try_expr(rhs).unwrap().kind,
9379 crate::ExprKind::Binary {
9380 op: crate::BinOp::Eq,
9381 ..
9382 }
9383 ));
9384 }
9385
9386 #[test]
9387 fn parse_nullish_with_parens_ok() {
9388 let (_, diags) = parse_str("const x: number = (a || b) ?? c;");
9389 assert!(diags.is_empty(), "unexpected: {diags:?}");
9390 }
9391
9392 #[test]
9393 fn parse_optional_field_chain() {
9394 let (ast, diags) = parse_str("const x: number = a?.b;");
9395 assert!(diags.is_empty(), "unexpected: {diags:?}");
9396 let stmt = single_stmt(&ast);
9397 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9398 panic!("expected Const");
9399 };
9400 match ast.try_expr(value).unwrap().kind {
9401 crate::ExprKind::OptionalChain { ref parts, .. } => {
9402 assert_eq!(parts.len(), 1);
9403 match &parts[0] {
9404 crate::ChainPart::Field { name, optional, .. } => {
9405 assert_eq!(name.name, "b");
9406 assert!(*optional);
9407 }
9408 other => panic!("expected Field, got {other:?}"),
9409 }
9410 }
9411 ref other => panic!("expected OptionalChain, got {other:?}"),
9412 }
9413 }
9414
9415 #[test]
9416 fn parse_optional_chain_continues_after_first_dot() {
9417 let (ast, diags) = parse_str("const x: number = a?.b.c;");
9418 assert!(diags.is_empty(), "unexpected: {diags:?}");
9419 let stmt = single_stmt(&ast);
9420 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9421 panic!("expected Const");
9422 };
9423 match ast.try_expr(value).unwrap().kind {
9424 crate::ExprKind::OptionalChain { ref parts, .. } => {
9425 assert_eq!(parts.len(), 2);
9426 if let crate::ChainPart::Field { optional, .. } = &parts[0] {
9427 assert!(*optional);
9428 }
9429 if let crate::ChainPart::Field { optional, .. } = &parts[1] {
9430 assert!(!*optional);
9431 }
9432 }
9433 ref other => panic!("expected OptionalChain, got {other:?}"),
9434 }
9435 }
9436
9437 #[test]
9438 fn parse_optional_chain_multiple_short_circuits() {
9439 let (ast, diags) = parse_str("const x: number = a?.b?.c;");
9440 assert!(diags.is_empty(), "unexpected: {diags:?}");
9441 let stmt = single_stmt(&ast);
9442 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9443 panic!("expected Const");
9444 };
9445 let crate::ExprKind::OptionalChain { parts, .. } = &ast.try_expr(value).unwrap().kind
9446 else {
9447 panic!("expected OptionalChain");
9448 };
9449 assert_eq!(parts.len(), 2);
9450 for p in parts {
9451 if let crate::ChainPart::Field { optional, .. } = p {
9452 assert!(*optional, "both parts should be optional");
9453 }
9454 }
9455 }
9456
9457 #[test]
9458 fn parse_optional_call_and_index() {
9459 let (_, diags) = parse_str("const x: number = f?.();");
9460 assert!(diags.is_empty(), "unexpected: {diags:?}");
9461 let (_, diags) = parse_str("const y: number = arr?.[0];");
9462 assert!(diags.is_empty(), "unexpected: {diags:?}");
9463 }
9464
9465 #[test]
9466 fn parse_optional_chain_terminates_at_questionmark_for_ternary() {
9467 let (ast, diags) = parse_str("const x: number = a?.b ? c : d;");
9468 assert!(diags.is_empty(), "unexpected: {diags:?}");
9469 let stmt = single_stmt(&ast);
9470 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9471 panic!("expected Const");
9472 };
9473 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
9474 panic!(
9475 "expected top-level Ternary, got {:?}",
9476 ast.try_expr(value).unwrap().kind
9477 );
9478 };
9479 assert!(matches!(
9480 ast.try_expr(cond).unwrap().kind,
9481 crate::ExprKind::OptionalChain { .. }
9482 ));
9483 }
9484
9485 #[test]
9486 fn parse_nullish_below_ternary() {
9487 let (ast, diags) = parse_str("const x: number = a ?? b ? c : d;");
9488 assert!(diags.is_empty(), "unexpected: {diags:?}");
9489 let stmt = single_stmt(&ast);
9490 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9491 panic!("expected Const");
9492 };
9493 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
9494 panic!("expected top-level Ternary");
9495 };
9496 match ast.try_expr(cond).unwrap().kind {
9497 crate::ExprKind::Binary { op, .. } => {
9498 assert_eq!(op, crate::BinOp::NullishCoalesce);
9499 }
9500 ref other => panic!("expected Binary ??, got {other:?}"),
9501 }
9502 }
9503
9504 #[test]
9505 fn let_missing_initializer_starts_undefined() {
9506 let (ast, diags) = parse_str("let x;");
9507 assert!(diags.is_empty());
9508 assert_eq!(ast.implicit_initializers.len(), 1);
9509 assert_eq!(ast.top_level.len(), 1);
9510 }
9511
9512 #[test]
9513 fn missing_identifier_after_let_diagnoses() {
9514 let (ast, diags) = parse_str("let = 1;");
9515 assert_eq!(diags.len(), 1);
9516 assert_eq!(diags[0].message, "expected identifier after `let`/`const`");
9517 assert!(ast.top_level.is_empty());
9518 }
9519
9520 #[test]
9521 fn missing_type_after_colon_diagnoses() {
9522 let (ast, diags) = parse_str("let x: = 1;");
9523 assert_eq!(diags.len(), 1);
9524 assert_eq!(diags[0].message, "expected type");
9525 assert!(ast.top_level.is_empty());
9526 }
9527
9528 #[test]
9529 fn missing_semicolon_after_declaration_diagnoses() {
9530 let (ast, diags) = parse_str("let x = 1 y;");
9531 assert_eq!(diags.len(), 1);
9532 assert_eq!(diags[0].message, "expected `;` after declaration");
9533 assert!(ast.top_level.is_empty());
9534 }
9535
9536 #[test]
9537 fn expression_statements_still_parse() {
9538 let (ast, diags) = parse_str("42;");
9539 assert!(diags.is_empty());
9540 assert_eq!(ast.top_level.len(), 1);
9541 }
9542
9543 #[test]
9544 fn snapshot_mixed_declarations() {
9545 let (ast, diags) =
9546 parse_str(r#"let x = 1; const y: string = "hi"; let z: number = 1 + 2;"#);
9547 assert!(diags.is_empty());
9548 assert_eq!(ast.top_level.len(), 3);
9549 insta::assert_debug_snapshot!(ast);
9550 }
9551
9552 #[test]
9553 fn parse_function_typical() {
9554 let (ast, diags) = parse_str("function f(a: number, b: string): boolean { }");
9555 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9556 let stmt = single_stmt(&ast);
9557 match stmt.kind {
9558 crate::StmtKind::Function {
9559 ref name,
9560 ref generics,
9561 ref params,
9562 ref return_type,
9563 body,
9564 ..
9565 } => {
9566 assert_eq!(name.name, "f");
9567 assert_eq!(name.span, crate::Span::new(F, 9, 10).unwrap());
9568 assert!(generics.is_empty());
9569 assert_eq!(params.len(), 2);
9570 assert_eq!(params[0].name.name, "a");
9571 assert_eq!(params[0].name.span, crate::Span::new(F, 11, 12).unwrap());
9572 let p0_ty = params[0]
9573 .ty
9574 .as_ref()
9575 .expect("function-decl param requires annotation");
9576 assert!(matches!(p0_ty.kind, crate::TypeAnnotationKind::Name { .. }));
9577 assert_eq!(p0_ty.span, crate::Span::new(F, 14, 20).unwrap()); assert_eq!(params[1].name.name, "b");
9579 assert_eq!(params[1].name.span, crate::Span::new(F, 22, 23).unwrap());
9580 let p1_ty = params[1]
9581 .ty
9582 .as_ref()
9583 .expect("function-decl param requires annotation");
9584 assert_eq!(p1_ty.span, crate::Span::new(F, 25, 31).unwrap()); let return_type = return_type.as_ref().expect("plain return type");
9586 assert!(matches!(
9587 return_type.kind,
9588 crate::TypeAnnotationKind::Name { .. }
9589 ));
9590 assert_eq!(return_type.span, crate::Span::new(F, 34, 41).unwrap()); let block = ast.try_stmt(body).unwrap();
9592 assert!(matches!(block.kind, crate::StmtKind::Block(ref v) if v.is_empty()));
9593 }
9594 _ => panic!("expected Function"),
9595 }
9596 }
9597
9598 #[test]
9599 fn parse_function_with_doc_comment() {
9600 let (ast, diags) = parse_str(
9601 "/**\n * Sum two numbers.\n * @param a First.\n * @param b Second.\n */\nfunction add(a: number, b: number): number { return a + b; }",
9602 );
9603 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9604 let stmt = single_stmt(&ast);
9605 match &stmt.kind {
9606 crate::StmtKind::Function { doc, .. } => {
9607 let d = doc.as_ref().expect("doc attached");
9608 assert_eq!(d.summary, "Sum two numbers.");
9609 assert_eq!(d.params.len(), 2);
9610 assert_eq!(d.params[0].name, "a");
9611 assert_eq!(d.params[1].name, "b");
9612 }
9613 _ => panic!("expected Function"),
9614 }
9615 }
9616
9617 #[test]
9618 fn parse_interface_member_docs() {
9619 let (ast, diags) = parse_str(
9620 "interface Foo {\n /** First method. */ first(): void;\n /** A field. */ field: number;\n}",
9621 );
9622 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9623 let stmt = single_stmt(&ast);
9624 match &stmt.kind {
9625 crate::StmtKind::InterfaceDecl { members, .. } => {
9626 assert_eq!(members.len(), 2);
9627 match &members[0] {
9628 crate::InterfaceMember::Method { doc, .. } => {
9629 assert_eq!(doc.as_ref().unwrap().summary, "First method.");
9630 }
9631 _ => panic!("expected Method first"),
9632 }
9633 match &members[1] {
9634 crate::InterfaceMember::Property { doc, .. } => {
9635 assert_eq!(doc.as_ref().unwrap().summary, "A field.");
9636 }
9637 _ => panic!("expected Property second"),
9638 }
9639 }
9640 _ => panic!("expected InterfaceDecl"),
9641 }
9642 }
9643
9644 #[test]
9645 fn parse_numeric_enum_all_implicit() {
9646 let (ast, diags) = parse_str("enum Direction { Up, Down, Left, Right }");
9647 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9648 let stmt = single_stmt(&ast);
9649 match &stmt.kind {
9650 crate::StmtKind::EnumDecl { name, members, doc } => {
9651 assert_eq!(name.name, "Direction");
9652 assert!(doc.is_none());
9653 assert_eq!(members.len(), 4);
9654 for (m, expected) in members.iter().zip(["Up", "Down", "Left", "Right"]) {
9655 assert_eq!(m.name.name, expected);
9656 assert!(m.value.is_none(), "expected no initializer on {expected}");
9657 }
9658 }
9659 _ => panic!("expected EnumDecl"),
9660 }
9661 }
9662
9663 #[test]
9664 fn parse_string_enum_all_explicit() {
9665 let (ast, diags) =
9666 parse_str("enum Status { Active = \"active\", Inactive = \"inactive\" }");
9667 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9668 let stmt = single_stmt(&ast);
9669 match &stmt.kind {
9670 crate::StmtKind::EnumDecl { name, members, .. } => {
9671 assert_eq!(name.name, "Status");
9672 assert_eq!(members.len(), 2);
9673 match &members[0].value {
9674 Some(crate::EnumInitializer::String { value, .. }) => {
9675 assert_eq!(value, "active");
9676 }
9677 other => panic!("expected String init, got {other:?}"),
9678 }
9679 match &members[1].value {
9680 Some(crate::EnumInitializer::String { value, .. }) => {
9681 assert_eq!(value, "inactive");
9682 }
9683 other => panic!("expected String init, got {other:?}"),
9684 }
9685 }
9686 _ => panic!("expected EnumDecl"),
9687 }
9688 }
9689
9690 #[test]
9691 fn parse_numeric_enum_with_negative_init() {
9692 let (ast, diags) = parse_str("enum D { Up = 1, Down = -1 }");
9693 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9694 let stmt = single_stmt(&ast);
9695 match &stmt.kind {
9696 crate::StmtKind::EnumDecl { members, .. } => {
9697 assert_eq!(members.len(), 2);
9698 match &members[0].value {
9699 Some(crate::EnumInitializer::Number { value, .. }) => {
9700 assert_eq!(*value, 1.0);
9701 }
9702 other => panic!("expected Number init, got {other:?}"),
9703 }
9704 match &members[1].value {
9705 Some(crate::EnumInitializer::Number { value, .. }) => {
9706 assert_eq!(*value, -1.0);
9707 }
9708 other => panic!("expected Number init, got {other:?}"),
9709 }
9710 }
9711 _ => panic!("expected EnumDecl"),
9712 }
9713 }
9714
9715 #[test]
9716 fn parse_enum_mixed_kinds_is_parse_error() {
9717 let (_ast, diags) = parse_str("enum Mix { A = 1, B = \"two\" }");
9718 assert!(
9719 diags
9720 .iter()
9721 .any(|d| d.message.contains("mixed numeric and string")),
9722 "expected mixed-kind diagnostic, got: {diags:?}"
9723 );
9724 }
9725
9726 #[test]
9727 fn parse_enum_member_docs() {
9728 let (ast, diags) = parse_str(
9729 "enum D {\n /** The first one. */ A = 1,\n /** The second one. */ B = 2,\n}",
9730 );
9731 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9732 let stmt = single_stmt(&ast);
9733 match &stmt.kind {
9734 crate::StmtKind::EnumDecl { members, .. } => {
9735 assert_eq!(members.len(), 2);
9736 assert_eq!(
9737 members[0].doc.as_ref().expect("first doc").summary,
9738 "The first one."
9739 );
9740 assert_eq!(
9741 members[1].doc.as_ref().expect("second doc").summary,
9742 "The second one."
9743 );
9744 }
9745 _ => panic!("expected EnumDecl"),
9746 }
9747 }
9748
9749 #[test]
9750 fn parse_enum_trailing_comma_ok() {
9751 let (ast, diags) = parse_str("enum D { A, B, }");
9752 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9753 let stmt = single_stmt(&ast);
9754 match &stmt.kind {
9755 crate::StmtKind::EnumDecl { members, .. } => assert_eq!(members.len(), 2),
9756 _ => panic!("expected EnumDecl"),
9757 }
9758 }
9759
9760 #[test]
9761 fn parse_function_empty_params() {
9762 let (ast, diags) = parse_str("function noop(): void { }");
9763 assert!(diags.is_empty());
9764 let stmt = single_stmt(&ast);
9765 match stmt.kind {
9766 crate::StmtKind::Function { ref params, .. } => assert!(params.is_empty()),
9767 _ => panic!("expected Function"),
9768 }
9769 }
9770
9771 #[test]
9772 fn parse_generic_function_single_param() {
9773 let (ast, diags) = parse_str("function identity<T>(x: T): T { return x; }");
9774 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9775 let stmt = single_stmt(&ast);
9776 match stmt.kind {
9777 crate::StmtKind::Function {
9778 ref name,
9779 ref generics,
9780 ref params,
9781 ..
9782 } => {
9783 assert_eq!(name.name, "identity");
9784 assert_eq!(generics.len(), 1);
9785 assert_eq!(generics[0].name, "T");
9786 assert_eq!(params.len(), 1);
9787 assert_eq!(params[0].name.name, "x");
9788 }
9789 _ => panic!("expected Function"),
9790 }
9791 }
9792
9793 #[test]
9794 fn parse_generic_function_multiple_params() {
9795 let (ast, diags) = parse_str("function pair<T, U>(a: T, b: U): T { return a; }");
9796 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9797 let stmt = single_stmt(&ast);
9798 match stmt.kind {
9799 crate::StmtKind::Function { ref generics, .. } => {
9800 assert_eq!(generics.len(), 2);
9801 assert_eq!(generics[0].name, "T");
9802 assert_eq!(generics[1].name, "U");
9803 }
9804 _ => panic!("expected Function"),
9805 }
9806 }
9807
9808 #[test]
9809 fn parse_non_generic_function_has_empty_generics() {
9810 let (ast, diags) = parse_str("function noop(): void { }");
9811 assert!(diags.is_empty());
9812 let stmt = single_stmt(&ast);
9813 match stmt.kind {
9814 crate::StmtKind::Function { ref generics, .. } => assert!(generics.is_empty()),
9815 _ => panic!("expected Function"),
9816 }
9817 }
9818
9819 #[test]
9820 fn parse_empty_generic_list_diagnoses() {
9821 let (_ast, diags) = parse_str("function f<>(): void { }");
9822 assert!(
9823 diags
9824 .iter()
9825 .any(|d| d.message.contains("empty generic parameter list")),
9826 "expected empty-generic-list diagnostic, got: {diags:?}",
9827 );
9828 }
9829
9830 #[test]
9831 fn parse_trailing_comma_in_generic_list_ok() {
9832 let (ast, diags) = parse_str("function f<T,>(): void { }");
9833 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9834 match single_stmt(&ast).kind {
9835 crate::StmtKind::Function { ref generics, .. } => assert_eq!(generics.len(), 1),
9836 _ => panic!("expected Function"),
9837 }
9838 }
9839
9840 #[test]
9841 fn parse_generic_call_single_type_arg() {
9842 let (ast, diags) = parse_str("identity<number>(42);");
9843 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9844 let stmt = single_stmt(&ast);
9845 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9846 panic!("expected Expr stmt");
9847 };
9848 match &ast.try_expr(call_id).unwrap().kind {
9849 crate::ExprKind::Call {
9850 type_args, args, ..
9851 } => {
9852 let ta = type_args.as_ref().expect("type_args set for generic call");
9853 assert_eq!(ta.len(), 1);
9854 assert_eq!(args.len(), 1);
9855 }
9856 other => panic!("expected Call, got {other:?}"),
9857 }
9858 }
9859
9860 #[test]
9861 fn parse_generic_call_trailing_comma_ok() {
9862 let (ast, diags) = parse_str("identity<number,>(42);");
9863 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9864 let stmt = single_stmt(&ast);
9865 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9866 panic!("expected Expr stmt");
9867 };
9868 match &ast.try_expr(call_id).unwrap().kind {
9869 crate::ExprKind::Call {
9870 type_args, args, ..
9871 } => {
9872 assert_eq!(type_args.as_ref().expect("type_args set").len(), 1);
9873 assert_eq!(args.len(), 1);
9874 }
9875 other => panic!("expected Call, got {other:?}"),
9876 }
9877 }
9878
9879 #[test]
9880 fn parse_generic_call_multi_type_args() {
9881 let (ast, diags) = parse_str("pair<number, string>(1, \"x\");");
9882 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9883 let stmt = single_stmt(&ast);
9884 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9885 panic!("expected Expr stmt");
9886 };
9887 match &ast.try_expr(call_id).unwrap().kind {
9888 crate::ExprKind::Call { type_args, .. } => {
9889 let ta = type_args.as_ref().expect("type_args set for generic call");
9890 assert_eq!(ta.len(), 2);
9891 }
9892 other => panic!("expected Call, got {other:?}"),
9893 }
9894 }
9895
9896 #[test]
9897 fn parse_comparison_chain_unaffected_by_generic_lookahead() {
9898 let (ast, diags) = parse_str("let z = a < b > c;");
9899 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9900 assert!(
9901 !ast_contains_call_with_type_args(&ast),
9902 "should not have parsed any generic call",
9903 );
9904 }
9905
9906 #[test]
9907 fn parse_lt_with_paren_rhs_is_comparison_not_generic() {
9908 let (ast, diags) = parse_str("let z = a < (b);");
9909 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9910 assert!(!ast_contains_call_with_type_args(&ast));
9911 }
9912
9913 #[test]
9914 fn parse_generic_call_no_args() {
9915 let (ast, diags) = parse_str("none<number>();");
9916 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9917 let stmt = single_stmt(&ast);
9918 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9919 panic!("expected Expr stmt");
9920 };
9921 match &ast.try_expr(call_id).unwrap().kind {
9922 crate::ExprKind::Call {
9923 type_args, args, ..
9924 } => {
9925 assert_eq!(type_args.as_ref().unwrap().len(), 1);
9926 assert!(args.is_empty());
9927 }
9928 other => panic!("expected Call, got {other:?}"),
9929 }
9930 }
9931
9932 fn ast_contains_call_with_type_args(ast: &crate::Ast) -> bool {
9933 for id in 0..ast.top_level.len() {
9934 let stmt = ast.try_stmt(ast.top_level[id]).unwrap();
9935 if walk_stmt_for_typed_call(ast, stmt) {
9936 return true;
9937 }
9938 }
9939 false
9940 }
9941
9942 fn walk_stmt_for_typed_call(ast: &crate::Ast, stmt: &crate::Stmt) -> bool {
9943 match &stmt.kind {
9944 crate::StmtKind::Let { value, .. } | crate::StmtKind::Const { value, .. } => {
9945 walk_expr_for_typed_call(ast, *value)
9946 }
9947 crate::StmtKind::Expr(e) => walk_expr_for_typed_call(ast, *e),
9948 crate::StmtKind::Block(stmts) => stmts
9949 .iter()
9950 .any(|sid| walk_stmt_for_typed_call(ast, ast.try_stmt(*sid).unwrap())),
9951 _ => false,
9952 }
9953 }
9954
9955 fn walk_expr_for_typed_call(ast: &crate::Ast, id: crate::ExprId) -> bool {
9956 match &ast.try_expr(id).unwrap().kind {
9957 crate::ExprKind::Call {
9958 type_args: Some(_), ..
9959 } => true,
9960 crate::ExprKind::Call { callee, args, .. } => {
9961 walk_expr_for_typed_call(ast, *callee)
9962 || args.iter().any(|a| walk_expr_for_typed_call(ast, *a))
9963 }
9964 crate::ExprKind::Binary { lhs, rhs, .. } => {
9965 walk_expr_for_typed_call(ast, *lhs) || walk_expr_for_typed_call(ast, *rhs)
9966 }
9967 crate::ExprKind::Unary { operand, .. } => walk_expr_for_typed_call(ast, *operand),
9968 crate::ExprKind::Paren(e) => walk_expr_for_typed_call(ast, *e),
9969 crate::ExprKind::FieldAccess { receiver, .. } => {
9970 walk_expr_for_typed_call(ast, *receiver)
9971 }
9972 crate::ExprKind::IndexAccess { receiver, index } => {
9973 walk_expr_for_typed_call(ast, *receiver) || walk_expr_for_typed_call(ast, *index)
9974 }
9975 _ => false,
9976 }
9977 }
9978
9979 #[test]
9980 fn parse_function_body_with_statements() {
9981 let (ast, diags) = parse_str("function g(x: number): number { let y = x; y; }");
9982 assert!(diags.is_empty());
9983 let stmt = single_stmt(&ast);
9984 match stmt.kind {
9985 crate::StmtKind::Function { body, .. } => {
9986 let block = ast.try_stmt(body).unwrap();
9987 match block.kind {
9988 crate::StmtKind::Block(ref v) => assert_eq!(v.len(), 2),
9989 _ => panic!("expected Block"),
9990 }
9991 }
9992 _ => panic!("expected Function"),
9993 }
9994 }
9995
9996 #[test]
9997 fn function_missing_return_type_diagnoses() {
9998 let (ast, diags) = parse_str("function f() { }");
9999 assert!(!diags.is_empty());
10000 assert_eq!(diags[0].message, "expected `:` and return type");
10001 assert!(ast.top_level.is_empty());
10002 }
10003
10004 #[test]
10005 fn method_missing_return_type_still_reads_the_body() {
10006 let (_ast, diags) = parse_str(
10007 "class Box {\n async run() {\n const x = await f();\n return x;\n }\n ok(): void {}\n}",
10008 );
10009 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
10010 assert_eq!(
10011 messages,
10012 [
10013 "`async` functions are not supported",
10014 "expected `:` and return type",
10015 "`await` is not supported"
10016 ],
10017 "{diags:?}"
10018 );
10019 assert_eq!(
10020 diags[1].help,
10021 ["write the return type: `run(…): T { … }`, with `void` when it returns nothing"]
10022 );
10023 }
10024
10025 #[test]
10026 fn an_unclosed_method_body_leaves_the_next_declaration_alone() {
10027 let (ast, diags) =
10028 parse_str("class Box {\n a() { return 1;\n}\nfunction main(): number { return 0; }");
10029 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
10030 assert_eq!(
10031 messages,
10032 [
10033 "expected `:` and return type",
10034 "expected `}` to close class"
10035 ],
10036 "{diags:?}"
10037 );
10038 assert_eq!(ast.top_level.len(), 1, "`main` is still declared");
10039 }
10040
10041 #[test]
10042 fn every_declaration_after_an_unclosed_method_body_is_kept() {
10043 for next in [
10044 "class B { ok(): number { return 2; } }",
10045 "interface I { a: number; }",
10046 "enum E { A }",
10047 "type X = number;",
10048 "import * as fs from \"submilli:fs\";",
10049 "async function g(): number { return 1; }",
10050 ] {
10051 let source = format!(
10052 "class A {{\n bad() {{\n return 1;\n}}\n{next}\nfunction main(): void {{}}"
10053 );
10054 let (ast, diags) = parse_str(&source);
10055 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
10056 let expected_async = next.starts_with("async");
10057 let mut expected = vec![
10058 "expected `:` and return type",
10059 "expected `}` to close class",
10060 ];
10061 if expected_async {
10062 expected.push("`async` functions are not supported");
10063 }
10064 assert_eq!(messages, expected, "{next}: {diags:?}");
10065 assert_eq!(
10066 ast.top_level.len(),
10067 2,
10068 "{next} and `main` are kept: {diags:?}"
10069 );
10070 }
10071 }
10072
10073 #[test]
10074 fn a_failed_statement_leaves_the_next_declaration_to_be_parsed() {
10075 for (source, kept) in [
10076 ("let x = 1 +\nimport { exists } from \"submilli:fs\";", 1),
10077 ("const y =\nclass C {}\nfunction main(): void {}", 2),
10078 (
10079 "interface I {\n a: number;\nimport * as fs from \"submilli:fs\";",
10080 1,
10081 ),
10082 ] {
10083 let (ast, diags) = parse_str(source);
10084 assert_eq!(diags.len(), 1, "{source}: {diags:?}");
10085 assert_eq!(ast.top_level.len(), kept, "{source}: {diags:?}");
10086 }
10087 }
10088
10089 #[test]
10090 fn a_dynamic_import_after_a_failed_statement_reports_once() {
10091 for source in [
10092 "const m = import(\"submilli:fs\");\nclass C {}",
10093 "const d = import.meta.dirname;\nclass C {}",
10094 ] {
10095 let (_ast, diags) = parse_str(source);
10096 assert_eq!(diags.len(), 1, "{source}: {diags:?}");
10097 }
10098 }
10099
10100 #[test]
10101 fn members_named_like_declarations_after_a_bad_method_stay_members() {
10102 let (_ast, diags) = parse_str(
10103 "class A {\n bad() {}\n type: number = 1;\n function(): void {}\n declare(): void {}\n async(): void {}\n}",
10104 );
10105 assert_eq!(diags.len(), 1, "{diags:?}");
10106 }
10107
10108 #[test]
10109 fn every_member_after_a_bad_method_is_still_read() {
10110 let (_ast, diags) = parse_str(
10111 "class A {\n a() { return 1; }\n x: number = 1;\n get g(): number { return 1; }\n constructor() {}\n b() {}\n ok(): void {}\n}\nclass B {\n c() {}\n}",
10112 );
10113 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
10114 assert_eq!(messages, ["expected `:` and return type"; 3], "{diags:?}");
10115 }
10116
10117 #[test]
10118 fn a_member_failing_after_a_type_literal_reports_once() {
10119 let (_ast, diags) = parse_str("class A {\n x: { a: number } 5\n}");
10120 assert_eq!(diags.len(), 1, "{diags:?}");
10121 }
10122
10123 #[test]
10124 fn multiple_declarators_become_one_statement_each() {
10125 let (ast, diags) = parse_str("let a = 0, b: string = \"s\", [c] = [1];");
10126 assert!(diags.is_empty(), "{diags:?}");
10127 let kinds: Vec<&crate::StmtKind> = ast
10128 .top_level
10129 .iter()
10130 .map(|id| &ast.try_stmt(*id).unwrap().kind)
10131 .collect();
10132 assert!(
10133 matches!(
10134 kinds.as_slice(),
10135 [
10136 crate::StmtKind::Let { name: a, ty: None, .. },
10137 crate::StmtKind::Let { name: b, ty: Some(_), .. },
10138 crate::StmtKind::LetPattern { .. },
10139 ] if a.name == "a" && b.name == "b"
10140 ),
10141 "{kinds:?}"
10142 );
10143 }
10144
10145 #[test]
10146 fn declarators_keep_their_own_spans() {
10147 let source = "const a = 1, b = 2;";
10148 let (ast, diags) = parse_str(source);
10149 assert!(diags.is_empty(), "{diags:?}");
10150 let spans: Vec<&str> = ast
10151 .top_level
10152 .iter()
10153 .map(|id| {
10154 let span = ast.try_stmt(*id).unwrap().span;
10155 &source[span.start as usize..span.end as usize]
10156 })
10157 .collect();
10158 assert_eq!(spans, ["const a = 1", "b = 2;"]);
10159 }
10160
10161 #[test]
10162 fn a_declarator_missing_its_initializer_is_reported_at_it() {
10163 let (_ast, diags) = parse_str("const a = 1, b;");
10164 assert_eq!(diags.len(), 1, "{diags:?}");
10165 assert_eq!(
10166 diags[0].message,
10167 "`const` declaration requires an initializer"
10168 );
10169 assert_eq!(diags[0].span.start, 14, "{diags:?}");
10170 let (_ast, diags) = parse_str("let a = 1, ;");
10171 assert_eq!(
10172 diags[0].message, "expected identifier after `let`/`const`",
10173 "{diags:?}"
10174 );
10175 }
10176
10177 #[test]
10178 fn a_typed_declarator_without_a_value_may_precede_another() {
10179 let (ast, diags) = parse_str("let a: number | undefined, b = 1;");
10180 assert!(diags.is_empty(), "{diags:?}");
10181 assert_eq!(ast.top_level.len(), 2);
10182 }
10183
10184 #[test]
10185 fn declarators_stay_in_their_block() {
10186 let (ast, diags) = parse_str("function f(): void { let a = 1, b = 2; }\nlet c = 3;");
10187 assert!(diags.is_empty(), "{diags:?}");
10188 assert_eq!(
10189 ast.top_level.len(),
10190 2,
10191 "the block's declarators don't leak out"
10192 );
10193 }
10194
10195 #[test]
10196 fn every_exported_declarator_is_exported() {
10197 let (ast, diags) = parse_str("export const a = 1, b = 2;");
10198 assert!(diags.is_empty(), "{diags:?}");
10199 assert_eq!(ast.top_level.len(), 2);
10200 let exported: Vec<crate::StmtId> =
10201 ast.exported_decls.iter().map(|decl| decl.stmt).collect();
10202 assert_eq!(exported, ast.top_level);
10203 }
10204
10205 #[test]
10206 fn a_for_head_holds_every_declarator() {
10207 let (ast, diags) = parse_str("for (let i = 0, j = 10; i < j; i++, j--) {}");
10208 assert!(diags.is_empty(), "{diags:?}");
10209 let crate::StmtKind::For { init, update, .. } = &single_stmt(&ast).kind else {
10210 panic!("expected For");
10211 };
10212 assert_eq!(init.len(), 2);
10213 let (ast2, diags) = parse_str("for (i = 0, j = 10; i < j; i++) {}");
10214 assert!(diags.is_empty(), "{diags:?}");
10215 let crate::StmtKind::For { init: assigns, .. } = &single_stmt(&ast2).kind else {
10216 panic!("expected For");
10217 };
10218 assert_eq!(assigns.len(), 2);
10219 let update = ast.try_stmt(update.unwrap()).unwrap();
10220 assert!(
10221 matches!(&update.kind, crate::StmtKind::Block(stmts) if stmts.len() == 2),
10222 "{update:?}"
10223 );
10224 }
10225
10226 #[test]
10227 fn missing_return_type_still_reads_the_body() {
10228 let (_ast, diags) = parse_str(
10229 "async function main() {\n const x = await f();\n if (x) { return 1; }\n return 2;\n}\nfunction g(): void {}",
10230 );
10231 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
10232 assert_eq!(
10233 messages,
10234 [
10235 "`async` functions are not supported",
10236 "expected `:` and return type",
10237 "`await` is not supported"
10238 ],
10239 "{diags:?}"
10240 );
10241 assert_eq!(
10242 diags[1].help,
10243 [
10244 "write the return type: `function main(…): T { … }`, with `void` when it returns nothing"
10245 ]
10246 );
10247 }
10248
10249 #[test]
10250 fn function_missing_param_type_diagnoses() {
10251 let (ast, diags) = parse_str("function f(a): void { }");
10252 assert!(!diags.is_empty());
10253 assert_eq!(diags[0].message, "parameter requires a type annotation");
10254 assert!(ast.top_level.is_empty());
10255 }
10256
10257 #[test]
10258 fn function_missing_name_diagnoses() {
10259 let (ast, diags) = parse_str("function (x: number): void { }");
10260 assert!(!diags.is_empty());
10261 assert_eq!(diags[0].message, "expected function name");
10262 assert!(ast.top_level.is_empty());
10263 }
10264
10265 #[test]
10266 fn function_trailing_param_comma_ok() {
10267 let (ast, diags) = parse_str("function f(a: number,): void { }");
10268 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10269 match single_stmt(&ast).kind {
10270 crate::StmtKind::Function { ref params, .. } => assert_eq!(params.len(), 1),
10271 _ => panic!("expected Function"),
10272 }
10273 }
10274
10275 #[test]
10276 fn function_missing_open_brace_diagnoses() {
10277 let (ast, diags) = parse_str("function f(): void");
10278 assert_eq!(diags.len(), 1);
10279 assert_eq!(diags[0].message, "expected `{`");
10280 assert!(ast.top_level.is_empty());
10281 }
10282
10283 #[test]
10284 fn snapshot_function_declaration() {
10285 let (ast, diags) =
10286 parse_str("function add(a: number, b: number): number { let sum = a + b; sum; }");
10287 assert!(diags.is_empty());
10288 insta::assert_debug_snapshot!(ast);
10289 }
10290
10291 #[test]
10292 fn rest_param_basic_parses() {
10293 let (_, diags) = parse_str("function sum(...nums: number[]): number { return 0; }");
10294 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
10295 }
10296
10297 #[test]
10298 fn rest_param_with_fixed_prefix_parses() {
10299 let (_, diags) =
10300 parse_str("function tag(label: string, ...vals: number[]): string { return label; }");
10301 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
10302 }
10303
10304 #[test]
10305 fn rest_param_in_arrow_parses() {
10306 let (_, diags) = parse_str("const f = (...n: number[]) => n.length;");
10307 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
10308 }
10309
10310 #[test]
10311 fn rest_param_in_function_type_annotation_parses() {
10312 let (_, diags) =
10313 parse_str("let f: (a: number, ...rest: string[]) => void = (a, ...r) => {};");
10314 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
10315 }
10316
10317 #[test]
10318 fn rest_param_not_last_rejected() {
10319 let (_, diags) = parse_str("function f(...xs: number[], y: number): void {}");
10320 assert!(
10321 diags.iter().any(|d| d
10322 .message
10323 .contains("rest parameter must be the last parameter")),
10324 "expected 'must be last' diagnostic, got {diags:?}",
10325 );
10326 }
10327
10328 #[test]
10329 fn rest_param_with_default_rejected() {
10330 let (_, diags) = parse_str("function f(...xs: number[] = []): void {}");
10331 assert!(
10332 diags.iter().any(|d| d
10333 .message
10334 .contains("rest parameter cannot have a default value")),
10335 "expected default-rejection diagnostic, got {diags:?}",
10336 );
10337 }
10338
10339 #[test]
10340 fn rest_param_without_annotation_rejected() {
10341 let (_, diags) = parse_str("function f(...xs): void {}");
10342 assert!(
10343 diags.iter().any(|d| d
10344 .message
10345 .contains("rest parameter requires a type annotation")),
10346 "expected missing-annotation diagnostic, got {diags:?}",
10347 );
10348 }
10349
10350 #[test]
10351 fn rest_param_destructured_rejected() {
10352 let (_, diags) = parse_str("function f(...{a, b}): void {}");
10353 assert!(
10354 diags
10355 .iter()
10356 .any(|d| d.message.contains("rest parameter cannot be destructured")),
10357 "expected destructured-rejection diagnostic, got {diags:?}",
10358 );
10359 }
10360
10361 #[test]
10362 fn rest_param_in_function_type_with_non_array_rejected() {
10363 let (_, diags) = parse_str("let f: (...xs: number) => void = () => {};");
10364 assert!(
10365 diags
10366 .iter()
10367 .any(|d| d.message.contains("rest parameter type must be an array")),
10368 "expected non-array-type diagnostic, got {diags:?}",
10369 );
10370 }
10371
10372 fn call(e: &crate::Expr) -> (crate::ExprId, &[crate::ExprId]) {
10373 match e.kind {
10374 crate::ExprKind::Call {
10375 callee, ref args, ..
10376 } => (callee, args.as_slice()),
10377 _ => panic!("expected Call, got {:?}", e.kind),
10378 }
10379 }
10380
10381 #[test]
10382 fn parse_call_no_args() {
10383 let (ast, diags) = parse_str("f();");
10384 assert!(diags.is_empty());
10385 let outer = expr_of_single_stmt(&ast);
10386 let (callee, args) = call(outer);
10387 assert!(matches!(
10388 ast.try_expr(callee).unwrap().kind,
10389 crate::ExprKind::Identifier(_)
10390 ));
10391 assert!(args.is_empty());
10392 assert_eq!(outer.span, crate::Span::new(F, 0, 3).unwrap());
10393 }
10394
10395 #[test]
10396 fn parse_call_one_arg() {
10397 let (ast, diags) = parse_str("f(1);");
10398 assert!(diags.is_empty());
10399 let outer = expr_of_single_stmt(&ast);
10400 let (callee, args) = call(outer);
10401 assert!(matches!(
10402 ast.try_expr(callee).unwrap().kind,
10403 crate::ExprKind::Identifier(_)
10404 ));
10405 assert_eq!(args.len(), 1);
10406 assert_eq!(
10407 ast.try_expr(args[0]).unwrap().kind,
10408 crate::ExprKind::Number(1.0)
10409 );
10410 }
10411
10412 #[test]
10413 fn parse_call_three_args() {
10414 let (ast, diags) = parse_str("f(1, 2, 3);");
10415 assert!(diags.is_empty());
10416 let outer = expr_of_single_stmt(&ast);
10417 let (_callee, args) = call(outer);
10418 assert_eq!(args.len(), 3);
10419 assert_eq!(
10420 ast.try_expr(args[0]).unwrap().kind,
10421 crate::ExprKind::Number(1.0)
10422 );
10423 assert_eq!(
10424 ast.try_expr(args[1]).unwrap().kind,
10425 crate::ExprKind::Number(2.0)
10426 );
10427 assert_eq!(
10428 ast.try_expr(args[2]).unwrap().kind,
10429 crate::ExprKind::Number(3.0)
10430 );
10431 }
10432
10433 #[test]
10434 fn parse_call_nested() {
10435 let (ast, diags) = parse_str("f(g(x));");
10436 assert!(diags.is_empty());
10437 let outer = expr_of_single_stmt(&ast);
10438 let (_callee, args) = call(outer);
10439 assert_eq!(args.len(), 1);
10440 let (_inner_callee, inner_args) = call(ast.try_expr(args[0]).unwrap());
10441 assert_eq!(inner_args.len(), 1);
10442 assert!(matches!(
10443 ast.try_expr(inner_args[0]).unwrap().kind,
10444 crate::ExprKind::Identifier(_)
10445 ));
10446 }
10447
10448 #[test]
10449 fn parse_curried_call() {
10450 let (ast, diags) = parse_str("f()(x);");
10451 assert!(diags.is_empty());
10452 let outer = expr_of_single_stmt(&ast);
10453 let (callee, args) = call(outer);
10454 assert_eq!(args.len(), 1);
10455 let (_inner_callee, inner_args) = call(ast.try_expr(callee).unwrap());
10456 assert!(inner_args.is_empty());
10457 }
10458
10459 #[test]
10460 fn unary_binds_looser_than_call() {
10461 let (ast, diags) = parse_str("-f();");
10462 assert!(diags.is_empty());
10463 let outer = expr_of_single_stmt(&ast);
10464 let (op, operand) = unary(outer);
10465 assert_eq!(op, crate::UnOp::Neg);
10466 let (_callee, args) = call(ast.try_expr(operand).unwrap());
10467 assert!(args.is_empty());
10468 }
10469
10470 #[test]
10471 fn call_inside_binary() {
10472 let (ast, diags) = parse_str("f() + 1;");
10473 assert!(diags.is_empty());
10474 let outer = expr_of_single_stmt(&ast);
10475 let (op, lhs, rhs) = binary(outer);
10476 assert_eq!(op, crate::BinOp::Add);
10477 let (_callee, args) = call(ast.try_expr(lhs).unwrap());
10478 assert!(args.is_empty());
10479 assert_eq!(
10480 ast.try_expr(rhs).unwrap().kind,
10481 crate::ExprKind::Number(1.0)
10482 );
10483 }
10484
10485 #[test]
10486 fn call_with_complex_args() {
10487 let (ast, diags) = parse_str("f(1 + 2, g(x));");
10488 assert!(diags.is_empty());
10489 let outer = expr_of_single_stmt(&ast);
10490 let (_callee, args) = call(outer);
10491 assert_eq!(args.len(), 2);
10492 let (op, ..) = binary(ast.try_expr(args[0]).unwrap());
10493 assert_eq!(op, crate::BinOp::Add);
10494 let (_, inner_args) = call(ast.try_expr(args[1]).unwrap());
10495 assert_eq!(inner_args.len(), 1);
10496 }
10497
10498 #[test]
10499 fn call_trailing_comma_ok() {
10500 let (ast, diags) = parse_str("f(1, 2,);");
10501 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10502 let (_callee, args) = call(expr_of_single_stmt(&ast));
10503 assert_eq!(args.len(), 2);
10504 }
10505
10506 #[test]
10507 fn unclosed_call_diagnoses() {
10508 let (ast, diags) = parse_str("f(1, 2");
10509 assert!(!diags.is_empty());
10510 assert!(diags[0].message == "expected `,` or `)`" || diags[0].message == "expected `)`");
10511 assert!(ast.top_level.is_empty());
10512 }
10513
10514 #[test]
10515 fn call_expression_statement() {
10516 let (ast, diags) = parse_str(r#"greet("world");"#);
10517 assert!(diags.is_empty());
10518 let outer = expr_of_single_stmt(&ast);
10519 let (callee, args) = call(outer);
10520 assert!(matches!(
10521 ast.try_expr(callee).unwrap().kind,
10522 crate::ExprKind::Identifier(_)
10523 ));
10524 assert_eq!(args.len(), 1);
10525 assert_eq!(
10526 ast.try_expr(args[0]).unwrap().kind,
10527 crate::ExprKind::String("world".to_string())
10528 );
10529 }
10530
10531 #[test]
10532 fn snapshot_multi_call_program() {
10533 let (ast, diags) = parse_str("f(); g(1); h(f(2), 3);");
10534 assert!(diags.is_empty());
10535 assert_eq!(ast.top_level.len(), 3);
10536 insta::assert_debug_snapshot!(ast);
10537 }
10538
10539 #[test]
10540 fn parse_if_simple() {
10541 let (ast, diags) = parse_str("if (a) { }");
10542 assert!(diags.is_empty());
10543 let stmt = single_stmt(&ast);
10544 match stmt.kind {
10545 crate::StmtKind::If {
10546 condition,
10547 then_block,
10548 else_block,
10549 } => {
10550 assert!(matches!(
10551 ast.try_expr(condition).unwrap().kind,
10552 crate::ExprKind::Identifier(_)
10553 ));
10554 assert!(matches!(
10555 ast.try_stmt(then_block).unwrap().kind,
10556 crate::StmtKind::Block(ref v) if v.is_empty()
10557 ));
10558 assert!(else_block.is_none());
10559 }
10560 _ => panic!("expected If"),
10561 }
10562 }
10563
10564 #[test]
10565 fn parse_if_else() {
10566 let (ast, diags) = parse_str("if (a) { } else { }");
10567 assert!(diags.is_empty());
10568 let stmt = single_stmt(&ast);
10569 match stmt.kind {
10570 crate::StmtKind::If { else_block, .. } => {
10571 let else_id = else_block.expect("expected else block");
10572 assert!(matches!(
10573 ast.try_stmt(else_id).unwrap().kind,
10574 crate::StmtKind::Block(ref v) if v.is_empty()
10575 ));
10576 }
10577 _ => panic!("expected If"),
10578 }
10579 }
10580
10581 #[test]
10582 fn parse_else_if_chain() {
10583 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10584 assert!(diags.is_empty());
10585 let stmt = single_stmt(&ast);
10586 let crate::StmtKind::If {
10587 else_block: Some(inner_id),
10588 ..
10589 } = stmt.kind
10590 else {
10591 panic!("expected outer If with else_block");
10592 };
10593 let inner = ast.try_stmt(inner_id).unwrap();
10594 let crate::StmtKind::If {
10595 else_block: Some(tail_id),
10596 ..
10597 } = inner.kind
10598 else {
10599 panic!("expected inner If for `else if`");
10600 };
10601 assert!(matches!(
10602 ast.try_stmt(tail_id).unwrap().kind,
10603 crate::StmtKind::Block(_)
10604 ));
10605 }
10606
10607 #[test]
10608 fn parse_if_with_body_statements() {
10609 let (ast, diags) = parse_str("if (a) { let x = 1; }");
10610 assert!(diags.is_empty());
10611 let stmt = single_stmt(&ast);
10612 match stmt.kind {
10613 crate::StmtKind::If { then_block, .. } => {
10614 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind
10615 else {
10616 panic!("expected Block")
10617 };
10618 assert_eq!(body.len(), 1);
10619 }
10620 _ => panic!("expected If"),
10621 }
10622 }
10623
10624 #[test]
10625 fn parse_if_braceless_body() {
10626 let (ast, diags) = parse_str("if (a) return b;");
10627 assert!(diags.is_empty(), "{diags:?}");
10628 let stmt = single_stmt(&ast);
10629 let crate::StmtKind::If {
10630 then_block,
10631 else_block,
10632 ..
10633 } = stmt.kind
10634 else {
10635 panic!("expected If");
10636 };
10637 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10638 panic!("braceless body should wrap in a Block");
10639 };
10640 assert_eq!(body.len(), 1);
10641 assert!(matches!(
10642 ast.try_stmt(body[0]).unwrap().kind,
10643 crate::StmtKind::Return(_)
10644 ));
10645 assert!(else_block.is_none());
10646 }
10647
10648 #[test]
10649 fn parse_braceless_dangling_else_binds_nearest_if() {
10650 let (ast, diags) = parse_str("if (a) if (b) x(); else y();");
10652 assert!(diags.is_empty(), "{diags:?}");
10653 let crate::StmtKind::If {
10654 then_block,
10655 else_block: outer_else,
10656 ..
10657 } = single_stmt(&ast).kind
10658 else {
10659 panic!("expected outer If");
10660 };
10661 assert!(outer_else.is_none(), "else must bind to the inner if");
10662 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10663 panic!("expected wrapped Block");
10664 };
10665 let crate::StmtKind::If {
10666 else_block: Some(_),
10667 ..
10668 } = ast.try_stmt(body[0]).unwrap().kind
10669 else {
10670 panic!("inner If should own the else");
10671 };
10672 }
10673
10674 #[test]
10675 fn parse_while_braceless_body() {
10676 let (ast, diags) = parse_str("while (a) x();");
10677 assert!(diags.is_empty(), "{diags:?}");
10678 let crate::StmtKind::While { body, .. } = single_stmt(&ast).kind else {
10679 panic!("expected While");
10680 };
10681 assert!(matches!(
10682 ast.try_stmt(body).unwrap().kind,
10683 crate::StmtKind::Block(ref v) if v.len() == 1
10684 ));
10685 }
10686
10687 #[test]
10688 fn parse_loop_with_empty_body() {
10689 for src in [
10690 "while (a());",
10691 "for (const x of xs);",
10692 "for (let i = 0; i < 3; i++);",
10693 "do ; while (a());",
10694 ] {
10695 let (ast, diags) = parse_str(src);
10696 assert!(diags.is_empty(), "{src}: {diags:?}");
10697 let body = match single_stmt(&ast).kind {
10698 crate::StmtKind::While { body, .. }
10699 | crate::StmtKind::DoWhile { body, .. }
10700 | crate::StmtKind::For { body, .. }
10701 | crate::StmtKind::ForOf { body, .. } => body,
10702 ref other => panic!("{src}: expected a loop, got {other:?}"),
10703 };
10704 assert!(
10705 matches!(ast.try_stmt(body).unwrap().kind, crate::StmtKind::Block(ref v) if v.is_empty()),
10706 "{src}"
10707 );
10708 }
10709 }
10710
10711 #[test]
10712 fn parse_if_with_empty_body_is_an_error() {
10713 let (_ast, diags) = parse_str("if (a);");
10714 assert_eq!(diags[0].message, "`if` has an empty body");
10715 }
10716
10717 #[test]
10718 fn parse_empty_branch_body_reports_once() {
10719 let (_ast, diags) = parse_str("if (a) ; else b();");
10720 assert_eq!(diags.len(), 1, "{diags:?}");
10721 assert_eq!(diags[0].message, "`if` has an empty body");
10722 let (_ast, diags) = parse_str("if (a) b(); else ;");
10723 assert_eq!(diags.len(), 1, "{diags:?}");
10724 assert_eq!(diags[0].message, "`else` has an empty body");
10725 let (_ast, diags) = parse_str("if (a) ; else if (b) ; else ;");
10727 let messages: Vec<_> = diags.iter().map(|d| d.message.as_str()).collect();
10728 assert_eq!(
10729 messages,
10730 [
10731 "`if` has an empty body",
10732 "`if` has an empty body",
10733 "`else` has an empty body"
10734 ]
10735 );
10736 }
10737
10738 #[test]
10739 fn parse_for_of_braceless_body() {
10740 let (ast, diags) = parse_str("for (const x of xs) f(x);");
10741 assert!(diags.is_empty(), "{diags:?}");
10742 let crate::StmtKind::ForOf { body, .. } = single_stmt(&ast).kind else {
10743 panic!("expected ForOf");
10744 };
10745 assert!(matches!(
10746 ast.try_stmt(body).unwrap().kind,
10747 crate::StmtKind::Block(ref v) if v.len() == 1
10748 ));
10749 }
10750
10751 #[test]
10752 fn parse_nested_if() {
10753 let (ast, diags) = parse_str("if (a) { if (b) { } }");
10754 assert!(diags.is_empty());
10755 let outer = single_stmt(&ast);
10756 let crate::StmtKind::If { then_block, .. } = outer.kind else {
10757 panic!("expected outer If");
10758 };
10759 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10760 panic!("expected Block")
10761 };
10762 assert_eq!(body.len(), 1);
10763 assert!(matches!(
10764 ast.try_stmt(body[0]).unwrap().kind,
10765 crate::StmtKind::If { .. }
10766 ));
10767 }
10768
10769 #[test]
10770 fn if_missing_open_paren_diagnoses() {
10771 let (ast, diags) = parse_str("if a { }");
10772 assert!(!diags.is_empty());
10773 assert_eq!(diags[0].message, "expected `(`");
10774 assert!(ast.top_level.is_empty());
10775 }
10776
10777 #[test]
10778 fn if_missing_close_paren_diagnoses() {
10779 let (ast, diags) = parse_str("if (a { }");
10780 assert!(!diags.is_empty());
10781 assert_eq!(diags[0].message, "expected `)`");
10782 assert!(ast.top_level.is_empty());
10783 }
10784
10785 #[test]
10786 fn if_braceless_body_parses() {
10787 let (ast, diags) = parse_str("if (a) x;");
10788 assert!(diags.is_empty(), "{diags:?}");
10789 let crate::StmtKind::If { then_block, .. } = single_stmt(&ast).kind else {
10790 panic!("expected If");
10791 };
10792 assert!(matches!(
10793 ast.try_stmt(then_block).unwrap().kind,
10794 crate::StmtKind::Block(ref v) if v.len() == 1
10795 ));
10796 }
10797
10798 #[test]
10799 fn snapshot_else_if_chain() {
10800 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10801 assert!(diags.is_empty());
10802 insta::assert_debug_snapshot!(ast);
10803 }
10804
10805 #[test]
10806 fn parse_while_simple() {
10807 let (ast, diags) = parse_str("while (a) { }");
10808 assert!(diags.is_empty());
10809 let stmt = single_stmt(&ast);
10810 match stmt.kind {
10811 crate::StmtKind::While { condition, body } => {
10812 assert!(matches!(
10813 ast.try_expr(condition).unwrap().kind,
10814 crate::ExprKind::Identifier(_)
10815 ));
10816 assert!(matches!(
10817 ast.try_stmt(body).unwrap().kind,
10818 crate::StmtKind::Block(ref v) if v.is_empty()
10819 ));
10820 }
10821 _ => panic!("expected While"),
10822 }
10823 }
10824
10825 #[test]
10826 fn parse_while_with_body() {
10827 let (ast, diags) = parse_str("while (a) { x; y; }");
10828 assert!(diags.is_empty());
10829 let stmt = single_stmt(&ast);
10830 let crate::StmtKind::While { body, .. } = stmt.kind else {
10831 panic!("expected While");
10832 };
10833 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10834 panic!("expected Block")
10835 };
10836 assert_eq!(stmts.len(), 2);
10837 }
10838
10839 #[test]
10840 fn while_missing_paren_diagnoses() {
10841 let (ast, diags) = parse_str("while a { }");
10842 assert!(!diags.is_empty());
10843 assert_eq!(diags[0].message, "expected `(`");
10844 assert!(ast.top_level.is_empty());
10845 }
10846
10847 #[test]
10848 fn parse_for_all_slots() {
10849 let (ast, diags) = parse_str("for (let i = 0; i < 10; i = i + 1) { x; }");
10850 assert!(diags.is_empty(), "{diags:?}");
10851 let stmt = single_stmt(&ast);
10852 let crate::StmtKind::For {
10853 init,
10854 condition,
10855 update,
10856 body,
10857 } = &stmt.kind
10858 else {
10859 panic!("expected For");
10860 };
10861 assert!(!init.is_empty(), "init missing");
10862 assert!(condition.is_some(), "condition missing");
10863 assert!(update.is_some(), "update missing");
10864 assert!(matches!(
10865 ast.try_stmt(*body).unwrap().kind,
10866 crate::StmtKind::Block(_)
10867 ));
10868 }
10869
10870 #[test]
10871 fn parse_for_empty_slots() {
10872 let (ast, diags) = parse_str("for (;;) { break; }");
10873 assert!(diags.is_empty(), "{diags:?}");
10874 let stmt = single_stmt(&ast);
10875 let crate::StmtKind::For {
10876 init,
10877 condition,
10878 update,
10879 ..
10880 } = &stmt.kind
10881 else {
10882 panic!("expected For");
10883 };
10884 assert!(init.is_empty() && condition.is_none() && update.is_none());
10885 }
10886
10887 #[test]
10888 fn parse_for_of_const_binding() {
10889 let (ast, diags) = parse_str("for (const x of arr) { y; }");
10890 assert!(diags.is_empty(), "{diags:?}");
10891 let stmt = single_stmt(&ast);
10892 let crate::StmtKind::ForOf {
10893 binding_kind,
10894 ref name,
10895 ref ty,
10896 ..
10897 } = stmt.kind
10898 else {
10899 panic!("expected ForOf");
10900 };
10901 assert!(matches!(binding_kind, crate::BindingKind::Const));
10902 assert_eq!(name.name, "x");
10903 assert!(ty.is_none());
10904 }
10905
10906 #[test]
10907 fn parse_for_of_let_with_annotation() {
10908 let (ast, diags) = parse_str("for (let x: number of arr) { y; }");
10909 assert!(diags.is_empty(), "{diags:?}");
10910 let stmt = single_stmt(&ast);
10911 let crate::StmtKind::ForOf {
10912 binding_kind,
10913 ref ty,
10914 ..
10915 } = stmt.kind
10916 else {
10917 panic!("expected ForOf");
10918 };
10919 assert!(matches!(binding_kind, crate::BindingKind::Let));
10920 assert!(ty.is_some());
10921 }
10922
10923 #[test]
10924 fn parse_do_while_simple() {
10925 let (ast, diags) = parse_str("do { x; } while (a);");
10926 assert!(diags.is_empty(), "{diags:?}");
10927 let stmt = single_stmt(&ast);
10928 assert!(matches!(stmt.kind, crate::StmtKind::DoWhile { .. }));
10929 }
10930
10931 #[test]
10932 fn parse_break_and_continue() {
10933 let (ast, diags) = parse_str("while (a) { break; continue; }");
10934 assert!(diags.is_empty(), "{diags:?}");
10935 let stmt = single_stmt(&ast);
10936 let crate::StmtKind::While { body, .. } = stmt.kind else {
10937 panic!("expected While");
10938 };
10939 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10940 panic!("expected Block");
10941 };
10942 assert!(matches!(
10943 ast.try_stmt(stmts[0]).unwrap().kind,
10944 crate::StmtKind::Break
10945 ));
10946 assert!(matches!(
10947 ast.try_stmt(stmts[1]).unwrap().kind,
10948 crate::StmtKind::Continue
10949 ));
10950 }
10951
10952 #[test]
10953 fn parse_bare_return_in_function() {
10954 let (ast, diags) = parse_str("function f(): void { return; }");
10955 assert!(diags.is_empty());
10956 let f = single_stmt(&ast);
10957 let crate::StmtKind::Function { body, .. } = f.kind else {
10958 panic!("expected Function");
10959 };
10960 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10961 panic!("expected Block")
10962 };
10963 assert_eq!(stmts.len(), 1);
10964 match ast.try_stmt(stmts[0]).unwrap().kind {
10965 crate::StmtKind::Return(None) => {}
10966 _ => panic!("expected Return(None)"),
10967 }
10968 }
10969
10970 #[test]
10971 fn parse_return_with_value() {
10972 let (ast, diags) = parse_str("function f(): number { return 1; }");
10973 assert!(diags.is_empty());
10974 let f = single_stmt(&ast);
10975 let crate::StmtKind::Function { body, .. } = f.kind else {
10976 panic!("expected Function");
10977 };
10978 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10979 panic!("expected Block")
10980 };
10981 match ast.try_stmt(stmts[0]).unwrap().kind {
10982 crate::StmtKind::Return(Some(value)) => {
10983 assert_eq!(
10984 ast.try_expr(value).unwrap().kind,
10985 crate::ExprKind::Number(1.0)
10986 );
10987 }
10988 _ => panic!("expected Return(Some)"),
10989 }
10990 }
10991
10992 #[test]
10993 fn parse_return_with_expression() {
10994 let (ast, diags) = parse_str("function f(): number { return 1 + 2; }");
10995 assert!(diags.is_empty());
10996 let f = single_stmt(&ast);
10997 let crate::StmtKind::Function { body, .. } = f.kind else {
10998 panic!("expected Function");
10999 };
11000 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
11001 panic!("expected Block")
11002 };
11003 match ast.try_stmt(stmts[0]).unwrap().kind {
11004 crate::StmtKind::Return(Some(value)) => {
11005 let (op, ..) = binary(ast.try_expr(value).unwrap());
11006 assert_eq!(op, crate::BinOp::Add);
11007 }
11008 _ => panic!("expected Return(Some(Binary))"),
11009 }
11010 }
11011
11012 #[test]
11013 fn return_missing_semicolon_diagnoses() {
11014 let (_, diags) = parse_str("function f(): number { return x x; }");
11015 assert!(!diags.is_empty());
11016 assert_eq!(diags[0].message, "expected `;` after return");
11017 }
11018
11019 #[test]
11020 fn parse_bare_block() {
11021 let (ast, diags) = parse_str("{ x; y; }");
11022 assert!(diags.is_empty());
11023 let stmt = single_stmt(&ast);
11024 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
11025 panic!("expected Block");
11026 };
11027 assert_eq!(stmts.len(), 2);
11028 }
11029
11030 #[test]
11031 fn parse_empty_bare_block() {
11032 let (ast, diags) = parse_str("{ }");
11033 assert!(diags.is_empty());
11034 let stmt = single_stmt(&ast);
11035 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
11036 panic!("expected Block");
11037 };
11038 assert!(stmts.is_empty());
11039 }
11040
11041 #[test]
11042 fn parse_nested_bare_blocks() {
11043 let (ast, diags) = parse_str("{ { } }");
11044 assert!(diags.is_empty());
11045 let outer = single_stmt(&ast);
11046 let crate::StmtKind::Block(ref stmts) = outer.kind else {
11047 panic!("expected outer Block");
11048 };
11049 assert_eq!(stmts.len(), 1);
11050 assert!(matches!(
11051 ast.try_stmt(stmts[0]).unwrap().kind,
11052 crate::StmtKind::Block(ref v) if v.is_empty()
11053 ));
11054 }
11055
11056 #[test]
11057 fn snapshot_function_with_if_and_return() {
11058 let source = "function isPositive(n: number): boolean {\n if (n > 0) {\n return true;\n } else {\n return false;\n }\n}";
11059 let (ast, diags) = parse_str(source);
11060 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11061 insta::assert_debug_snapshot!(ast);
11062 }
11063
11064 fn object_literal(e: &crate::Expr) -> &[crate::ObjectLiteralMember] {
11065 match e.kind {
11066 crate::ExprKind::ObjectLiteral { ref members } => members.as_slice(),
11067 _ => panic!("expected ObjectLiteral, got {:?}", e.kind),
11068 }
11069 }
11070
11071 fn object_literal_field(m: &crate::ObjectLiteralMember) -> &crate::ObjectLiteralField {
11072 match m {
11073 crate::ObjectLiteralMember::Field(f) => f,
11074 crate::ObjectLiteralMember::Spread { .. }
11075 | crate::ObjectLiteralMember::Computed { .. } => {
11076 panic!("expected Field member, got Spread")
11077 }
11078 }
11079 }
11080
11081 fn array_literal(e: &crate::Expr) -> &[crate::ArrayLiteralElement] {
11082 match e.kind {
11083 crate::ExprKind::ArrayLiteral { ref elements } => elements.as_slice(),
11084 _ => panic!("expected ArrayLiteral, got {:?}", e.kind),
11085 }
11086 }
11087
11088 fn array_literal_value(el: &crate::ArrayLiteralElement) -> crate::ExprId {
11089 match el {
11090 crate::ArrayLiteralElement::Value(id) => *id,
11091 crate::ArrayLiteralElement::Spread { .. } => {
11092 panic!("expected Value element, got Spread")
11093 }
11094 }
11095 }
11096
11097 #[test]
11098 fn parse_empty_object_literal() {
11099 let (ast, diags) = parse_str("let x = {};");
11100 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11101 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11102 panic!("expected Let");
11103 };
11104 assert!(object_literal(ast.try_expr(value).unwrap()).is_empty());
11105 }
11106
11107 #[test]
11108 fn parse_object_literal_simple() {
11109 let (ast, diags) = parse_str("let p = { x: 1, y: 2 };");
11110 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11111 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11112 panic!("expected Let");
11113 };
11114 let members = object_literal(ast.try_expr(value).unwrap());
11115 assert_eq!(members.len(), 2);
11116 let f0 = object_literal_field(&members[0]);
11117 let f1 = object_literal_field(&members[1]);
11118 assert_eq!(f0.name.name, "x");
11119 assert_eq!(f1.name.name, "y");
11120 assert_eq!(
11121 ast.try_expr(f0.value).unwrap().kind,
11122 crate::ExprKind::Number(1.0)
11123 );
11124 assert_eq!(
11125 ast.try_expr(f1.value).unwrap().kind,
11126 crate::ExprKind::Number(2.0)
11127 );
11128 }
11129
11130 #[test]
11131 fn parse_object_literal_keyword_keys() {
11132 let (ast, diags) = parse_str("let p = { type: 1, default: 2, null: 3, true: 4 };");
11133 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11134 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11135 panic!("expected Let");
11136 };
11137 let names: Vec<&str> = object_literal(ast.try_expr(value).unwrap())
11138 .iter()
11139 .map(object_literal_field)
11140 .map(|f| f.name.name.as_str())
11141 .collect();
11142 assert_eq!(names, vec!["type", "default", "null", "true"]);
11143 }
11144
11145 #[test]
11146 fn parse_object_literal_string_key() {
11147 let (ast, diags) = parse_str(r#"let p = { "hello": 1 };"#);
11148 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11149 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11150 panic!("expected Let");
11151 };
11152 let members = object_literal(ast.try_expr(value).unwrap());
11153 assert_eq!(members.len(), 1);
11154 let f0 = object_literal_field(&members[0]);
11155 assert_eq!(f0.name.name, "hello");
11156 }
11157
11158 #[test]
11159 fn parse_object_literal_string_key_decodes_escape() {
11160 let (ast, diags) = parse_str(r#"let p = { "content\u002dtype": 1 };"#);
11161 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11162 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11163 panic!("expected Let");
11164 };
11165 let members = object_literal(ast.try_expr(value).unwrap());
11166 assert_eq!(members.len(), 1);
11167 let f0 = object_literal_field(&members[0]);
11168 assert_eq!(f0.name.name, "content-type");
11169 }
11170
11171 #[test]
11172 fn parse_object_literal_shorthand() {
11173 let (ast, diags) = parse_str("let p = { x, y: 2 };");
11174 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11175 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11176 panic!("expected Let");
11177 };
11178 let members = object_literal(ast.try_expr(value).unwrap());
11179 assert_eq!(members.len(), 2);
11180 let f0 = object_literal_field(&members[0]);
11181 assert_eq!(f0.name.name, "x");
11182 assert_eq!(
11184 ast.try_expr(f0.value).unwrap().kind,
11185 crate::ExprKind::Identifier(crate::Ident {
11186 name: "x".into(),
11187 span: f0.name.span,
11188 })
11189 );
11190 let f1 = object_literal_field(&members[1]);
11191 assert_eq!(f1.name.name, "y");
11192 assert_eq!(
11193 ast.try_expr(f1.value).unwrap().kind,
11194 crate::ExprKind::Number(2.0)
11195 );
11196 }
11197
11198 #[test]
11199 fn parse_object_literal_string_key_shorthand_rejected() {
11200 let (_ast, diags) = parse_str(r#"let p = { "x" };"#);
11201 assert!(
11202 diags
11203 .iter()
11204 .any(|d| d.message.contains("expected `:` after field name")),
11205 "string-key shorthand should be rejected: {diags:?}"
11206 );
11207 }
11208
11209 #[test]
11210 fn parse_object_literal_trailing_comma() {
11211 let (ast, diags) = parse_str("let p = { x: 1, y: 2, };");
11212 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11213 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11214 panic!("expected Let");
11215 };
11216 assert_eq!(object_literal(ast.try_expr(value).unwrap()).len(), 2);
11217 }
11218
11219 #[test]
11220 fn parse_object_literal_duplicate_key_diagnoses() {
11221 let (_, diags) = parse_str("let p = { x: 1, x: 2 };");
11222 assert_eq!(diags.len(), 1);
11223 assert_eq!(diags[0].message, "duplicate field `x` in object literal");
11224 }
11225
11226 #[test]
11227 fn parse_empty_array_literal() {
11228 let (ast, diags) = parse_str("let xs = [];");
11229 assert!(diags.is_empty());
11230 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11231 panic!("expected Let");
11232 };
11233 assert!(array_literal(ast.try_expr(value).unwrap()).is_empty());
11234 }
11235
11236 #[test]
11237 fn parse_array_literal_simple() {
11238 let (ast, diags) = parse_str("let xs = [1, 2, 3];");
11239 assert!(diags.is_empty());
11240 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11241 panic!("expected Let");
11242 };
11243 let elements = array_literal(ast.try_expr(value).unwrap());
11244 assert_eq!(elements.len(), 3);
11245 assert_eq!(
11246 ast.try_expr(array_literal_value(&elements[0]))
11247 .unwrap()
11248 .kind,
11249 crate::ExprKind::Number(1.0)
11250 );
11251 assert_eq!(
11252 ast.try_expr(array_literal_value(&elements[2]))
11253 .unwrap()
11254 .kind,
11255 crate::ExprKind::Number(3.0)
11256 );
11257 }
11258
11259 #[test]
11260 fn parse_array_literal_trailing_comma() {
11261 let (ast, diags) = parse_str("let xs = [1, 2,];");
11262 assert!(diags.is_empty());
11263 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11264 panic!("expected Let");
11265 };
11266 assert_eq!(array_literal(ast.try_expr(value).unwrap()).len(), 2);
11267 }
11268
11269 #[test]
11270 fn parse_nested_array_in_object() {
11271 let (ast, diags) = parse_str("let p = { items: [1, 2] };");
11272 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11273 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11274 panic!("expected Let");
11275 };
11276 let members = object_literal(ast.try_expr(value).unwrap());
11277 let f0 = object_literal_field(&members[0]);
11278 assert_eq!(f0.name.name, "items");
11279 let inner = array_literal(ast.try_expr(f0.value).unwrap());
11280 assert_eq!(inner.len(), 2);
11281 }
11282
11283 #[test]
11284 fn parse_nested_object_in_array() {
11285 let (ast, diags) = parse_str("let xs = [{ x: 1 }, { x: 2 }];");
11286 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11287 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11288 panic!("expected Let");
11289 };
11290 let elements = array_literal(ast.try_expr(value).unwrap());
11291 assert_eq!(elements.len(), 2);
11292 assert_eq!(
11293 object_literal(ast.try_expr(array_literal_value(&elements[0])).unwrap()).len(),
11294 1
11295 );
11296 assert_eq!(
11297 object_literal(ast.try_expr(array_literal_value(&elements[1])).unwrap()).len(),
11298 1
11299 );
11300 }
11301
11302 #[test]
11303 fn brace_at_statement_start_is_block_not_object() {
11304 let (_, diags) = parse_str("{ x: 1 }");
11305 assert!(
11306 !diags.is_empty(),
11307 "expected a diagnostic for `x:` inside block"
11308 );
11309 }
11310
11311 fn field_access(e: &crate::Expr) -> (crate::ExprId, &str) {
11312 match e.kind {
11313 crate::ExprKind::FieldAccess { receiver, ref name } => (receiver, name.name.as_str()),
11314 _ => panic!("expected FieldAccess, got {:?}", e.kind),
11315 }
11316 }
11317
11318 fn index_access(e: &crate::Expr) -> (crate::ExprId, crate::ExprId) {
11319 match e.kind {
11320 crate::ExprKind::IndexAccess { receiver, index } => (receiver, index),
11321 _ => panic!("expected IndexAccess, got {:?}", e.kind),
11322 }
11323 }
11324
11325 #[test]
11326 fn parse_field_access_simple() {
11327 let (ast, diags) = parse_str("a.b;");
11328 assert!(diags.is_empty());
11329 let outer = expr_of_single_stmt(&ast);
11330 let (receiver, name) = field_access(outer);
11331 assert!(matches!(
11332 ast.try_expr(receiver).unwrap().kind,
11333 crate::ExprKind::Identifier(_)
11334 ));
11335 assert_eq!(name, "b");
11336 }
11337
11338 #[test]
11339 fn parse_field_access_keyword_name() {
11340 let (ast, diags) = parse_str("status.type.default.null;");
11341 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11342 let outer = expr_of_single_stmt(&ast);
11343 let (receiver, name) = field_access(outer);
11344 assert_eq!(name, "null");
11345 let (receiver, name) = field_access(ast.try_expr(receiver).unwrap());
11346 assert_eq!(name, "default");
11347 let (_receiver, name) = field_access(ast.try_expr(receiver).unwrap());
11348 assert_eq!(name, "type");
11349 }
11350
11351 #[test]
11352 fn parse_field_access_chain_left_assoc() {
11353 let (ast, diags) = parse_str("a.b.c;");
11354 assert!(diags.is_empty());
11355 let outer = expr_of_single_stmt(&ast);
11356 let (mid_id, c) = field_access(outer);
11357 assert_eq!(c, "c");
11358 let (a_id, b) = field_access(ast.try_expr(mid_id).unwrap());
11359 assert_eq!(b, "b");
11360 assert!(matches!(
11361 ast.try_expr(a_id).unwrap().kind,
11362 crate::ExprKind::Identifier(_)
11363 ));
11364 }
11365
11366 #[test]
11367 fn parse_index_access_simple() {
11368 let (ast, diags) = parse_str("a[0];");
11369 assert!(diags.is_empty());
11370 let outer = expr_of_single_stmt(&ast);
11371 let (receiver, idx) = index_access(outer);
11372 assert!(matches!(
11373 ast.try_expr(receiver).unwrap().kind,
11374 crate::ExprKind::Identifier(_)
11375 ));
11376 assert_eq!(
11377 ast.try_expr(idx).unwrap().kind,
11378 crate::ExprKind::Number(0.0)
11379 );
11380 }
11381
11382 #[test]
11383 fn parse_index_access_chain() {
11384 let (ast, diags) = parse_str("a[0][1];");
11385 assert!(diags.is_empty());
11386 let outer = expr_of_single_stmt(&ast);
11387 let (mid_id, one) = index_access(outer);
11388 assert_eq!(
11389 ast.try_expr(one).unwrap().kind,
11390 crate::ExprKind::Number(1.0)
11391 );
11392 let (a_id, zero) = index_access(ast.try_expr(mid_id).unwrap());
11393 assert_eq!(
11394 ast.try_expr(zero).unwrap().kind,
11395 crate::ExprKind::Number(0.0)
11396 );
11397 assert!(matches!(
11398 ast.try_expr(a_id).unwrap().kind,
11399 crate::ExprKind::Identifier(_)
11400 ));
11401 }
11402
11403 #[test]
11404 fn parse_mixed_access_and_call_chain() {
11405 let (ast, diags) = parse_str("a.b[c].d();");
11406 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11407 let outer = expr_of_single_stmt(&ast);
11408 let (callee, args) = call(outer);
11409 assert!(args.is_empty());
11410 let (idx_id, d_name) = field_access(ast.try_expr(callee).unwrap());
11411 assert_eq!(d_name, "d");
11412 let (b_id, c_idx) = index_access(ast.try_expr(idx_id).unwrap());
11413 assert!(matches!(
11414 ast.try_expr(c_idx).unwrap().kind,
11415 crate::ExprKind::Identifier(_)
11416 ));
11417 let (a_id, b_name) = field_access(ast.try_expr(b_id).unwrap());
11418 assert_eq!(b_name, "b");
11419 assert!(matches!(
11420 ast.try_expr(a_id).unwrap().kind,
11421 crate::ExprKind::Identifier(_)
11422 ));
11423 }
11424
11425 #[test]
11426 fn parse_call_then_field() {
11427 let (ast, diags) = parse_str("f().x;");
11428 assert!(diags.is_empty());
11429 let outer = expr_of_single_stmt(&ast);
11430 let (call_id, x_name) = field_access(outer);
11431 assert_eq!(x_name, "x");
11432 let (_callee, args) = call(ast.try_expr(call_id).unwrap());
11433 assert!(args.is_empty());
11434 }
11435
11436 #[test]
11437 fn parse_index_with_complex_expression() {
11438 let (ast, diags) = parse_str("a[i + 1];");
11439 assert!(diags.is_empty());
11440 let outer = expr_of_single_stmt(&ast);
11441 let (_, idx) = index_access(outer);
11442 let (op, ..) = binary(ast.try_expr(idx).unwrap());
11443 assert_eq!(op, crate::BinOp::Add);
11444 }
11445
11446 #[test]
11447 fn field_access_missing_name_diagnoses() {
11448 let (_, diags) = parse_str("a.;");
11449 assert!(!diags.is_empty());
11450 assert_eq!(diags[0].message, "expected field name after `.`");
11451 }
11452
11453 #[test]
11454 fn parse_postfix_increment() {
11455 let (ast, diags) = parse_str("x++;");
11456 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11457 let expr = expr_of_single_stmt(&ast);
11458 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
11459 panic!("expected PostfixUnary, got {:?}", expr.kind);
11460 };
11461 assert_eq!(op, crate::PostfixOp::Inc);
11462 assert!(matches!(
11463 ast.try_expr(operand).unwrap().kind,
11464 crate::ExprKind::Identifier(_)
11465 ));
11466 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
11467 }
11468
11469 #[test]
11470 fn parse_postfix_decrement() {
11471 let (ast, diags) = parse_str("x--;");
11472 assert!(diags.is_empty());
11473 let expr = expr_of_single_stmt(&ast);
11474 let crate::ExprKind::PostfixUnary { op, .. } = expr.kind else {
11475 panic!("expected PostfixUnary");
11476 };
11477 assert_eq!(op, crate::PostfixOp::Dec);
11478 }
11479
11480 #[test]
11481 fn parse_postfix_non_null_assertion() {
11482 let (ast, diags) = parse_str("x!;");
11483 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11484 let expr = expr_of_single_stmt(&ast);
11485 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
11486 panic!("expected PostfixUnary, got {:?}", expr.kind);
11487 };
11488 assert_eq!(op, crate::PostfixOp::NonNullAssert);
11489 assert!(matches!(
11490 ast.try_expr(operand).unwrap().kind,
11491 crate::ExprKind::Identifier(_)
11492 ));
11493 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
11494 }
11495
11496 #[test]
11497 fn parse_non_null_assertion_continues_chain() {
11498 let (ast, diags) = parse_str("x!.y;");
11499 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11500 let expr = expr_of_single_stmt(&ast);
11501 let crate::ExprKind::FieldAccess { receiver, name } = &expr.kind else {
11502 panic!("expected FieldAccess, got {:?}", expr.kind);
11503 };
11504 assert_eq!(name.name, "y");
11505 assert!(matches!(
11506 ast.try_expr(*receiver).unwrap().kind,
11507 crate::ExprKind::PostfixUnary {
11508 op: crate::PostfixOp::NonNullAssert,
11509 ..
11510 }
11511 ));
11512 }
11513
11514 #[test]
11515 fn parse_postfix_on_field_access() {
11516 let (ast, diags) = parse_str("o.f++;");
11517 assert!(diags.is_empty());
11518 let expr = expr_of_single_stmt(&ast);
11519 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
11520 panic!("expected PostfixUnary");
11521 };
11522 assert!(matches!(
11523 ast.try_expr(operand).unwrap().kind,
11524 crate::ExprKind::FieldAccess { .. }
11525 ));
11526 }
11527
11528 #[test]
11529 fn parse_postfix_on_index_access() {
11530 let (ast, diags) = parse_str("xs[0]++;");
11531 assert!(diags.is_empty());
11532 let expr = expr_of_single_stmt(&ast);
11533 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
11534 panic!("expected PostfixUnary");
11535 };
11536 assert!(matches!(
11537 ast.try_expr(operand).unwrap().kind,
11538 crate::ExprKind::IndexAccess { .. }
11539 ));
11540 }
11541
11542 #[test]
11543 fn parse_postfix_terminates_chain() {
11544 let (_, diags) = parse_str("x++.y;");
11545 assert!(!diags.is_empty());
11546 assert_eq!(diags[0].message, "expected `;` after expression");
11547 }
11548
11549 #[test]
11550 fn parse_postfix_in_binary_continues() {
11551 let (ast, diags) = parse_str("let y = x++ + 1;");
11552 assert!(diags.is_empty(), "unexpected: {diags:?}");
11553 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11554 panic!("expected Let");
11555 };
11556 let (op, lhs, _) = binary(ast.try_expr(value).unwrap());
11557 assert_eq!(op, crate::BinOp::Add);
11558 assert!(matches!(
11559 ast.try_expr(lhs).unwrap().kind,
11560 crate::ExprKind::PostfixUnary { .. }
11561 ));
11562 }
11563
11564 #[test]
11565 fn parse_index_assignment() {
11566 let (ast, diags) = parse_str("xs[0] = 1;");
11567 assert!(diags.is_empty(), "unexpected: {diags:?}");
11568 let stmt = single_stmt(&ast);
11569 let crate::StmtKind::AssignIndex {
11570 receiver,
11571 index,
11572 value,
11573 } = stmt.kind
11574 else {
11575 panic!("expected AssignIndex, got {:?}", stmt.kind);
11576 };
11577 assert!(matches!(
11578 ast.try_expr(receiver).unwrap().kind,
11579 crate::ExprKind::Identifier(_)
11580 ));
11581 assert_eq!(
11582 ast.try_expr(index).unwrap().kind,
11583 crate::ExprKind::Number(0.0)
11584 );
11585 assert_eq!(
11586 ast.try_expr(value).unwrap().kind,
11587 crate::ExprKind::Number(1.0)
11588 );
11589 }
11590
11591 #[test]
11592 fn parse_for_update_postfix() {
11593 let (ast, diags) = parse_str("function main(): void { for (let i = 0; i < 3; i++) {} }");
11594 assert!(diags.is_empty(), "unexpected: {diags:?}");
11595 assert_eq!(ast.top_level.len(), 1);
11596 }
11597
11598 #[test]
11599 fn parse_for_update_index_assignment() {
11600 let (ast, diags) =
11601 parse_str("function main(): void { for (let i = 0; i < 3; xs[i] = i) {} }");
11602 assert!(diags.is_empty(), "unexpected: {diags:?}");
11603 assert_eq!(ast.top_level.len(), 1);
11604 }
11605
11606 #[test]
11607 fn index_access_missing_close_bracket_diagnoses() {
11608 let (_, diags) = parse_str("a[1;");
11609 assert!(!diags.is_empty());
11610 assert_eq!(diags[0].message, "expected `]`");
11611 }
11612
11613 #[test]
11614 fn field_access_inside_object_literal_value_does_not_steal_dot() {
11615 let (ast, diags) = parse_str("let x = { a: 1 }.foo;");
11616 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11617 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11618 panic!("expected Let");
11619 };
11620 let (recv_id, name) = field_access(ast.try_expr(value).unwrap());
11621 assert_eq!(name, "foo");
11622 assert_eq!(object_literal(ast.try_expr(recv_id).unwrap()).len(), 1);
11623 }
11624
11625 fn type_of_let(stmt: &crate::Stmt) -> &crate::TypeAnnotation {
11626 match stmt.kind {
11627 crate::StmtKind::Let { ref ty, .. } => ty.as_ref().expect("expected type annotation"),
11628 _ => panic!("expected Let"),
11629 }
11630 }
11631
11632 #[test]
11633 fn parse_array_type_annotation() {
11634 let (ast, diags) = parse_str("let xs: number[] = null;");
11635 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11636 let ty = type_of_let(single_stmt(&ast));
11637 match ty.kind {
11638 crate::TypeAnnotationKind::Array(ref inner) => {
11639 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Name { .. }));
11640 }
11641 _ => panic!("expected Array, got {:?}", ty.kind),
11642 }
11643 }
11644
11645 #[test]
11646 fn parse_nested_array_type_annotation() {
11647 let (ast, diags) = parse_str("let xs: number[][] = null;");
11648 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11649 let ty = type_of_let(single_stmt(&ast));
11650 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11651 panic!("expected outer Array");
11652 };
11653 let crate::TypeAnnotationKind::Array(ref inner2) = inner.kind else {
11654 panic!("expected inner Array");
11655 };
11656 assert!(matches!(
11657 inner2.kind,
11658 crate::TypeAnnotationKind::Name { .. }
11659 ));
11660 }
11661
11662 #[test]
11663 fn parse_object_type_annotation_simple() {
11664 let (ast, diags) = parse_str("let p: { x: number; y: number } = null;");
11665 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11666 let ty = type_of_let(single_stmt(&ast));
11667 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11668 panic!("expected Object, got {:?}", ty.kind);
11669 };
11670 assert_eq!(fields.len(), 2);
11671 assert_eq!(fields[0].name.name, "x");
11672 assert_eq!(fields[1].name.name, "y");
11673 }
11674
11675 #[test]
11676 fn readonly_is_rejected_on_methods_accessors_and_method_signatures() {
11677 let source = "class K {\n\
11678 readonly m(): number { return 1; }\n\
11679 public readonly n<T>(): number { return 1; }\n\
11680 readonly get g(): number { return 1; }\n\
11681 readonly p: number = 1;\n\
11682 }\n\
11683 type T = { readonly r(a: number): boolean; readonly f: () => void };\n\
11684 interface I { readonly s(): void; readonly q: number; }\n";
11685 let (_, diags) = parse_str(source);
11686 let lines: Vec<usize> = diags
11687 .iter()
11688 .map(|d| {
11689 assert_eq!(
11690 d.message,
11691 "`readonly` can only modify a property or index signature"
11692 );
11693 source[..d.span.start as usize].matches('\n').count() + 1
11694 })
11695 .collect();
11696 assert_eq!(lines, vec![2, 3, 4, 7, 8]);
11697 }
11698
11699 #[test]
11700 fn parse_object_type_method_members() {
11701 let (ast, diags) =
11704 parse_str("let p: { m(): number; opt?(): string; r(a: number): boolean } = null;");
11705 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11706 let ty = type_of_let(single_stmt(&ast));
11707 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11708 panic!("expected Object, got {:?}", ty.kind);
11709 };
11710 assert_eq!(fields.len(), 3);
11711 for field in fields {
11712 assert!(
11713 matches!(field.ty.kind, crate::TypeAnnotationKind::Function { .. }),
11714 "member `{}` should be function-typed, got {:?}",
11715 field.name.name,
11716 field.ty.kind,
11717 );
11718 }
11719 assert!(fields[1].optional, "`opt?()` is an optional member");
11720 let crate::TypeAnnotationKind::Function { ref params, .. } = fields[2].ty.kind else {
11721 unreachable!("checked above");
11722 };
11723 assert_eq!(params.len(), 1);
11724 assert_eq!(params[0].name.name, "a");
11725 }
11726
11727 #[test]
11728 fn parse_object_type_annotation_with_commas() {
11729 let (ast, diags) = parse_str("let p: { x: number, y: number } = null;");
11730 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11731 let ty = type_of_let(single_stmt(&ast));
11732 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11733 panic!("expected Object");
11734 };
11735 assert_eq!(fields.len(), 2);
11736 }
11737
11738 #[test]
11739 fn parse_object_type_annotation_empty() {
11740 let (ast, diags) = parse_str("let p: {} = null;");
11741 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11742 let ty = type_of_let(single_stmt(&ast));
11743 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11744 panic!("expected Object");
11745 };
11746 assert!(fields.is_empty());
11747 }
11748
11749 #[test]
11750 fn parse_object_type_with_optional_field() {
11751 let (ast, diags) = parse_str("let p: { id: number; nick?: string } = null;");
11752 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11753 let ty = type_of_let(single_stmt(&ast));
11754 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11755 panic!("expected Object");
11756 };
11757 assert_eq!(fields.len(), 2);
11758 assert_eq!(fields[0].name.name, "id");
11759 assert!(!fields[0].optional);
11760 assert_eq!(fields[1].name.name, "nick");
11761 assert!(fields[1].optional);
11762 }
11763
11764 #[test]
11765 fn parse_object_type_with_readonly_fields() {
11766 let (ast, diags) = parse_str(
11767 "let issue: { readonly id: string; readonly title?: string; body: string } = null;",
11768 );
11769 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11770 let ty = type_of_let(single_stmt(&ast));
11771 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11772 panic!("expected Object");
11773 };
11774 let fields: Vec<(&str, bool, bool)> = fields
11775 .iter()
11776 .map(|field| (field.name.name.as_str(), field.optional, field.readonly))
11777 .collect();
11778 assert_eq!(
11779 fields,
11780 vec![
11781 ("id", false, true),
11782 ("title", true, true),
11783 ("body", false, false),
11784 ]
11785 );
11786 }
11787
11788 #[test]
11789 fn parse_object_type_mixed_optional_and_required() {
11790 let (ast, diags) = parse_str("let p: { a?: number; b: string; c?: boolean } = null;");
11791 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11792 let ty = type_of_let(single_stmt(&ast));
11793 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11794 panic!("expected Object");
11795 };
11796 let opt: Vec<(&str, bool)> = fields
11797 .iter()
11798 .map(|f| (f.name.name.as_str(), f.optional))
11799 .collect();
11800 assert_eq!(opt, vec![("a", true), ("b", false), ("c", true)]);
11801 }
11802
11803 #[test]
11804 fn parse_object_type_keyword_fields() {
11805 let (ast, diags) =
11806 parse_str("let p: { type: string; default?: number; null: boolean } = null;");
11807 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11808 let ty = type_of_let(single_stmt(&ast));
11809 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11810 panic!("expected Object");
11811 };
11812 let opt: Vec<(&str, bool)> = fields
11813 .iter()
11814 .map(|f| (f.name.name.as_str(), f.optional))
11815 .collect();
11816 assert_eq!(
11817 opt,
11818 vec![("type", false), ("default", true), ("null", false)]
11819 );
11820 }
11821
11822 #[test]
11823 fn parse_object_type_quoted_fields() {
11824 let (ast, diags) =
11825 parse_str(r#"let p: { "content-type": string; "x\u002drequest-id"?: string } = null;"#);
11826 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11827 let ty = type_of_let(single_stmt(&ast));
11828 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11829 panic!("expected Object");
11830 };
11831 let opt: Vec<(&str, bool)> = fields
11832 .iter()
11833 .map(|f| (f.name.name.as_str(), f.optional))
11834 .collect();
11835 assert_eq!(opt, vec![("content-type", false), ("x-request-id", true)]);
11836 }
11837
11838 #[test]
11839 fn parse_interface_with_optional_property() {
11840 let (ast, diags) = parse_str("interface User { id: number; deletedAt?: string; }");
11841 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11842 let stmt = single_stmt(&ast);
11843 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11844 panic!("expected interface decl");
11845 };
11846 let opt: Vec<(&str, bool)> = members
11847 .iter()
11848 .map(|m| match m {
11849 crate::InterfaceMember::Property { name, optional, .. } => {
11850 (name.name.as_str(), *optional)
11851 }
11852 _ => panic!("expected property"),
11853 })
11854 .collect();
11855 assert_eq!(opt, vec![("id", false), ("deletedAt", true)]);
11856 }
11857
11858 #[test]
11859 fn parse_interface_with_readonly_properties() {
11860 let (ast, diags) = parse_str(
11861 "interface Issue { readonly id: string; readonly title?: string; body: string; }",
11862 );
11863 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11864 let stmt = single_stmt(&ast);
11865 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11866 panic!("expected interface decl");
11867 };
11868 let opt: Vec<(&str, bool, bool)> = members
11869 .iter()
11870 .map(|member| match member {
11871 crate::InterfaceMember::Property {
11872 name,
11873 optional,
11874 readonly,
11875 ..
11876 } => (name.name.as_str(), *optional, *readonly),
11877 _ => panic!("expected property"),
11878 })
11879 .collect();
11880 assert_eq!(
11881 opt,
11882 vec![
11883 ("id", false, true),
11884 ("title", true, true),
11885 ("body", false, false),
11886 ]
11887 );
11888 }
11889
11890 #[test]
11891 fn parse_readonly_property_name_without_modifier() {
11892 let (ast, diags) = parse_str(
11893 "interface Meta { readonly: boolean; }\nlet meta: { readonly: boolean } = null;",
11894 );
11895 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11896 let crate::StmtKind::InterfaceDecl { ref members, .. } =
11897 ast.try_stmt(ast.top_level[0]).unwrap().kind
11898 else {
11899 panic!("expected interface decl");
11900 };
11901 let crate::InterfaceMember::Property {
11902 ref name, readonly, ..
11903 } = members[0]
11904 else {
11905 panic!("expected property");
11906 };
11907 assert_eq!(name.name, "readonly");
11908 assert!(
11909 !readonly,
11910 "`readonly` here is the property name, not a modifier"
11911 );
11912
11913 let ty = type_of_let(ast.try_stmt(ast.top_level[1]).unwrap());
11914 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11915 panic!("expected Object");
11916 };
11917 assert_eq!(fields[0].name.name, "readonly");
11918 assert!(!fields[0].readonly);
11919 }
11920
11921 #[test]
11922 fn parse_interface_keyword_members() {
11923 let (ast, diags) =
11924 parse_str("interface IssueStatus { type: string; default(): string; null: boolean; }");
11925 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11926 let stmt = single_stmt(&ast);
11927 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11928 panic!("expected interface decl");
11929 };
11930 let names: Vec<&str> = members
11931 .iter()
11932 .map(|m| match m {
11933 crate::InterfaceMember::Method { name, .. }
11934 | crate::InterfaceMember::Property { name, .. } => name.name.as_str(),
11935 crate::InterfaceMember::IndexSignature(_) => panic!("unexpected index signature"),
11936 })
11937 .collect();
11938 assert_eq!(names, vec!["type", "default", "null"]);
11939 }
11940
11941 #[test]
11942 fn parse_interface_quoted_property_members() {
11943 let (ast, diags) = parse_str(
11944 r#"interface Headers { "content-type": string; "x\u002drequest-id"?: string; }"#,
11945 );
11946 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11947 let stmt = single_stmt(&ast);
11948 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11949 panic!("expected interface decl");
11950 };
11951 let names: Vec<(&str, bool)> = members
11952 .iter()
11953 .map(|m| match m {
11954 crate::InterfaceMember::Property { name, optional, .. } => {
11955 (name.name.as_str(), *optional)
11956 }
11957 _ => panic!("expected property"),
11958 })
11959 .collect();
11960 assert_eq!(names, vec![("content-type", false), ("x-request-id", true)]);
11961 }
11962
11963 #[test]
11964 fn parse_param_optional_marker() {
11965 let (ast, diags) = parse_str("function f(x?: number): void {}");
11966 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11967 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
11968 panic!("expected function")
11969 };
11970 assert!(params[0].optional);
11971 assert!(params[0].default.is_none());
11972 }
11973
11974 #[test]
11975 fn parse_interface_optional_method() {
11976 let (ast, diags) = parse_str("interface F { foo?(x?: number): void; }");
11977 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11978 let crate::StmtKind::InterfaceDecl { ref members, .. } = single_stmt(&ast).kind else {
11979 panic!("expected interface")
11980 };
11981 let crate::InterfaceMember::Property { optional, ty, .. } = &members[0] else {
11982 panic!("expected callable property")
11983 };
11984 assert!(*optional);
11985 let crate::TypeAnnotationKind::Function { params, .. } = &ty.kind else {
11986 panic!("expected function type")
11987 };
11988 assert!(params[0].optional);
11989 }
11990
11991 #[test]
11992 fn parse_interface_with_call_signature() {
11993 let (ast, diags) = parse_str("interface F { (s: string): number; }");
11994 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11995 let stmt = single_stmt(&ast);
11996 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11997 panic!("expected interface decl");
11998 };
11999 assert_eq!(members.len(), 1);
12000 match &members[0] {
12001 crate::InterfaceMember::Method { name, params, .. } => {
12002 assert_eq!(name.name, "@call");
12003 assert_eq!(params.len(), 1);
12004 assert_eq!(params[0].name.name, "s");
12005 }
12006 other => panic!("expected method (call signature), got {other:?}"),
12007 }
12008 }
12009
12010 #[test]
12011 fn parse_interface_mixed_call_signature_and_method() {
12012 let (ast, diags) = parse_str("interface F { greet(): string; (x: number): number; }");
12013 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12014 let stmt = single_stmt(&ast);
12015 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
12016 panic!("expected interface decl");
12017 };
12018 let names: Vec<&str> = members
12019 .iter()
12020 .map(|m| match m {
12021 crate::InterfaceMember::Method { name, .. } => name.name.as_str(),
12022 _ => "<property>",
12023 })
12024 .collect();
12025 assert_eq!(names, vec!["greet", "@call"]);
12026 }
12027
12028 #[test]
12029 fn parse_interface_rejects_generic_call_signature() {
12030 let (_ast, diags) = parse_str("interface F { <T>(x: T): T; }");
12031 assert!(
12032 diags
12033 .iter()
12034 .any(|d| d.message.contains("generic call signatures")),
12035 "expected generic-call-signature diagnostic, got {diags:?}"
12036 );
12037 }
12038
12039 #[test]
12040 fn parse_object_type_with_array_field() {
12041 let (ast, diags) = parse_str("let p: { xs: number[] } = null;");
12042 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12043 let ty = type_of_let(single_stmt(&ast));
12044 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
12045 panic!("expected Object");
12046 };
12047 assert_eq!(fields[0].name.name, "xs");
12048 assert!(matches!(
12049 fields[0].ty.kind,
12050 crate::TypeAnnotationKind::Array(_)
12051 ));
12052 }
12053
12054 #[test]
12055 fn parse_array_of_objects() {
12056 let (ast, diags) = parse_str("let xs: { x: number }[] = null;");
12057 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12058 let ty = type_of_let(single_stmt(&ast));
12059 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
12060 panic!("expected Array");
12061 };
12062 assert!(matches!(
12063 inner.kind,
12064 crate::TypeAnnotationKind::Object { .. }
12065 ));
12066 }
12067
12068 #[test]
12069 fn parse_tuple_type_annotation() {
12070 let (ast, diags) = parse_str("let p: [string, number] = null;");
12071 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12072 let ty = type_of_let(single_stmt(&ast));
12073 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
12074 panic!("expected Tuple, got {:?}", ty.kind);
12075 };
12076 assert_eq!(elems.len(), 2);
12077 assert!(matches!(
12078 elems[0].kind,
12079 crate::TypeAnnotationKind::Name { .. }
12080 ));
12081 assert!(matches!(
12082 elems[1].kind,
12083 crate::TypeAnnotationKind::Name { .. }
12084 ));
12085 }
12086
12087 #[test]
12088 fn parse_singleton_tuple_type() {
12089 let (ast, diags) = parse_str("let p: [number] = null;");
12090 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12091 let ty = type_of_let(single_stmt(&ast));
12092 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
12093 panic!("expected Tuple");
12094 };
12095 assert_eq!(elems.len(), 1);
12096 }
12097
12098 #[test]
12099 fn parse_nested_tuple_type() {
12100 let (ast, diags) = parse_str("let p: [[number, string], boolean] = null;");
12101 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12102 let ty = type_of_let(single_stmt(&ast));
12103 let crate::TypeAnnotationKind::Tuple(ref outer) = ty.kind else {
12104 panic!("expected outer Tuple");
12105 };
12106 assert_eq!(outer.len(), 2);
12107 let crate::TypeAnnotationKind::Tuple(ref inner) = outer[0].kind else {
12108 panic!("expected inner Tuple");
12109 };
12110 assert_eq!(inner.len(), 2);
12111 }
12112
12113 #[test]
12114 fn parse_array_of_tuple_type() {
12115 let (ast, diags) = parse_str("let xs: [string, number][] = null;");
12116 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12117 let ty = type_of_let(single_stmt(&ast));
12118 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
12119 panic!("expected outer Array, got {:?}", ty.kind);
12120 };
12121 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Tuple(_)));
12122 }
12123
12124 #[test]
12125 fn parse_trailing_comma_tuple_type() {
12126 let (ast, diags) = parse_str("let p: [number, string,] = null;");
12127 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12128 let ty = type_of_let(single_stmt(&ast));
12129 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
12130 panic!("expected Tuple");
12131 };
12132 assert_eq!(elems.len(), 2);
12133 }
12134
12135 #[test]
12136 fn parse_empty_tuple_type_rejected() {
12137 let (_ast, diags) = parse_str("let p: [] = null;");
12138 assert!(
12139 diags.iter().any(|d| d
12140 .message
12141 .contains("tuple types must have at least one element")),
12142 "expected empty-tuple diagnostic, got: {diags:?}"
12143 );
12144 }
12145
12146 #[test]
12147 fn parse_labeled_tuple_drops_labels() {
12148 let (ast, diags) = parse_str("let p: [first: number, string] = null;");
12149 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12150 let ty = type_of_let(single_stmt(&ast));
12151 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
12152 panic!("expected Tuple, got {:?}", ty.kind);
12153 };
12154 assert_eq!(elems.len(), 2);
12155 assert!(
12156 elems
12157 .iter()
12158 .all(|e| matches!(e.kind, crate::TypeAnnotationKind::Name { .. }))
12159 );
12160 }
12161
12162 #[test]
12163 fn parse_labeled_optional_tuple_element() {
12164 let (ast, diags) = parse_str("let p: [first: number, second?: string] = null;");
12165 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12166 let ty = type_of_let(single_stmt(&ast));
12167 let crate::TypeAnnotationKind::Tuple(elements) = &ty.kind else {
12168 panic!("expected tuple")
12169 };
12170 assert!(matches!(
12171 elements[1].kind,
12172 crate::TypeAnnotationKind::Optional(_)
12173 ));
12174 }
12175
12176 #[test]
12177 fn parse_readonly_binds_to_the_postfix_type() {
12178 let (ast, diags) = parse_str("let p: readonly number[][] | null = null;");
12179 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12180 let ty = type_of_let(single_stmt(&ast));
12181 let crate::TypeAnnotationKind::Union(ref members) = ty.kind else {
12182 panic!("expected Union, got {:?}", ty.kind);
12183 };
12184 let crate::TypeAnnotationKind::Readonly(ref operand) = members[0].kind else {
12185 panic!("expected Readonly, got {:?}", members[0].kind);
12186 };
12187 let crate::TypeAnnotationKind::Array(ref inner) = operand.kind else {
12188 panic!("expected Array, got {:?}", operand.kind);
12189 };
12190 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Array(_)));
12191 }
12192
12193 #[test]
12194 fn parse_readonly_as_a_type_name_is_not_the_operator() {
12195 let (ast, diags) = parse_str("let p: readonly = null;");
12196 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12197 let ty = type_of_let(single_stmt(&ast));
12198 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
12199 }
12200
12201 #[test]
12202 fn parse_generic_one_arg() {
12203 let (ast, diags) = parse_str("let x: Foo<number> = null;");
12204 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12205 let ty = type_of_let(single_stmt(&ast));
12206 let crate::TypeAnnotationKind::Name { ref name, ref args } = ty.kind else {
12207 panic!("expected Name, got {:?}", ty.kind);
12208 };
12209 assert_eq!(name.span, crate::Span::new(F, 7, 10).unwrap());
12210 assert_eq!(ty.span, crate::Span::new(F, 7, 18).unwrap());
12211 assert_eq!(args.len(), 1);
12212 assert!(matches!(
12213 args[0].kind,
12214 crate::TypeAnnotationKind::Name { .. }
12215 ));
12216 }
12217
12218 #[test]
12219 fn parse_generic_two_args() {
12220 let (ast, diags) = parse_str("let m: Map<string, number> = null;");
12221 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12222 let ty = type_of_let(single_stmt(&ast));
12223 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
12224 panic!("expected Name");
12225 };
12226 assert_eq!(args.len(), 2);
12227 }
12228
12229 #[test]
12230 fn parse_generic_nested() {
12231 let (ast, diags) = parse_str("let b: Box<Box<T>> = null;");
12232 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12233 let ty = type_of_let(single_stmt(&ast));
12234 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
12235 panic!("expected outer Name");
12236 };
12237 assert_eq!(args.len(), 1);
12238 let crate::TypeAnnotationKind::Name {
12239 args: ref inner, ..
12240 } = args[0].kind
12241 else {
12242 panic!("expected inner Name");
12243 };
12244 assert_eq!(inner.len(), 1);
12245 }
12246
12247 #[test]
12248 fn parse_generic_array_postfix() {
12249 let (ast, diags) = parse_str("let xs: Foo<number>[] = null;");
12250 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12251 let ty = type_of_let(single_stmt(&ast));
12252 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
12253 panic!("expected Array");
12254 };
12255 assert!(
12256 matches!(inner.kind, crate::TypeAnnotationKind::Name { ref args, .. } if args.len() == 1)
12257 );
12258 }
12259
12260 #[test]
12261 fn parse_generic_empty_diagnoses() {
12262 let (_ast, diags) = parse_str("let x: Foo<> = null;");
12263 assert!(
12264 diags
12265 .iter()
12266 .any(|d| d.message.contains("empty generic argument list")),
12267 "expected empty-generic-args diagnostic, got: {diags:?}"
12268 );
12269 }
12270
12271 #[test]
12272 fn parse_generic_trailing_comma_ok() {
12273 let (_ast, diags) = parse_str("let x: Foo<number,> = null;");
12274 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12275 }
12276
12277 #[test]
12278 fn parse_generic_missing_close_diagnoses() {
12279 let (_ast, diags) = parse_str("let x: Foo<number = null;");
12280 assert!(
12281 diags
12282 .iter()
12283 .any(|d| d.message.contains("expected `,` or `>`")),
12284 "expected unterminated-generic diagnostic, got: {diags:?}"
12285 );
12286 }
12287
12288 #[test]
12289 fn parse_qualified_two_segments() {
12290 let (ast, diags) = parse_str("let x: Foo.Bar = null;");
12291 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12292 let ty = type_of_let(single_stmt(&ast));
12293 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
12294 panic!("expected Qualified, got {:?}", ty.kind);
12295 };
12296 assert_eq!(path.len(), 2);
12297 assert_eq!(path[0].span, crate::Span::new(F, 7, 10).unwrap());
12299 assert_eq!(path[1].span, crate::Span::new(F, 11, 14).unwrap());
12300 assert!(args.is_empty());
12301 assert_eq!(ty.span, crate::Span::new(F, 7, 14).unwrap());
12302 }
12303
12304 #[test]
12305 fn parse_qualified_three_segments() {
12306 let (ast, diags) = parse_str("let x: Foo.Bar.Baz = null;");
12307 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12308 let ty = type_of_let(single_stmt(&ast));
12309 let crate::TypeAnnotationKind::Qualified { ref path, .. } = ty.kind else {
12310 panic!("expected Qualified, got {:?}", ty.kind);
12311 };
12312 assert_eq!(path.len(), 3);
12313 }
12314
12315 #[test]
12316 fn parse_qualified_with_generic_args() {
12317 let (ast, diags) = parse_str("let x: Foo.Box<number> = null;");
12318 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12319 let ty = type_of_let(single_stmt(&ast));
12320 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
12321 panic!("expected Qualified, got {:?}", ty.kind);
12322 };
12323 assert_eq!(path.len(), 2);
12324 assert_eq!(args.len(), 1);
12325 assert!(matches!(
12326 args[0].kind,
12327 crate::TypeAnnotationKind::Name { .. }
12328 ));
12329 }
12330
12331 #[test]
12332 fn parse_qualified_with_array_postfix() {
12333 let (ast, diags) = parse_str("let xs: Foo.Bar[] = null;");
12334 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12335 let ty = type_of_let(single_stmt(&ast));
12336 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
12337 panic!("expected Array, got {:?}", ty.kind);
12338 };
12339 assert!(matches!(
12340 inner.kind,
12341 crate::TypeAnnotationKind::Qualified { .. }
12342 ));
12343 }
12344
12345 #[test]
12346 fn parse_qualified_trailing_dot_diagnoses() {
12347 let (_ast, diags) = parse_str("let x: Foo. = null;");
12348 assert!(
12349 diags.iter().any(|d| d
12350 .message
12351 .contains("expected identifier after `.` in type name")),
12352 "expected dotted-name diagnostic, got: {diags:?}"
12353 );
12354 }
12355
12356 #[test]
12357 fn parse_qualified_void_root_rejected_dot() {
12358 let (ast, diags) = parse_str("function f(): void.Bar { }");
12359 assert!(!diags.is_empty(), "expected a diagnostic for `void.Bar`");
12360 let _ = ast;
12361 }
12362
12363 fn union_members(ty: &crate::TypeAnnotation) -> &[crate::TypeAnnotation] {
12364 match &ty.kind {
12365 crate::TypeAnnotationKind::Union(members) => members,
12366 other => panic!("expected Union, got {other:?}"),
12367 }
12368 }
12369
12370 #[test]
12371 fn parse_union_two_members() {
12372 let (ast, diags) = parse_str("let x: number | string = null;");
12373 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12374 let ty = type_of_let(single_stmt(&ast));
12375 let members = union_members(ty);
12376 assert_eq!(members.len(), 2);
12377 assert!(matches!(
12378 members[0].kind,
12379 crate::TypeAnnotationKind::Name { .. }
12380 ));
12381 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
12383 members[1].kind,
12384 crate::TypeAnnotationKind::Name { .. }
12385 ));
12386 assert_eq!(members[1].span, crate::Span::new(F, 16, 22).unwrap()); assert_eq!(ty.span, crate::Span::new(F, 7, 22).unwrap());
12388 }
12389
12390 #[test]
12391 fn parse_union_three_members() {
12392 let (ast, diags) = parse_str("let x: A | B | C = null;");
12393 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12394 let ty = type_of_let(single_stmt(&ast));
12395 let members = union_members(ty);
12396 assert_eq!(members.len(), 3);
12397 for m in members {
12398 assert!(matches!(m.kind, crate::TypeAnnotationKind::Name { .. }));
12399 }
12400 }
12401
12402 #[test]
12403 fn parse_union_lower_than_array() {
12404 let (ast, diags) = parse_str("let x: number[] | string = null;");
12405 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12406 let ty = type_of_let(single_stmt(&ast));
12407 let members = union_members(ty);
12408 assert_eq!(members.len(), 2);
12409 assert!(
12410 matches!(members[0].kind, crate::TypeAnnotationKind::Array(_)),
12411 "expected first member to be Array, got {:?}",
12412 members[0].kind
12413 );
12414 assert!(matches!(
12415 members[1].kind,
12416 crate::TypeAnnotationKind::Name { .. }
12417 ));
12418 }
12419
12420 #[test]
12421 fn parse_union_with_null_member() {
12422 let (ast, diags) = parse_str("let x: number | null = null;");
12423 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12424 let ty = type_of_let(single_stmt(&ast));
12425 let members = union_members(ty);
12426 assert_eq!(members.len(), 2);
12427 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
12429 members[1].kind,
12430 crate::TypeAnnotationKind::Name { .. }
12431 ));
12432 assert_eq!(members[1].span, crate::Span::new(F, 16, 20).unwrap()); }
12434
12435 #[test]
12436 fn parse_union_in_function_return_type() {
12437 let (ast, diags) = parse_str("function f(): number | string { }");
12438 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12439 let stmt = single_stmt(&ast);
12440 match stmt.kind {
12441 crate::StmtKind::Function {
12442 ref return_type, ..
12443 } => {
12444 let return_type = return_type.as_ref().expect("plain return type");
12445 let members = union_members(return_type);
12446 assert_eq!(members.len(), 2);
12447 }
12448 _ => panic!("expected Function"),
12449 }
12450 }
12451
12452 #[test]
12453 fn parse_union_in_function_param_type() {
12454 let (ast, diags) = parse_str("function f(x: number | string): void { }");
12455 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12456 let stmt = single_stmt(&ast);
12457 match stmt.kind {
12458 crate::StmtKind::Function { ref params, .. } => {
12459 let p_ty = params[0].ty.as_ref().expect("param type");
12460 let members = union_members(p_ty);
12461 assert_eq!(members.len(), 2);
12462 }
12463 _ => panic!("expected Function"),
12464 }
12465 }
12466
12467 #[test]
12468 fn parse_union_in_object_field_type() {
12469 let (ast, diags) = parse_str("let p: { x: number | string } = null;");
12470 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12471 let ty = type_of_let(single_stmt(&ast));
12472 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
12473 panic!("expected Object, got {:?}", ty.kind);
12474 };
12475 assert_eq!(fields.len(), 1);
12476 let members = union_members(&fields[0].ty);
12477 assert_eq!(members.len(), 2);
12478 }
12479
12480 #[test]
12481 fn parse_union_leading_pipe_is_forgiven() {
12482 let (ast, diags) = parse_str("let x: | number | string = null;");
12483 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12484 let ty = type_of_let(single_stmt(&ast));
12485 let members = union_members(ty);
12486 assert_eq!(members.len(), 2);
12487 }
12488
12489 #[test]
12490 fn parse_union_leading_pipe_single_member_unwraps() {
12491 let (ast, diags) = parse_str("let x: | number = null;");
12492 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12493 let ty = type_of_let(single_stmt(&ast));
12494 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
12495 }
12496
12497 #[test]
12498 fn parse_union_trailing_pipe_diagnoses() {
12499 let (_, diags) = parse_str("let x: number | = null;");
12500 assert!(!diags.is_empty());
12501 assert_eq!(diags[0].message, "expected type");
12502 }
12503
12504 #[test]
12505 fn snapshot_union_type_mix() {
12506 let (ast, diags) = parse_str("let xs: number[] | string | null = null;");
12507 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12508 insta::assert_debug_snapshot!(ast);
12509 }
12510
12511 #[test]
12512 fn parse_object_type_duplicate_field_diagnoses() {
12513 let (_, diags) = parse_str("let p: { readonly x: number; x: string } = null;");
12514 assert!(!diags.is_empty());
12515 assert!(
12516 diags
12517 .iter()
12518 .any(|d| d.message == "duplicate field `x` in object type"),
12519 "expected duplicate-field diagnostic, got: {diags:?}"
12520 );
12521 }
12522
12523 #[test]
12524 fn snapshot_object_and_array_literals() {
12525 let (ast, diags) = parse_str(r#"let mix = [{ a: 1, b: "hi" }, {}];"#);
12526 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12527 insta::assert_debug_snapshot!(ast);
12528 }
12529
12530 #[test]
12531 fn snapshot_function_with_while() {
12532 let source = "function loop(n: number): void {\n while (n > 0) {\n log(n);\n }\n}";
12533 let (ast, diags) = parse_str(source);
12534 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12535 insta::assert_debug_snapshot!(ast);
12536 }
12537
12538 fn parse_arrow_const(
12539 source: &str,
12540 ) -> (
12541 Ast,
12542 Vec<crate::ParamDecl>,
12543 Option<crate::TypeAnnotation>,
12544 crate::ArrowBody,
12545 ) {
12546 let (ast, diags) = parse_str(source);
12547 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12548 assert_eq!(ast.top_level.len(), 1);
12549 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12550 let value = match &stmt.kind {
12551 StmtKind::Const { value, .. } => *value,
12552 _ => panic!("expected const declaration"),
12553 };
12554 let value = match ast.try_expr(value).unwrap().kind {
12555 ExprKind::FunctionExpression { function, .. } => function,
12556 _ => value,
12557 };
12558 let expr = ast.try_expr(value).unwrap().clone();
12559 let (params, return_type, body) = match expr.kind {
12560 ExprKind::Arrow {
12561 params,
12562 return_type,
12563 body,
12564 ..
12565 } => (params, return_type, body),
12566 other => panic!("expected Arrow, got {other:?}"),
12567 };
12568 (ast, params, return_type, body)
12569 }
12570
12571 #[test]
12572 fn parses_anonymous_function_expression_as_arrow() {
12573 let (ast, params, return_type, body) =
12574 parse_arrow_const("const f = function (x: number): number { return x + 1; };");
12575 assert_eq!(params.len(), 1);
12576 assert_eq!(params[0].name.name, "x");
12577 assert!(return_type.is_some());
12578 assert!(matches!(body, crate::ArrowBody::Block(_)));
12579 let _ = ast;
12580 }
12581
12582 #[test]
12583 fn parses_function_expression_with_no_params() {
12584 let (_ast, params, _return_type, body) =
12585 parse_arrow_const("const f = function (): void {};");
12586 assert!(params.is_empty());
12587 assert!(matches!(body, crate::ArrowBody::Block(_)));
12588 }
12589
12590 #[test]
12592 fn parses_named_function_expression_signature() {
12593 let (_ast, params, _return_type, _body) =
12594 parse_arrow_const("const f = function named(x: number): number { return x + 1; };");
12595 assert_eq!(params.len(), 1);
12596 }
12597
12598 #[test]
12600 fn accepts_a_named_function_expression_that_calls_itself() {
12601 let (_ast, diags) =
12602 parse_str("const f = function bar(x: number): number { return bar(x); };");
12603 assert!(diags.is_empty(), "{diags:?}");
12604 }
12605
12606 #[test]
12608 fn accepts_a_named_function_expression_using_the_name_as_a_property() {
12609 let (_ast, diags) = parse_str("const f = function bar(o: O): number { return o.bar; };");
12610 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12611 }
12612
12613 #[test]
12615 fn accepts_a_named_function_expression_using_the_name_as_an_object_key() {
12616 let (_ast, diags) = parse_str(
12617 "const f = function bar(x: number): number { const o = { bar: 1 }; return o.bar + x; };",
12618 );
12619 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12620 }
12621
12622 #[test]
12624 fn accepts_a_named_function_expression_with_its_name_in_a_string() {
12625 let (_ast, diags) =
12626 parse_str("const f = function bar(x: number): string { return \"bar\"; };");
12627 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12628 }
12629
12630 #[test]
12632 fn accepts_a_self_call_inside_a_template_substitution() {
12633 let (_ast, diags) =
12634 parse_str("const f = function bar(x: number): string { return `${bar(0)}`; };");
12635 assert!(diags.is_empty(), "{diags:?}");
12636 }
12637
12638 #[test]
12639 fn rejects_a_generic_function_expression() {
12640 let (_ast, diags) = parse_str("const f = function <T>(x: T): T { return x; };");
12641 assert!(
12642 diags
12643 .iter()
12644 .any(|d| d.message.contains("generic function expressions")),
12645 "generic function expression should be rejected: {diags:?}"
12646 );
12647 }
12648
12649 #[test]
12650 fn accepts_this_inside_a_function_expression_in_a_class_method() {
12651 let (_ast, diags) = parse_str(
12652 "class C { x: number = 1; m(): number { \
12653 const f = function (this: { x: number }): number { return this.x; }; return f(); } }",
12654 );
12655 assert!(diags.is_empty(), "{diags:?}");
12656 }
12657
12658 #[test]
12659 fn retains_this_boundary_inside_method_shorthand() {
12660 let (ast, diags) = parse_str(
12661 "class C { x: number = 1; m(): number { \
12662 const o = { x: 2, g(): number { return this.x; } }; return o.g(); } }",
12663 );
12664 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12665 assert!((0..ast.exprs_len()).any(|index| matches!(
12666 ast.try_expr(crate::ExprId(index as u32)).unwrap().kind,
12667 ExprKind::ThisOutsideReceiver
12668 )));
12669 }
12670
12671 #[test]
12672 fn this_still_parses_in_an_ordinary_class_method() {
12673 let (_ast, diags) = parse_str("class C { x: number = 1; m(): number { return this.x; } }");
12674 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12675 }
12676
12677 #[test]
12679 fn function_keyword_still_parses_as_an_object_key() {
12680 let (_ast, diags) = parse_str("let o = { function: 1 };");
12681 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12682 }
12683
12684 fn object_literal_member_values(source: &str) -> (Ast, Vec<crate::ExprId>) {
12685 let (ast, diags) = parse_str(source);
12686 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12687 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12688 let value = match &stmt.kind {
12689 StmtKind::Const { value, .. } => *value,
12690 other => panic!("expected a const declaration, got {other:?}"),
12691 };
12692 let members = match &ast.try_expr(value).unwrap().kind {
12693 ExprKind::ObjectLiteral { members } => members
12694 .iter()
12695 .map(crate::ObjectLiteralMember::value)
12696 .collect::<Vec<_>>(),
12697 other => panic!("expected ObjectLiteral, got {other:?}"),
12698 };
12699 (ast, members)
12700 }
12701
12702 #[test]
12703 fn parses_object_literal_method_shorthand_as_arrow() {
12704 let (ast, members) =
12705 object_literal_member_values("const o = { m(x: number): number { return x; } };");
12706 assert_eq!(members.len(), 1);
12707 match &ast.try_expr(members[0]).unwrap().kind {
12708 ExprKind::Arrow { params, body, .. } => {
12709 assert_eq!(params.len(), 1);
12710 assert_eq!(params[0].name.name, "x");
12711 assert!(matches!(body, crate::ArrowBody::Block(_)));
12712 }
12713 other => panic!("expected Arrow, got {other:?}"),
12714 }
12715 }
12716
12717 #[test]
12719 fn parses_method_shorthand_with_a_keyword_name() {
12720 let (ast, members) =
12721 object_literal_member_values("const o = { if(x: number): number { return x; } };");
12722 assert!(matches!(
12723 ast.try_expr(members[0]).unwrap().kind,
12724 ExprKind::Arrow { .. }
12725 ));
12726 }
12727
12728 #[test]
12729 fn parses_method_shorthand_alongside_plain_and_shorthand_properties() {
12730 let (ast, members) =
12731 object_literal_member_values("const o = { a: 1, m(): void {}, b: 2 };");
12732 assert_eq!(members.len(), 3);
12733 assert!(matches!(
12734 ast.try_expr(members[1]).unwrap().kind,
12735 ExprKind::Arrow { .. }
12736 ));
12737 }
12738
12739 #[test]
12740 fn parses_bare_ident_arrow_expression_body() {
12741 let (_ast, params, return_type, body) = parse_arrow_const("const f = x => x + 1;");
12742 assert_eq!(params.len(), 1);
12743 assert_eq!(params[0].name.name, "x");
12744 assert!(params[0].ty.is_none());
12745 assert!(return_type.is_none());
12746 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12747 }
12748
12749 #[test]
12750 fn parses_zero_param_arrow() {
12751 let (_ast, params, return_type, body) = parse_arrow_const("const f = () => 0;");
12752 assert!(params.is_empty());
12753 assert!(return_type.is_none());
12754 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12755 }
12756
12757 #[test]
12758 fn parses_paren_unannotated_arrow() {
12759 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x) => x;");
12760 assert_eq!(params.len(), 1);
12761 assert_eq!(params[0].name.name, "x");
12762 assert!(params[0].ty.is_none());
12763 }
12764
12765 #[test]
12766 fn parses_typed_param_arrow() {
12767 let (_ast, params, return_type, body) =
12768 parse_arrow_const("const f = (x: number) => x * 2;");
12769 assert_eq!(params.len(), 1);
12770 assert!(params[0].ty.is_some());
12771 assert!(return_type.is_none());
12772 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12773 }
12774
12775 #[test]
12776 fn parses_typed_param_and_return_arrow() {
12777 let (_ast, params, return_type, body) =
12778 parse_arrow_const("const f = (x: number): number => x * 2;");
12779 assert_eq!(params.len(), 1);
12780 assert!(params[0].ty.is_some());
12781 assert!(return_type.is_some());
12782 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12783 }
12784
12785 #[test]
12786 fn parses_multi_param_unannotated_arrow() {
12787 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x, y) => x + y;");
12788 assert_eq!(params.len(), 2);
12789 assert_eq!(params[0].name.name, "x");
12790 assert_eq!(params[1].name.name, "y");
12791 assert!(params[0].ty.is_none());
12792 assert!(params[1].ty.is_none());
12793 }
12794
12795 #[test]
12796 fn parses_multi_param_typed_arrow() {
12797 let (_ast, params, _return_type, _body) =
12798 parse_arrow_const("const f = (x: number, y: number) => x + y;");
12799 assert_eq!(params.len(), 2);
12800 assert!(params[0].ty.is_some());
12801 assert!(params[1].ty.is_some());
12802 }
12803
12804 #[test]
12805 fn parses_block_body_arrow() {
12806 let (_ast, params, _return_type, body) =
12807 parse_arrow_const("const f = (x: number) => { return x; };");
12808 assert_eq!(params.len(), 1);
12809 assert!(matches!(body, crate::ArrowBody::Block(_)));
12810 }
12811
12812 #[test]
12813 fn rejects_typed_bare_ident_arrow() {
12814 let (_ast, diags) = parse_str("const f = x: number => x;");
12815 assert!(
12816 !diags.is_empty(),
12817 "expected diagnostics for `x: number => …`",
12818 );
12819 }
12820
12821 #[test]
12822 fn arrow_inside_call_argument_parses() {
12823 let (ast, diags) = parse_str("f((x: number) => x + 1, 2);");
12824 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12825 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12826 let call = match &stmt.kind {
12827 StmtKind::Expr(eid) => ast.try_expr(*eid).unwrap(),
12828 _ => panic!("expected expression statement"),
12829 };
12830 let args = match &call.kind {
12831 ExprKind::Call { args, .. } => args.clone(),
12832 other => panic!("expected Call, got {other:?}"),
12833 };
12834 assert_eq!(args.len(), 2);
12835 assert!(matches!(
12836 ast.try_expr(args[0]).unwrap().kind,
12837 ExprKind::Arrow { .. }
12838 ));
12839 }
12840
12841 #[test]
12842 fn function_decl_param_still_requires_annotation() {
12843 let (_ast, diags) = parse_str("function f(x): number { return x; }");
12844 assert!(
12845 diags.iter().any(|d| d.message.contains("type annotation")),
12846 "expected `parameter requires a type annotation` diagnostic, got {diags:?}",
12847 );
12848 }
12849
12850 #[test]
12851 fn paren_expression_still_parses_when_no_arrow() {
12852 let (ast, diags) = parse_str("const a = (1 + 2);");
12853 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12854 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12855 let value = match &stmt.kind {
12856 StmtKind::Const { value, .. } => *value,
12857 _ => panic!("expected const declaration"),
12858 };
12859 assert!(matches!(
12860 ast.try_expr(value).unwrap().kind,
12861 ExprKind::Paren(_)
12862 ));
12863 }
12864
12865 #[test]
12866 fn parse_named_import_single() {
12867 let (ast, diags) = parse_str("import { v4 } from \"submilli:uuid\";");
12868 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12869 assert_eq!(ast.top_level.len(), 1);
12870 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12871 let (module, kind) = match &stmt.kind {
12872 StmtKind::Import { module, kind, .. } => (module, kind),
12873 other => panic!("expected Import, got {other:?}"),
12874 };
12875 assert_eq!(module, "submilli:uuid");
12876 let specs = match kind {
12877 ImportKind::Named(specs) => specs,
12878 other => panic!("expected Named, got {other:?}"),
12879 };
12880 assert_eq!(specs.len(), 1);
12881 assert_eq!(specs[0].imported_name.name, "v4");
12882 assert_eq!(specs[0].local_name.name, "v4");
12883 }
12884
12885 #[test]
12886 fn parse_named_import_multi_and_alias() {
12887 let (ast, diags) =
12888 parse_str("import { v4, v7 as makeId, validate } from \"submilli:uuid\";");
12889 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12890 let specs = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12891 StmtKind::Import {
12892 kind: ImportKind::Named(s),
12893 ..
12894 } => s.clone(),
12895 other => panic!("expected named import, got {other:?}"),
12896 };
12897 assert_eq!(specs.len(), 3);
12898 assert_eq!(specs[0].imported_name.name, "v4");
12899 assert_eq!(specs[0].local_name.name, "v4");
12900 assert_eq!(specs[1].imported_name.name, "v7");
12901 assert_eq!(specs[1].local_name.name, "makeId");
12902 assert_eq!(specs[2].imported_name.name, "validate");
12903 assert_eq!(specs[2].local_name.name, "validate");
12904 }
12905
12906 #[test]
12907 fn parse_namespace_import() {
12908 let (ast, diags) = parse_str("import uuid from \"submilli:uuid\";");
12909 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12910 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12911 StmtKind::Import { kind, .. } => kind.clone(),
12912 other => panic!("expected import, got {other:?}"),
12913 };
12914 let local = match kind {
12915 ImportKind::Namespace { local_name } => local_name,
12916 other => panic!("expected Namespace, got {other:?}"),
12917 };
12918 assert_eq!(local.name, "uuid");
12919 }
12920
12921 #[test]
12922 fn parse_wildcard_namespace_import_synonym() {
12923 let (ast, diags) = parse_str("import * as uuid from \"submilli:uuid\";");
12924 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12925 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12926 StmtKind::Import { kind, .. } => kind.clone(),
12927 other => panic!("expected import, got {other:?}"),
12928 };
12929 let local = match kind {
12930 ImportKind::Namespace { local_name } => local_name,
12931 other => panic!("expected Namespace, got {other:?}"),
12932 };
12933 assert_eq!(local.name, "uuid");
12934 }
12935
12936 #[test]
12937 fn parse_wildcard_namespace_import_without_as_rejected() {
12938 let (_ast, diags) = parse_str("import * from \"submilli:uuid\";");
12939 assert!(
12940 diags
12941 .iter()
12942 .any(|d| d.message.contains("expected `as <name>`")),
12943 "expected missing-as diagnostic, got: {diags:?}",
12944 );
12945 }
12946
12947 #[test]
12948 fn parse_combined_default_and_named_rejected() {
12949 let (_ast, diags) = parse_str("import uuid, { v4 } from \"submilli:uuid\";");
12950 assert!(
12951 diags
12952 .iter()
12953 .any(|d| d.message.contains("combining default and named")),
12954 "expected combined-import diagnostic, got: {diags:?}",
12955 );
12956 }
12957
12958 #[test]
12959 fn parse_side_effect_import_rejected() {
12960 let (_ast, diags) = parse_str("import \"submilli:uuid\";");
12961 assert!(
12962 diags
12963 .iter()
12964 .any(|d| d.message.contains("side-effect-only imports")),
12965 "expected side-effect-import diagnostic, got: {diags:?}",
12966 );
12967 }
12968
12969 #[test]
12970 fn parse_empty_specifier_list_rejected() {
12971 let (_ast, diags) = parse_str("import {} from \"submilli:uuid\";");
12972 assert!(
12973 diags
12974 .iter()
12975 .any(|d| d.message.contains("empty import specifier list")),
12976 "expected empty-specifier diagnostic, got: {diags:?}",
12977 );
12978 }
12979
12980 #[test]
12981 fn parse_import_inside_function_body_rejected() {
12982 let (_ast, diags) =
12983 parse_str("function main(): void { import { v4 } from \"submilli:uuid\"; }");
12984 assert!(
12985 diags
12986 .iter()
12987 .any(|d| d.message.contains("must appear at the top of the file")),
12988 "expected top-level diagnostic, got: {diags:?}",
12989 );
12990 }
12991
12992 #[test]
12993 fn parse_missing_from_rejected() {
12994 let (_ast, diags) = parse_str("import { v4 } \"submilli:uuid\";");
12995 assert!(
12996 diags.iter().any(|d| d.message.contains("expected `from`")),
12997 "expected missing-from diagnostic, got: {diags:?}",
12998 );
12999 }
13000
13001 #[test]
13002 fn parse_missing_module_specifier_rejected() {
13003 let (_ast, diags) = parse_str("import { v4 } from;");
13004 assert!(
13005 diags
13006 .iter()
13007 .any(|d| d.message.contains("expected module specifier")),
13008 "expected missing-module diagnostic, got: {diags:?}",
13009 );
13010 }
13011
13012 #[test]
13013 fn parse_export_function_marks_exported() {
13014 let (ast, diags) = parse_str("export function main(): void {}");
13017 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13018 let stmt = single_stmt(&ast);
13019 assert!(
13020 matches!(stmt.kind, crate::StmtKind::Function { .. }),
13021 "expected Function decl, got {:?}",
13022 stmt.kind,
13023 );
13024 assert_eq!(ast.exported_decls.len(), 1, "one exported decl");
13025 assert_eq!(ast.exported_decls[0].stmt, ast.top_level[0]);
13026 }
13027
13028 #[test]
13029 fn parse_export_const_type_interface_enum_are_marked() {
13030 for src in [
13031 "export const X: number = 1;",
13032 "export type Id = string;",
13033 "export interface P { x: number; }",
13034 "export enum E { A, B }",
13035 ] {
13036 let (ast, diags) = parse_str(src);
13037 assert!(diags.is_empty(), "unexpected diags for {src:?}: {diags:?}");
13038 assert_eq!(ast.top_level.len(), 1, "one decl for {src:?}");
13039 assert_eq!(ast.exported_decls.len(), 1, "one exported decl for {src:?}");
13040 }
13041 }
13042
13043 #[test]
13044 fn parse_export_default_is_rejected_with_focused_message() {
13045 let (_ast, diags) = parse_str("export default function main(): void {}");
13046 assert!(
13047 diags
13048 .iter()
13049 .any(|d| d.message.contains("`export default` is not supported")),
13050 "expected focused export-default diagnostic, got: {diags:?}",
13051 );
13052 assert!(
13053 !diags.iter().any(|d| d.message.contains("expected `;`")),
13054 "the spurious missing-semicolon error must be gone: {diags:?}",
13055 );
13056 }
13057
13058 #[test]
13059 fn parse_export_list_parses_as_export_from() {
13060 let (ast, diags) = parse_str("function main(): void {}\nexport { main };");
13063 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13064 assert_eq!(ast.top_level.len(), 2, "function + export-from");
13065 let export = ast.try_stmt(ast.top_level[1]).unwrap();
13066 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
13067 panic!("expected ExportFrom, got {:?}", export.kind);
13068 };
13069 assert!(source.is_none(), "bare `export {{ x }}` has no source");
13070 assert_eq!(specs.len(), 1);
13071 assert_eq!(specs[0].imported_name.name, "main");
13072 assert_eq!(specs[0].local_name.name, "main");
13073 }
13074
13075 #[test]
13076 fn parse_export_from_with_source_and_alias_parses() {
13077 let (ast, diags) = parse_str("export { foo, bar as baz } from \"./util\";");
13078 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13079 let export = ast.try_stmt(ast.top_level[0]).unwrap();
13080 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
13081 panic!("expected ExportFrom, got {:?}", export.kind);
13082 };
13083 let Some((module, _)) = source else {
13084 panic!("expected a `from` source");
13085 };
13086 assert_eq!(module, "./util");
13087 assert_eq!(specs.len(), 2);
13088 assert_eq!(specs[1].imported_name.name, "bar");
13089 assert_eq!(specs[1].local_name.name, "baz");
13090 }
13091
13092 #[test]
13093 fn parse_export_inside_function_body_rejected() {
13094 let (_ast, diags) = parse_str("function main(): void { export const X: number = 1; }");
13095 assert!(
13096 diags
13097 .iter()
13098 .any(|d| d.message.contains("must appear at the top of the file")),
13099 "expected top-level diagnostic, got: {diags:?}",
13100 );
13101 }
13102
13103 #[test]
13104 fn parse_throw_with_expression() {
13105 let (ast, diags) = parse_str(r#"throw new Error("oops");"#);
13106 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13107 let stmt = single_stmt(&ast);
13108 let crate::StmtKind::Throw { value } = stmt.kind else {
13109 panic!("expected Throw, got {:?}", stmt.kind);
13110 };
13111 assert!(matches!(
13112 ast.try_expr(value).unwrap().kind,
13113 crate::ExprKind::New { .. }
13114 ));
13115 }
13116
13117 #[test]
13118 fn parse_new_with_dotted_callee() {
13119 let (ast, diags) = parse_str("new Temporal.Duration({});");
13120 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13121 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
13122 panic!("expected expression statement");
13123 };
13124 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
13125 panic!("expected New");
13126 };
13127 let crate::ExprKind::FieldAccess { name, .. } = &ast.try_expr(*callee).unwrap().kind else {
13128 panic!("expected FieldAccess callee");
13129 };
13130 assert_eq!(name.name, "Duration");
13131 }
13132
13133 #[test]
13134 fn parse_new_with_three_level_dotted_callee() {
13135 let (ast, diags) = parse_str("new Foo.Bar.Baz();");
13136 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13137 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
13138 panic!("expected expression statement");
13139 };
13140 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
13141 panic!("expected New");
13142 };
13143 let crate::ExprKind::FieldAccess { receiver, name } = &ast.try_expr(*callee).unwrap().kind
13144 else {
13145 panic!("expected outer FieldAccess");
13146 };
13147 assert_eq!(name.name, "Baz");
13148 let crate::ExprKind::FieldAccess { name: mid_name, .. } =
13149 &ast.try_expr(*receiver).unwrap().kind
13150 else {
13151 panic!("expected inner FieldAccess");
13152 };
13153 assert_eq!(mid_name.name, "Bar");
13154 }
13155
13156 #[test]
13157 fn parse_new_with_trailing_dot_diagnoses() {
13158 let (_ast, diags) = parse_str("new Foo.;");
13159 assert!(
13160 diags.iter().any(|d| d
13161 .message
13162 .contains("expected identifier after `.` in constructor name")),
13163 "expected trailing-dot diagnostic, got: {diags:?}",
13164 );
13165 }
13166
13167 #[test]
13168 fn throw_without_expression_diagnoses() {
13169 let (_ast, diags) = parse_str("throw;");
13170 assert!(
13171 diags
13172 .iter()
13173 .any(|d| d.message == "expected expression after `throw`"),
13174 "expected bare-throw diagnostic, got: {diags:?}",
13175 );
13176 }
13177
13178 #[test]
13179 fn parse_try_catch() {
13180 let (ast, diags) = parse_str("try { } catch (e: Error) { }");
13181 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13182 let stmt = single_stmt(&ast);
13183 let crate::StmtKind::Try {
13184 body,
13185 ref catches,
13186 ref finally,
13187 } = stmt.kind
13188 else {
13189 panic!("expected Try, got {:?}", stmt.kind);
13190 };
13191 assert!(matches!(
13192 ast.try_stmt(body).unwrap().kind,
13193 crate::StmtKind::Block(ref v) if v.is_empty()
13194 ));
13195 let [clause] = catches.as_slice() else {
13196 panic!("expected one catch clause, got {catches:?}");
13197 };
13198 assert_eq!(clause.binding.name, "e");
13199 assert!(clause.ty.is_some());
13200 assert!(finally.is_none());
13201 }
13202
13203 #[test]
13204 fn parse_try_finally() {
13205 let (ast, diags) = parse_str("try { } finally { }");
13206 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13207 let stmt = single_stmt(&ast);
13208 let crate::StmtKind::Try {
13209 ref catches,
13210 ref finally,
13211 ..
13212 } = stmt.kind
13213 else {
13214 panic!("expected Try");
13215 };
13216 assert!(catches.is_empty());
13217 let finally_id = finally.expect("finally block");
13218 assert!(matches!(
13219 ast.try_stmt(finally_id).unwrap().kind,
13220 crate::StmtKind::Block(_)
13221 ));
13222 }
13223
13224 #[test]
13225 fn parse_try_catch_finally() {
13226 let (ast, diags) = parse_str("try { } catch (e: Error) { } finally { }");
13227 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13228 let stmt = single_stmt(&ast);
13229 let crate::StmtKind::Try {
13230 ref catches,
13231 ref finally,
13232 ..
13233 } = stmt.kind
13234 else {
13235 panic!("expected Try");
13236 };
13237 assert_eq!(catches.len(), 1);
13238 assert!(finally.is_some());
13239 }
13240
13241 #[test]
13242 fn bare_try_diagnoses() {
13243 let (_ast, diags) = parse_str("try { }");
13244 assert!(
13245 diags
13246 .iter()
13247 .any(|d| d.message == "try requires at least one of `catch` or `finally`"),
13248 "expected bare-try diagnostic, got: {diags:?}",
13249 );
13250 }
13251
13252 #[test]
13253 fn parse_untyped_catch() {
13254 let (ast, diags) = parse_str("try { } catch (e) { }");
13255 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13256 let stmt = single_stmt(&ast);
13257 let crate::StmtKind::Try {
13258 ref catches,
13259 ref finally,
13260 ..
13261 } = stmt.kind
13262 else {
13263 panic!("expected Try");
13264 };
13265 let [clause] = catches.as_slice() else {
13266 panic!("expected one catch clause, got {catches:?}");
13267 };
13268 assert_eq!(clause.binding.name, "e");
13269 assert!(clause.ty.is_none());
13270 assert!(finally.is_none());
13271 }
13272
13273 #[test]
13274 fn parse_multiple_catch_clauses() {
13275 let (ast, diags) = parse_str("try { } catch (e: HttpError) { } catch (f) { }");
13276 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13277 let stmt = single_stmt(&ast);
13278 let crate::StmtKind::Try {
13279 ref catches,
13280 ref finally,
13281 ..
13282 } = stmt.kind
13283 else {
13284 panic!("expected Try");
13285 };
13286 let [first, second] = catches.as_slice() else {
13287 panic!("expected two catch clauses, got {catches:?}");
13288 };
13289 assert_eq!(first.binding.name, "e");
13290 assert!(first.ty.is_some());
13291 assert_eq!(second.binding.name, "f");
13292 assert!(second.ty.is_none());
13293 assert!(finally.is_none());
13294 }
13295
13296 #[test]
13297 fn catch_after_finally_diagnoses() {
13298 let (_ast, diags) = parse_str("try { } finally { } catch (e: Error) { }");
13299 assert!(
13300 !diags.is_empty(),
13301 "expected diagnostic for catch-after-finally"
13302 );
13303 }
13304
13305 #[test]
13306 fn catch_missing_open_paren_diagnoses() {
13307 let (_ast, diags) = parse_str("try { } catch e: Error) { }");
13308 assert!(
13309 diags
13310 .iter()
13311 .any(|d| d.message == "expected `(` after `catch`"),
13312 "expected open-paren diagnostic, got: {diags:?}",
13313 );
13314 }
13315
13316 fn class_members(ast: &Ast) -> &[crate::ClassMember] {
13317 match &single_stmt(ast).kind {
13318 StmtKind::ClassDecl { members, .. } => members,
13319 other => panic!("expected ClassDecl, got {other:?}"),
13320 }
13321 }
13322
13323 #[test]
13324 fn parse_animal_class() {
13325 let src = r#"
13326class Animal {
13327 name: string;
13328 private sound: string;
13329 readonly species: string;
13330
13331 constructor(name: string, sound: string, species: string) {
13332 this.name = name;
13333 this.sound = sound;
13334 this.species = species;
13335 }
13336
13337 speak(): string {
13338 return this.name + " says " + this.sound;
13339 }
13340}
13341"#;
13342 let (ast, diags) = parse_str(src);
13343 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13344 let StmtKind::ClassDecl {
13345 name,
13346 generics,
13347 extends,
13348 implements,
13349 members,
13350 ..
13351 } = &single_stmt(&ast).kind
13352 else {
13353 panic!("expected ClassDecl");
13354 };
13355 assert_eq!(name.name, "Animal");
13356 assert!(generics.is_empty());
13357 assert!(extends.is_none());
13358 assert!(implements.is_empty());
13359 assert_eq!(members.len(), 5);
13360
13361 let crate::ClassMember::Field {
13362 name, modifiers, ..
13363 } = &members[0]
13364 else {
13365 panic!("member 0 should be a field");
13366 };
13367 assert_eq!(name.name, "name");
13368 assert_eq!(modifiers.visibility, crate::Visibility::Public);
13369 assert!(modifiers.readonly.is_none());
13370
13371 let crate::ClassMember::Field { modifiers, .. } = &members[1] else {
13372 panic!("member 1 should be a field");
13373 };
13374 assert_eq!(modifiers.visibility, crate::Visibility::Private);
13375
13376 let crate::ClassMember::Field { modifiers, .. } = &members[2] else {
13377 panic!("member 2 should be a field");
13378 };
13379 assert!(modifiers.readonly.is_some());
13380 assert_eq!(modifiers.visibility, crate::Visibility::Public);
13381
13382 assert!(matches!(
13383 &members[3],
13384 crate::ClassMember::Constructor { .. }
13385 ));
13386
13387 let crate::ClassMember::Method { name, .. } = &members[4] else {
13388 panic!("member 4 should be a method");
13389 };
13390 assert_eq!(name.name, "speak");
13391 }
13392
13393 #[test]
13394 fn parse_dog_class_extends_super_and_void_method() {
13395 let src = r#"
13396class Dog extends Animal {
13397 private tricks: string[];
13398
13399 constructor(name: string) {
13400 super(name, "woof", "canis familiaris");
13401 this.tricks = [];
13402 }
13403
13404 learn(trick: string): void {
13405 this.tricks.push(trick);
13406 }
13407}
13408"#;
13409 let (ast, diags) = parse_str(src);
13410 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13411 let StmtKind::ClassDecl {
13412 extends, members, ..
13413 } = &single_stmt(&ast).kind
13414 else {
13415 panic!("expected ClassDecl");
13416 };
13417 assert!(extends.is_some(), "Dog should extend Animal");
13418 assert_eq!(members.len(), 3);
13419 assert!(matches!(&members[0], crate::ClassMember::Field { .. }));
13420 assert!(matches!(
13421 &members[1],
13422 crate::ClassMember::Constructor { .. }
13423 ));
13424 assert!(matches!(&members[2], crate::ClassMember::Method { .. }));
13425 }
13426
13427 #[test]
13428 fn parse_class_implements_list() {
13429 let (ast, diags) = parse_str("class C implements I, J { run(): void {} }");
13430 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13431 let StmtKind::ClassDecl { implements, .. } = &single_stmt(&ast).kind else {
13432 panic!("expected ClassDecl");
13433 };
13434 assert_eq!(implements.len(), 2);
13435 }
13436
13437 #[test]
13438 fn parse_class_extends_and_implements() {
13439 let (ast, diags) = parse_str("class C extends B implements I { run(): void {} }");
13440 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13441 let StmtKind::ClassDecl {
13442 extends,
13443 implements,
13444 ..
13445 } = &single_stmt(&ast).kind
13446 else {
13447 panic!("expected ClassDecl");
13448 };
13449 assert!(extends.is_some());
13450 assert_eq!(implements.len(), 1);
13451 }
13452
13453 #[test]
13454 fn parse_class_generics_optional_field_and_initializer() {
13455 let (ast, diags) = parse_str("class Box<T> { value?: T; items: T[] = []; }");
13456 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13457 let StmtKind::ClassDecl {
13458 generics, members, ..
13459 } = &single_stmt(&ast).kind
13460 else {
13461 panic!("expected ClassDecl");
13462 };
13463 assert_eq!(generics.len(), 1);
13464 assert_eq!(generics[0].name, "T");
13465
13466 let crate::ClassMember::Field {
13467 optional,
13468 initializer,
13469 ..
13470 } = &members[0]
13471 else {
13472 panic!("member 0 should be a field");
13473 };
13474 assert!(*optional);
13475 assert!(initializer.is_none());
13476
13477 let crate::ClassMember::Field { initializer, .. } = &members[1] else {
13478 panic!("member 1 should be a field");
13479 };
13480 assert!(initializer.is_some());
13481 }
13482
13483 #[test]
13484 fn export_class_is_marked() {
13485 let (ast, diags) = parse_str("export class Foo {}");
13486 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13487 assert_eq!(ast.top_level.len(), 1);
13488 assert_eq!(ast.exported_decls.len(), 1);
13489 assert!(matches!(
13490 &single_stmt(&ast).kind,
13491 StmtKind::ClassDecl { .. }
13492 ));
13493 }
13494
13495 #[test]
13496 fn parameterless_index_signature_is_reported_from_its_member_start() {
13497 for (source, expected) in [
13498 ("interface P { readonly []: number }", "readonly []"),
13499 ("let p: { readonly []: number } = {};", "readonly []"),
13500 ("let q: { []: number } = {};", "[]"),
13501 ] {
13502 let (_ast, diags) = parse_str(source);
13503 let diag = diags
13504 .iter()
13505 .find(|d| d.message.contains("must declare exactly one parameter"))
13506 .unwrap_or_else(|| panic!("no diagnostic for {source}"));
13507 let reported = &source[diag.span.start as usize..diag.span.end as usize];
13508 assert_eq!(reported, expected, "{source}");
13509 }
13510 }
13511
13512 #[test]
13513 fn unterminated_object_type_expects_closing_brace() {
13514 let (_ast, diags) = parse_str("let p: { a: number\n");
13515 assert!(
13516 diags.iter().any(|d| d.message == "expected `}`"),
13517 "{diags:?}"
13518 );
13519 }
13520
13521 #[test]
13522 fn conflicting_visibility_modifiers_diagnose() {
13523 let (_ast, diags) = parse_str("class C { public private x: number; }");
13524 assert!(
13525 diags
13526 .iter()
13527 .any(|d| d.message.contains("at most one visibility modifier")),
13528 "got: {diags:?}",
13529 );
13530 }
13531
13532 #[test]
13533 fn duplicate_readonly_modifier_diagnoses() {
13534 let (_ast, diags) = parse_str("class C { readonly readonly x: number; }");
13535 assert!(
13536 diags
13537 .iter()
13538 .any(|d| d.message.contains("duplicate `readonly`")),
13539 "got: {diags:?}",
13540 );
13541 }
13542
13543 #[test]
13544 fn field_named_like_a_modifier_is_allowed() {
13545 let (ast, diags) = parse_str("class C { private: number; }");
13546 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13547 let crate::ClassMember::Field {
13548 name, modifiers, ..
13549 } = &class_members(&ast)[0]
13550 else {
13551 panic!("expected a field");
13552 };
13553 assert_eq!(name.name, "private");
13554 assert_eq!(modifiers.visibility, crate::Visibility::Public);
13555 }
13556
13557 #[test]
13558 fn method_named_get_is_allowed() {
13559 let (ast, diags) = parse_str("class C { get(): number { return 1; } }");
13560 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13561 let crate::ClassMember::Method { name, .. } = &class_members(&ast)[0] else {
13562 panic!("expected a method");
13563 };
13564 assert_eq!(name.name, "get");
13565 }
13566
13567 #[test]
13568 fn static_method_parses() {
13569 let (ast, diags) = parse_str("class C { static f(): number { return 1; } }");
13570 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13571 let crate::ClassMember::Method {
13572 name, modifiers, ..
13573 } = &class_members(&ast)[0]
13574 else {
13575 panic!("expected a method");
13576 };
13577 assert_eq!(name.name, "f");
13578 assert!(modifiers.static_span.is_some());
13579 }
13580
13581 #[test]
13582 fn static_readonly_field_parses() {
13583 let (ast, diags) = parse_str("class C { static readonly x: number = 1; }");
13584 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13585 let crate::ClassMember::Field {
13586 name,
13587 modifiers,
13588 initializer,
13589 ..
13590 } = &class_members(&ast)[0]
13591 else {
13592 panic!("expected a field");
13593 };
13594 assert_eq!(name.name, "x");
13595 assert!(modifiers.static_span.is_some());
13596 assert!(modifiers.readonly.is_some());
13597 assert!(initializer.is_some());
13598 }
13599
13600 #[test]
13601 fn private_static_method_parses() {
13602 let (ast, diags) = parse_str("class C { private static f(): void {} }");
13603 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13604 let crate::ClassMember::Method { modifiers, .. } = &class_members(&ast)[0] else {
13605 panic!("expected a method");
13606 };
13607 assert_eq!(modifiers.visibility, crate::Visibility::Private);
13608 assert!(modifiers.static_span.is_some());
13609 }
13610
13611 #[test]
13612 fn method_named_static_is_allowed() {
13613 let (ast, diags) = parse_str("class C { static(): number { return 1; } }");
13614 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13615 let crate::ClassMember::Method {
13616 name, modifiers, ..
13617 } = &class_members(&ast)[0]
13618 else {
13619 panic!("expected a method");
13620 };
13621 assert_eq!(name.name, "static");
13622 assert!(modifiers.static_span.is_none());
13623 }
13624
13625 #[test]
13626 fn visibility_after_static_diagnoses() {
13627 let (_ast, diags) = parse_str("class C { static private f(): void {} }");
13628 assert!(
13629 diags
13630 .iter()
13631 .any(|d| d.message.contains("`private` must come before `static`")),
13632 "got: {diags:?}",
13633 );
13634 }
13635
13636 #[test]
13637 fn duplicate_static_modifier_diagnoses() {
13638 let (_ast, diags) = parse_str("class C { static static f(): void {} }");
13639 assert!(
13640 diags
13641 .iter()
13642 .any(|d| d.message.contains("duplicate `static` modifier")),
13643 "got: {diags:?}",
13644 );
13645 }
13646
13647 #[test]
13648 fn static_accessor_is_rejected() {
13649 let (_ast, diags) = parse_str("class C { static get x(): number { return 1; } }");
13650 assert!(
13651 diags
13652 .iter()
13653 .any(|d| d.message.contains("static accessors are not supported")),
13654 "got: {diags:?}",
13655 );
13656 }
13657
13658 #[test]
13659 fn static_constructor_is_rejected() {
13660 let (_ast, diags) = parse_str("class C { static constructor() {} }");
13661 assert!(
13662 diags
13663 .iter()
13664 .any(|d| d.message.contains("a constructor cannot be `static`")),
13665 "got: {diags:?}",
13666 );
13667 }
13668
13669 #[test]
13670 fn optional_static_field_is_rejected() {
13671 let (_ast, diags) = parse_str("class C { static x?: number; }");
13672 assert!(
13673 diags
13674 .iter()
13675 .any(|d| d.message.contains("a static field cannot be optional")),
13676 "got: {diags:?}",
13677 );
13678 }
13679
13680 #[test]
13681 fn async_function_names_the_synchronous_rewrite() {
13682 let (_ast, diags) = parse_str(
13683 "async function main(): Promise<string> {\n const x = await f();\n return x;\n}",
13684 );
13685 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13686 assert_eq!(
13687 messages,
13688 [
13689 "`async` functions are not supported",
13690 "`await` is not supported"
13691 ],
13692 "{diags:?}"
13693 );
13694 assert_eq!(
13695 diags[0].help,
13696 [
13697 "calls return their value directly: remove `async`, write the return type `T` \
13698 instead of `Promise<T>`, and drop `await`"
13699 ]
13700 );
13701 assert_eq!(
13702 diags[1].help,
13703 ["calls return their value directly: remove `await`"]
13704 );
13705 }
13706
13707 #[test]
13708 fn async_is_rejected_on_every_function_form() {
13709 for source in [
13710 "export async function f(): void {}",
13711 "const f = async (): void => {};",
13712 "const f = async x => x;",
13713 "const f = async function (): void {};",
13714 "class C { async run(): void {} }",
13715 "class C { static async run(): void {} }",
13716 ] {
13717 let (_ast, diags) = parse_str(source);
13718 assert_eq!(
13719 diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13720 ["`async` functions are not supported"],
13721 "{source}: {diags:?}"
13722 );
13723 }
13724 }
13725
13726 #[test]
13727 fn await_before_a_template_is_reported() {
13728 for source in ["const a = await `hi`;", "const a = await `a${1}b`;"] {
13729 let (_ast, diags) = parse_str(source);
13730 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13731 assert_eq!(
13732 messages,
13733 ["`await` is not supported"],
13734 "{source}: {diags:?}"
13735 );
13736 }
13737 }
13738
13739 #[test]
13740 fn async_and_await_stay_ordinary_names() {
13741 for source in [
13742 "const async = 1; const y = async + 1;",
13743 "function async(n: number): number { return n; } async(1);",
13744 "const await = 2; const z = await * 2;",
13745 "const await = (n: number): number => n; const a = await(1);",
13746 "const await = [1, 2]; const b = await[0];",
13747 "const async = { k: 1 }; const c = \"k\" in async; const d = async in ({ o: 1 });",
13748 "const async: unknown = 1; const n = async as (number);",
13749 "const await: unknown = 1; const m = await as number;",
13750 "const await = 1; const t = `${await}`;",
13751 "class C { async(): void {} async: number = 1; }",
13752 ] {
13753 let (_ast, diags) = parse_str(source);
13754 assert!(diags.is_empty(), "{source}: {diags:?}");
13755 }
13756 }
13757
13758 #[test]
13759 fn an_async_object_literal_method_names_the_rewrite_once() {
13760 assert_eq!(
13761 crate::typechecker::rules::test_util::compile_messages(
13762 "function main(): string {\n const o = { async run(): string { return \"a\"; } };\n return o.run();\n}\n"
13763 ),
13764 ["`async` functions are not supported"]
13765 );
13766 let (_ast, diags) = parse_str("const o = { async run(): string { return \"a\"; } };");
13767 assert_eq!(
13768 diags[0].help,
13769 [
13770 "calls return their value directly: remove `async`, write the return type `T` \
13771 instead of `Promise<T>`, and drop `await`"
13772 ]
13773 );
13774 }
13775
13776 #[test]
13777 fn await_in_an_async_object_literal_method_is_reported_too() {
13778 let (_ast, diags) =
13779 parse_str("const o = { async run(): string { const x = await f(); return x; } };");
13780 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13781 assert_eq!(
13782 messages,
13783 [
13784 "`async` functions are not supported",
13785 "`await` is not supported"
13786 ],
13787 "{diags:?}"
13788 );
13789 }
13790
13791 #[test]
13792 fn every_async_member_form_reports_async() {
13793 for source in [
13794 "const o = { async *run(): void {} };",
13795 "class C { async *run(): void {} }",
13796 "const o = { async [k](): void {} };",
13797 "const o = { async in(): void {} };",
13798 "class C { async as(): void {} }",
13799 ] {
13800 let (_ast, diags) = parse_str(source);
13801 assert_eq!(
13802 diags[0].message, "`async` functions are not supported",
13803 "{source}: {diags:?}"
13804 );
13805 }
13806 }
13807
13808 #[test]
13809 fn async_string_key_methods_report_only_async() {
13810 for source in [
13811 "const o = { async \"s\"(): string { return \"a\"; } };",
13812 "class C { async \"s\"(): string { return \"a\"; } }",
13813 ] {
13814 let (_ast, diags) = parse_str(source);
13815 assert_eq!(
13816 diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13817 ["`async` functions are not supported"],
13818 "{source}: {diags:?}"
13819 );
13820 }
13821 }
13822
13823 #[test]
13824 fn async_before_a_line_break_is_a_member_of_its_own() {
13825 for source in [
13826 "class C {\n async\n run(): void {}\n}",
13827 "const o = { async\n run(): void {} };",
13828 ] {
13829 let (_ast, diags) = parse_str(source);
13830 assert!(
13831 diags
13832 .iter()
13833 .all(|d| d.message != "`async` functions are not supported"),
13834 "{source}: {diags:?}"
13835 );
13836 }
13837 }
13838
13839 #[test]
13840 fn async_stays_an_object_literal_key() {
13841 let messages = crate::typechecker::rules::test_util::compile_messages(
13842 "function main(): number {\n const async = 2;\n const a = { async: 1 };\n const b = { async };\n const c = { async(): number { return 3; } };\n const d = { async, e: async * 2 };\n return a.async + b.async + c.async() + d.e;\n}\n",
13843 );
13844 assert!(messages.is_empty(), "{messages:?}");
13845 }
13846
13847 #[test]
13848 fn for_await_names_the_rewrite_once() {
13849 assert_eq!(
13850 crate::typechecker::rules::test_util::compile_messages(
13851 "import * as fs from \"submilli:fs\";\nfunction main(): string {\n for await (const e of fs.list(\".\", { recursive: false })) { }\n return \"x\";\n}\n"
13852 ),
13853 ["`for await` is not supported"]
13854 );
13855 let (ast, diags) = parse_str("for await (const e of [1]) { }");
13856 assert_eq!(
13857 diags[0].help,
13858 ["calls return their values directly: remove `await` and write `for (const e of …)`"]
13859 );
13860 assert_eq!(ast.top_level.len(), 1, "the loop is still read");
13861 }
13862
13863 #[test]
13864 fn a_broken_switch_leaves_later_declarations_at_the_top_level() {
13865 assert_eq!(
13866 crate::typechecker::rules::test_util::compile_messages(
13867 "function g(): void { switch (1) { case 1 } }\nclass C {}\nfunction main() { return \"x\"; }\n"
13868 ),
13869 [
13870 "expected `:` after `case` label",
13871 "expected expression",
13872 "expected `:` and return type"
13873 ]
13874 );
13875 }
13876
13877 #[test]
13878 fn every_switch_arm_error_restores_the_nesting() {
13879 for switch in [
13880 "switch (1) { case 1 }",
13881 "switch (1) { case : break; }",
13882 "switch (1) { default: break; default: break; }",
13883 "switch (1) { default }",
13884 "switch (1) { x; }",
13885 "switch (1) { case 1: }",
13886 ] {
13887 let source = format!("function g(): void {{ {switch} }}\nclass C {{}}\n");
13888 let (_ast, diags) = parse_str(&source);
13889 assert!(
13890 diags.iter().all(|d| !d.message.contains("top level")),
13891 "{switch}: {diags:?}"
13892 );
13893 }
13894 }
13895
13896 #[test]
13897 fn protected_member_is_rejected() {
13898 let (_ast, diags) = parse_str("class C { protected x: number; }");
13899 assert!(
13900 diags
13901 .iter()
13902 .any(|d| d.message.contains("`protected` is not supported")),
13903 "got: {diags:?}",
13904 );
13905 }
13906
13907 #[test]
13908 fn abstract_member_is_rejected() {
13909 let (_ast, diags) = parse_str("class C { abstract run(): void; }");
13910 assert!(
13911 diags
13912 .iter()
13913 .any(|d| d.message.contains("abstract classes are not supported")),
13914 "got: {diags:?}",
13915 );
13916 }
13917
13918 #[test]
13919 fn getter_parses_as_accessor() {
13920 let (ast, diags) = parse_str("class C { get value(): number { return 1; } }");
13921 assert!(diags.is_empty(), "got: {diags:?}");
13922 let crate::ClassMember::Accessor { kind, name, .. } = &class_members(&ast)[0] else {
13923 panic!("expected an accessor member");
13924 };
13925 assert_eq!(*kind, crate::AccessorKind::Get);
13926 assert_eq!(name.name, "value");
13927 }
13928
13929 #[test]
13930 fn getter_without_return_type_is_rejected() {
13931 let (ast, diags) = parse_str("class C { get value() { return 1; } }");
13932 assert_eq!(diags.len(), 1, "got: {diags:?}");
13933 assert_eq!(diags[0].message, "expected `:` and return type");
13934 assert_eq!(diags[0].help, vec!["get name(): T { … }".to_string()]);
13935 assert!(
13936 matches!(
13937 &class_members(&ast)[0],
13938 crate::ClassMember::Accessor {
13939 return_type: None,
13940 ..
13941 }
13942 ),
13943 "the getter is still recovered as a member"
13944 );
13945 }
13946
13947 #[test]
13948 fn setter_parses_as_accessor() {
13949 let (ast, diags) = parse_str("class C { set value(v: number) {} }");
13950 assert!(diags.is_empty(), "got: {diags:?}");
13951 let crate::ClassMember::Accessor { kind, param, .. } = &class_members(&ast)[0] else {
13952 panic!("expected an accessor member");
13953 };
13954 assert_eq!(*kind, crate::AccessorKind::Set);
13955 assert!(param.is_some(), "setter should have a parameter");
13956 }
13957
13958 #[test]
13959 fn param_property_modifier_outside_constructor_is_rejected() {
13960 let (_ast, diags) = parse_str("function f(public x: number): void {}");
13961 assert!(
13962 diags.iter().any(|d| d
13963 .message
13964 .contains("parameter properties are only allowed in a constructor")),
13965 "got: {diags:?}",
13966 );
13967 }
13968
13969 #[test]
13970 fn constructor_return_type_is_rejected() {
13971 let (_ast, diags) = parse_str("class C { constructor(): void {} }");
13972 assert!(
13973 diags.iter().any(|d| d
13974 .message
13975 .contains("constructor cannot declare a return type")),
13976 "got: {diags:?}",
13977 );
13978 }
13979
13980 #[test]
13981 fn field_initializer_without_type_is_rejected() {
13982 let (_ast, diags) = parse_str("class C { x = 1; }");
13983 assert!(
13984 diags
13985 .iter()
13986 .any(|d| d.message.contains("class fields require a type annotation")),
13987 "got: {diags:?}",
13988 );
13989 }
13990
13991 #[test]
13992 fn this_outside_method_is_left_to_typechecker() {
13993 let (_ast, diags) = parse_str("function f(): string { return this.name; }");
13994 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13995 }
13996
13997 #[test]
13998 fn super_outside_method_is_rejected() {
13999 let (_ast, diags) = parse_str("function f(): void { super(1); }");
14000 assert!(
14001 diags.iter().any(|d| d
14002 .message
14003 .contains("`super` is only valid inside a class method or constructor body")),
14004 "got: {diags:?}",
14005 );
14006 }
14007
14008 #[test]
14009 fn this_inside_method_is_accepted_by_parser() {
14010 let (_ast, diags) = parse_str("class C { x: number; read(): number { return this.x; } }");
14011 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
14012 }
14013 #[test]
14014 fn parse_void_expression_preserves_operand_and_precedence() {
14015 let (ast, diags) = parse_str("void effect() === undefined;");
14016 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
14017 let expression = expr_of_single_stmt(&ast);
14018 let crate::ExprKind::Binary { lhs, rhs, .. } = expression.kind else {
14019 panic!("expected equality")
14020 };
14021 let crate::ExprKind::Void { operand } = ast.try_expr(lhs).unwrap().kind else {
14022 panic!("expected void")
14023 };
14024 assert!(matches!(
14025 ast.try_expr(operand).unwrap().kind,
14026 crate::ExprKind::Call { .. }
14027 ));
14028 assert!(matches!(
14029 ast.try_expr(rhs).unwrap().kind,
14030 crate::ExprKind::Identifier(_)
14031 ));
14032 }
14033
14034 #[test]
14035 fn parse_void_newline_and_object_operands() {
14036 for source in [
14037 "void\n0;",
14038 "void { value: 1 };",
14039 "void\n{ value: 1 };",
14040 "void /x/;",
14041 "void void 0;",
14042 ] {
14043 let (_, diags) = parse_str(source);
14044 assert!(diags.is_empty(), "{source}: {diags:?}");
14045 }
14046 let (_, diags) = parse_str("void; 0;");
14047 assert!(
14048 !diags.is_empty(),
14049 "an explicit semicolon cannot split void from its operand"
14050 );
14051 }
14052
14053 #[test]
14054 fn parse_optional_parameters_and_defaults_in_callable_forms() {
14055 for source in [
14056 "function f(x = 1): number { return x; }",
14057 "function f(x: number = 1, y: number): number { return y; }",
14058 "function f({ x } = { x: 1 }): number { return x; }",
14059 "const f = (x?: number) => x;",
14060 "const f = (x: number = effect()) => x;",
14061 "const f = function(x: number = effect()) { return x; };",
14062 "const f = { m(x: number = effect()): number { return x; } };",
14063 "const f = ([x = 1]: number[] = values) => x;",
14064 "let f: (x?: number, ...rest: string[]) => void = value;",
14065 "let f: { method?(x?: number): void } = value;",
14066 "function f(x: number = 1, y?: number): void {}",
14067 ] {
14068 let (_, diags) = parse_str(source);
14069 assert!(diags.is_empty(), "{source}: {diags:?}");
14070 }
14071 }
14072
14073 #[test]
14074 fn parse_invalid_optional_parameters() {
14075 for source in [
14076 "function f(x?: number, y: number): void {}",
14077 "const f = (x?: number, y: number) => y;",
14078 "let f: (x?: number, y: number) => void = value;",
14079 "function f(...x?: number[]): void {}",
14080 "function f(x?: number = 1): void {}",
14081 "const f = (x?: number = 1) => x;",
14082 "function f({x}?: T): void {}",
14083 ] {
14084 let (_, diags) = parse_str(source);
14085 assert!(!diags.is_empty(), "expected diagnostic for {source}");
14086 }
14087 }
14088
14089 #[test]
14090 fn parse_optional_tuple_forms_and_order() {
14091 for source in [
14092 "let x: [number, string?] = value;",
14093 "let x: [x?: number, y?: string] = value;",
14094 "let x: readonly [(number | null)?, string?] = value;",
14095 ] {
14096 let (_, diags) = parse_str(source);
14097 assert!(diags.is_empty(), "{source}: {diags:?}");
14098 }
14099 for source in [
14100 "let x: [number?, string] = value;",
14101 "let x: [x: number?] = value;",
14102 ] {
14103 let (_, diags) = parse_str(source);
14104 assert!(!diags.is_empty(), "expected diagnostic for {source}");
14105 }
14106 }
14107
14108 #[test]
14109 fn parse_array_pattern_defaults_preserve_holes() {
14110 let (ast, diags) = parse_str("const [, a = effect(), b] = values;");
14111 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
14112 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
14113 panic!("expected pattern")
14114 };
14115 let crate::Binding::Array {
14116 elems, defaults, ..
14117 } = binding
14118 else {
14119 panic!("expected array")
14120 };
14121 assert_eq!(elems.len(), 3);
14122 assert_eq!(defaults.len(), elems.len());
14123 assert!(elems[0].is_none());
14124 assert!(defaults[0].is_none());
14125 assert!(defaults[1].is_some());
14126 assert!(defaults[2].is_none());
14127 }
14128
14129 #[test]
14130 fn parse_optional_class_method_forms() {
14131 let (ast, diags) =
14132 parse_str("class C { declared?(x?: number): void; implemented?(): void\n{} }");
14133 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
14134 let members = class_members(&ast);
14135 assert!(matches!(
14136 &members[0],
14137 crate::ClassMember::Field {
14138 optional: true,
14139 initializer: None,
14140 ty: crate::TypeAnnotation {
14141 kind: crate::TypeAnnotationKind::Function { .. },
14142 ..
14143 },
14144 ..
14145 }
14146 ));
14147 assert!(matches!(
14148 &members[1],
14149 crate::ClassMember::Method { optional: true, .. }
14150 ));
14151 }
14152 #[test]
14153 fn defaults_retain_their_callable_receiver_scope() {
14154 for source in [
14155 "class C { value: number = 1; method(value: number = this.value): number { return value; } }",
14156 "class C { constructor(value: number = this.value) {} value: number = 1; }",
14157 "const f = function(this: { value: number }, value: number = this.value): number { return value; };",
14158 ] {
14159 let (ast, diagnostics) = parse_str(source);
14160 assert!(diagnostics.is_empty(), "{source}: {diagnostics:?}");
14161 assert!(
14162 ast.source_expressions()
14163 .iter()
14164 .any(|expr| matches!(expr.kind, ExprKind::This))
14165 );
14166 assert!(
14167 !ast.source_expressions()
14168 .iter()
14169 .any(|expr| matches!(expr.kind, ExprKind::ThisOutsideReceiver))
14170 );
14171 }
14172 }
14173}