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 file,
69 pos: 0,
70 ast: Ast::new(),
71 diagnostics: Vec::new(),
72 block_depth: 0,
73 class_member_body_depth: 0,
74 function_expression_body_depth: 0,
75 recursion_depth: 0,
76 recursion_limit_span: None,
77 resume_at_next_member: false,
78 last_as_type_end: None,
79 trailing_declarators: Vec::new(),
80 eof,
81 fatal: None,
82 };
83 p.parse_program();
84 if p.fatal.is_none() {
85 p.reject_use_strict_with_non_simple_params();
86 }
87 if let Some(span) = p.recursion_limit_span {
88 p.fatal = Some(CompilerFailure::Limit {
89 stage: CompilerStage::Parse,
90 span: Some(span),
91 message: "parser recursion limit exceeded".into(),
92 help: vec!["simplify nested syntax or split it into separate declarations".into()],
93 });
94 }
95 if let Some(fatal) = p.fatal {
96 return Err(CompileError {
97 diagnostics: p.diagnostics,
98 fatal: Some(fatal),
99 });
100 }
101 p.ast
102 .validate_source(source, file)
103 .map_err(|error| CompileError {
104 diagnostics: p.diagnostics.clone(),
105 fatal: Some(error.into_compiler_failure(CompilerStage::Parse)),
106 })?;
107 crate::tree_height::check_syntax(&p.ast).map_err(|fatal| CompileError {
108 diagnostics: p.diagnostics.clone(),
109 fatal: Some(fatal),
110 })?;
111 Ok((p.ast, p.diagnostics))
112}
113
114fn validate_token_stream(source: &str, tokens: &[Token], file: FileId) -> Result<(), &'static str> {
115 if !matches!(tokens.last().map(|token| &token.kind), Some(TokenKind::Eof)) {
116 return Err("token stream must end with EOF");
117 }
118 let mut previous = 0;
119 for (index, token) in tokens.iter().enumerate() {
120 let span = token.span;
121 if span.file != file
122 || span.start < previous
123 || span.start > span.end
124 || source.get(span.start as usize..span.end as usize).is_none()
125 {
126 return Err("token span is outside its source or out of order");
127 }
128 if matches!(token.kind, TokenKind::Eof) && index != tokens.len() - 1 {
129 return Err("tokens follow EOF");
130 }
131 if let Some(doc) = &token.leading_doc {
132 let span = doc.span;
133 if span.file != file
134 || span.start > span.end
135 || span.end > token.span.start
136 || source.get(span.start as usize..span.end as usize) != Some(doc.text.as_str())
137 || !doc.text.starts_with("/**")
138 || !doc.text.ends_with("*/")
139 {
140 return Err("documentation metadata does not match its source");
141 }
142 }
143 previous = span.start;
144 }
145 Ok(())
146}
147
148pub(crate) struct Parser<'a> {
149 source: &'a str,
150 tokens: Vec<Token>,
151 file: FileId,
152 pos: usize,
153 ast: Ast,
154 diagnostics: Vec<Diagnostic>,
155 block_depth: u32,
157 class_member_body_depth: u32,
159 function_expression_body_depth: u32,
160 recursion_depth: usize,
161 recursion_limit_span: Option<Span>,
162 resume_at_next_member: bool,
166 last_as_type_end: Option<u32>,
170 trailing_declarators: Vec<StmtId>,
172 eof: Token,
173 fatal: Option<CompilerFailure>,
174}
175
176impl<'a> Parser<'a> {
177 fn span(&self, start: u32, end: u32) -> Span {
178 Span {
179 file: self.file,
180 start,
181 end,
182 }
183 }
184
185 fn type_name(&mut self, span: Span) -> Option<Ident> {
186 match span.text(self.source, self.file) {
187 Ok(name) => Some(Ident {
188 name: name.to_string(),
189 span,
190 }),
191 Err(error) => {
192 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
193 None
194 }
195 }
196 }
197
198 fn parse_program(&mut self) {
199 while !self.is_at_eof() {
200 if self.fatal.is_some() || self.error_count() >= MAX_ERRORS {
201 break;
202 }
203 if matches!(self.peek().kind, TokenKind::Semicolon) {
204 self.advance();
205 continue;
206 }
207 let pos_before = self.pos;
208 if let Some(id) = self.parse_statement() {
209 self.ast.top_level.push(id);
210 let trailing = self.take_trailing_declarators();
211 self.ast.top_level.extend(trailing);
212 } else {
213 if self.fatal.is_some() {
214 break;
215 }
216 if self.pos != pos_before && self.peek_starts_top_level_item() {
221 continue;
222 }
223 self.recover();
224 if self.pos != pos_before && matches!(self.peek().kind, TokenKind::RightBrace) {
225 self.advance();
226 continue;
227 }
228 if self.pos == pos_before && !self.is_at_eof() {
230 self.advance();
231 }
232 }
233 }
234 }
235
236 fn parse_statement(&mut self) -> Option<StmtId> {
237 self.trailing_declarators.clear();
238 self.with_recursion_limit(Self::parse_statement_inner)
239 }
240
241 fn parse_statement_inner(&mut self) -> Option<StmtId> {
242 self.reject_async_modifier();
243 match self.peek().kind {
244 TokenKind::Let => self.parse_let_or_const(false),
245 TokenKind::Const => self.parse_let_or_const(true),
246 TokenKind::Function => self.parse_function_decl(),
247 TokenKind::Class => self.parse_class_decl(),
248 TokenKind::Interface => self.parse_interface_decl(),
249 TokenKind::Enum => self.parse_enum_decl(),
250 TokenKind::Identifier if self.at_type_alias_head() => self.parse_type_alias_decl(),
251 TokenKind::Export => self.parse_export(),
252 TokenKind::Import => self.parse_import_decl(),
253 TokenKind::If => self.parse_if(),
254 TokenKind::While => self.parse_while(),
255 TokenKind::Do => self.parse_do_while(),
256 TokenKind::For => self.parse_for(),
257 TokenKind::Switch => self.parse_switch(),
258 TokenKind::Break => self.parse_break(),
259 TokenKind::Continue => self.parse_continue(),
260 TokenKind::Return => self.parse_return(),
261 TokenKind::Throw => self.parse_throw(),
262 TokenKind::Try => self.parse_try(),
263 TokenKind::LeftBrace => self.parse_block(),
264 _ => self.parse_expression_statement(),
265 }
266 }
267
268 fn parse_expression_statement(&mut self) -> Option<StmtId> {
269 let expr_id = self.parse_expression()?;
270 let expr_span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
271 if !self.at_statement_end() {
272 let what = if matches!(
273 parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.kind,
274 ExprKind::Assign { .. }
275 ) {
276 "assignment"
277 } else {
278 "expression"
279 };
280 self.error_at_peek(format!("expected `;` after {what}"));
281 return None;
282 }
283 let end = self.finish_statement();
284 let span = self.span(expr_span.start, end);
285 let kind = self.statement_kind_for(expr_id)?;
286 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
287 }
288
289 fn statement_kind_for(&mut self, expr_id: ExprId) -> Option<StmtKind> {
292 let ExprKind::Assign {
293 target,
294 op,
295 op_span,
296 value,
297 } = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?
298 .kind
299 .clone()
300 else {
301 return Some(StmtKind::Expr(expr_id));
302 };
303 Some(
304 match (
305 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?
306 .kind
307 .clone(),
308 op,
309 ) {
310 (ExprKind::Identifier(target), None) => StmtKind::Assign { target, value },
311 (ExprKind::Identifier(target), Some(op)) => StmtKind::CompoundAssign {
312 target,
313 op,
314 op_span,
315 value,
316 },
317 (ExprKind::FieldAccess { receiver, name }, None) => StmtKind::AssignField {
318 receiver,
319 field_name: name,
320 value,
321 },
322 (ExprKind::FieldAccess { receiver, name }, Some(op)) => {
323 StmtKind::CompoundAssignField {
324 receiver,
325 field_name: name,
326 op,
327 op_span,
328 value,
329 }
330 }
331 (ExprKind::IndexAccess { receiver, index }, None) => StmtKind::AssignIndex {
332 receiver,
333 index,
334 value,
335 },
336 (ExprKind::IndexAccess { receiver, index }, Some(op)) => {
337 StmtKind::CompoundAssignIndex {
338 receiver,
339 index,
340 op,
341 op_span,
342 value,
343 }
344 }
345 _ => return self.invariant_failure("invalid assignment target"),
346 },
347 )
348 }
349
350 fn parse_let_or_const(&mut self, is_const: bool) -> Option<StmtId> {
355 let mut doc = self.take_leading_doc();
356 let kw = self.advance();
357
358 let mut declarators = Vec::new();
359 let mut start = kw.span.start;
360 loop {
361 let Some(kind) = self.parse_declarator(is_const, doc.take()) else {
362 self.discard_detached_patterns(&declarators);
363 return None;
364 };
365 if !matches!(self.peek().kind, TokenKind::Comma) {
366 if !self.at_statement_end() {
367 self.error_at_peek("expected `;` after declaration");
368 self.discard_detached_patterns(&declarators);
369 return None;
370 }
371 let end = self.finish_statement();
372 declarators.push(self.push_declarator(kind, start, end)?);
373 break;
374 }
375 let end = self.prev_token_end();
376 declarators.push(self.push_declarator(kind, start, end)?);
377 self.advance();
378 start = self.peek().span.start;
379 }
380
381 let first = declarators.remove(0);
382 self.trailing_declarators = declarators;
383 Some(first)
384 }
385
386 fn push_declarator(&mut self, kind: StmtKind, start: u32, end: u32) -> Option<StmtId> {
387 let span = self.span(start, end);
388 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
389 }
390
391 fn take_trailing_declarators(&mut self) -> Vec<StmtId> {
395 std::mem::take(&mut self.trailing_declarators)
396 }
397
398 fn parse_declarator(
400 &mut self,
401 is_const: bool,
402 doc: Option<crate::DocComment>,
403 ) -> Option<StmtKind> {
404 let binding = match self.peek().kind {
405 TokenKind::LeftBrace | TokenKind::LeftBracket => Some(self.parse_binding()?),
406 _ => None,
407 };
408
409 let name = if binding.is_none() {
410 let name_tok =
411 self.expect_binding_identifier("expected identifier after `let`/`const`")?;
412 Some(self.ident_from_token(&name_tok))
413 } else {
414 None
415 };
416
417 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
418 self.advance();
419 Some(self.parse_type_annotation()?)
420 } else {
421 None
422 };
423
424 let implicit = !is_const
425 && ty.is_some()
426 && matches!(self.peek().kind, TokenKind::Semicolon | TokenKind::Comma);
427 let value = if let (true, Some(name)) = (implicit, &name) {
428 let value = parse_arena_result(
430 self.ast.try_push_expr(Expr {
431 kind: ExprKind::Identifier(Ident {
432 name: "undefined".to_string(),
433 span: name.span,
434 }),
435 span: name.span,
436 }),
437 &mut self.fatal,
438 )?;
439 self.ast.implicit_initializers.insert(value);
440 self.ast.synthetic_undefined.insert(value);
441 value
442 } else {
443 if !matches!(self.peek().kind, TokenKind::Equals) {
444 let (msg, help) = if is_const {
445 (
446 "`const` declaration requires an initializer",
447 vec!["const x: T = expr;".to_string()],
448 )
449 } else {
450 (
451 "`let` declaration requires an initializer",
452 vec![
453 "let x: T = expr;".to_string(),
454 "or declare one that starts empty: `let x: T | undefined;`".to_string(),
455 ],
456 )
457 };
458 self.error_at_peek_with_help(msg, help);
459 return None;
460 }
461 self.advance();
462 self.parse_expression()?
463 };
464
465 Some(match (binding, name) {
466 (Some(binding), _) => {
467 if is_const {
468 StmtKind::ConstPattern {
469 binding,
470 ty,
471 value,
472 doc,
473 }
474 } else {
475 StmtKind::LetPattern {
476 binding,
477 ty,
478 value,
479 doc,
480 }
481 }
482 }
483 (None, Some(name)) => {
484 if is_const {
485 StmtKind::Const {
486 name,
487 ty,
488 value,
489 doc,
490 }
491 } else {
492 StmtKind::Let {
493 name,
494 ty,
495 value,
496 doc,
497 }
498 }
499 }
500 (None, None) => return self.invariant_failure("either binding or name must be Some"),
501 })
502 }
503
504 fn parse_binding(&mut self) -> Option<Binding> {
506 match self.peek().kind {
507 TokenKind::LeftBrace => self.parse_object_binding(),
508 TokenKind::LeftBracket => self.parse_array_binding(),
509 _ => {
510 self.error_at_peek("expected `{` or `[` to start a destructuring pattern");
511 None
512 }
513 }
514 }
515
516 fn parse_object_binding(&mut self) -> Option<Binding> {
517 let open = self.advance();
518 let mut fields: Vec<ObjectPatternField> = Vec::new();
519 let mut rest: Option<Ident> = None;
520
521 if matches!(self.peek().kind, TokenKind::RightBrace) {
522 self.error_at_peek("empty object destructuring pattern");
523 return None;
524 }
525
526 loop {
527 if matches!(self.peek().kind, TokenKind::DotDotDot) {
528 let dots = self.advance();
529 let name_tok = self.expect_binding_identifier("expected identifier after `...`")?;
530 let name = self.ident_from_token(&name_tok);
531 if !matches!(self.peek().kind, TokenKind::RightBrace) {
532 self.error_at_peek_with_help(
533 "rest element must be the last element in a destructuring pattern",
534 vec!["move `...` after every other binding".to_string()],
535 );
536 return None;
537 }
538 rest = Some(Ident {
539 name: name.name,
540 span: self.span(dots.span.start, name.span.end),
541 });
542 break;
543 }
544
545 let source_tok = self.expect_property_name("expected field name in object pattern")?;
546 let source = self.ident_from_token(&source_tok);
547 let field_span_start = source.span.start;
548
549 let (local, field_end) = if matches!(self.peek().kind, TokenKind::Colon) {
550 self.advance();
551 match self.peek().kind {
552 TokenKind::LeftBrace | TokenKind::LeftBracket => {
553 self.error_at_peek_with_help(
554 "nested destructuring is not supported",
555 vec![
556 "destructure once and access nested fields explicitly: const { a } = obj; const inner = a.b;"
557 .to_string(),
558 ],
559 );
560 return None;
561 }
562 _ => {
563 let local_tok = self.expect_binding_identifier(
564 "expected identifier after `:` in pattern",
565 )?;
566 let local = self.ident_from_token(&local_tok);
567 let end = local.span.end;
568 (local, end)
569 }
570 }
571 } else {
572 if !matches!(source_tok.kind, TokenKind::Identifier) {
573 self.error_at_peek("expected `:` after keyword field name in object pattern");
574 return None;
575 }
576 self.reject_strict_mode_reserved_word(&source_tok);
577 self.reject_eval_or_arguments(source_tok.span);
578 let end = source.span.end;
579 (source.clone(), end)
580 };
581
582 let default = if matches!(self.peek().kind, TokenKind::Equals) {
583 self.advance();
584 Some(self.parse_expression()?)
585 } else {
586 None
587 };
588 let field_end = match default {
589 Some(value) => {
590 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?
591 .span
592 .end
593 }
594 None => field_end,
595 };
596
597 fields.push(ObjectPatternField {
598 source,
599 local,
600 default,
601 span: self.span(field_span_start, field_end),
602 });
603
604 match self.peek().kind {
605 TokenKind::Comma => {
606 self.advance();
607 if matches!(self.peek().kind, TokenKind::RightBrace) {
608 break;
609 }
610 }
611 TokenKind::RightBrace => break,
612 _ => {
613 self.error_at_peek("expected `,` or `}` in object pattern");
614 return None;
615 }
616 }
617 }
618
619 if !matches!(self.peek().kind, TokenKind::RightBrace) {
620 self.error_at_peek("expected `}` to close object pattern");
621 return None;
622 }
623 let close = self.advance();
624
625 Some(Binding::Object {
626 fields,
627 rest,
628 span: self.span(open.span.start, close.span.end),
629 })
630 }
631
632 fn parse_array_binding(&mut self) -> Option<Binding> {
633 let open = self.advance();
634 let mut elems: Vec<Option<Ident>> = Vec::new();
635 let mut defaults = Vec::new();
636 let mut rest: Option<Ident> = None;
637
638 if matches!(self.peek().kind, TokenKind::RightBracket) {
639 self.error_at_peek("empty array destructuring pattern");
640 return None;
641 }
642
643 loop {
644 match self.peek().kind {
645 TokenKind::Comma => {
646 elems.push(None);
647 defaults.push(None);
648 self.advance();
649 if matches!(self.peek().kind, TokenKind::RightBracket) {
650 break;
651 }
652 continue;
653 }
654 TokenKind::RightBracket => break,
655 TokenKind::DotDotDot => {
656 let dots = self.advance();
657 let name_tok =
658 self.expect_binding_identifier("expected identifier after `...`")?;
659 let name = self.ident_from_token(&name_tok);
660 if !matches!(self.peek().kind, TokenKind::RightBracket) {
661 self.error_at_peek_with_help(
662 "rest element must be the last element in a destructuring pattern",
663 vec!["move `...` after every other binding".to_string()],
664 );
665 return None;
666 }
667 rest = Some(Ident {
668 name: name.name,
669 span: self.span(dots.span.start, name.span.end),
670 });
671 break;
672 }
673 TokenKind::LeftBrace | TokenKind::LeftBracket => {
674 self.error_at_peek_with_help(
675 "nested destructuring is not supported",
676 vec![
677 "destructure once and access nested elements explicitly: const [a] = arr; const inner = a[0];"
678 .to_string(),
679 ],
680 );
681 return None;
682 }
683 _ => {
684 let name_tok =
685 self.expect_binding_identifier("expected identifier in array pattern")?;
686 let name = self.ident_from_token(&name_tok);
687
688 let default = if matches!(self.peek().kind, TokenKind::Equals) {
689 self.advance();
690 Some(self.parse_expression()?)
691 } else {
692 None
693 };
694 defaults.push(default);
695 elems.push(Some(name));
696
697 match self.peek().kind {
698 TokenKind::Comma => {
699 self.advance();
700 if matches!(self.peek().kind, TokenKind::RightBracket) {
701 break;
702 }
703 }
704 TokenKind::RightBracket => break,
705 _ => {
706 self.error_at_peek("expected `,` or `]` in array pattern");
707 return None;
708 }
709 }
710 }
711 }
712 }
713
714 if !matches!(self.peek().kind, TokenKind::RightBracket) {
715 self.error_at_peek("expected `]` to close array pattern");
716 return None;
717 }
718 let close = self.advance();
719
720 Some(Binding::Array {
721 elems,
722 defaults,
723 rest,
724 span: self.span(open.span.start, close.span.end),
725 })
726 }
727
728 fn parse_function_decl(&mut self) -> Option<StmtId> {
729 let doc = self.take_leading_doc();
730 let kw = self.advance();
731
732 let name_tok = self.expect_binding_identifier("expected function name")?;
733 let name = self.ident_from_token(&name_tok);
734 let is_main = self.block_depth == 0 && name.name == "main";
735 let decl = self.parse_function_decl_after_name(doc, kw, name);
736 if decl.is_none() && is_main {
737 self.ast.unparsed_main = true;
738 }
739 decl
740 }
741
742 fn parse_function_decl_after_name(
743 &mut self,
744 doc: Option<crate::DocComment>,
745 kw: Token,
746 name: Ident,
747 ) -> Option<StmtId> {
748 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
749 self.parse_generic_param_list()?
750 } else {
751 Vec::new()
752 };
753
754 if !matches!(self.peek().kind, TokenKind::LeftParen) {
755 self.error_at_peek("expected `(` after function name");
756 return None;
757 }
758 self.advance();
759
760 let params = self.parse_param_list(false)?;
761 self.advance(); if !matches!(self.peek().kind, TokenKind::Colon) {
764 self.error_at_peek_with_help(
765 "expected `:` and return type",
766 vec![format!(
767 "write the return type: `function {}(…): T {{ … }}`, with `void` when it returns nothing",
768 name.name
769 )],
770 );
771 if matches!(self.peek().kind, TokenKind::LeftBrace) {
774 let _ = self.parse_outer_this_boundary(Self::parse_block);
775 }
776 return None;
777 }
778 self.advance();
779 let (return_type, type_predicate) =
780 self.parse_predicate_or_return_type(TypePos::Anywhere)?;
781
782 let body = self.parse_outer_this_boundary(Self::parse_block)?;
785 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
786 .span
787 .end;
788
789 parse_arena_result(
790 self.ast.try_push_stmt(Stmt {
791 kind: StmtKind::Function {
792 name,
793 generics,
794 params,
795 return_type,
796 type_predicate,
797 body,
798 doc,
799 },
800 span: self.span(kw.span.start, body_end),
801 }),
802 &mut self.fatal,
803 )
804 }
805
806 fn parse_class_decl(&mut self) -> Option<StmtId> {
807 let doc = self.take_leading_doc();
808 let kw = self.advance();
809 if self.block_depth > 0 {
812 self.error_at_with_help(
813 kw.span,
814 "a `class` must be declared at the top level of the module",
815 vec!["move this `class` outside any block or function body".to_string()],
816 );
817 }
818 let name = self.parse_type_decl_name("a", "class")?;
819
820 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
821 self.parse_generic_param_list()?
822 } else {
823 Vec::new()
824 };
825
826 let extends = if matches!(self.peek().kind, TokenKind::Extends) {
827 self.advance();
828 Some(self.parse_type_annotation()?)
829 } else {
830 None
831 };
832
833 let mut implements: Vec<crate::TypeAnnotation> = Vec::new();
834 if matches!(self.peek().kind, TokenKind::Implements) {
835 self.advance();
836 loop {
837 implements.push(self.parse_type_annotation()?);
838 if matches!(self.peek().kind, TokenKind::Comma) {
839 self.advance();
840 continue;
841 }
842 break;
843 }
844 }
845
846 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
847 self.error_at_peek("expected `{` after class header");
848 return None;
849 }
850 self.advance();
851
852 let mut members: Vec<crate::ClassMember> = Vec::new();
853 let mut member_failed = false;
854 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
858 && !(member_failed && self.peek_starts_top_level_item())
859 {
860 if matches!(self.peek().kind, TokenKind::Semicolon) {
862 self.advance();
863 continue;
864 }
865 match self.parse_class_member() {
866 Some(member) => members.push(member),
867 None if std::mem::take(&mut self.resume_at_next_member) => member_failed = true,
868 None => return None,
869 }
870 }
871
872 if !matches!(self.peek().kind, TokenKind::RightBrace) {
873 self.error_at_peek("expected `}` to close class");
874 return None;
875 }
876 let close = self.advance();
877 if member_failed {
878 return None;
879 }
880
881 parse_arena_result(
882 self.ast.try_push_stmt(Stmt {
883 kind: StmtKind::ClassDecl {
884 name,
885 generics,
886 extends,
887 implements,
888 members,
889 doc,
890 },
891 span: self.span(kw.span.start, close.span.end),
892 }),
893 &mut self.fatal,
894 )
895 }
896
897 fn parse_class_member(&mut self) -> Option<crate::ClassMember> {
898 let doc = self.take_leading_doc();
899 let member_start = self.peek().span.start;
900
901 if self.peek_identifier_text_is("protected") {
903 let tok = self.advance();
904 self.error_at_with_help(
905 tok.span,
906 "`protected` is not supported",
907 vec!["use `private` or `public`".to_string()],
908 );
909 return None;
910 }
911 if self.peek_identifier_text_is("abstract") {
912 let tok = self.advance();
913 self.error_at_with_help(
914 tok.span,
915 "abstract classes are not supported",
916 vec!["provide a concrete implementation instead of `abstract`".to_string()],
917 );
918 return None;
919 }
920
921 let modifiers = self.parse_class_modifiers();
922 self.reject_async_member_modifier();
923
924 for (kw, kind) in [
927 ("get", crate::AccessorKind::Get),
928 ("set", crate::AccessorKind::Set),
929 ] {
930 if self.peek_identifier_text_is(kw)
931 && is_property_name(&self.peek_at(1).kind)
932 && matches!(self.peek_at(2).kind, TokenKind::LeftParen)
933 {
934 if let Some(span) = modifiers.readonly {
935 self.reject_readonly_modifier(span);
936 }
937 if let Some(span) = modifiers.static_span {
938 self.error_at_with_help(
939 span,
940 "static accessors are not supported",
941 vec!["use a static method: `static name(): T { … }`".to_string()],
942 );
943 return None;
944 }
945 return self.parse_accessor(member_start, modifiers, kind, doc);
946 }
947 }
948
949 if self.peek_identifier_text_is("constructor")
951 && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
952 {
953 if let Some(span) = modifiers.static_span {
954 self.error_at_with_help(
955 span,
956 "a constructor cannot be `static`",
957 vec![
958 "remove `static` — the constructor already belongs to the class, not \
959 instances"
960 .to_string(),
961 ],
962 );
963 return None;
964 }
965 if let Some(span) = modifiers.readonly {
966 self.error_at_with_help(
967 span,
968 "a constructor cannot be `readonly`",
969 vec!["remove `readonly`; it applies to fields".to_string()],
970 );
971 return None;
972 }
973 return self.parse_constructor(member_start, modifiers.visibility, doc);
974 }
975
976 let name = self.expect_property_ident("expected class member name")?;
977 let optional_span = if matches!(self.peek().kind, TokenKind::Question) {
978 Some(self.advance().span)
979 } else {
980 None
981 };
982 let optional = optional_span.is_some();
983
984 if matches!(self.peek().kind, TokenKind::LessThan | TokenKind::LeftParen) {
986 if let Some(span) = modifiers.readonly {
987 self.reject_readonly_modifier(span);
988 }
989 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
990 self.parse_generic_param_list()?
991 } else {
992 Vec::new()
993 };
994 if !matches!(self.peek().kind, TokenKind::LeftParen) {
995 self.error_at_peek("expected `(` to start the method parameter list");
996 return None;
997 }
998 self.advance();
999 let params = self.parse_class_member_params(false)?;
1000 self.advance(); if !matches!(self.peek().kind, TokenKind::Colon) {
1002 self.error_at_peek_with_help(
1003 "expected `:` and return type",
1004 vec![format!(
1005 "write the return type: `{}(…): T {{ … }}`, with `void` when it returns nothing",
1006 name.name
1007 )],
1008 );
1009 if matches!(self.peek().kind, TokenKind::LeftBrace)
1011 && self.parse_class_member_body().is_some()
1012 {
1013 self.resume_at_next_member = true;
1014 }
1015 return None;
1016 }
1017 self.advance();
1018 let return_type = self.parse_type_annotation()?;
1019 if self.at_inserted_semicolon() && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1020 {
1021 self.advance();
1022 }
1023 if optional
1024 && matches!(
1025 self.peek().kind,
1026 TokenKind::Semicolon | TokenKind::RightBrace
1027 )
1028 {
1029 if !generics.is_empty() {
1030 self.error_at(
1031 name.span,
1032 "generic optional method declarations are not supported",
1033 );
1034 return None;
1035 }
1036 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1037 self.advance().span.end
1038 } else {
1039 return_type.span.end
1040 };
1041 let ty = self.method_declaration_type(
1042 params,
1043 return_type,
1044 self.span(name.span.start, end),
1045 )?;
1046 return Some(crate::ClassMember::Field {
1047 name,
1048 modifiers,
1049 optional: true,
1050 method_signature: true,
1051 ty,
1052 initializer: None,
1053 span: self.span(member_start, end),
1054 doc,
1055 });
1056 }
1057 let body = self.parse_class_member_body()?;
1058 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1059 .span
1060 .end;
1061 return Some(crate::ClassMember::Method {
1062 name,
1063 optional,
1064 modifiers,
1065 generics,
1066 params,
1067 return_type,
1068 body,
1069 span: self.span(member_start, body_end),
1070 doc,
1071 });
1072 }
1073
1074 if let Some(span) = optional_span
1076 && modifiers.static_span.is_some()
1077 {
1078 self.error_at_with_help(
1079 span,
1080 "a static field cannot be optional",
1081 vec!["a static field must be initialized; give it a value with `= …`".to_string()],
1082 );
1083 return None;
1084 }
1085 if !matches!(self.peek().kind, TokenKind::Colon) {
1086 if matches!(self.peek().kind, TokenKind::Equals) {
1087 self.error_at_peek_with_help(
1088 "class fields require a type annotation",
1089 vec!["add `: T` before the initializer, e.g. `name: string = …`".to_string()],
1090 );
1091 } else {
1092 self.error_at_peek("expected `:` and a type for the class field");
1093 }
1094 return None;
1095 }
1096 self.advance();
1097 let ty = self.parse_type_annotation()?;
1098 let initializer = if matches!(self.peek().kind, TokenKind::Equals) {
1099 self.advance();
1100 self.class_member_body_depth += 1;
1103 let init = self.parse_expression();
1104 self.class_member_body_depth -= 1;
1105 Some(init?)
1106 } else {
1107 None
1108 };
1109 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1110 self.error_at_peek("expected `;` after class field");
1111 return None;
1112 }
1113 let semi = self.advance();
1114 Some(crate::ClassMember::Field {
1115 name,
1116 modifiers,
1117 optional,
1118 method_signature: false,
1119 ty,
1120 initializer,
1121 span: self.span(member_start, semi.span.end),
1122 doc,
1123 })
1124 }
1125
1126 fn parse_constructor(
1127 &mut self,
1128 member_start: u32,
1129 visibility: crate::Visibility,
1130 doc: Option<crate::DocComment>,
1131 ) -> Option<crate::ClassMember> {
1132 self.advance(); self.advance(); let params = self.parse_class_member_params(true)?;
1135 self.advance(); if matches!(self.peek().kind, TokenKind::Colon) {
1137 let colon = self.advance();
1138 let _ = self.parse_type_annotation();
1139 self.error_at_with_help(
1140 colon.span,
1141 "a constructor cannot declare a return type",
1142 vec![
1143 "drop the `: T` — a constructor always returns the class instance".to_string(),
1144 ],
1145 );
1146 }
1147 let body = self.parse_class_member_body()?;
1148 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1149 .span
1150 .end;
1151 Some(crate::ClassMember::Constructor {
1152 visibility,
1153 params,
1154 body,
1155 span: self.span(member_start, body_end),
1156 doc,
1157 })
1158 }
1159
1160 fn parse_accessor(
1163 &mut self,
1164 member_start: u32,
1165 modifiers: crate::ClassModifiers,
1166 kind: crate::AccessorKind,
1167 doc: Option<crate::DocComment>,
1168 ) -> Option<crate::ClassMember> {
1169 self.advance(); let name = self.expect_property_ident("expected accessor name")?;
1171 self.advance(); let mut params = self.parse_class_member_params(false)?;
1173 self.advance(); let param = match kind {
1175 crate::AccessorKind::Get => {
1176 if !params.is_empty() {
1177 self.error_at(name.span, "a getter cannot declare parameters");
1178 }
1179 None
1180 }
1181 crate::AccessorKind::Set => {
1182 if params.len() != 1 {
1183 self.error_at(name.span, "a setter must declare exactly one parameter");
1184 return None;
1185 }
1186 let param = params.remove(0);
1187 if param.rest {
1190 self.error_at(
1191 param.name.span,
1192 "a setter's parameter can't be a rest parameter",
1193 );
1194 } else if param.default.is_some() {
1195 self.error_at(name.span, "a setter's parameter can't have a default value");
1196 } else if param.optional {
1197 self.error_at(param.name.span, "a setter's parameter can't be optional");
1198 }
1199 Some(Box::new(param))
1200 }
1201 };
1202 let return_type = if matches!(self.peek().kind, TokenKind::Colon) {
1203 self.advance();
1204 Some(self.parse_type_annotation()?)
1205 } else {
1206 if kind == crate::AccessorKind::Get {
1210 self.error_at_peek_with_help(
1211 "expected `:` and return type",
1212 vec!["get name(): T { … }".to_string()],
1213 );
1214 }
1215 None
1216 };
1217 let body = self.parse_class_member_body()?;
1218 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1219 .span
1220 .end;
1221 Some(crate::ClassMember::Accessor {
1222 name,
1223 modifiers,
1224 kind,
1225 param,
1226 return_type,
1227 body,
1228 span: self.span(member_start, body_end),
1229 doc,
1230 })
1231 }
1232
1233 fn parse_class_modifiers(&mut self) -> crate::ClassModifiers {
1239 let mut visibility = crate::Visibility::Public;
1240 let mut visibility_span: Option<Span> = None;
1241 let mut readonly: Option<Span> = None;
1242 let mut static_span: Option<Span> = None;
1243 loop {
1244 if self.peek_word_is_class_modifier("public")
1245 || self.peek_word_is_class_modifier("private")
1246 {
1247 let (kind, word) = if self.peek_identifier_text_is("private") {
1248 (crate::Visibility::Private, "private")
1249 } else {
1250 (crate::Visibility::Public, "public")
1251 };
1252 let tok = self.advance();
1253 if visibility_span.is_some() {
1254 self.error_at_with_help(
1255 tok.span,
1256 "a class member may have at most one visibility modifier",
1257 vec!["keep a single `public` or `private`".to_string()],
1258 );
1259 continue;
1260 }
1261 let preceding = static_span
1264 .map(|_| "static")
1265 .or(readonly.map(|_| "readonly"));
1266 if let Some(preceding) = preceding {
1267 self.error_at_with_help(
1268 tok.span,
1269 format!("`{word}` must come before `{preceding}`"),
1270 vec![format!("write `{word} {preceding} <name>`")],
1271 );
1272 }
1273 visibility = kind;
1274 visibility_span = Some(tok.span);
1275 continue;
1276 }
1277 if self.peek_word_is_class_modifier("static") {
1278 let tok = self.advance();
1279 if static_span.is_some() {
1280 self.error_at(tok.span, "duplicate `static` modifier");
1281 } else {
1282 static_span = Some(tok.span);
1283 }
1284 continue;
1285 }
1286 if self.peek_word_is_class_modifier("readonly") {
1287 let tok = self.advance();
1288 if readonly.is_some() {
1289 self.error_at(tok.span, "duplicate `readonly` modifier");
1290 } else {
1291 readonly = Some(tok.span);
1292 }
1293 continue;
1294 }
1295 break;
1296 }
1297 crate::ClassModifiers {
1298 visibility,
1299 visibility_span,
1300 readonly,
1301 static_span,
1302 }
1303 }
1304
1305 fn peek_word_is_class_modifier(&self, word: &str) -> bool {
1311 if !self.peek_identifier_text_is(word) {
1312 return false;
1313 }
1314 if word != "static" && self.line_break_after_peek() {
1315 return false;
1316 }
1317 let next = &self.peek_at(1).kind;
1318 is_property_name(next) || matches!(next, TokenKind::StringLiteral(_))
1319 }
1320
1321 fn parse_class_member_params(
1322 &mut self,
1323 allow_param_properties: bool,
1324 ) -> Option<Vec<ParamDecl>> {
1325 let saved = self.class_member_body_depth;
1326 self.class_member_body_depth = 1;
1327 let params = self.parse_param_list(allow_param_properties);
1328 self.class_member_body_depth = saved;
1329 params
1330 }
1331
1332 fn parse_class_member_body(&mut self) -> Option<StmtId> {
1333 self.class_member_body_depth += 1;
1334 let body = self.parse_block();
1335 self.class_member_body_depth -= 1;
1336 body
1337 }
1338
1339 fn parse_interface_decl(&mut self) -> Option<StmtId> {
1340 let doc = self.take_leading_doc();
1341 let kw = self.advance();
1342 let name = self.parse_type_decl_name("an", "interface")?;
1343
1344 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1345 self.parse_generic_param_list()?
1346 } else {
1347 Vec::new()
1348 };
1349
1350 let mut extends = Vec::new();
1351 if matches!(self.peek().kind, TokenKind::Extends) {
1352 self.advance();
1353 loop {
1354 extends.push(self.parse_type_annotation()?);
1355 if !matches!(self.peek().kind, TokenKind::Comma) {
1356 break;
1357 }
1358 self.advance();
1359 }
1360 }
1361 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1362 self.error_at_peek("expected `{` after interface name");
1363 return None;
1364 }
1365 self.advance();
1366
1367 let mut members: Vec<crate::InterfaceMember> = Vec::new();
1368 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
1369 && !self.peek_starts_top_level_item()
1370 {
1371 let member_doc = self.take_leading_doc();
1375 if matches!(self.peek().kind, TokenKind::LeftParen) {
1376 let open = self.advance();
1377 let params = self.parse_param_list(false)?;
1378 self.advance();
1379 if !matches!(self.peek().kind, TokenKind::Colon) {
1380 self.error_at_peek("expected `:` and return type");
1381 return None;
1382 }
1383 self.advance();
1384 let return_type = self.parse_type_annotation()?;
1385 let end = self.finish_interface_member(return_type.span.end)?;
1386 let span = self.span(open.span.start, end);
1387 let sentinel = Ident {
1388 name: "@call".to_string(),
1389 span: open.span,
1390 };
1391 members.push(crate::InterfaceMember::Method {
1392 name: sentinel,
1393 optional: false,
1394 generics: Vec::new(),
1395 params,
1396 return_type,
1397 span,
1398 doc: member_doc,
1399 });
1400 continue;
1401 }
1402 if matches!(self.peek().kind, TokenKind::LessThan) {
1403 self.error_at_peek("generic call signatures are not yet supported");
1404 return None;
1405 }
1406 let member_start = self.peek().span.start;
1407 let readonly = self.eat_readonly_property_modifier();
1408 if self.peek_is_parameterless_index_signature() {
1409 self.skip_parameterless_index_signature(member_start)?;
1410 self.finish_interface_member(self.prev_token_end())?;
1411 continue;
1412 }
1413 if matches!(self.peek().kind, TokenKind::LeftBracket) {
1414 let signature = self.parse_index_signature(readonly)?;
1415 self.finish_interface_member(signature.span.end)?;
1416 members.push(crate::InterfaceMember::IndexSignature(signature));
1417 continue;
1418 }
1419 let member_name = self.expect_property_ident("expected interface member name")?;
1420 let optional = matches!(self.peek().kind, TokenKind::Question);
1421 if optional {
1422 self.advance();
1423 }
1424 if matches!(self.peek().kind, TokenKind::Colon) {
1425 self.advance();
1426 let ty = self.parse_type_annotation()?;
1427 let end = self.finish_interface_member(ty.span.end)?;
1428 let span = self.span(member_name.span.start, end);
1429 members.push(crate::InterfaceMember::Property {
1430 name: member_name,
1431 ty,
1432 optional,
1433 readonly,
1434 span,
1435 doc: member_doc,
1436 });
1437 continue;
1438 }
1439 if readonly {
1440 self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
1441 }
1442 let m_generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1443 self.parse_generic_param_list()?
1444 } else {
1445 Vec::new()
1446 };
1447 if !matches!(self.peek().kind, TokenKind::LeftParen) {
1448 self.error_at_peek(
1449 "expected `(` to start a method signature or `:` to start a property",
1450 );
1451 return None;
1452 }
1453 self.advance();
1454 let params = self.parse_param_list(false)?;
1455 self.advance();
1456 if !matches!(self.peek().kind, TokenKind::Colon) {
1457 self.error_at_peek("expected `:` and return type");
1458 return None;
1459 }
1460 self.advance();
1461 let return_type = self.parse_type_annotation()?;
1462 let end = self.finish_interface_member(return_type.span.end)?;
1463 let span = self.span(member_name.span.start, end);
1464 if optional {
1465 if !m_generics.is_empty() {
1466 self.error_at(
1467 member_name.span,
1468 "generic optional method declarations are not supported",
1469 );
1470 return None;
1471 }
1472 let ty = self.method_declaration_type(params, return_type, span)?;
1473 members.push(crate::InterfaceMember::Property {
1474 name: member_name,
1475 ty,
1476 optional: true,
1477 readonly,
1478 span,
1479 doc: member_doc,
1480 });
1481 } else {
1482 members.push(crate::InterfaceMember::Method {
1483 name: member_name,
1484 optional,
1485 generics: m_generics,
1486 params,
1487 return_type,
1488 span,
1489 doc: member_doc,
1490 });
1491 }
1492 }
1493
1494 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1495 self.error_at_peek("expected `}` to close interface");
1496 return None;
1497 }
1498 let close = self.advance();
1499
1500 parse_arena_result(
1501 self.ast.try_push_stmt(Stmt {
1502 kind: StmtKind::InterfaceDecl {
1503 name,
1504 generics,
1505 extends,
1506 members,
1507 doc,
1508 },
1509 span: self.span(kw.span.start, close.span.end),
1510 }),
1511 &mut self.fatal,
1512 )
1513 }
1514
1515 fn method_declaration_type(
1516 &mut self,
1517 params: Vec<ParamDecl>,
1518 return_type: TypeAnnotation,
1519 span: Span,
1520 ) -> Option<TypeAnnotation> {
1521 let mut fields = Vec::new();
1522 for param in params {
1523 if param.pattern.is_some() || param.default.is_some() {
1524 self.error_at(
1525 param.name.span,
1526 "a method declaration cannot contain a parameter pattern or default value",
1527 );
1528 return None;
1529 }
1530 let Some(ty) = param.ty else {
1531 return self.invariant_failure("method parameter annotation was not retained");
1532 };
1533 fields.push(TypeAnnotationField {
1534 name: param.name,
1535 ty,
1536 optional: param.optional,
1537 readonly: false,
1538 rest: param.rest,
1539 method: false,
1540 });
1541 }
1542 Some(TypeAnnotation {
1543 kind: TypeAnnotationKind::Function {
1544 params: fields,
1545 return_type: Box::new(return_type),
1546 },
1547 span,
1548 })
1549 }
1550
1551 fn finish_interface_member(&mut self, body_end: u32) -> Option<u32> {
1552 self.finish_type_member(body_end, "expected `;`, `,`, or `}` after interface member")
1553 }
1554
1555 fn finish_type_member(&mut self, body_end: u32, message: &str) -> Option<u32> {
1561 match self.peek().kind {
1562 TokenKind::Semicolon | TokenKind::Comma => Some(self.advance().span.end),
1563 TokenKind::RightBrace => Some(body_end),
1564 _ if self.line_break_before_peek() => Some(body_end),
1565 _ => {
1566 self.error_at_peek(message);
1567 None
1568 }
1569 }
1570 }
1571
1572 fn parse_type_alias_decl(&mut self) -> Option<StmtId> {
1573 let doc = self.take_leading_doc();
1574 let kw = self.advance();
1575 let name = self.parse_type_decl_name("a", "type alias")?;
1576
1577 let generics: Vec<Ident> = if matches!(self.peek().kind, TokenKind::LessThan) {
1578 self.parse_generic_param_list()?
1579 } else {
1580 Vec::new()
1581 };
1582
1583 if !matches!(self.peek().kind, TokenKind::Equals) {
1584 self.error_at_peek("expected `=` after type alias name");
1585 return None;
1586 }
1587 self.advance();
1588
1589 let ty = self.parse_type_annotation()?;
1590
1591 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1594 self.advance().span.end
1595 } else if self.line_break_before_peek() {
1596 self.prev_token_end()
1597 } else {
1598 self.error_at_peek("expected `;` after type alias body");
1599 return None;
1600 };
1601
1602 parse_arena_result(
1603 self.ast.try_push_stmt(Stmt {
1604 kind: StmtKind::TypeAliasDecl {
1605 name,
1606 generics,
1607 ty,
1608 doc,
1609 },
1610 span: self.span(kw.span.start, end),
1611 }),
1612 &mut self.fatal,
1613 )
1614 }
1615
1616 fn parse_enum_decl(&mut self) -> Option<StmtId> {
1618 #[derive(Copy, Clone)]
1619 enum Kind {
1620 Number,
1621 String,
1622 }
1623 let doc = self.take_leading_doc();
1624 let kw = self.advance();
1625 let name = self.parse_type_decl_name("an", "enum")?;
1626
1627 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1628 self.error_at_peek("expected `{` after enum name");
1629 return None;
1630 }
1631 self.advance();
1632
1633 let mut members: Vec<EnumMember> = Vec::new();
1634 let mut first_explicit: Option<(Kind, Span)> = None;
1635 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1636 if matches!(self.peek().kind, TokenKind::Semicolon) {
1638 self.advance();
1639 continue;
1640 }
1641 let member_doc = self.take_leading_doc();
1642 let member_name_tok = self.expect_identifier_name("expected enum member name")?;
1643 let member_name = self.ident_from_token(&member_name_tok);
1644
1645 let value = if matches!(self.peek().kind, TokenKind::Equals) {
1646 self.advance();
1647 let init = self.parse_enum_initializer()?;
1648 let (this_kind, this_span) = match &init {
1649 EnumInitializer::Number { span, .. } => (Kind::Number, *span),
1650 EnumInitializer::String { span, .. } => (Kind::String, *span),
1651 };
1652 match first_explicit {
1653 None => first_explicit = Some((this_kind, this_span)),
1654 Some((prev, _)) => {
1655 let mismatched = matches!(
1656 (prev, this_kind),
1657 (Kind::Number, Kind::String) | (Kind::String, Kind::Number)
1658 );
1659 if mismatched {
1660 self.error_at(
1661 this_span,
1662 "mixed numeric and string enum members are not allowed",
1663 );
1664 }
1665 }
1666 }
1667 Some(init)
1668 } else {
1669 None
1670 };
1671
1672 let end_span = value
1673 .as_ref()
1674 .map_or(member_name.span, super::ast::EnumInitializer::span);
1675 let member_span = self.span(member_name.span.start, end_span.end);
1676 members.push(EnumMember {
1677 name: member_name,
1678 value,
1679 span: member_span,
1680 doc: member_doc,
1681 });
1682
1683 match self.peek().kind {
1684 TokenKind::Comma | TokenKind::Semicolon => {
1687 self.advance();
1688 }
1689 TokenKind::RightBrace | TokenKind::Eof => {}
1690 _ => {
1691 self.error_at_peek("expected `,` or `}` after enum member");
1692 return None;
1693 }
1694 }
1695 }
1696
1697 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1698 self.error_at_peek("expected `}` to close enum");
1699 return None;
1700 }
1701 let close = self.advance();
1702
1703 parse_arena_result(
1704 self.ast.try_push_stmt(Stmt {
1705 kind: StmtKind::EnumDecl { name, members, doc },
1706 span: self.span(kw.span.start, close.span.end),
1707 }),
1708 &mut self.fatal,
1709 )
1710 }
1711
1712 fn parse_export(&mut self) -> Option<StmtId> {
1719 let export_tok = self.advance();
1720 let export_span = export_tok.span;
1721
1722 let nested = self.block_depth > 0;
1723 if nested {
1724 self.error_at_with_help(
1725 export_span,
1726 "`export` statements must appear at the top of the file",
1727 vec!["move this `export` to the top level of the module".to_string()],
1728 );
1729 }
1732
1733 if self.peek_identifier_text_is("type")
1736 && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1737 {
1738 self.advance();
1739 }
1740
1741 self.reject_async_modifier();
1742 let declares = matches!(
1743 self.peek().kind,
1744 TokenKind::Function
1745 | TokenKind::Class
1746 | TokenKind::Const
1747 | TokenKind::Let
1748 | TokenKind::Interface
1749 | TokenKind::Enum
1750 ) || self.at_type_alias_head();
1751 if declares {
1752 if export_tok.leading_doc.is_some() && self.peek().leading_doc.is_none() {
1755 if let Some(token) = self.tokens.get_mut(self.pos) {
1756 token.leading_doc = export_tok.leading_doc;
1757 } else {
1758 return self.invariant_failure("export cursor is outside token stream");
1759 }
1760 }
1761 let id = self.parse_statement()?;
1762 if nested {
1763 let detached: Vec<StmtId> = std::iter::once(id)
1764 .chain(self.take_trailing_declarators())
1765 .collect();
1766 self.discard_detached_patterns(&detached);
1767 return None;
1768 }
1769 for stmt in std::iter::once(id).chain(self.trailing_declarators.clone()) {
1771 self.ast
1772 .exported_decls
1773 .push(ExportedDecl { stmt, export_span });
1774 }
1775 return Some(id);
1776 }
1777
1778 match self.peek().kind {
1779 TokenKind::LeftBrace => {
1780 let doc = export_tok
1783 .leading_doc
1784 .as_ref()
1785 .and_then(|raw| self.parse_doc(raw))
1786 .or_else(|| self.take_leading_doc());
1787 self.parse_export_from(export_span, nested, doc)
1788 }
1789 TokenKind::Default => {
1790 self.error_at_peek_with_help(
1791 "`export default` is not supported",
1792 vec![
1793 "default exports are out of scope; drop `default` — a named \
1794 `export` marks the declaration visible to sibling modules"
1795 .to_string(),
1796 ],
1797 );
1798 None
1799 }
1800 _ => {
1801 self.error_at_peek_with_help(
1802 "expected a declaration after `export`",
1803 vec![
1804 "`export` may precede `function`, `class`, `const`, `let`, `type`, \
1805 `interface`, or `enum`, or take the form `export { … }`"
1806 .to_string(),
1807 ],
1808 );
1809 None
1810 }
1811 }
1812 }
1813
1814 fn parse_export_from(
1817 &mut self,
1818 export_span: Span,
1819 nested: bool,
1820 doc: Option<crate::DocComment>,
1821 ) -> Option<StmtId> {
1822 let (specs, open_span) = self.parse_specifier_list(SpecifierList::Export)?;
1823 if specs.is_empty() {
1824 self.error_at_with_help(
1825 open_span,
1826 "empty export specifier list",
1827 vec!["name what you re-export: `export { name } from \"./util\";`".to_string()],
1828 );
1829 return None;
1830 }
1831
1832 let source = if self.peek_identifier_text_is("from") {
1833 self.advance();
1834 let module_tok = self.peek().clone();
1835 let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1836 s.clone()
1837 } else {
1838 self.error_at_peek("expected module specifier string after `from`");
1839 return None;
1840 };
1841 self.advance();
1842 Some((module, module_tok.span))
1843 } else {
1844 None
1845 };
1846
1847 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1848 self.error_at_peek("expected `;` after export statement");
1849 return None;
1850 }
1851 let semi = self.advance();
1852
1853 if nested {
1854 return None;
1855 }
1856
1857 parse_arena_result(
1858 self.ast.try_push_stmt(Stmt {
1859 kind: StmtKind::ExportFrom { specs, source, doc },
1860 span: self.span(export_span.start, semi.span.end),
1861 }),
1862 &mut self.fatal,
1863 )
1864 }
1865
1866 fn parse_import_decl(&mut self) -> Option<StmtId> {
1867 let doc = self.take_leading_doc();
1868 let kw = self.advance();
1869 let kw_span = kw.span;
1870
1871 let nested = self.block_depth > 0;
1872 if nested {
1873 self.error_at_with_help(
1874 kw_span,
1875 "`import` statements must appear at the top of the file",
1876 vec!["move this `import` outside any block / function body".to_string()],
1877 );
1878 }
1880
1881 let kind = self.parse_import_kind()?;
1882
1883 if !self.peek_identifier_text_is("from") {
1884 self.error_at_peek("expected `from` after import specifier list");
1885 return None;
1886 }
1887 self.advance();
1888
1889 let module_tok = self.peek().clone();
1890 let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1891 s.clone()
1892 } else {
1893 self.error_at_peek("expected module specifier string after `from`");
1894 return None;
1895 };
1896 let module_span = module_tok.span;
1897 self.advance();
1898
1899 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1900 self.error_at_peek("expected `;` after import statement");
1901 return None;
1902 }
1903 let semi = self.advance();
1904
1905 if nested {
1906 return None;
1907 }
1908
1909 parse_arena_result(
1910 self.ast.try_push_stmt(Stmt {
1911 kind: StmtKind::Import {
1912 module,
1913 module_span,
1914 kind,
1915 doc,
1916 },
1917 span: self.span(kw_span.start, semi.span.end),
1918 }),
1919 &mut self.fatal,
1920 )
1921 }
1922
1923 fn parse_import_kind(&mut self) -> Option<ImportKind> {
1924 if self.peek_identifier_text_is("type")
1929 && match self.peek_at(1).kind {
1930 TokenKind::LeftBrace | TokenKind::Star => true,
1931 TokenKind::Identifier => {
1932 !(self.peek_at_is_word(1, "from")
1933 && matches!(self.peek_at(2).kind, TokenKind::StringLiteral(_)))
1934 }
1935 _ => false,
1936 }
1937 {
1938 self.advance();
1939 }
1940
1941 match self.peek().kind {
1942 TokenKind::LeftBrace => self.parse_named_imports(),
1943 TokenKind::Identifier => {
1944 let local_tok = self.advance();
1945 let local_name = self.ident_from_token(&local_tok);
1946 if matches!(self.peek().kind, TokenKind::Comma) {
1947 self.error_at_peek_with_help(
1948 "combining default and named imports is not supported",
1949 vec!["use two separate `import` statements".to_string()],
1950 );
1951 return None;
1952 }
1953 Some(ImportKind::Namespace { local_name })
1954 }
1955 TokenKind::Star => {
1958 self.advance();
1959 if !self.peek_identifier_text_is("as") {
1960 self.error_at_peek_with_help(
1961 "expected `as <name>` after `import *`",
1962 vec![
1963 "name the namespace binding: \
1964 `import * as ns from \"pkg\";` (or use the \
1965 short form: `import ns from \"pkg\";`)"
1966 .to_string(),
1967 ],
1968 );
1969 return None;
1970 }
1971 self.advance();
1972 let local_tok = self.expect_identifier("expected namespace name after `as`")?;
1973 let local_name = self.ident_from_token(&local_tok);
1974 Some(ImportKind::Namespace { local_name })
1975 }
1976 TokenKind::StringLiteral(_) => {
1977 self.error_at_peek_with_help(
1978 "side-effect-only imports are not supported",
1979 vec![
1980 "name what you import: `import { name } from \"pkg\";` \
1981 or `import ns from \"pkg\";`"
1982 .to_string(),
1983 ],
1984 );
1985 None
1986 }
1987 _ => {
1988 self.error_at_peek("expected `{` or namespace name after `import`");
1989 None
1990 }
1991 }
1992 }
1993
1994 fn parse_named_imports(&mut self) -> Option<ImportKind> {
1995 let (specs, open_span) = self.parse_specifier_list(SpecifierList::Import)?;
1996 if specs.is_empty() {
1997 self.error_at_with_help(
1998 open_span,
1999 "empty import specifier list",
2000 vec!["name what you import: `import { name } from \"pkg\";`".to_string()],
2001 );
2002 return None;
2003 }
2004 Some(ImportKind::Named(specs))
2005 }
2006
2007 fn parse_specifier_list(
2013 &mut self,
2014 list: SpecifierList,
2015 ) -> Option<(Vec<ImportSpecifier>, Span)> {
2016 let what = list.noun();
2017 let open = self.advance();
2018 let mut specs: Vec<ImportSpecifier> = Vec::new();
2019 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2020 if self.peek_identifier_text_is("type")
2023 && matches!(self.peek_at(1).kind, TokenKind::Identifier)
2024 && !self.peek_at_is_word(1, "as")
2025 {
2026 self.advance();
2027 }
2028 let imported_tok =
2029 self.expect_identifier_name(&format!("expected {what} specifier name"))?;
2030 let imported_name = self.ident_from_token(&imported_tok);
2031
2032 let binds_local = list == SpecifierList::Import;
2033 let local_name = if self.peek_identifier_text_is("as") {
2034 self.advance();
2035 let local_tok = self.expect_identifier_name("expected local name after `as`")?;
2036 if binds_local {
2037 self.reject_strict_mode_reserved_word(&local_tok);
2038 }
2039 self.ident_from_token(&local_tok)
2040 } else {
2041 if binds_local {
2042 self.reject_strict_mode_reserved_word(&imported_tok);
2043 }
2044 imported_name.clone()
2045 };
2046
2047 specs.push(ImportSpecifier {
2048 imported_name,
2049 local_name,
2050 });
2051
2052 match self.peek().kind {
2053 TokenKind::Comma => {
2054 self.advance();
2055 }
2056 TokenKind::RightBrace | TokenKind::Eof => {}
2057 _ => {
2058 self.error_at_peek(format!("expected `,` or `}}` after {what} specifier"));
2059 return None;
2060 }
2061 }
2062 }
2063
2064 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2065 self.error_at_peek(format!("expected `}}` to close {what} specifier list"));
2066 return None;
2067 }
2068 self.advance();
2069
2070 Some((specs, open.span))
2071 }
2072
2073 fn parse_enum_initializer(&mut self) -> Option<EnumInitializer> {
2074 let neg = if matches!(self.peek().kind, TokenKind::Minus) {
2075 Some(self.advance())
2076 } else {
2077 None
2078 };
2079 let tok = self.advance();
2080 match &tok.kind {
2081 TokenKind::NumberLiteral(n) => {
2082 let value = if neg.is_some() { -n } else { *n };
2083 let value = if value == 0.0 { 0.0 } else { value };
2085 let start = neg.as_ref().map_or(tok.span.start, |t| t.span.start);
2086 Some(EnumInitializer::Number {
2087 value,
2088 span: self.span(start, tok.span.end),
2089 })
2090 }
2091 TokenKind::StringLiteral(s) => {
2092 if let Some(neg_tok) = &neg {
2093 self.error_at(
2094 self.span(neg_tok.span.start, tok.span.end),
2095 "cannot negate a string enum initializer",
2096 );
2097 return None;
2098 }
2099 Some(EnumInitializer::String {
2100 value: s.clone(),
2101 span: tok.span,
2102 })
2103 }
2104 _ => {
2105 let span = neg.map_or(tok.span, |t| self.span(t.span.start, tok.span.end));
2106 self.error_at(
2107 span,
2108 "expected a number or string literal for enum initializer",
2109 );
2110 None
2111 }
2112 }
2113 }
2114
2115 fn is_contextual_type_operator(&self, index: usize, operator: &str) -> bool {
2123 let Some(tok) = self.tokens.get(index) else {
2124 return false;
2125 };
2126 if !self.token_is_word(tok, operator) {
2127 return false;
2128 }
2129 matches!(
2130 self.tokens.get(index + 1).map(|t| &t.kind),
2131 Some(
2132 TokenKind::Identifier
2133 | TokenKind::Void
2134 | TokenKind::LeftBrace
2135 | TokenKind::LeftParen
2136 | TokenKind::LeftBracket
2137 | TokenKind::StringLiteral(_)
2138 | TokenKind::NumberLiteral(_)
2139 | TokenKind::BooleanLiteral(_)
2140 | TokenKind::NullLiteral
2141 )
2142 )
2143 }
2144
2145 fn parse_type_argument_list(&mut self) -> Option<(Vec<TypeAnnotation>, u32)> {
2146 let lt = self.advance();
2147 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2148 self.error_at(lt.span, "empty generic argument list `<>` is not allowed");
2149 return None;
2150 }
2151 let mut args = Vec::new();
2152 loop {
2153 args.push(self.parse_type_annotation()?);
2154 match self.peek().kind {
2155 TokenKind::Comma => {
2156 self.advance();
2157 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2158 let gt = self.advance();
2159 return Some((args, gt.span.end));
2160 }
2161 }
2162 TokenKind::GreaterThan => {
2163 let gt = self.advance();
2164 return Some((args, gt.span.end));
2165 }
2166 _ => {
2167 self.error_at(
2168 lt.span,
2169 "expected `,` or `>` to close generic argument list",
2170 );
2171 return None;
2172 }
2173 }
2174 }
2175 }
2176
2177 fn parse_type_decl_name(&mut self, article: &str, noun: &str) -> Option<Ident> {
2181 let name_tok = self.expect_identifier(&format!("expected {noun} name"))?;
2182 let name = self.ident_from_token(&name_tok);
2183 if BUILT_IN_TYPE_NAMES.contains(&name.name.as_str()) {
2184 self.error_at_with_help(
2185 name.span,
2186 format!(
2187 "`{}` is a built-in type and can't be used as {article} {noun} name",
2188 name.name
2189 ),
2190 vec![format!("rename the {noun}")],
2191 );
2192 }
2193 Some(name)
2194 }
2195
2196 fn parse_generic_param_list(&mut self) -> Option<Vec<Ident>> {
2197 let lt = self.advance();
2198 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2199 self.error_at(lt.span, "empty generic parameter list `<>` is not allowed");
2200 return None;
2201 }
2202 let mut generics = Vec::new();
2203 loop {
2204 let name_tok = self.expect_identifier("expected generic parameter name")?;
2205 let name = self.ident_from_token(&name_tok);
2206 if generics.iter().any(|g: &Ident| g.name == name.name) {
2210 self.error_at_with_help(
2211 name.span,
2212 format!("duplicate type parameter `{}`", name.name),
2213 vec!["give each type parameter its own name".to_string()],
2214 );
2215 } else {
2216 generics.push(name);
2217 }
2218 match self.peek().kind {
2219 TokenKind::Comma => {
2220 self.advance();
2221 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2222 self.advance();
2223 return Some(generics);
2224 }
2225 }
2226 TokenKind::GreaterThan => {
2227 self.advance();
2228 return Some(generics);
2229 }
2230 _ => {
2231 self.error_at_peek("expected `,` or `>`");
2232 return None;
2233 }
2234 }
2235 }
2236 }
2237
2238 fn parse_param_list(&mut self, allow_param_properties: bool) -> Option<Vec<ParamDecl>> {
2239 if matches!(self.peek().kind, TokenKind::RightParen) {
2240 return Some(Vec::new());
2241 }
2242 let mut params = Vec::new();
2243 loop {
2244 let param = self.parse_param(allow_param_properties)?;
2245 let is_rest = param.rest;
2246 params.push(param);
2247 match self.peek().kind {
2248 TokenKind::Comma => {
2249 if is_rest {
2250 let comma = self.advance();
2251 self.error_at(comma.span, "rest parameter must be the last parameter");
2252 return None;
2253 }
2254 self.advance();
2255 if matches!(self.peek().kind, TokenKind::RightParen) {
2256 break;
2257 }
2258 }
2259 TokenKind::RightParen => break,
2260 _ => {
2261 self.error_at_peek("expected `,` or `)`");
2262 return None;
2263 }
2264 }
2265 }
2266 self.check_parameter_order(params.iter().map(ParamOrder::of_param));
2267 Some(params)
2268 }
2269
2270 fn parse_param(&mut self, allow_param_properties: bool) -> Option<ParamDecl> {
2271 let modifiers = if self.peek_word_is_class_modifier("public")
2275 || self.peek_word_is_class_modifier("private")
2276 || self.peek_word_is_class_modifier("readonly")
2277 {
2278 let span = self.peek().span;
2279 let parsed = self.parse_class_modifiers();
2280 if allow_param_properties {
2281 Some(parsed)
2282 } else {
2283 self.error_at_with_help(
2284 span,
2285 "parameter properties are only allowed in a constructor",
2286 vec![
2287 "move the `public`/`private`/`readonly` modifier to a constructor parameter"
2288 .to_string(),
2289 ],
2290 );
2291 None
2292 }
2293 } else {
2294 None
2295 };
2296
2297 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
2298 self.advance();
2299 true
2300 } else {
2301 false
2302 };
2303 if modifiers.is_some() && rest {
2304 self.error_at_peek("a parameter property cannot be a rest parameter");
2305 return None;
2306 }
2307
2308 let (name, pattern) = match self.peek().kind {
2311 TokenKind::LeftBrace | TokenKind::LeftBracket => {
2312 if rest {
2313 self.error_at_peek("rest parameter cannot be destructured");
2314 return None;
2315 }
2316 if modifiers.is_some() {
2317 self.error_at_peek("a parameter property cannot be destructured");
2318 return None;
2319 }
2320 let binding = self.parse_binding()?;
2321 let placeholder = Ident {
2322 name: String::new(),
2323 span: binding.span(),
2324 };
2325 (placeholder, Some(binding))
2326 }
2327 _ => {
2328 let name_tok = self.expect_binding_identifier(if rest {
2329 "expected identifier after `...`"
2330 } else {
2331 "expected parameter name"
2332 })?;
2333 let name = self.ident_from_token(&name_tok);
2334 (name, None)
2335 }
2336 };
2337
2338 let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
2339 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2340 self.advance();
2341 Some(self.parse_type_annotation()?)
2342 } else if !rest && matches!(self.peek().kind, TokenKind::Equals) {
2343 None
2344 } else {
2345 if rest {
2346 self.error_at_peek_with_help(
2347 "rest parameter requires a type annotation",
2348 vec!["function name(...args: T[]): R { … }".to_string()],
2349 );
2350 } else {
2351 self.error_at_peek_with_help(
2352 "parameter requires a type annotation",
2353 vec!["add a type (`x: T`) or a default initializer (`x = value`)".to_string()],
2354 );
2355 }
2356 return None;
2357 };
2358 let default = self.parse_parameter_default(rest, optional)?;
2359 Some(ParamDecl {
2360 name,
2361 ty,
2362 default,
2363 optional,
2364 pattern,
2365 rest,
2366 modifiers,
2367 })
2368 }
2369
2370 fn parse_parameter_optional(&mut self, rest: bool, pattern: bool) -> Option<bool> {
2371 if !matches!(self.peek().kind, TokenKind::Question) {
2372 return Some(false);
2373 }
2374 let question = self.advance();
2375 if rest || pattern {
2376 self.error_at(
2377 question.span,
2378 if rest {
2379 "a rest parameter cannot be optional"
2380 } else {
2381 "a destructured parameter cannot be optional; use a default value"
2382 },
2383 );
2384 return None;
2385 }
2386 Some(true)
2387 }
2388
2389 fn parse_parameter_default(&mut self, rest: bool, optional: bool) -> Option<Option<ExprId>> {
2392 if !matches!(self.peek().kind, TokenKind::Equals) {
2393 return Some(None);
2394 }
2395 if rest {
2396 self.error_rest_parameter_default();
2397 return None;
2398 }
2399 if optional {
2400 self.error_at_peek_with_help(
2401 "an optional parameter cannot have a default value",
2402 vec!["drop the `?`: a parameter with a default is already optional".to_string()],
2403 );
2404 }
2405 self.advance();
2406 Some(Some(self.parse_expression()?))
2407 }
2408
2409 fn error_rest_parameter_default(&mut self) {
2411 self.error_at_peek_with_help(
2412 "rest parameter cannot have a default value",
2413 vec!["omit the `= …`; an unspecified rest defaults to `[]`".to_string()],
2414 );
2415 }
2416
2417 fn check_parameter_order<'p>(&mut self, params: impl IntoIterator<Item = ParamOrder<'p>>) {
2421 let mut follows_optional = false;
2422 for param in params {
2423 if param.optional {
2424 follows_optional = true;
2425 continue;
2426 }
2427 if !follows_optional || !param.required {
2428 continue;
2429 }
2430 let label = match param.name {
2431 Some(name) => format!("parameter `{name}`"),
2432 None => "destructured parameter".to_string(),
2433 };
2434 self.error_at_with_help(
2435 param.span,
2436 format!("required {label} cannot follow an optional parameter"),
2437 vec!["make it optional too (`x?: T`), give it a default (`x: T = …`), or move it before the optional parameters".to_string()],
2438 );
2439 return;
2440 }
2441 }
2442
2443 fn eat_asi_semicolon_before(&mut self, next: TokenKind) {
2448 if matches!(self.peek().kind, TokenKind::Semicolon) && self.peek_at(1).kind == next {
2449 self.advance();
2450 }
2451 }
2452
2453 fn parse_block(&mut self) -> Option<StmtId> {
2454 self.eat_asi_semicolon_before(TokenKind::LeftBrace);
2455 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2456 self.error_at_peek("expected `{`");
2457 return None;
2458 }
2459 let open = self.advance();
2460 self.block_depth += 1;
2461
2462 let mut stmts = Vec::new();
2463 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2464 if matches!(self.peek().kind, TokenKind::Semicolon) {
2465 self.advance();
2466 continue;
2467 }
2468 let pos_before = self.pos;
2469 if let Some(id) = self.parse_statement() {
2470 stmts.push(id);
2471 stmts.extend(self.take_trailing_declarators());
2472 } else {
2473 self.recover();
2474 if self.pos == pos_before && !self.is_at_eof() {
2475 self.advance();
2476 }
2477 }
2478 }
2479
2480 self.block_depth -= 1;
2481 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2482 self.error_at_peek("expected `}`");
2483 return None;
2484 }
2485 let close = self.advance();
2486
2487 let span = self.span(open.span.start, close.span.end);
2488 parse_arena_result(
2489 self.ast.try_push_stmt(Stmt {
2490 kind: StmtKind::Block(stmts),
2491 span,
2492 }),
2493 &mut self.fatal,
2494 )
2495 }
2496
2497 fn parse_control_body(&mut self) -> Option<StmtId> {
2502 if matches!(self.peek().kind, TokenKind::LeftBrace) {
2503 return self.parse_block();
2504 }
2505 let keyword = match self.peek().kind {
2510 TokenKind::Let => Some(("a", "let")),
2511 TokenKind::Const => Some(("a", "const")),
2512 TokenKind::Function => Some(("a", "function")),
2513 TokenKind::Interface => Some(("an", "interface")),
2515 TokenKind::Enum => Some(("an", "enum")),
2516 _ if self.peek_identifier_text_is("type") => Some(("a", "type")),
2517 _ => None,
2518 };
2519 let declares = match self.peek_at(1).kind {
2523 TokenKind::Identifier => true,
2524 TokenKind::LeftBracket | TokenKind::LeftBrace => !matches!(keyword, Some((_, "type"))),
2525 _ => false,
2526 };
2527 if let Some((article, keyword)) = keyword
2528 && declares
2529 {
2530 self.error_at_peek_with_help(
2531 format!(
2532 "{article} `{keyword}` declaration can't be the body of a statement without braces"
2533 ),
2534 vec![format!("wrap it in braces: `{{ {keyword} … }}`")],
2535 );
2536 }
2537 self.block_depth += 1;
2538 let stmt = self.parse_statement();
2539 self.block_depth -= 1;
2540 let stmt = stmt?;
2541 let mut stmts = vec![stmt];
2542 stmts.extend(self.take_trailing_declarators());
2543 let first = parse_arena_result(self.ast.try_stmt(stmt), &mut self.fatal)?.span;
2544 let last_id = stmts.last().copied().unwrap_or(stmt);
2545 let last = parse_arena_result(self.ast.try_stmt(last_id), &mut self.fatal)?.span;
2546 let span = self.span(first.start, last.end);
2547 parse_arena_result(
2548 self.ast.try_push_stmt(Stmt {
2549 kind: StmtKind::Block(stmts),
2550 span,
2551 }),
2552 &mut self.fatal,
2553 )
2554 }
2555
2556 fn parse_loop_body(&mut self) -> Option<StmtId> {
2559 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2560 return self.parse_control_body();
2561 }
2562 self.empty_body_at_semicolon()
2563 }
2564
2565 fn parse_branch_body(&mut self, keyword: &str) -> Option<StmtId> {
2569 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2570 return self.parse_control_body();
2571 }
2572 self.error_at_peek_with_help(
2573 format!("`{keyword}` has an empty body"),
2574 vec![format!("remove the `;` after `{keyword}`")],
2575 );
2576 self.empty_body_at_semicolon()
2577 }
2578
2579 fn empty_body_at_semicolon(&mut self) -> Option<StmtId> {
2581 let semi = self.advance();
2582 parse_arena_result(
2583 self.ast.try_push_stmt(Stmt {
2584 kind: StmtKind::Block(vec![]),
2585 span: semi.span,
2586 }),
2587 &mut self.fatal,
2588 )
2589 }
2590
2591 fn parse_if(&mut self) -> Option<StmtId> {
2592 self.with_recursion_limit(Self::parse_if_inner)
2593 }
2594
2595 fn parse_if_inner(&mut self) -> Option<StmtId> {
2596 let kw = self.advance();
2597 let condition = self.parse_paren_condition()?;
2598 let then_block = self.parse_branch_body("if")?;
2599
2600 let else_block = if matches!(self.peek().kind, TokenKind::Else) {
2601 self.advance();
2602 if matches!(self.peek().kind, TokenKind::If) {
2603 Some(self.parse_if()?)
2604 } else {
2605 Some(self.parse_branch_body("else")?)
2606 }
2607 } else {
2608 None
2609 };
2610
2611 let end = match else_block {
2612 Some(id) => {
2613 parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
2614 .span
2615 .end
2616 }
2617 None => {
2618 parse_arena_result(self.ast.try_stmt(then_block), &mut self.fatal)?
2619 .span
2620 .end
2621 }
2622 };
2623
2624 parse_arena_result(
2625 self.ast.try_push_stmt(Stmt {
2626 kind: StmtKind::If {
2627 condition,
2628 then_block,
2629 else_block,
2630 },
2631 span: self.span(kw.span.start, end),
2632 }),
2633 &mut self.fatal,
2634 )
2635 }
2636
2637 fn parse_while(&mut self) -> Option<StmtId> {
2638 let kw = self.advance();
2639 let condition = self.parse_paren_condition()?;
2640 let body = self.parse_loop_body()?;
2641 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2642 .span
2643 .end;
2644
2645 parse_arena_result(
2646 self.ast.try_push_stmt(Stmt {
2647 kind: StmtKind::While { condition, body },
2648 span: self.span(kw.span.start, body_end),
2649 }),
2650 &mut self.fatal,
2651 )
2652 }
2653
2654 fn parse_for(&mut self) -> Option<StmtId> {
2656 let kw = self.advance();
2657 if self.peek_identifier_text_is("await")
2660 && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
2661 {
2662 let tok = self.advance();
2663 self.error_at_with_help(
2664 tok.span,
2665 "`for await` is not supported",
2666 vec![
2667 "calls return their values directly: remove `await` and write \
2668 `for (const e of …)`"
2669 .to_string(),
2670 ],
2671 );
2672 }
2673 if !matches!(self.peek().kind, TokenKind::LeftParen) {
2674 self.error_at_peek("expected `(` after `for`");
2675 return None;
2676 }
2677 self.advance();
2678
2679 if self.scan_for_of_shape() {
2680 self.parse_for_of_tail(kw)
2681 } else {
2682 self.parse_c_for_tail(kw)
2683 }
2684 }
2685
2686 fn scan_for_of_shape(&self) -> bool {
2688 if !matches!(self.peek().kind, TokenKind::Let | TokenKind::Const) {
2689 return false;
2690 }
2691 let (start, mut depth) = match self.peek_at(1).kind {
2693 TokenKind::Identifier => match self.peek_at(2).kind {
2694 TokenKind::Identifier if self.peek_at_is_word(2, "of") => return true,
2695 TokenKind::Colon => (3, 0i32),
2696 _ => return false,
2697 },
2698 TokenKind::LeftBracket | TokenKind::LeftBrace => (2, 1i32),
2699 _ => return false,
2700 };
2701 let mut i = start;
2702 loop {
2703 match self.peek_at(i).kind {
2704 TokenKind::Eof => return false,
2705 TokenKind::LessThan
2706 | TokenKind::LeftParen
2707 | TokenKind::LeftBracket
2708 | TokenKind::LeftBrace => {
2709 depth += 1;
2710 }
2711 TokenKind::GreaterThan
2712 | TokenKind::RightParen
2713 | TokenKind::RightBracket
2714 | TokenKind::RightBrace => {
2715 if depth == 0 {
2716 return false;
2717 }
2718 depth -= 1;
2719 }
2720 TokenKind::Identifier if depth == 0 && self.peek_at_is_word(i, "of") => {
2723 return true;
2724 }
2725 TokenKind::Equals | TokenKind::Semicolon if depth == 0 => return false,
2726 _ => {}
2727 }
2728 i += 1;
2729 }
2730 }
2731
2732 fn parse_c_for_tail(&mut self, kw: Token) -> Option<StmtId> {
2734 let init = match self.peek().kind {
2735 TokenKind::Semicolon => {
2736 self.advance();
2737 Vec::new()
2738 }
2739 TokenKind::Let | TokenKind::Const => {
2740 self.trailing_declarators.clear();
2741 let is_const = matches!(self.peek().kind, TokenKind::Const);
2742 let mut init = vec![self.parse_let_or_const(is_const)?];
2743 init.extend(self.take_trailing_declarators());
2744 init
2745 }
2746 _ => self.parse_for_init_expressions()?,
2747 };
2748 let for_stmt = self.finish_c_for(kw, init.clone());
2749 if for_stmt.is_none() {
2750 self.discard_detached_patterns(&init);
2751 }
2752 for_stmt
2753 }
2754
2755 fn finish_c_for(&mut self, kw: Token, init: Vec<StmtId>) -> Option<StmtId> {
2756 let condition = if matches!(self.peek().kind, TokenKind::Semicolon) {
2757 None
2758 } else {
2759 Some(self.parse_expression()?)
2760 };
2761 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2762 self.error_at_peek("expected `;` after `for` condition");
2763 return None;
2764 }
2765 self.advance();
2766
2767 let update = if matches!(self.peek().kind, TokenKind::RightParen) {
2768 None
2769 } else {
2770 Some(self.parse_for_update_stmt()?)
2771 };
2772 if !matches!(self.peek().kind, TokenKind::RightParen) {
2773 self.error_at_peek("expected `)` after `for` update");
2774 return None;
2775 }
2776 self.advance();
2777
2778 let body = self.parse_loop_body()?;
2779 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2780 .span
2781 .end;
2782 parse_arena_result(
2783 self.ast.try_push_stmt(Stmt {
2784 kind: StmtKind::For {
2785 init,
2786 condition,
2787 update,
2788 body,
2789 },
2790 span: self.span(kw.span.start, body_end),
2791 }),
2792 &mut self.fatal,
2793 )
2794 }
2795
2796 fn discard_detached_patterns(&mut self, stmts: &[StmtId]) {
2801 for &id in stmts {
2802 let Some(stmt) = parse_arena_result(self.ast.try_stmt_mut(id), &mut self.fatal) else {
2803 return;
2804 };
2805 if matches!(
2806 stmt.kind,
2807 StmtKind::LetPattern { .. } | StmtKind::ConstPattern { .. }
2808 ) {
2809 stmt.kind = StmtKind::Block(Vec::new());
2810 }
2811 }
2812 }
2813
2814 fn parse_for_init_expressions(&mut self) -> Option<Vec<StmtId>> {
2817 let mut init = vec![self.parse_for_clause_expr()?];
2818 while matches!(self.peek().kind, TokenKind::Comma) {
2819 self.advance();
2820 init.push(self.parse_for_clause_expr()?);
2821 }
2822 if !self.at_statement_end() {
2823 let last = parse_arena_result(self.ast.try_stmt(*init.last()?), &mut self.fatal)?;
2824 let what = if matches!(last.kind, StmtKind::Expr(_)) {
2825 "expression"
2826 } else {
2827 "assignment"
2828 };
2829 self.error_at_peek(format!("expected `;` after {what}"));
2830 return None;
2831 }
2832 self.finish_statement();
2833 Some(init)
2834 }
2835
2836 fn parse_for_update_stmt(&mut self) -> Option<StmtId> {
2839 let first = self.parse_for_clause_expr()?;
2840 if !matches!(self.peek().kind, TokenKind::Comma) {
2841 return Some(first);
2842 }
2843 let mut stmts = vec![first];
2844 while matches!(self.peek().kind, TokenKind::Comma) {
2845 self.advance();
2846 stmts.push(self.parse_for_clause_expr()?);
2847 }
2848 let start = parse_arena_result(self.ast.try_stmt(first), &mut self.fatal)?
2849 .span
2850 .start;
2851 let span = self.span(start, self.prev_token_end());
2852 parse_arena_result(
2853 self.ast.try_push_stmt(Stmt {
2854 kind: StmtKind::Block(stmts),
2855 span,
2856 }),
2857 &mut self.fatal,
2858 )
2859 }
2860
2861 fn parse_for_clause_expr(&mut self) -> Option<StmtId> {
2862 let expr_id = self.parse_expression()?;
2863 let span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
2864 let kind = self.statement_kind_for(expr_id)?;
2865 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
2866 }
2867
2868 fn parse_for_of_tail(&mut self, kw: Token) -> Option<StmtId> {
2869 let binding_tok = self.advance();
2870 let binding_kind = match binding_tok.kind {
2871 TokenKind::Let => crate::BindingKind::Let,
2872 TokenKind::Const => crate::BindingKind::Const,
2873 _ => return self.invariant_failure("scan_for_of_shape gated this"),
2874 };
2875 let (name, binding) = match self.peek().kind {
2876 TokenKind::LeftBracket => {
2877 let b = self.parse_binding()?;
2878 (None, Some(b))
2879 }
2880 TokenKind::LeftBrace => {
2881 let b = self.parse_binding()?;
2882 self.error_at(
2883 b.span(),
2884 "object destructuring is not supported in `for-of`; use array destructuring or unpack inside the loop body",
2885 );
2886 return None;
2887 }
2888 _ => {
2889 let name_tok = self.expect_binding_identifier(
2890 "expected identifier after `let`/`const` in `for-of`",
2891 )?;
2892 (Some(self.ident_from_token(&name_tok)), None)
2893 }
2894 };
2895 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2896 self.advance();
2897 Some(self.parse_type_annotation()?)
2898 } else {
2899 None
2900 };
2901 if !self.peek_identifier_text_is("of") {
2902 self.error_at_peek("expected `of` in `for-of`");
2903 return None;
2904 }
2905 self.advance();
2906
2907 let iter = self.parse_expression()?;
2908 if !matches!(self.peek().kind, TokenKind::RightParen) {
2909 self.error_at_peek("expected `)` after `for-of` iterable");
2910 return None;
2911 }
2912 self.advance();
2913
2914 let body = self.parse_loop_body()?;
2915 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2916 .span
2917 .end;
2918 let kind = match (binding, name) {
2919 (Some(binding), _) => StmtKind::ForOfPattern {
2920 binding_kind,
2921 binding,
2922 ty,
2923 iter,
2924 body,
2925 },
2926 (None, Some(name)) => StmtKind::ForOf {
2927 binding_kind,
2928 name,
2929 ty,
2930 iter,
2931 body,
2932 },
2933 (None, None) => return self.invariant_failure("either binding or name must be Some"),
2934 };
2935 parse_arena_result(
2936 self.ast.try_push_stmt(Stmt {
2937 kind,
2938 span: self.span(kw.span.start, body_end),
2939 }),
2940 &mut self.fatal,
2941 )
2942 }
2943
2944 fn parse_do_while(&mut self) -> Option<StmtId> {
2945 let kw = self.advance();
2946 let body = self.parse_loop_body()?;
2947 self.eat_asi_semicolon_before(TokenKind::While);
2948 if !matches!(self.peek().kind, TokenKind::While) {
2949 self.error_at_peek("expected `while` after `do` body");
2950 return None;
2951 }
2952 self.advance();
2953 let condition = self.parse_paren_condition()?;
2954 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
2958 self.advance().span.end
2959 } else {
2960 parse_arena_result(self.ast.try_expr(condition), &mut self.fatal)?
2961 .span
2962 .end
2963 };
2964 parse_arena_result(
2965 self.ast.try_push_stmt(Stmt {
2966 kind: StmtKind::DoWhile { body, condition },
2967 span: self.span(kw.span.start, end),
2968 }),
2969 &mut self.fatal,
2970 )
2971 }
2972
2973 fn parse_switch(&mut self) -> Option<StmtId> {
2975 let kw = self.advance();
2976 if !matches!(self.peek().kind, TokenKind::LeftParen) {
2977 self.error_at_peek("expected `(` after `switch`");
2978 return None;
2979 }
2980 self.advance();
2981 let discriminant = self.parse_expression()?;
2982 if !matches!(self.peek().kind, TokenKind::RightParen) {
2983 self.error_at_peek("expected `)` after `switch` discriminant");
2984 return None;
2985 }
2986 self.advance();
2987
2988 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2989 self.error_at_peek("expected `{` to start `switch` body");
2990 return None;
2991 }
2992 self.advance();
2993 self.block_depth += 1;
2994 let arms = self.parse_switch_arms();
2995 self.block_depth -= 1;
2996 let (cases, default) = arms?;
2997
2998 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2999 self.error_at_peek("expected `}` to close `switch` body");
3000 return None;
3001 }
3002 let close = self.advance();
3003
3004 parse_arena_result(
3005 self.ast.try_push_stmt(Stmt {
3006 kind: StmtKind::Switch {
3007 discriminant,
3008 cases,
3009 default,
3010 },
3011 span: self.span(kw.span.start, close.span.end),
3012 }),
3013 &mut self.fatal,
3014 )
3015 }
3016
3017 fn parse_switch_arms(&mut self) -> Option<(Vec<SwitchCase>, Option<SwitchDefault>)> {
3019 let mut cases: Vec<SwitchCase> = Vec::new();
3020 let mut default: Option<SwitchDefault> = None;
3021 let mut pending_values: Vec<ExprId> = Vec::new();
3022 let mut pending_label_start: Option<u32> = None;
3023 let mut pending_default: Option<Span> = None;
3024
3025 loop {
3026 match self.peek().kind {
3027 TokenKind::Eof => break,
3028 TokenKind::Semicolon => {
3030 self.advance();
3031 }
3032 TokenKind::RightBrace => {
3033 if pending_label_start.is_some() {
3035 self.error_at_peek(
3036 "expected case body before `}` — case labels must be followed by statements",
3037 );
3038 return None;
3039 }
3040 break;
3041 }
3042 TokenKind::Case => {
3043 let case_kw = self.advance();
3044 if pending_label_start.is_none() {
3045 pending_label_start = Some(case_kw.span.start);
3046 }
3047 let value = self.parse_expression()?;
3048 if !matches!(self.peek().kind, TokenKind::Colon) {
3049 self.error_at_peek("expected `:` after `case` label");
3050 return None;
3051 }
3052 self.advance();
3053 pending_values.push(value);
3054 }
3055 TokenKind::Default => {
3056 let kw_tok = self.advance();
3057 if default.is_some() {
3058 self.error_at(kw_tok.span, "duplicate `default` clause in `switch`");
3059 return None;
3060 }
3061 if !matches!(self.peek().kind, TokenKind::Colon) {
3062 self.error_at_peek("expected `:` after `default`");
3063 return None;
3064 }
3065 self.advance();
3066 if pending_label_start.is_none() {
3067 pending_label_start = Some(kw_tok.span.start);
3068 }
3069 pending_default = Some(kw_tok.span);
3070 }
3071 _ => {
3072 if pending_label_start.is_none() {
3073 self.error_at_peek(
3074 "expected `case` or `default` at the start of a `switch` body",
3075 );
3076 return None;
3077 }
3078 let Some(label_start) = pending_label_start else {
3079 return self.invariant_failure("switch arm has no label");
3080 };
3081 let body = self.parse_switch_arm_body(label_start)?;
3082 let arm_span =
3083 parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
3084 if !pending_values.is_empty() {
3085 let first = parse_arena_result(
3086 self.ast.try_expr(pending_values[0]),
3087 &mut self.fatal,
3088 )?
3089 .span
3090 .start;
3091 let case_span = self.span(first, arm_span.end);
3092 cases.push(SwitchCase {
3093 values: std::mem::take(&mut pending_values),
3094 body,
3095 span: case_span,
3096 });
3097 }
3098 if let Some(def_kw_span) = pending_default.take() {
3099 default = Some(SwitchDefault {
3100 body,
3101 span: self.span(def_kw_span.start, arm_span.end),
3102 });
3103 }
3104 pending_label_start = None;
3105 }
3106 }
3107 }
3108 Some((cases, default))
3109 }
3110
3111 fn parse_switch_arm_body(&mut self, body_start: u32) -> Option<StmtId> {
3113 let mut stmts: Vec<StmtId> = Vec::new();
3114 let mut end_offset = body_start;
3115 loop {
3116 if matches!(
3117 self.peek().kind,
3118 TokenKind::Case | TokenKind::Default | TokenKind::RightBrace | TokenKind::Eof
3119 ) {
3120 break;
3121 }
3122 if matches!(self.peek().kind, TokenKind::Semicolon) {
3123 end_offset = self.peek().span.end;
3124 self.advance();
3125 continue;
3126 }
3127 let pos_before = self.pos;
3128 if let Some(id) = self.parse_statement() {
3129 stmts.push(id);
3130 stmts.extend(self.take_trailing_declarators());
3131 let last = stmts.last().copied().unwrap_or(id);
3132 end_offset = parse_arena_result(self.ast.try_stmt(last), &mut self.fatal)?
3133 .span
3134 .end;
3135 } else {
3136 self.recover();
3137 if self.pos == pos_before && !self.is_at_eof() {
3138 self.advance();
3139 }
3140 end_offset = end_offset.max(self.prev_token_end());
3143 }
3144 }
3145 let span = self.span(body_start, end_offset);
3146 parse_arena_result(
3147 self.ast.try_push_stmt(Stmt {
3148 kind: StmtKind::Block(stmts),
3149 span,
3150 }),
3151 &mut self.fatal,
3152 )
3153 }
3154
3155 fn parse_break(&mut self) -> Option<StmtId> {
3156 let kw = self.advance();
3157 if !matches!(self.peek().kind, TokenKind::Semicolon) {
3158 self.error_at_peek("expected `;` after `break`");
3159 return None;
3160 }
3161 let semi = self.advance();
3162 parse_arena_result(
3163 self.ast.try_push_stmt(Stmt {
3164 kind: StmtKind::Break,
3165 span: self.span(kw.span.start, semi.span.end),
3166 }),
3167 &mut self.fatal,
3168 )
3169 }
3170
3171 fn parse_continue(&mut self) -> Option<StmtId> {
3172 let kw = self.advance();
3173 if !matches!(self.peek().kind, TokenKind::Semicolon) {
3174 self.error_at_peek("expected `;` after `continue`");
3175 return None;
3176 }
3177 let semi = self.advance();
3178 parse_arena_result(
3179 self.ast.try_push_stmt(Stmt {
3180 kind: StmtKind::Continue,
3181 span: self.span(kw.span.start, semi.span.end),
3182 }),
3183 &mut self.fatal,
3184 )
3185 }
3186
3187 fn parse_return(&mut self) -> Option<StmtId> {
3188 let kw = self.advance();
3189
3190 let value = if matches!(self.peek().kind, TokenKind::Semicolon) {
3191 None
3192 } else {
3193 Some(self.parse_expression()?)
3194 };
3195
3196 if !self.at_statement_end() {
3197 self.error_at_peek("expected `;` after return");
3198 return None;
3199 }
3200 let end = self.finish_statement();
3201
3202 parse_arena_result(
3203 self.ast.try_push_stmt(Stmt {
3204 kind: StmtKind::Return(value),
3205 span: self.span(kw.span.start, end),
3206 }),
3207 &mut self.fatal,
3208 )
3209 }
3210
3211 fn parse_throw(&mut self) -> Option<StmtId> {
3212 let kw = self.advance();
3213
3214 if matches!(self.peek().kind, TokenKind::Semicolon) {
3215 self.error_at_peek_with_help(
3216 "expected expression after `throw`",
3217 vec!["throw new Error(\"message\");".to_string()],
3218 );
3219 return None;
3220 }
3221
3222 let value = self.parse_expression()?;
3223
3224 if !self.at_statement_end() {
3225 self.error_at_peek("expected `;` after `throw`");
3226 return None;
3227 }
3228 let end = self.finish_statement();
3229
3230 parse_arena_result(
3231 self.ast.try_push_stmt(Stmt {
3232 kind: StmtKind::Throw { value },
3233 span: self.span(kw.span.start, end),
3234 }),
3235 &mut self.fatal,
3236 )
3237 }
3238
3239 fn parse_try(&mut self) -> Option<StmtId> {
3240 let kw = self.advance();
3241
3242 let body = self.parse_block()?;
3243
3244 let mut catches: Vec<CatchClause> = Vec::new();
3245 let mut finally: Option<StmtId> = None;
3246 let mut tail_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3247 .span
3248 .end;
3249
3250 loop {
3251 match self.peek().kind {
3252 TokenKind::Catch => {
3253 if finally.is_some() {
3254 self.error_at_peek("`catch` clause must appear before `finally`");
3255 return None;
3256 }
3257 let clause = self.parse_catch_clause()?;
3258 tail_end = clause.span.end;
3259 catches.push(clause);
3260 }
3261 TokenKind::Finally => {
3262 if finally.is_some() {
3263 self.error_at_peek("duplicate `finally` clause");
3264 return None;
3265 }
3266 self.advance();
3267 let block = self.parse_block()?;
3268 tail_end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
3269 .span
3270 .end;
3271 finally = Some(block);
3272 }
3273 _ => break,
3274 }
3275 }
3276
3277 if catches.is_empty() && finally.is_none() {
3278 self.error_at(
3279 self.span(kw.span.start, tail_end),
3280 "try requires at least one of `catch` or `finally`",
3281 );
3282 return None;
3283 }
3284
3285 parse_arena_result(
3286 self.ast.try_push_stmt(Stmt {
3287 kind: StmtKind::Try {
3288 body,
3289 catches,
3290 finally,
3291 },
3292 span: self.span(kw.span.start, tail_end),
3293 }),
3294 &mut self.fatal,
3295 )
3296 }
3297
3298 fn parse_catch_clause(&mut self) -> Option<CatchClause> {
3299 let kw = self.advance();
3300
3301 let (binding, ty) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
3302 (
3304 Ident {
3305 name: "#catch".into(),
3306 span: kw.span,
3307 },
3308 None,
3309 )
3310 } else {
3311 self.parse_catch_binding()?
3312 };
3313
3314 let body = self.parse_block()?;
3315 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3316 .span
3317 .end;
3318
3319 Some(CatchClause {
3320 binding,
3321 ty,
3322 body,
3323 span: self.span(kw.span.start, body_end),
3324 })
3325 }
3326
3327 fn parse_catch_binding(&mut self) -> Option<(Ident, Option<TypeAnnotation>)> {
3328 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3329 self.error_at_peek("expected `(` after `catch`");
3330 return None;
3331 }
3332 self.advance();
3333 let name_tok = self.expect_binding_identifier("expected catch binding name")?;
3334 let binding = self.ident_from_token(&name_tok);
3335 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3336 self.advance();
3337 if self.reject_any_catch_annotation() {
3338 None
3340 } else {
3341 Some(self.parse_type_annotation()?)
3342 }
3343 } else {
3344 None
3345 };
3346 if !matches!(self.peek().kind, TokenKind::RightParen) {
3347 self.error_at_peek("expected `)` after catch binding");
3348 return None;
3349 }
3350 self.advance();
3351 Some((binding, ty))
3352 }
3353
3354 fn reject_any_catch_annotation(&mut self) -> bool {
3357 let is_bare_any = self.peek_identifier_text_is("any")
3358 && matches!(self.peek_at(1).kind, TokenKind::RightParen);
3359 if !is_bare_any {
3360 return false;
3361 }
3362 let any_tok = self.advance();
3363 self.error_at_with_help(
3365 any_tok.span,
3366 "`any` is not supported",
3367 vec![
3368 "write `catch (e)` to catch every thrown error, or name an error class, \
3369 as in `catch (e: RangeError)`, to catch only that type"
3370 .to_string(),
3371 ],
3372 );
3373 true
3374 }
3375
3376 fn parse_paren_condition(&mut self) -> Option<ExprId> {
3377 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3378 self.error_at_peek("expected `(`");
3379 return None;
3380 }
3381 self.advance();
3382 let expr = self.parse_expression()?;
3383 if !matches!(self.peek().kind, TokenKind::RightParen) {
3384 self.error_at_peek("expected `)`");
3385 return None;
3386 }
3387 self.advance();
3388 Some(expr)
3389 }
3390
3391 fn parse_predicate_or_return_type(
3392 &mut self,
3393 type_pos: TypePos,
3394 ) -> Option<(
3395 Option<TypeAnnotation>,
3396 Option<crate::TypePredicateAnnotation>,
3397 )> {
3398 if matches!(self.peek().kind, TokenKind::Identifier) && self.peek_at_is_word(1, "is") {
3399 let param_tok = self.advance();
3400 let param = self.ident_from_token(¶m_tok);
3401 self.advance();
3402 let asserted = self.parse_type_annotation_in(type_pos)?;
3403 let span = self.span(param.span.start, asserted.span.end);
3404 return Some((
3405 None,
3406 Some(crate::TypePredicateAnnotation {
3407 param,
3408 asserted,
3409 span,
3410 }),
3411 ));
3412 }
3413 Some((Some(self.parse_type_annotation_in(type_pos)?), None))
3414 }
3415
3416 fn parse_type_annotation(&mut self) -> Option<TypeAnnotation> {
3417 self.parse_type_annotation_in(TypePos::Anywhere)
3418 }
3419
3420 fn parse_type_annotation_in(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3421 let leading_pipe = matches!(self.peek().kind, TokenKind::Pipe);
3423 if leading_pipe {
3424 self.advance();
3425 }
3426 let first = self.parse_type_array(type_pos)?;
3427 if !matches!(self.peek().kind, TokenKind::Pipe) {
3428 return Some(first);
3429 }
3430 let start = first.span.start;
3431 let mut end = first.span.end;
3432 let mut members = vec![first];
3433 while matches!(self.peek().kind, TokenKind::Pipe) {
3434 self.advance();
3435 let member = self.parse_type_array(type_pos)?;
3436 end = member.span.end;
3437 members.push(member);
3438 }
3439 Some(TypeAnnotation {
3440 kind: TypeAnnotationKind::Union(members),
3441 span: self.span(start, end),
3442 })
3443 }
3444
3445 fn parse_type_array(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3446 self.with_recursion_limit(|parser| parser.parse_type_array_inner(type_pos))
3447 }
3448
3449 fn parse_type_array_inner(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3450 if self.is_contextual_type_operator(self.pos, "keyof") {
3454 let kw = self.advance();
3455 let operand = self.parse_type_array(type_pos)?;
3456 let span = self.span(kw.span.start, operand.span.end);
3457 return Some(TypeAnnotation {
3458 kind: TypeAnnotationKind::KeyOf(Box::new(operand)),
3459 span,
3460 });
3461 }
3462 if self.is_contextual_type_operator(self.pos, "readonly") {
3463 return self.parse_readonly_type(type_pos);
3464 }
3465
3466 let mut ty = match self.peek().kind {
3467 TokenKind::Typeof => {
3473 let kw = self.advance();
3474 let first = self.expect_identifier("expected a value name after `typeof`")?;
3475 let mut path = vec![self.type_name(first.span)?];
3476 let mut end = first.span.end;
3477 while matches!(self.peek().kind, TokenKind::Dot) {
3478 self.advance();
3479 let seg = self.expect_identifier_name("expected a property name after `.`")?;
3480 end = seg.span.end;
3481 path.push(self.type_name(seg.span)?);
3482 }
3483 TypeAnnotation {
3484 kind: TypeAnnotationKind::TypeOf { path },
3485 span: self.span(kw.span.start, end),
3486 }
3487 }
3488 TokenKind::Identifier | TokenKind::Void => {
3489 let leading_is_identifier = matches!(self.peek().kind, TokenKind::Identifier);
3491 let tok = self.advance();
3492 let first_span = tok.span;
3493 let mut path_end = first_span.end;
3494 let first_name = self.type_name(first_span)?;
3495 let mut path = vec![first_name.clone()];
3496 if leading_is_identifier {
3497 while matches!(self.peek().kind, TokenKind::Dot) {
3498 self.advance();
3499 if !matches!(self.peek().kind, TokenKind::Identifier) {
3500 self.error_at_peek("expected identifier after `.` in type name");
3501 return None;
3502 }
3503 let seg = self.advance();
3504 path_end = seg.span.end;
3505 path.push(self.type_name(seg.span)?);
3506 }
3507 }
3508 let (args, end) = if matches!(self.peek().kind, TokenKind::LessThan)
3510 && !self.line_break_before_peek()
3511 {
3512 self.parse_type_argument_list()?
3513 } else {
3514 (Vec::new(), path_end)
3515 };
3516 let outer_span = self.span(first_span.start, end);
3517 let kind = if path.len() == 1 {
3518 if first_name.name == "any" {
3519 self.error_at_with_help(
3520 first_span,
3521 "`any` is not supported",
3522 vec![
3523 "annotate a concrete type instead — an object shape \
3524 like `{ id: string }`, a named `interface`, or \
3525 `unknown` (a safe dynamic type you narrow before use)"
3526 .to_string(),
3527 ],
3528 );
3529 }
3530 TypeAnnotationKind::Name {
3531 name: first_name,
3532 args,
3533 }
3534 } else {
3535 TypeAnnotationKind::Qualified { path, args }
3536 };
3537 TypeAnnotation {
3538 kind,
3539 span: outer_span,
3540 }
3541 }
3542 TokenKind::NullLiteral => {
3544 let tok = self.advance();
3545 TypeAnnotation {
3546 kind: TypeAnnotationKind::Name {
3547 name: self.type_name(tok.span)?,
3548 args: Vec::new(),
3549 },
3550 span: tok.span,
3551 }
3552 }
3553 TokenKind::StringLiteral(_) => {
3554 let tok = self.advance();
3555 let span = tok.span;
3556 let TokenKind::StringLiteral(s) = tok.kind else {
3557 unreachable!("advance preserves the matched StringLiteral variant");
3559 };
3560 TypeAnnotation {
3561 kind: TypeAnnotationKind::StringLiteral(s),
3562 span,
3563 }
3564 }
3565 TokenKind::NumberLiteral(_) => {
3566 let tok = self.advance();
3567 let span = tok.span;
3568 let TokenKind::NumberLiteral(v) = tok.kind else {
3569 unreachable!("advance preserves the matched NumberLiteral variant");
3571 };
3572 let canonical = if v == 0.0 { 0.0 } else { v };
3574 TypeAnnotation {
3575 kind: TypeAnnotationKind::NumberLiteral(crate::types::LiteralF64(canonical)),
3576 span,
3577 }
3578 }
3579 TokenKind::BigIntLiteral(_) => {
3580 let tok = self.advance();
3581 let TokenKind::BigIntLiteral(digits) = tok.kind else {
3582 unreachable!("advance preserves the matched BigIntLiteral variant");
3584 };
3585 TypeAnnotation {
3586 kind: TypeAnnotationKind::BigIntLiteral(digits),
3587 span: tok.span,
3588 }
3589 }
3590 TokenKind::Minus
3591 if matches!(
3592 self.peek_at(1).kind,
3593 TokenKind::NumberLiteral(_) | TokenKind::BigIntLiteral(_)
3594 ) =>
3595 {
3596 let minus = self.advance().span;
3597 let tok = self.advance();
3598 let span = self.span(minus.start, tok.span.end);
3599 let kind =
3600 match tok.kind {
3601 TokenKind::NumberLiteral(v) => TypeAnnotationKind::NumberLiteral(
3602 crate::types::LiteralF64(if v == 0.0 { 0.0 } else { -v }),
3603 ),
3604 TokenKind::BigIntLiteral(digits) => TypeAnnotationKind::BigIntLiteral(
3605 crate::types::negate_bigint_digits(&digits),
3606 ),
3607 _ => unreachable!("guarded to a numeric literal after `-`"),
3609 };
3610 TypeAnnotation { kind, span }
3611 }
3612 TokenKind::BooleanLiteral(value) => {
3613 let span = self.advance().span;
3614 TypeAnnotation {
3615 kind: TypeAnnotationKind::BooleanLiteral(value),
3616 span,
3617 }
3618 }
3619 TokenKind::LeftBrace => self.parse_object_type_annotation()?,
3620 TokenKind::LeftParen if self.paren_at_opens_function_type(self.pos, type_pos) => {
3621 self.parse_function_type_annotation()?
3622 }
3623 TokenKind::LeftParen => self.parse_grouped_or_function_type()?,
3624 TokenKind::LeftBracket => self.parse_tuple_type_annotation()?,
3626 _ => {
3627 self.error_at_peek("expected type");
3628 return None;
3629 }
3630 };
3631 let mut array_suffixes = 0usize;
3634 while matches!(self.peek().kind, TokenKind::LeftBracket) && !self.line_break_before_peek() {
3637 array_suffixes += 1;
3638 if self.recursion_depth.saturating_add(array_suffixes) >= MAX_PARSE_DEPTH {
3639 self.stop_at_recursion_limit();
3640 return None;
3641 }
3642 let open = self.advance();
3643 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3644 self.error_at(open.span, "expected `]` to close array type");
3645 return None;
3646 }
3647 let close = self.advance();
3648 let span = self.span(ty.span.start, close.span.end);
3649 ty = TypeAnnotation {
3650 kind: TypeAnnotationKind::Array(Box::new(ty)),
3651 span,
3652 };
3653 }
3654 Some(ty)
3655 }
3656
3657 fn parse_readonly_type(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3661 let kw = self.advance();
3662 let opens_group = matches!(self.peek().kind, TokenKind::LeftParen);
3663 let operand = self.parse_type_array(type_pos)?;
3664 let grouped = opens_group
3667 && matches!(
3668 self.pos
3669 .checked_sub(1)
3670 .and_then(|index| self.tokens.get(index))
3671 .map(|token| &token.kind),
3672 Some(TokenKind::RightParen)
3673 );
3674 if grouped
3675 || !matches!(
3676 operand.kind,
3677 TypeAnnotationKind::Array(_) | TypeAnnotationKind::Tuple(_)
3678 )
3679 {
3680 self.error_at_with_help(
3681 kw.span,
3682 "`readonly` only applies to array and tuple types",
3683 vec![
3684 "write `readonly T[]` or `readonly [A, B]`; to protect an object's \
3685 properties, mark each one `readonly` instead"
3686 .to_string(),
3687 ],
3688 );
3689 return Some(operand);
3690 }
3691 let span = self.span(kw.span.start, operand.span.end);
3692 Some(TypeAnnotation {
3693 kind: TypeAnnotationKind::Readonly(Box::new(operand)),
3694 span,
3695 })
3696 }
3697
3698 fn paren_at_opens_function_type(&self, index: usize, type_pos: TypePos) -> bool {
3709 let after = |offset: usize| self.tokens.get(index + offset).map(|t| &t.kind);
3710 match (after(1), after(2)) {
3711 (Some(TokenKind::RightParen | TokenKind::DotDotDot), _) => true,
3713 (Some(TokenKind::Identifier), Some(TokenKind::Colon | TokenKind::Question)) => true,
3715 (Some(TokenKind::Identifier), Some(TokenKind::Comma | TokenKind::RightParen)) => {
3724 type_pos == TypePos::Anywhere
3725 && self.index_after_matching_paren(index).is_some_and(|i| {
3726 matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow))
3727 })
3728 }
3729 _ => false,
3731 }
3732 }
3733
3734 fn parse_grouped_or_function_type(&mut self) -> Option<TypeAnnotation> {
3739 let open = self.pos;
3740 let diagnostics_before = self.diagnostics.len();
3741 if let Some(ty) = self.parse_grouped_type_annotation() {
3742 return Some(ty);
3743 }
3744 let arrow_follows = self
3745 .index_after_matching_paren(open)
3746 .is_some_and(|i| matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)));
3747 if !arrow_follows {
3748 return None;
3749 }
3750 self.pos = open;
3751 self.diagnostics.truncate(diagnostics_before);
3752 self.parse_function_type_annotation()
3753 }
3754
3755 fn parse_grouped_type_annotation(&mut self) -> Option<TypeAnnotation> {
3758 let open = self.advance();
3759 let inner = self.parse_type_annotation()?;
3760 if !matches!(self.peek().kind, TokenKind::RightParen) {
3761 self.error_at_peek("expected `)` to close a parenthesized type");
3762 return None;
3763 }
3764 let close = self.advance();
3765 Some(TypeAnnotation {
3766 span: self.span(open.span.start, close.span.end),
3767 ..inner
3768 })
3769 }
3770
3771 fn parse_function_type_annotation(&mut self) -> Option<TypeAnnotation> {
3773 let open = self.advance();
3774 let params = self.parse_function_type_params()?;
3775 if !matches!(self.peek().kind, TokenKind::Arrow) {
3776 self.error_at_peek("expected `=>` in function-type annotation");
3777 return None;
3778 }
3779 self.advance();
3780 let return_type = self.parse_type_annotation()?;
3781 let end = return_type.span.end;
3782 Some(TypeAnnotation {
3783 kind: TypeAnnotationKind::Function {
3784 params,
3785 return_type: Box::new(return_type),
3786 },
3787 span: self.span(open.span.start, end),
3788 })
3789 }
3790
3791 fn parse_function_type_params(&mut self) -> Option<Vec<TypeAnnotationField>> {
3795 let mut params: Vec<TypeAnnotationField> = Vec::new();
3796 if !matches!(self.peek().kind, TokenKind::RightParen) {
3797 loop {
3798 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
3799 self.advance();
3800 true
3801 } else {
3802 false
3803 };
3804 let name_tok = self.expect_binding_identifier(if rest {
3805 "expected identifier after `...` in function-type annotation"
3806 } else {
3807 "expected parameter name in function-type annotation"
3808 })?;
3809 let name = self.ident_from_token(&name_tok);
3810 let optional = self.parse_parameter_optional(rest, false)?;
3811 if !matches!(self.peek().kind, TokenKind::Colon) {
3812 if rest {
3813 self.error_at_peek_with_help(
3814 "rest parameter requires a type annotation",
3815 vec!["(...args: T[]) => R".to_string()],
3816 );
3817 } else {
3818 self.error_at_peek(
3819 "expected `:` after parameter name (function-type params \
3820 require named annotations in v1)",
3821 );
3822 }
3823 return None;
3824 }
3825 self.advance();
3826 let ty = self.parse_type_annotation()?;
3827 if rest && !is_rest_array_annotation(&ty) {
3828 self.error_at(ty.span, "rest parameter type must be an array");
3829 return None;
3830 }
3831 if matches!(self.peek().kind, TokenKind::Equals) {
3832 if rest {
3833 self.error_rest_parameter_default();
3834 } else {
3835 self.error_at_peek(
3836 "default values in function-type annotations are not yet supported",
3837 );
3838 }
3839 return None;
3840 }
3841 params.push(TypeAnnotationField {
3842 name,
3843 ty,
3844 optional,
3845 readonly: false,
3846 rest,
3847 method: false,
3848 });
3849 match self.peek().kind {
3850 TokenKind::Comma => {
3851 if rest {
3852 let comma = self.advance();
3853 self.error_at(comma.span, "rest parameter must be the last parameter");
3854 return None;
3855 }
3856 self.advance();
3857 if matches!(self.peek().kind, TokenKind::RightParen) {
3858 break;
3859 }
3860 }
3861 TokenKind::RightParen => break,
3862 _ => {
3863 self.error_at_peek("expected `,` or `)`");
3864 return None;
3865 }
3866 }
3867 }
3868 self.check_parameter_order(params.iter().map(ParamOrder::of_field));
3869 }
3870 if !matches!(self.peek().kind, TokenKind::RightParen) {
3871 self.error_at_peek("expected `)`");
3872 return None;
3873 }
3874 self.advance();
3875 Some(params)
3876 }
3877
3878 fn parse_tuple_type_annotation(&mut self) -> Option<TypeAnnotation> {
3879 let open = self.advance();
3880 if matches!(self.peek().kind, TokenKind::RightBracket) {
3881 let close = self.peek().span;
3882 self.error_at(
3883 self.span(open.span.start, close.end),
3884 "tuple types must have at least one element",
3885 );
3886 return None;
3887 }
3888 let mut elements: Vec<TypeAnnotation> = Vec::new();
3889 loop {
3890 if matches!(self.peek().kind, TokenKind::DotDotDot) {
3891 self.error_at_with_help(
3892 self.peek().span,
3893 "rest elements in tuple types are not supported",
3894 vec!["use an array type `T[]` for a list of varying length".to_string()],
3895 );
3896 return None;
3897 }
3898 let label = self.skip_tuple_element_label();
3901 let mut ty = self.parse_type_annotation()?;
3902 let suffix_optional = matches!(self.peek().kind, TokenKind::Question);
3903 if suffix_optional && label != TupleElementLabel::None {
3904 self.error_at_peek_with_help(
3905 "put the optional marker after the tuple element label",
3906 vec!["write `name?: T` instead of `name: T?`".to_string()],
3907 );
3908 return None;
3909 }
3910 let optional = label == TupleElementLabel::Optional || suffix_optional;
3911 if optional {
3912 let end = if suffix_optional {
3913 self.advance().span.end
3914 } else {
3915 ty.span.end
3916 };
3917 let span = self.span(ty.span.start, end);
3918 ty = TypeAnnotation {
3919 kind: TypeAnnotationKind::Optional(Box::new(ty)),
3920 span,
3921 };
3922 } else if elements
3923 .iter()
3924 .any(|element| matches!(element.kind, TypeAnnotationKind::Optional(_)))
3925 {
3926 self.error_at(
3927 ty.span,
3928 "a required tuple element cannot follow an optional element",
3929 );
3930 return None;
3931 }
3932 elements.push(ty);
3933 match self.peek().kind {
3934 TokenKind::Comma => {
3935 self.advance();
3936 if matches!(self.peek().kind, TokenKind::RightBracket) {
3937 break;
3938 }
3939 }
3940 TokenKind::RightBracket => break,
3941 _ => {
3942 self.error_at_peek("expected `,` or `]`");
3943 return None;
3944 }
3945 }
3946 }
3947 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3948 self.error_at_peek("expected `]`");
3949 return None;
3950 }
3951 let close = self.advance();
3952 Some(TypeAnnotation {
3953 kind: TypeAnnotationKind::Tuple(elements),
3954 span: self.span(open.span.start, close.span.end),
3955 })
3956 }
3957
3958 fn skip_tuple_element_label(&mut self) -> TupleElementLabel {
3960 if !is_property_name(&self.peek().kind) {
3961 return TupleElementLabel::None;
3962 }
3963 match self.peek_at(1).kind {
3964 TokenKind::Colon => {
3965 self.advance();
3966 self.advance();
3967 TupleElementLabel::Required
3968 }
3969 TokenKind::Question if matches!(self.peek_at(2).kind, TokenKind::Colon) => {
3970 self.advance();
3971 self.advance();
3972 self.advance();
3973 TupleElementLabel::Optional
3974 }
3975 _ => TupleElementLabel::None,
3976 }
3977 }
3978
3979 fn peek_is_parameterless_index_signature(&self) -> bool {
3982 matches!(self.peek().kind, TokenKind::LeftBracket)
3983 && matches!(self.peek_at(1).kind, TokenKind::RightBracket)
3984 }
3985
3986 fn skip_parameterless_index_signature(&mut self, member_start: u32) -> Option<()> {
3990 self.advance();
3991 let close = self.advance();
3992 self.error_at_with_help(
3993 self.span(member_start, close.span.end),
3994 "an index signature must declare exactly one parameter",
3995 vec!["write `[key: string]: V`".to_string()],
3996 );
3997 if matches!(self.peek().kind, TokenKind::Colon) {
3998 self.advance();
3999 self.parse_type_annotation()?;
4000 }
4001 Some(())
4002 }
4003
4004 fn parse_index_signature(&mut self, readonly: bool) -> Option<crate::IndexSignatureAnnotation> {
4005 let open = self.advance();
4006 let parameter = self.expect_property_ident("expected index parameter name")?;
4007 self.reject_eval_or_arguments(parameter.span);
4008 if !matches!(self.peek().kind, TokenKind::Colon) {
4009 self.error_at_peek("expected `:` after index parameter name");
4010 return None;
4011 }
4012 self.advance();
4013 if !self.peek_identifier_text_is("string") {
4014 self.error_at_peek("index signatures require string keys; use `[key: string]: V`");
4015 return None;
4016 }
4017 self.advance();
4018 if !matches!(self.peek().kind, TokenKind::RightBracket) {
4019 self.error_at_peek("expected `]` after string index key type");
4020 return None;
4021 }
4022 self.advance();
4023 if !matches!(self.peek().kind, TokenKind::Colon) {
4024 self.error_at_peek("expected `:` before index value type");
4025 return None;
4026 }
4027 self.advance();
4028 let value = self.parse_type_annotation()?;
4029 Some(crate::IndexSignatureAnnotation {
4030 span: self.span(open.span.start, value.span.end),
4031 value,
4032 readonly,
4033 })
4034 }
4035
4036 fn parse_object_type_annotation(&mut self) -> Option<TypeAnnotation> {
4037 let open = self.advance();
4038 let mut fields: Vec<TypeAnnotationField> = Vec::new();
4039 let mut index = None;
4040 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
4041 && !self.peek_starts_top_level_item()
4042 {
4043 let member_start = self.peek().span.start;
4044 let readonly = self.eat_readonly_property_modifier();
4045 if self.peek_is_parameterless_index_signature() {
4046 self.skip_parameterless_index_signature(member_start)?;
4047 } else if self.peek_is_construct_signature() {
4048 self.reject_construct_signature()?;
4049 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
4050 let signature = self.parse_index_signature(readonly)?;
4051 if index.is_some() {
4052 self.error_at_with_help(
4053 signature.span,
4054 "duplicate string index signature",
4055 vec![],
4056 );
4057 }
4058 index = Some(Box::new(signature));
4059 } else {
4060 let field = self.parse_object_type_field(readonly, member_start)?;
4061 if let Some(existing) = fields.iter().find(|f| f.name.name == field.name.name) {
4062 self.diagnostics.push(Diagnostic {
4063 severity: Severity::Error,
4064 span: field.name.span,
4065 message: format!("duplicate field `{}` in object type", field.name.name),
4066 help: vec![],
4067 notes: vec![(existing.name.span, "first defined here".into())],
4068 });
4069 }
4070 fields.push(field);
4071 }
4072 self.finish_type_member(self.prev_token_end(), "expected `;`, `,`, or `}`")?;
4073 }
4074 if !matches!(self.peek().kind, TokenKind::RightBrace) {
4075 self.error_at_peek("expected `}`");
4076 return None;
4077 }
4078 let close = self.advance();
4079 Some(TypeAnnotation {
4080 kind: TypeAnnotationKind::Object { index, fields },
4081 span: self.span(open.span.start, close.span.end),
4082 })
4083 }
4084
4085 fn peek_starts_top_level_item(&self) -> bool {
4088 let import_statement = matches!(self.peek().kind, TokenKind::Import)
4089 && matches!(
4090 self.peek_at(1).kind,
4091 TokenKind::LeftBrace
4092 | TokenKind::Star
4093 | TokenKind::Identifier
4094 | TokenKind::StringLiteral(_)
4095 );
4096 import_statement || self.peek_starts_declaration()
4097 }
4098
4099 fn peek_starts_declaration(&self) -> bool {
4106 if matches!(self.peek().kind, TokenKind::Export)
4107 && matches!(
4108 self.peek_at(1).kind,
4109 TokenKind::Default | TokenKind::LeftBrace | TokenKind::Star
4110 )
4111 {
4112 return true;
4113 }
4114 let mut offset = 0;
4115 while self.peek_at_is_declaration_modifier(offset) {
4116 offset += 1;
4117 }
4118 let declaration_keyword = matches!(
4119 self.peek_at(offset).kind,
4120 TokenKind::Function
4121 | TokenKind::Class
4122 | TokenKind::Let
4123 | TokenKind::Const
4124 | TokenKind::Enum
4125 | TokenKind::Interface
4126 ) || self.peek_at_is_word(offset, "type");
4127 declaration_keyword && matches!(self.peek_at(offset + 1).kind, TokenKind::Identifier)
4128 }
4129
4130 fn peek_at_is_declaration_modifier(&self, offset: usize) -> bool {
4132 matches!(self.peek_at(offset).kind, TokenKind::Export)
4133 || ["declare", "async", "abstract"]
4134 .iter()
4135 .any(|word| self.peek_at_is_word(offset, word))
4136 }
4137
4138 fn peek_is_construct_signature(&self) -> bool {
4143 matches!(self.peek().kind, TokenKind::New)
4144 && matches!(
4145 self.peek_at(1).kind,
4146 TokenKind::LeftParen | TokenKind::LessThan
4147 )
4148 }
4149
4150 fn reject_construct_signature(&mut self) -> Option<()> {
4154 let keyword = self.advance();
4155 self.error_at_with_help(
4156 keyword.span,
4157 "construct signatures are not supported in object types",
4158 vec![
4159 "declare an `interface` with the `new (…)` signature, or pass a factory \
4160 function such as `() => T`"
4161 .to_string(),
4162 ],
4163 );
4164 if matches!(self.peek().kind, TokenKind::LessThan) {
4165 self.parse_generic_param_list()?;
4166 }
4167 if !matches!(self.peek().kind, TokenKind::LeftParen) {
4168 self.error_at_peek("expected `(` after construct signature type parameters");
4169 return None;
4170 }
4171 self.parse_object_type_method_signature(keyword.span.start)?;
4172 Some(())
4173 }
4174
4175 fn parse_object_type_field(
4177 &mut self,
4178 readonly: bool,
4179 member_start: u32,
4180 ) -> Option<TypeAnnotationField> {
4181 let name = self.expect_property_ident("expected field name in object type")?;
4182 let optional = matches!(self.peek().kind, TokenKind::Question);
4184 if optional {
4185 self.advance();
4186 }
4187 let method = matches!(self.peek().kind, TokenKind::LeftParen);
4188 let ty = if method {
4189 if readonly {
4190 self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
4191 }
4192 self.parse_object_type_method_signature(name.span.start)?
4193 } else {
4194 if !matches!(self.peek().kind, TokenKind::Colon) {
4195 self.error_at_peek("expected `:` after field name");
4196 return None;
4197 }
4198 self.advance();
4199 self.parse_type_annotation()?
4200 };
4201 Some(TypeAnnotationField {
4202 name,
4203 ty,
4204 optional,
4205 readonly,
4206 rest: false,
4207 method,
4208 })
4209 }
4210
4211 fn parse_object_type_method_signature(&mut self, start: u32) -> Option<TypeAnnotation> {
4216 self.advance();
4217 let params = self.parse_function_type_params()?;
4218 if !matches!(self.peek().kind, TokenKind::Colon) {
4219 self.error_at_peek("expected `:` and return type after method parameters");
4220 return None;
4221 }
4222 self.advance();
4223 let return_type = self.parse_type_annotation()?;
4224 let end = return_type.span.end;
4225 Some(TypeAnnotation {
4226 kind: TypeAnnotationKind::Function {
4227 params,
4228 return_type: Box::new(return_type),
4229 },
4230 span: self.span(start, end),
4231 })
4232 }
4233
4234 fn expect_identifier(&mut self, message: &str) -> Option<Token> {
4237 let tok = self.expect_identifier_name(message)?;
4238 self.reject_strict_mode_reserved_word(&tok);
4239 Some(tok)
4240 }
4241
4242 fn expect_binding_identifier(&mut self, message: &str) -> Option<Token> {
4246 let tok = self.expect_identifier(message)?;
4247 self.reject_eval_or_arguments(tok.span);
4248 Some(tok)
4249 }
4250
4251 fn expect_identifier_name(&mut self, message: &str) -> Option<Token> {
4254 if matches!(self.peek().kind, TokenKind::Identifier) {
4255 return Some(self.advance());
4256 }
4257 if is_reserved_identifier_word(&self.peek().kind) {
4258 let tok = self.advance();
4259 let keyword = &self.source[tok.span.start as usize..tok.span.end as usize];
4260 self.error_at_with_help(
4261 tok.span,
4262 format!("`{keyword}` is a reserved keyword and can't be used as a name"),
4263 vec![format!(
4264 "rename it, for example: `{}`",
4265 reserved_keyword_rename_example(keyword)
4266 )],
4267 );
4268 return None;
4269 }
4270 self.error_at_peek(message.to_string());
4271 None
4272 }
4273
4274 fn reject_strict_mode_reserved_word(&mut self, tok: &Token) {
4277 let word = &self.source[tok.span.start as usize..tok.span.end as usize];
4278 if !STRICT_MODE_RESERVED_WORDS.contains(&word) {
4279 return;
4280 }
4281 self.error_at_with_help(
4282 tok.span,
4283 format!("`{word}` is a reserved word in strict mode and can't be used as a name"),
4284 vec![format!(
4285 "rename it, for example: `{}`",
4286 reserved_keyword_rename_example(word)
4287 )],
4288 );
4289 }
4290
4291 fn reject_eval_or_arguments(&mut self, span: Span) {
4294 let word = &self.source[span.start as usize..span.end as usize];
4295 if !matches!(word, "eval" | "arguments") {
4296 return;
4297 }
4298 self.error_at_with_help(
4299 span,
4300 format!("`{word}` can't name a binding in strict mode"),
4301 vec![format!(
4302 "rename it, for example: `{}`",
4303 reserved_keyword_rename_example(word)
4304 )],
4305 );
4306 }
4307
4308 fn ident_from_token(&self, tok: &Token) -> Ident {
4309 Ident {
4310 name: self.source[tok.span.start as usize..tok.span.end as usize].to_string(),
4311 span: tok.span,
4312 }
4313 }
4314
4315 fn property_ident_from_token(&self, tok: &Token) -> Ident {
4316 if let TokenKind::StringLiteral(s) = &tok.kind {
4317 Ident {
4318 name: s.clone(),
4319 span: tok.span,
4320 }
4321 } else {
4322 self.ident_from_token(tok)
4323 }
4324 }
4325
4326 fn expect_property_name(&mut self, message: &str) -> Option<Token> {
4327 if !is_property_name(&self.peek().kind) {
4328 self.error_at_peek(message.to_string());
4329 return None;
4330 }
4331 Some(self.advance())
4332 }
4333
4334 fn expect_property_ident(&mut self, message: &str) -> Option<Ident> {
4335 if !is_property_name(&self.peek().kind)
4336 && !matches!(self.peek().kind, TokenKind::StringLiteral(_))
4337 {
4338 self.error_at_peek(message.to_string());
4339 return None;
4340 }
4341 let tok = self.advance();
4342 Some(self.property_ident_from_token(&tok))
4343 }
4344
4345 fn readonly_modifier_span(&self, member_start: u32) -> Span {
4348 self.span(member_start, member_start + "readonly".len() as u32)
4349 }
4350
4351 fn reject_readonly_modifier(&mut self, span: Span) {
4354 self.error_at_with_help(
4355 span,
4356 "`readonly` can only modify a property or index signature",
4357 vec!["remove `readonly`".to_string()],
4358 );
4359 }
4360
4361 fn eat_readonly_property_modifier(&mut self) -> bool {
4362 if self.peek_identifier_text_is("readonly")
4363 && !self.line_break_after_peek()
4364 && (self.peek_starts_property_after_readonly()
4365 || matches!(self.peek_at(1).kind, TokenKind::LeftBracket))
4366 {
4367 self.advance();
4368 return true;
4369 }
4370 false
4371 }
4372
4373 fn peek_starts_property_after_readonly(&self) -> bool {
4374 let name = &self.peek_at(1).kind;
4375 let after_name = &self.peek_at(2).kind;
4376 let after_optional = &self.peek_at(3).kind;
4377 let starts_member_type =
4380 |kind: &TokenKind| matches!(kind, TokenKind::Colon | TokenKind::LeftParen);
4381 (is_property_name(name) || matches!(name, TokenKind::StringLiteral(_)))
4382 && (starts_member_type(after_name)
4383 || matches!(after_name, TokenKind::Question) && starts_member_type(after_optional))
4384 }
4385
4386 fn peek_at_is_binary_word(&self, offset: usize) -> bool {
4388 self.peek_at_is_word(offset, "as") || self.peek_at_is_word(offset, "satisfies")
4389 }
4390
4391 fn peek_identifier_text_is(&self, expected: &str) -> bool {
4392 self.token_is_word(self.peek(), expected)
4393 }
4394
4395 fn peek_at_is_word(&self, offset: usize, expected: &str) -> bool {
4396 self.token_is_word(self.peek_at(offset), expected)
4397 }
4398
4399 fn token_is_word(&self, tok: &Token, expected: &str) -> bool {
4400 matches!(tok.kind, TokenKind::Identifier)
4401 && &self.source[tok.span.start as usize..tok.span.end as usize] == expected
4402 }
4403
4404 fn at_delete_operator(&self) -> bool {
4411 if !self.peek_identifier_text_is("delete") {
4412 return false;
4413 }
4414 if !matches!(
4415 self.peek_at(1).kind,
4416 TokenKind::Identifier | TokenKind::This
4417 ) {
4418 return false;
4419 }
4420 let next_is_cast_keyword =
4421 self.peek_at_is_word(1, "as") || self.peek_at_is_word(1, "satisfies");
4422 !next_is_cast_keyword || cannot_start_type(&self.peek_at(2).kind)
4423 }
4424
4425 fn at_type_alias_head(&self) -> bool {
4429 self.peek_identifier_text_is("type")
4430 && matches!(self.peek_at(1).kind, TokenKind::Identifier)
4431 }
4432
4433 fn parse_expression(&mut self) -> Option<ExprId> {
4436 self.with_recursion_limit(Self::parse_expression_inner)
4437 }
4438
4439 fn parse_expression_inner(&mut self) -> Option<ExprId> {
4440 let written = self.parse_conditional()?;
4441 let shift_assignment = self.peek_shift().filter(|shift| shift.assignment);
4442 if !is_assign_lookahead(&self.peek().kind) && shift_assignment.is_none() {
4443 return Some(written);
4444 }
4445 let target_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4446 let target = self.assignment_target(written)?;
4447 let op_tok = self.advance();
4448 let mut op_span = op_tok.span;
4449 let op = if let Some(ShiftToken { op, tokens, .. }) = shift_assignment {
4450 for _ in 1..tokens {
4451 op_span.end = self.advance().span.end;
4452 }
4453 Some(op)
4454 } else {
4455 compound_op_for_token(&op_tok.kind)
4456 };
4457 let value = self.parse_expression()?;
4458 let value_span = parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
4459 parse_arena_result(
4460 self.ast.try_push_expr(Expr {
4461 kind: ExprKind::Assign {
4462 target,
4463 op,
4464 op_span,
4465 value,
4466 },
4467 span: self.span(target_span.start, value_span.end),
4468 }),
4469 &mut self.fatal,
4470 )
4471 }
4472
4473 fn assignment_target(&mut self, written: ExprId) -> Option<ExprId> {
4476 let mut target = written;
4477 while let ExprKind::Paren(inner) =
4478 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind
4479 {
4480 target = inner;
4481 }
4482 if matches!(
4483 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind,
4484 ExprKind::Identifier(_) | ExprKind::FieldAccess { .. } | ExprKind::IndexAccess { .. }
4485 ) {
4486 return Some(target);
4487 }
4488 let error_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4489 self.error_at_with_help(
4490 error_span,
4491 "invalid assignment target",
4492 vec![
4493 "assign to a variable, a field (`o.f = …`), or an element (`a[i] = …`)".to_string(),
4494 ],
4495 );
4496 None
4497 }
4498
4499 fn parse_conditional(&mut self) -> Option<ExprId> {
4500 self.reject_async_modifier();
4501 if self.is_arrow_start() {
4502 return self.parse_arrow();
4503 }
4504 let cond = self.parse_binary(0)?;
4505 if !matches!(self.peek().kind, TokenKind::Question) {
4507 return Some(cond);
4508 }
4509 self.advance();
4510 let then_ = self.parse_expression()?;
4511 if !matches!(self.peek().kind, TokenKind::Colon) {
4512 self.error_at_peek_with_help(
4513 "expected `:` to complete ternary expression",
4514 vec!["const x = cond ? then : else;".to_string()],
4515 );
4516 return None;
4517 }
4518 self.advance();
4519 let else_ = self.parse_expression()?;
4520 let cond_span = parse_arena_result(self.ast.try_expr(cond), &mut self.fatal)?.span;
4521 let else_span = parse_arena_result(self.ast.try_expr(else_), &mut self.fatal)?.span;
4522 parse_arena_result(
4523 self.ast.try_push_expr(Expr {
4524 kind: ExprKind::Ternary { cond, then_, else_ },
4525 span: self.span(cond_span.start, else_span.end),
4526 }),
4527 &mut self.fatal,
4528 )
4529 }
4530
4531 fn is_arrow_start(&self) -> bool {
4532 match self.peek().kind {
4533 TokenKind::Identifier => matches!(self.peek_at(1).kind, TokenKind::Arrow),
4534 TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos),
4535 _ => false,
4536 }
4537 }
4538
4539 fn reject_async_modifier(&mut self) {
4542 if !self.peek_identifier_text_is("async") {
4543 return;
4544 }
4545 let modifies = match self.peek_at(1).kind {
4546 TokenKind::Function => true,
4547 TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos + 1),
4549 ref name
4553 if is_property_name(name)
4554 && !matches!(name, TokenKind::In)
4555 && !self.peek_at_is_binary_word(1) =>
4556 {
4557 matches!(
4558 self.peek_at(2).kind,
4559 TokenKind::Arrow | TokenKind::LeftParen | TokenKind::LessThan
4560 )
4561 }
4562 _ => false,
4563 };
4564 if modifies {
4565 self.report_async_modifier();
4566 }
4567 }
4568
4569 fn reject_async_member_modifier(&mut self) {
4574 if !self.peek_identifier_text_is("async") || self.line_break_after_peek() {
4575 return;
4576 }
4577 let modifies = match self.peek_at(1).kind {
4578 TokenKind::Star
4579 | TokenKind::LeftBracket
4580 | TokenKind::StringLiteral(_)
4581 | TokenKind::NumberLiteral(_) => true,
4582 ref name if is_property_name(name) => {
4583 matches!(
4584 self.peek_at(2).kind,
4585 TokenKind::LeftParen | TokenKind::LessThan
4586 )
4587 }
4588 _ => false,
4589 };
4590 if modifies {
4591 self.report_async_modifier();
4592 }
4593 }
4594
4595 fn report_async_modifier(&mut self) {
4599 let tok = self.advance();
4600 self.error_at_with_help(
4601 tok.span,
4602 "`async` functions are not supported",
4603 vec![
4604 "calls return their value directly: remove `async`, write the return type \
4605 `T` instead of `Promise<T>`, and drop `await`"
4606 .to_string(),
4607 ],
4608 );
4609 }
4610
4611 fn reject_await_operator(&mut self) -> bool {
4617 if !self.peek_identifier_text_is("await") {
4618 return false;
4619 }
4620 let operand_starts = matches!(
4621 self.peek_at(1).kind,
4622 TokenKind::Identifier
4623 | TokenKind::This
4624 | TokenKind::Super
4625 | TokenKind::New
4626 | TokenKind::Function
4627 | TokenKind::StringLiteral(_)
4628 | TokenKind::NumberLiteral(_)
4629 | TokenKind::NullLiteral
4630 | TokenKind::BooleanLiteral(_)
4631 | TokenKind::TemplateHead(_)
4634 | TokenKind::TemplateNoSubstitution(_)
4635 ) && !self.peek_at_is_binary_word(1);
4636 if !operand_starts || self.line_break_after_peek() {
4637 return false;
4638 }
4639 let tok = self.advance();
4640 self.error_at_with_help(
4641 tok.span,
4642 "`await` is not supported",
4643 vec!["calls return their value directly: remove `await`".to_string()],
4644 );
4645 true
4646 }
4647
4648 fn index_after_matching_paren(&self, index: usize) -> Option<usize> {
4651 if !matches!(
4652 self.tokens.get(index).map(|t| &t.kind),
4653 Some(TokenKind::LeftParen)
4654 ) {
4655 return None;
4656 }
4657 self.scan_past_balanced(
4658 index,
4659 |k| matches!(k, TokenKind::LeftParen),
4660 |k| matches!(k, TokenKind::RightParen),
4661 )
4662 }
4663
4664 fn find_arrow_after_matching_paren(&self, open: usize) -> bool {
4665 let Some(i) = self.index_after_matching_paren(open) else {
4666 return false;
4667 };
4668 let after_type = if matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Colon)) {
4669 match self.scan_past_type_annotation(i + 1, TypePos::ArrowReturn) {
4670 Some(p) => p,
4671 None => return false,
4672 }
4673 } else {
4674 i
4675 };
4676 matches!(
4677 self.tokens.get(after_type).map(|t| &t.kind),
4678 Some(TokenKind::Arrow),
4679 )
4680 }
4681
4682 fn scan_past_type_annotation(&self, start: usize, type_pos: TypePos) -> Option<usize> {
4688 self.scan_past_type_annotation_at_depth(start, type_pos, 0)
4689 }
4690
4691 fn scan_past_type_annotation_at_depth(
4692 &self,
4693 start: usize,
4694 type_pos: TypePos,
4695 depth: usize,
4696 ) -> Option<usize> {
4697 if depth >= MAX_PARSE_DEPTH {
4698 return None;
4699 }
4700 if matches!(
4701 self.tokens.get(start).map(|t| &t.kind),
4702 Some(TokenKind::Identifier)
4703 ) && self
4704 .tokens
4705 .get(start + 1)
4706 .is_some_and(|t| self.token_is_word(t, "is"))
4707 {
4708 return self.scan_past_type_annotation_at_depth(start + 2, type_pos, depth + 1);
4709 }
4710 let start = if matches!(
4712 self.tokens.get(start).map(|t| &t.kind),
4713 Some(TokenKind::Pipe)
4714 ) {
4715 start + 1
4716 } else {
4717 start
4718 };
4719 let mut i = self.scan_past_single_type_member(start, type_pos, depth + 1)?;
4720 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Pipe)) {
4721 i = self.scan_past_single_type_member(i + 1, type_pos, depth + 1)?;
4722 }
4723 Some(i)
4724 }
4725
4726 fn scan_past_single_type_member(
4727 &self,
4728 start: usize,
4729 type_pos: TypePos,
4730 depth: usize,
4731 ) -> Option<usize> {
4732 if depth >= MAX_PARSE_DEPTH {
4733 return None;
4734 }
4735 if self.is_contextual_type_operator(start, "keyof")
4736 || self.is_contextual_type_operator(start, "readonly")
4737 {
4738 return self.scan_past_single_type_member(start + 1, type_pos, depth + 1);
4739 }
4740 let mut i = start;
4741 match self.tokens.get(i)?.kind {
4742 TokenKind::Identifier | TokenKind::Void => {
4743 i += 1;
4744 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Dot)) {
4745 if !matches!(
4746 self.tokens.get(i + 1).map(|t| &t.kind),
4747 Some(TokenKind::Identifier)
4748 ) {
4749 return None;
4750 }
4751 i += 2;
4752 }
4753 if matches!(
4754 self.tokens.get(i).map(|t| &t.kind),
4755 Some(TokenKind::LessThan)
4756 ) {
4757 i = self.scan_past_balanced(
4758 i,
4759 |k| matches!(k, TokenKind::LessThan),
4760 |k| matches!(k, TokenKind::GreaterThan),
4761 )?;
4762 }
4763 }
4764 TokenKind::NullLiteral
4765 | TokenKind::StringLiteral(_)
4766 | TokenKind::NumberLiteral(_)
4767 | TokenKind::BooleanLiteral(_) => {
4768 i += 1;
4769 }
4770 TokenKind::LeftBrace => {
4771 i = self.scan_past_balanced(
4772 i,
4773 |k| matches!(k, TokenKind::LeftBrace),
4774 |k| matches!(k, TokenKind::RightBrace),
4775 )?;
4776 }
4777 TokenKind::LeftBracket => {
4778 i = self.scan_past_balanced(
4779 i,
4780 |k| matches!(k, TokenKind::LeftBracket),
4781 |k| matches!(k, TokenKind::RightBracket),
4782 )?;
4783 }
4784 TokenKind::LeftParen => {
4789 let is_function_type = self.paren_at_opens_function_type(i, type_pos);
4790 i = self.scan_past_balanced(
4791 i,
4792 |k| matches!(k, TokenKind::LeftParen),
4793 |k| matches!(k, TokenKind::RightParen),
4794 )?;
4795 if is_function_type {
4796 if !matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)) {
4797 return None;
4798 }
4799 return self.scan_past_type_annotation_at_depth(
4800 i + 1,
4801 TypePos::Anywhere,
4802 depth + 1,
4803 );
4804 }
4805 }
4806 _ => return None,
4807 }
4808 while matches!(
4809 self.tokens.get(i).map(|t| &t.kind),
4810 Some(TokenKind::LeftBracket)
4811 ) {
4812 i += 1;
4813 if !matches!(
4814 self.tokens.get(i).map(|t| &t.kind),
4815 Some(TokenKind::RightBracket)
4816 ) {
4817 return None;
4818 }
4819 i += 1;
4820 }
4821 Some(i)
4822 }
4823
4824 fn scan_past_balanced(
4827 &self,
4828 start: usize,
4829 is_open: fn(&TokenKind) -> bool,
4830 is_close: fn(&TokenKind) -> bool,
4831 ) -> Option<usize> {
4832 let mut depth: i32 = 1;
4833 let mut i = start + 1;
4834 while i < self.tokens.len() && depth > 0 {
4835 let token = self.tokens.get(i)?;
4836 let kind = &token.kind;
4837 if is_open(kind) {
4838 depth += 1;
4839 } else if is_close(kind) {
4840 depth -= 1;
4841 } else if matches!(kind, TokenKind::Eof) {
4842 return None;
4843 }
4844 i += 1;
4845 }
4846 if depth == 0 { Some(i) } else { None }
4847 }
4848
4849 fn parse_arrow(&mut self) -> Option<ExprId> {
4850 let start_span = self.peek().span;
4851
4852 let params = match self.peek().kind {
4853 TokenKind::Identifier => {
4854 let name_tok = self.advance();
4855 self.reject_eval_or_arguments(name_tok.span);
4856 let name = self.ident_from_token(&name_tok);
4857 vec![ParamDecl {
4858 name,
4859 ty: None,
4860 default: None,
4861 optional: false,
4862 pattern: None,
4863 rest: false,
4864 modifiers: None,
4865 }]
4866 }
4867 TokenKind::LeftParen => {
4868 self.advance();
4869 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4870 Vec::new()
4871 } else {
4872 self.parse_arrow_param_list()?
4873 };
4874 if !matches!(self.peek().kind, TokenKind::RightParen) {
4875 self.error_at_peek("expected `)`");
4876 return None;
4877 }
4878 self.advance();
4879 params
4880 }
4881 _ => return self.invariant_failure("is_arrow_start guarded the entry"),
4882 };
4883
4884 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4885 self.advance();
4886 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4887 } else {
4888 (None, None)
4889 };
4890
4891 if !matches!(self.peek().kind, TokenKind::Arrow) {
4892 self.error_at_peek("expected `=>` in arrow function");
4893 return None;
4894 }
4895 if self.line_break_before_peek() {
4897 let arrow_span = self.peek().span;
4898 self.error_at(arrow_span, "a line break is not allowed before `=>`");
4899 }
4900 self.advance();
4901
4902 let (body, end_pos) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
4903 let block = self.parse_block()?;
4904 let span = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?.span;
4905 (ArrowBody::Block(block), span.end)
4906 } else {
4907 let expr = self.parse_expression()?;
4908 let span = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?.span;
4909 (ArrowBody::Expr(expr), span.end)
4910 };
4911
4912 parse_arena_result(
4913 self.ast.try_push_expr(Expr {
4914 kind: ExprKind::Arrow {
4915 params,
4916 return_type,
4917 type_predicate,
4918 body,
4919 },
4920 span: self.span(start_span.start, end_pos),
4921 }),
4922 &mut self.fatal,
4923 )
4924 }
4925
4926 fn parse_function_expression(&mut self) -> Option<ExprId> {
4929 let kw = self.advance();
4930
4931 if matches!(self.peek().kind, TokenKind::LessThan) {
4932 self.error_at_peek("generic function expressions are not supported");
4933 return None;
4934 }
4935
4936 let self_name = if matches!(self.peek().kind, TokenKind::Identifier) {
4937 let name_tok = self.advance();
4938 self.reject_eval_or_arguments(name_tok.span);
4939 Some(self.ident_from_token(&name_tok))
4940 } else {
4941 None
4942 };
4943
4944 if !matches!(self.peek().kind, TokenKind::LeftParen) {
4945 self.error_at_peek("expected `(` after `function`");
4946 return None;
4947 }
4948 self.advance();
4949
4950 let this_type = if matches!(self.peek().kind, TokenKind::This) {
4951 self.advance();
4952 if !matches!(self.peek().kind, TokenKind::Colon) {
4953 self.error_at_peek("expected a type annotation after `this`");
4954 return None;
4955 }
4956 self.advance();
4957 let ty = self.parse_type_annotation()?;
4958 if matches!(self.peek().kind, TokenKind::Comma) {
4959 self.advance();
4960 } else if !matches!(self.peek().kind, TokenKind::RightParen) {
4961 self.error_at_peek("expected `,` or `)` after the `this` parameter");
4962 return None;
4963 }
4964 Some(ty)
4965 } else {
4966 None
4967 };
4968 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4969 Vec::new()
4970 } else {
4971 let saved_class = self.class_member_body_depth;
4972 let saved_function = self.function_expression_body_depth;
4973 self.class_member_body_depth = 0;
4974 self.function_expression_body_depth = 1;
4975 let params = self.parse_arrow_param_list();
4976 self.class_member_body_depth = saved_class;
4977 self.function_expression_body_depth = saved_function;
4978 params?
4979 };
4980 if !matches!(self.peek().kind, TokenKind::RightParen) {
4981 self.error_at_peek("expected `)`");
4982 return None;
4983 }
4984 self.advance();
4985
4986 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4987 self.advance();
4988 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4989 } else {
4990 (None, None)
4991 };
4992
4993 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
4994 self.error_at_peek("expected `{` to open the function body");
4995 return None;
4996 }
4997 let saved = self.class_member_body_depth;
4998 self.class_member_body_depth = 0;
4999 self.function_expression_body_depth += 1;
5000 let block = self.parse_block();
5001 self.function_expression_body_depth -= 1;
5002 self.class_member_body_depth = saved;
5003 let block = block?;
5004 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
5005 .span
5006 .end;
5007
5008 let function = parse_arena_result(
5009 self.ast.try_push_expr(Expr {
5010 kind: ExprKind::Arrow {
5011 params,
5012 return_type,
5013 type_predicate,
5014 body: ArrowBody::Block(block),
5015 },
5016 span: self.span(kw.span.start, end),
5017 }),
5018 &mut self.fatal,
5019 )?;
5020 parse_arena_result(
5021 self.ast.try_push_expr(Expr {
5022 kind: ExprKind::FunctionExpression {
5023 name: self_name,
5024 this_type,
5025 function,
5026 },
5027 span: self.span(kw.span.start, end),
5028 }),
5029 &mut self.fatal,
5030 )
5031 }
5032
5033 fn parse_outer_this_boundary<T>(
5036 &mut self,
5037 parse: impl FnOnce(&mut Self) -> Option<T>,
5038 ) -> Option<T> {
5039 let saved = self.class_member_body_depth;
5040 self.class_member_body_depth = 0;
5041 let saved_function = self.function_expression_body_depth;
5042 self.function_expression_body_depth = 0;
5043 let parsed = parse(self);
5044 self.class_member_body_depth = saved;
5045 self.function_expression_body_depth = saved_function;
5046 parsed
5047 }
5048
5049 fn parse_arrow_param_list(&mut self) -> Option<Vec<ParamDecl>> {
5050 let mut params = Vec::new();
5051 loop {
5052 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
5053 self.advance();
5054 true
5055 } else {
5056 false
5057 };
5058 let (name, pattern) = match self.peek().kind {
5061 TokenKind::LeftBrace | TokenKind::LeftBracket => {
5062 if rest {
5063 self.error_at_peek("rest parameter cannot be destructured");
5064 return None;
5065 }
5066 let binding = self.parse_binding()?;
5067 let placeholder = Ident {
5068 name: String::new(),
5069 span: binding.span(),
5070 };
5071 (placeholder, Some(binding))
5072 }
5073 _ => {
5074 let name_tok = self.expect_binding_identifier(if rest {
5075 "expected identifier after `...`"
5076 } else {
5077 "expected parameter name"
5078 })?;
5079 let name = self.ident_from_token(&name_tok);
5080 (name, None)
5081 }
5082 };
5083 let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
5084 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
5085 self.advance();
5086 Some(self.parse_type_annotation()?)
5087 } else {
5088 None
5091 };
5092 let default = self.parse_parameter_default(rest, optional)?;
5093 params.push(ParamDecl {
5094 name,
5095 ty,
5096 default,
5097 optional,
5098 pattern,
5099 rest,
5100 modifiers: None,
5101 });
5102 match self.peek().kind {
5103 TokenKind::Comma => {
5104 if rest {
5105 let comma = self.advance();
5106 self.error_at(comma.span, "rest parameter must be the last parameter");
5107 return None;
5108 }
5109 self.advance();
5110 if matches!(self.peek().kind, TokenKind::RightParen) {
5111 break;
5112 }
5113 }
5114 TokenKind::RightParen => break,
5115 _ => {
5116 self.error_at_peek("expected `,` or `)`");
5117 return None;
5118 }
5119 }
5120 }
5121 self.check_parameter_order(params.iter().map(ParamOrder::of_param));
5122 Some(params)
5123 }
5124
5125 fn parse_binary(&mut self, min_prec: u8) -> Option<ExprId> {
5126 self.with_recursion_limit(|parser| parser.parse_binary_inner(min_prec))
5127 }
5128
5129 fn peek_shift(&self) -> Option<ShiftToken> {
5132 let first = self.peek();
5133 let second = self.peek_at(1);
5134 if first.span.end != second.span.start {
5135 return None;
5136 }
5137 match (&first.kind, &second.kind) {
5138 (TokenKind::LessThan, TokenKind::LessThan) => {
5139 Some(ShiftToken::new(BinOp::Shl, 2, false))
5140 }
5141 (TokenKind::LessThan, TokenKind::LessEquals) => {
5142 Some(ShiftToken::new(BinOp::Shl, 2, true))
5143 }
5144 (TokenKind::GreaterThan, TokenKind::GreaterEquals) => {
5145 Some(ShiftToken::new(BinOp::Shr, 2, true))
5146 }
5147 (TokenKind::GreaterThan, TokenKind::GreaterThan) => {
5148 let third = self.peek_at(2);
5149 if second.span.end == third.span.start {
5150 match third.kind {
5151 TokenKind::GreaterThan => {
5152 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, false));
5153 }
5154 TokenKind::GreaterEquals => {
5155 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, true));
5156 }
5157 _ => {}
5158 }
5159 }
5160 Some(ShiftToken::new(BinOp::Shr, 2, false))
5161 }
5162 _ => None,
5163 }
5164 }
5165
5166 fn peek_binary(&self) -> Option<BinaryToken> {
5167 if let Some(shift) = self.peek_shift() {
5168 return (!shift.assignment).then_some(BinaryToken {
5169 op: shift.op,
5170 precedence: SHIFT_PRECEDENCE,
5171 tokens: shift.tokens,
5172 });
5173 }
5174 peek_binop(&self.peek().kind).map(|(op, precedence)| BinaryToken {
5175 op,
5176 precedence,
5177 tokens: 1,
5178 })
5179 }
5180
5181 fn parse_binary_inner(&mut self, min_prec: u8) -> Option<ExprId> {
5182 let mut lhs = self.parse_cast()?;
5183 let mut last_op: Option<BinOp> = None;
5187 let mut reported_mixing = false;
5188 while let Some(BinaryToken {
5189 op,
5190 precedence: prec,
5191 tokens: count,
5192 }) = self.peek_binary()
5193 {
5194 if prec < min_prec {
5195 break;
5196 }
5197 if !reported_mixing
5198 && let Some(last) = last_op
5199 && mixed_logical(last, op)
5200 {
5201 reported_mixing = true;
5202 let op_span = self.peek().span;
5203 self.error_at_with_help(
5207 op_span,
5208 "mixing `??` with `||` / `&&` requires parentheses",
5209 vec![mixed_logical_help(last, op)],
5210 );
5211 }
5212 if op == BinOp::Pow
5213 && matches!(
5214 parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.kind,
5215 ExprKind::Unary { .. }
5216 | ExprKind::Typeof { .. }
5217 | ExprKind::Delete { .. }
5218 | ExprKind::Void { .. }
5219 )
5220 {
5221 let error_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5222 self.error_at_with_help(
5223 error_span,
5224 "an unparenthesized unary expression cannot be the left operand of `**`",
5225 vec!["choose the grouping explicitly: `(-x) ** 2` or `-(x ** 2)`".into()],
5226 );
5227 return None;
5228 }
5229 let loose = matches!(self.peek().kind, TokenKind::EqEq | TokenKind::BangEq);
5230 for _ in 0..count {
5231 self.advance();
5232 }
5233 let rhs = self.parse_binary(right_operand_min_prec(op, prec))?;
5234 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5235 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
5236 if loose {
5237 self.reject_loose_nullish_comparison(op, lhs, rhs)?;
5238 }
5239 lhs = parse_arena_result(
5240 self.ast.try_push_expr(Expr {
5241 kind: ExprKind::Binary { op, lhs, rhs },
5242 span: self.span(lhs_span.start, rhs_span.end),
5243 }),
5244 &mut self.fatal,
5245 )?;
5246 last_op = Some(op);
5247 }
5248 Some(lhs)
5249 }
5250
5251 fn parse_cast(&mut self) -> Option<ExprId> {
5253 let mut expr = self.parse_unary()?;
5254 while matches!(self.peek().kind, TokenKind::Instanceof)
5255 || self.peek_identifier_text_is("as")
5256 {
5257 let is_instanceof = matches!(self.peek().kind, TokenKind::Instanceof);
5258 self.advance();
5259 let ty = self.parse_type_annotation()?;
5260 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5261 .span
5262 .start;
5263 let end = ty.span.end;
5264 let kind = if is_instanceof {
5265 ExprKind::InstanceOf { value: expr, ty }
5266 } else {
5267 self.last_as_type_end = Some(end);
5268 ExprKind::As { expr, ty }
5269 };
5270 expr = parse_arena_result(
5271 self.ast.try_push_expr(Expr {
5272 kind,
5273 span: self.span(start, end),
5274 }),
5275 &mut self.fatal,
5276 )?;
5277 }
5278 Some(expr)
5279 }
5280
5281 fn parse_unary(&mut self) -> Option<ExprId> {
5282 self.with_recursion_limit(Self::parse_unary_inner)
5283 }
5284
5285 fn parse_unary_inner(&mut self) -> Option<ExprId> {
5286 if self.reject_await_operator() {
5287 return self.parse_unary();
5288 }
5289 if matches!(self.peek().kind, TokenKind::Typeof) {
5292 return self.parse_prefix_expr(|operand| ExprKind::Typeof { operand });
5293 }
5294 if matches!(self.peek().kind, TokenKind::Void) {
5295 return self.parse_prefix_expr(|operand| ExprKind::Void { operand });
5296 }
5297 if self.at_delete_operator() {
5298 return self.parse_prefix_expr(|operand| ExprKind::Delete { operand });
5299 }
5300 if matches!(self.peek().kind, TokenKind::New) {
5301 let op_tok = self.advance();
5302 let mut callee = self.parse_atom()?;
5303 while matches!(self.peek().kind, TokenKind::Dot) {
5304 self.advance();
5305 if !matches!(self.peek().kind, TokenKind::Identifier) {
5306 self.error_at_peek("expected identifier after `.` in constructor name");
5307 return None;
5308 }
5309 let ident_tok = self.advance();
5310 let name = self.ident_from_token(&ident_tok);
5311 let receiver_span =
5312 parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5313 let combined_span = self.span(receiver_span.start, ident_tok.span.end);
5314 callee = parse_arena_result(
5315 self.ast.try_push_expr(Expr {
5316 kind: ExprKind::FieldAccess {
5317 receiver: callee,
5318 name,
5319 },
5320 span: combined_span,
5321 }),
5322 &mut self.fatal,
5323 )?;
5324 }
5325 let type_args = if matches!(self.peek().kind, TokenKind::LessThan) {
5326 let saved_pos = self.pos;
5327 let saved_diag_len = self.diagnostics.len();
5328 if let Some(args) = self.try_parse_type_args_only() {
5329 Some(args)
5330 } else {
5331 self.pos = saved_pos;
5332 self.diagnostics.truncate(saved_diag_len);
5333 None
5334 }
5335 } else {
5336 None
5337 };
5338 if !matches!(self.peek().kind, TokenKind::LeftParen) {
5339 self.error_at_peek("expected `(` after constructor name in `new` expression");
5340 return None;
5341 }
5342 self.advance();
5343 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5344 Vec::new()
5345 } else {
5346 self.parse_call_args()?
5347 };
5348 if !matches!(self.peek().kind, TokenKind::RightParen) {
5349 self.error_at_peek("expected `)`");
5350 return None;
5351 }
5352 let close = self.advance();
5353 let new_expr = parse_arena_result(
5354 self.ast.try_push_expr(Expr {
5355 kind: ExprKind::New {
5356 callee,
5357 type_args,
5358 args,
5359 },
5360 span: self.span(op_tok.span.start, close.span.end),
5361 }),
5362 &mut self.fatal,
5363 )?;
5364 return self.parse_postfix_from(new_expr);
5367 }
5368 let op = match self.peek().kind {
5369 TokenKind::Bang => UnOp::Not,
5370 TokenKind::Tilde => UnOp::BitNot,
5371 TokenKind::Minus => UnOp::Neg,
5372 TokenKind::Plus => UnOp::Pos,
5373 _ => return self.parse_postfix(),
5374 };
5375 self.parse_prefix_expr(|operand| ExprKind::Unary { op, operand })
5376 }
5377
5378 fn parse_prefix_expr(&mut self, kind: impl FnOnce(ExprId) -> ExprKind) -> Option<ExprId> {
5380 let op_tok = self.advance();
5381 if matches!(op_tok.kind, TokenKind::Void) && self.at_inserted_semicolon() {
5384 self.advance();
5385 }
5386 let operand = self.parse_unary()?;
5387 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5388 parse_arena_result(
5389 self.ast.try_push_expr(Expr {
5390 kind: kind(operand),
5391 span: self.span(op_tok.span.start, operand_span.end),
5392 }),
5393 &mut self.fatal,
5394 )
5395 }
5396
5397 fn parse_postfix(&mut self) -> Option<ExprId> {
5398 let expr = self.parse_atom()?;
5399 self.parse_postfix_from(expr)
5400 }
5401
5402 fn parse_postfix_from(&mut self, mut expr: ExprId) -> Option<ExprId> {
5403 let mut chain_parts: Option<(ExprId, Vec<crate::ChainPart>)> = None;
5407 loop {
5408 match self.peek().kind {
5409 TokenKind::LeftParen => {
5410 if let Some((_, parts)) = &mut chain_parts {
5411 let part = self.parse_chain_call_tail(false)?;
5412 parts.push(part);
5413 } else {
5414 expr = self.parse_call_tail(expr, None)?;
5415 }
5416 }
5417 TokenKind::Dot => {
5418 if let Some((_, parts)) = &mut chain_parts {
5419 let part = self.parse_chain_field_tail(false)?;
5420 parts.push(part);
5421 } else {
5422 expr = self.parse_field_access_tail(expr)?;
5423 }
5424 }
5425 TokenKind::LeftBracket => {
5426 if let Some((_, parts)) = &mut chain_parts {
5427 let part = self.parse_chain_index_tail(false)?;
5428 parts.push(part);
5429 } else {
5430 expr = self.parse_index_access_tail(expr)?;
5431 }
5432 }
5433 TokenKind::QuestionDot => {
5434 if chain_parts.is_none() {
5435 chain_parts = Some((expr, Vec::new()));
5436 }
5437 self.advance();
5438 let part = match self.peek().kind {
5439 TokenKind::LeftParen => self.parse_chain_call_tail(true)?,
5440 TokenKind::LeftBracket => self.parse_chain_index_tail(true)?,
5441 _ if is_property_name(&self.peek().kind) => {
5442 self.parse_chain_field_tail(true)?
5443 }
5444 _ => {
5445 self.error_at_peek(
5446 "expected field name, `(` for call, or `[` for index after `?.`",
5447 );
5448 return None;
5449 }
5450 };
5451 if let Some((_, parts)) = &mut chain_parts {
5452 parts.push(part);
5453 }
5454 }
5455 TokenKind::LessThan => {
5459 if chain_parts.is_some() {
5460 break;
5462 }
5463 let saved_pos = self.pos;
5464 let saved_diag_len = self.diagnostics.len();
5465 if let Some(call_id) = self.try_parse_generic_call_tail(expr) {
5466 expr = call_id;
5467 } else {
5468 self.pos = saved_pos;
5469 self.diagnostics.truncate(saved_diag_len);
5470 break;
5471 }
5472 }
5473 TokenKind::Bang => {
5474 if let Some((_, parts)) = &mut chain_parts {
5475 let tok = self.advance();
5476 parts.push(crate::ChainPart::NonNull { span: tok.span });
5477 continue;
5478 }
5479 let tok = self.advance();
5480 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5481 .span
5482 .start;
5483 expr = parse_arena_result(
5484 self.ast.try_push_expr(Expr {
5485 kind: ExprKind::PostfixUnary {
5486 op: PostfixOp::NonNullAssert,
5487 operand: expr,
5488 },
5489 span: self.span(start, tok.span.end),
5490 }),
5491 &mut self.fatal,
5492 )?;
5493 }
5494 TokenKind::PlusPlus | TokenKind::MinusMinus => {
5496 if chain_parts.is_some() {
5497 break;
5498 }
5499 let tok = self.advance();
5500 let op = if matches!(tok.kind, TokenKind::PlusPlus) {
5501 PostfixOp::Inc
5502 } else {
5503 PostfixOp::Dec
5504 };
5505 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5506 .span
5507 .start;
5508 expr = parse_arena_result(
5509 self.ast.try_push_expr(Expr {
5510 kind: ExprKind::PostfixUnary { op, operand: expr },
5511 span: self.span(start, tok.span.end),
5512 }),
5513 &mut self.fatal,
5514 )?;
5515 break;
5516 }
5517 _ => break,
5518 }
5519 }
5520 if let Some((base, parts)) = chain_parts {
5521 let base_span = parse_arena_result(self.ast.try_expr(base), &mut self.fatal)?.span;
5522 let end = parts.last().map_or(base_span.end, |p| p.span().end);
5523 Some(parse_arena_result(
5524 self.ast.try_push_expr(Expr {
5525 kind: ExprKind::OptionalChain { base, parts },
5526 span: self.span(base_span.start, end),
5527 }),
5528 &mut self.fatal,
5529 )?)
5530 } else {
5531 Some(expr)
5532 }
5533 }
5534
5535 fn parse_chain_field_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5537 let start_pos = self.peek().span.start;
5538 if !optional {
5539 self.advance();
5540 }
5541 let name_tok = if is_property_name(&self.peek().kind) {
5542 self.advance()
5543 } else {
5544 let what = if optional { "?." } else { "." };
5545 self.error_at_peek(format!("expected field name after `{what}`"));
5546 return None;
5547 };
5548 let name = self.ident_from_token(&name_tok);
5549 let end = name.span.end;
5550 Some(crate::ChainPart::Field {
5551 name,
5552 optional,
5553 span: self.span(start_pos, end),
5554 })
5555 }
5556
5557 fn parse_chain_index_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5558 let open = self.advance();
5559 let idx = self.parse_expression()?;
5560 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5561 self.error_at_peek("expected `]`");
5562 return None;
5563 }
5564 let close = self.advance();
5565 Some(crate::ChainPart::Index {
5566 idx,
5567 optional,
5568 span: self.span(open.span.start, close.span.end),
5569 })
5570 }
5571
5572 fn parse_chain_call_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5573 let open = self.advance();
5574 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5575 Vec::new()
5576 } else {
5577 self.parse_call_args()?
5578 };
5579 if !matches!(self.peek().kind, TokenKind::RightParen) {
5580 self.error_at_peek("expected `)`");
5581 return None;
5582 }
5583 let close = self.advance();
5584 Some(crate::ChainPart::Call {
5585 args,
5586 type_args: None,
5587 optional,
5588 span: self.span(open.span.start, close.span.end),
5589 })
5590 }
5591
5592 fn parse_field_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5593 self.advance();
5594 let name_tok = if is_property_name(&self.peek().kind) {
5596 self.advance()
5597 } else {
5598 self.error_at_peek("expected field name after `.`");
5599 return None;
5600 };
5601 let name = self.ident_from_token(&name_tok);
5602 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5603 parse_arena_result(
5604 self.ast.try_push_expr(Expr {
5605 kind: ExprKind::FieldAccess {
5606 receiver,
5607 name: name.clone(),
5608 },
5609 span: self.span(receiver_span.start, name.span.end),
5610 }),
5611 &mut self.fatal,
5612 )
5613 }
5614
5615 fn parse_index_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5616 self.advance();
5617 let index = self.parse_expression()?;
5618 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5619 self.error_at_peek("expected `]`");
5620 return None;
5621 }
5622 let close = self.advance();
5623 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5624 parse_arena_result(
5625 self.ast.try_push_expr(Expr {
5626 kind: ExprKind::IndexAccess { receiver, index },
5627 span: self.span(receiver_span.start, close.span.end),
5628 }),
5629 &mut self.fatal,
5630 )
5631 }
5632
5633 fn parse_call_tail(
5634 &mut self,
5635 callee: ExprId,
5636 type_args: Option<Vec<TypeAnnotation>>,
5637 ) -> Option<ExprId> {
5638 self.advance();
5639 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5640 Vec::new()
5641 } else {
5642 self.parse_call_args()?
5643 };
5644 if !matches!(self.peek().kind, TokenKind::RightParen) {
5645 self.error_at_peek("expected `)`");
5646 return None;
5647 }
5648 let close = self.advance();
5649
5650 let callee_span = parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5651 parse_arena_result(
5652 self.ast.try_push_expr(Expr {
5653 kind: ExprKind::Call {
5654 callee,
5655 type_args,
5656 args,
5657 },
5658 span: self.span(callee_span.start, close.span.end),
5659 }),
5660 &mut self.fatal,
5661 )
5662 }
5663
5664 fn try_parse_generic_call_tail(&mut self, callee: ExprId) -> Option<ExprId> {
5666 let type_args = self.try_parse_type_args_only()?;
5667 if !matches!(self.peek().kind, TokenKind::LeftParen) {
5668 return None;
5669 }
5670 self.parse_call_tail(callee, Some(type_args))
5671 }
5672
5673 fn try_parse_type_args_only(&mut self) -> Option<Vec<TypeAnnotation>> {
5674 if !matches!(self.peek().kind, TokenKind::LessThan) {
5675 return self.invariant_failure("type argument parser expected `<`");
5676 }
5677 self.advance();
5678
5679 if matches!(self.peek().kind, TokenKind::GreaterThan) {
5680 return None;
5681 }
5682
5683 let mut type_args = Vec::new();
5684 loop {
5685 let ta = self.parse_type_annotation()?;
5686 type_args.push(ta);
5687 match self.peek().kind {
5688 TokenKind::Comma => {
5689 self.advance();
5690 if matches!(self.peek().kind, TokenKind::GreaterThan) {
5691 self.advance();
5692 break;
5693 }
5694 }
5695 TokenKind::GreaterThan => {
5696 self.advance();
5697 break;
5698 }
5699 _ => return None,
5700 }
5701 }
5702 Some(type_args)
5703 }
5704
5705 fn parse_call_args(&mut self) -> Option<Vec<ExprId>> {
5706 let mut args = Vec::new();
5707 loop {
5708 let arg = self.parse_expression()?;
5709 args.push(arg);
5710 match self.peek().kind {
5711 TokenKind::Comma => {
5712 self.advance();
5713 if matches!(self.peek().kind, TokenKind::RightParen) {
5714 return Some(args);
5715 }
5716 }
5717 TokenKind::RightParen => return Some(args),
5718 _ => {
5719 self.error_at_peek("expected `,` or `)`");
5720 return None;
5721 }
5722 }
5723 }
5724 }
5725
5726 fn parse_angle_cast(&mut self) -> Option<ExprId> {
5733 let open = self.advance();
5734 let ty = self.parse_type_annotation()?;
5735 if !matches!(self.peek().kind, TokenKind::GreaterThan) {
5736 self.error_at_peek("expected `>` to close a type assertion");
5737 return None;
5738 }
5739 self.advance();
5740 let expr = self.parse_unary()?;
5741 let end = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5742 .span
5743 .end;
5744 parse_arena_result(
5745 self.ast.try_push_expr(Expr {
5746 kind: ExprKind::As { expr, ty },
5747 span: self.span(open.span.start, end),
5748 }),
5749 &mut self.fatal,
5750 )
5751 }
5752
5753 fn parse_atom(&mut self) -> Option<ExprId> {
5754 match self.peek().kind {
5755 TokenKind::LessThan => return self.parse_angle_cast(),
5760 TokenKind::LeftParen => return self.parse_paren(),
5761 TokenKind::LeftBrace => return self.parse_object_literal(),
5764 TokenKind::LeftBracket => return self.parse_array_literal(),
5765 TokenKind::TemplateHead(_) => return self.parse_template_literal(),
5766 TokenKind::This => return self.parse_this_or_super(true),
5767 TokenKind::Super => return self.parse_this_or_super(false),
5768 TokenKind::Function => return self.parse_function_expression(),
5769 _ => {}
5770 }
5771 if !matches!(
5772 self.peek().kind,
5773 TokenKind::NumberLiteral(_)
5774 | TokenKind::BigIntLiteral(_)
5775 | TokenKind::StringLiteral(_)
5776 | TokenKind::TemplateNoSubstitution(_)
5777 | TokenKind::BooleanLiteral(_)
5778 | TokenKind::NullLiteral
5779 | TokenKind::Identifier
5780 | TokenKind::RegexLiteral { .. }
5781 ) {
5782 self.error_at_peek("expected expression");
5783 return None;
5784 }
5785 let tok = self.advance();
5786 let span = tok.span;
5787 let kind = match tok.kind {
5788 TokenKind::NumberLiteral(v) => ExprKind::Number(v),
5789 TokenKind::BigIntLiteral(s) => ExprKind::BigInt(s),
5790 TokenKind::StringLiteral(s) => ExprKind::String(s),
5791 TokenKind::TemplateNoSubstitution(s) => ExprKind::String(s),
5792 TokenKind::BooleanLiteral(b) => ExprKind::Boolean(b),
5793 TokenKind::NullLiteral => ExprKind::Null,
5794 TokenKind::Identifier => ExprKind::Identifier(Ident {
5795 name: self.source[span.start as usize..span.end as usize].to_string(),
5796 span,
5797 }),
5798 TokenKind::RegexLiteral { source, flags } => ExprKind::Regex { source, flags },
5799 _ => unreachable!("advance preserves the accepted expression token"),
5801 };
5802 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5803 }
5804
5805 fn parse_this_or_super(&mut self, is_this: bool) -> Option<ExprId> {
5808 let tok = self.advance();
5809 let span = tok.span;
5810 if !is_this && self.class_member_body_depth == 0 {
5811 self.error_at_with_help(
5812 span,
5813 "`super` is only valid inside a class method or constructor body",
5814 vec![
5815 "call `super(...)` in a subclass constructor or `super.method(...)` \
5816 in a subclass method"
5817 .to_string(),
5818 ],
5819 );
5820 }
5821 let kind = if is_this
5822 && self.class_member_body_depth == 0
5823 && self.function_expression_body_depth == 0
5824 {
5825 ExprKind::ThisOutsideReceiver
5826 } else if is_this {
5827 ExprKind::This
5828 } else {
5829 ExprKind::Super
5830 };
5831 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5832 }
5833
5834 fn parse_template_literal(&mut self) -> Option<ExprId> {
5837 let head_tok = self.advance();
5838 let TokenKind::TemplateHead(head) = head_tok.kind else {
5839 unreachable!("template dispatch requires TemplateHead");
5840 };
5841 let start = head_tok.span.start;
5842 let mut parts: Vec<String> = vec![head];
5843 let mut exprs: Vec<ExprId> = Vec::new();
5844 let mut substitution_spans: Vec<Span> = Vec::new();
5845 let mut substitution_start = head_tok.span.end.saturating_sub(2);
5848 loop {
5849 let expr = self.parse_expression()?;
5850 exprs.push(expr);
5851 let closing = self.peek().span.start;
5852 let substitution_span = self.span(substitution_start, closing.saturating_add(1));
5853 match self.peek().kind.clone() {
5854 TokenKind::TemplateMiddle(s) => {
5855 let tok = self.advance();
5856 substitution_spans.push(substitution_span);
5857 substitution_start = tok.span.end.saturating_sub(2);
5858 parts.push(s);
5859 }
5860 TokenKind::TemplateTail(s) => {
5861 let tok = self.advance();
5862 substitution_spans.push(substitution_span);
5863 parts.push(s);
5864 return parse_arena_result(
5865 self.ast.try_push_expr(Expr {
5866 kind: ExprKind::TemplateLiteral {
5867 parts,
5868 exprs,
5869 substitution_spans,
5870 },
5871 span: self.span(start, tok.span.end),
5872 }),
5873 &mut self.fatal,
5874 );
5875 }
5876 _ => {
5877 self.error_at_peek("expected `}` to close template interpolation");
5878 return None;
5879 }
5880 }
5881 }
5882 }
5883
5884 fn parse_object_literal(&mut self) -> Option<ExprId> {
5885 let open = self.advance();
5886
5887 let mut members: Vec<ObjectLiteralMember> = Vec::new();
5888 if !matches!(self.peek().kind, TokenKind::RightBrace) {
5889 loop {
5890 if matches!(self.peek().kind, TokenKind::DotDotDot) {
5891 let dots = self.advance();
5892 let value = self.parse_expression()?;
5893 let value_span =
5894 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5895 members.push(ObjectLiteralMember::Spread {
5896 value,
5897 span: self.span(dots.span.start, value_span.end),
5898 });
5899 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
5900 self.advance();
5901 let key = self.parse_expression()?;
5902 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5903 self.error_at_peek("expected `]` after computed property key");
5904 return None;
5905 }
5906 self.advance();
5907 if !matches!(self.peek().kind, TokenKind::Colon) {
5908 self.error_at_peek("expected `:` after computed property key; computed methods are not supported");
5909 return None;
5910 }
5911 self.advance();
5912 let value = self.parse_expression()?;
5913 members.push(ObjectLiteralMember::Computed { key, value });
5914 } else {
5915 let field = self.parse_object_literal_field()?;
5916 if let Some(existing) = members.iter().find_map(|m| match m {
5919 ObjectLiteralMember::Field(f) if f.name.name == field.name.name => Some(f),
5920 _ => None,
5921 }) {
5922 self.diagnostics.push(Diagnostic {
5923 severity: Severity::Error,
5924 span: field.name.span,
5925 message: format!(
5926 "duplicate field `{}` in object literal",
5927 field.name.name
5928 ),
5929 help: vec![],
5930 notes: vec![(existing.name.span, "first defined here".into())],
5931 });
5932 }
5934 members.push(ObjectLiteralMember::Field(field));
5935 }
5936 if self.at_inserted_semicolon()
5937 && matches!(self.peek_at(1).kind, TokenKind::RightBrace)
5938 {
5939 self.advance();
5940 }
5941 match self.peek().kind {
5942 TokenKind::Comma => {
5943 self.advance();
5944 if matches!(self.peek().kind, TokenKind::RightBrace) {
5945 break;
5946 }
5947 }
5948 TokenKind::RightBrace => break,
5949 _ => {
5950 self.error_at_peek("expected `,` or `}`");
5951 return None;
5952 }
5953 }
5954 }
5955 }
5956
5957 if !matches!(self.peek().kind, TokenKind::RightBrace) {
5958 self.error_at_peek("expected `}`");
5959 return None;
5960 }
5961 let close = self.advance();
5962
5963 parse_arena_result(
5964 self.ast.try_push_expr(Expr {
5965 kind: ExprKind::ObjectLiteral { members },
5966 span: self.span(open.span.start, close.span.end),
5967 }),
5968 &mut self.fatal,
5969 )
5970 }
5971
5972 fn parse_object_literal_field(&mut self) -> Option<ObjectLiteralField> {
5973 self.reject_async_member_modifier();
5974 let (name, shorthandable) = match self.peek().kind.clone() {
5975 TokenKind::Identifier => {
5976 let tok = self.advance();
5977 (self.ident_from_token(&tok), true)
5978 }
5979 kind if is_property_name(&kind) => {
5980 let tok = self.advance();
5981 (self.ident_from_token(&tok), false)
5982 }
5983 TokenKind::StringLiteral(_) => {
5984 let tok = self.advance();
5985 (self.property_ident_from_token(&tok), false)
5986 }
5987 _ => {
5988 self.error_at_peek("expected field name");
5989 return None;
5990 }
5991 };
5992
5993 if matches!(self.peek().kind, TokenKind::LeftParen) {
5999 let value = self.parse_method_shorthand_body(name.span)?;
6000 return Some(ObjectLiteralField { name, value });
6001 }
6002
6003 if !matches!(self.peek().kind, TokenKind::Colon) {
6004 if shorthandable {
6007 let value = parse_arena_result(
6008 self.ast.try_push_expr(Expr {
6009 kind: ExprKind::Identifier(name.clone()),
6010 span: name.span,
6011 }),
6012 &mut self.fatal,
6013 )?;
6014 return Some(ObjectLiteralField { name, value });
6015 }
6016 self.error_at_peek("expected `:` after field name");
6017 return None;
6018 }
6019 self.advance();
6020
6021 let value = self.parse_expression()?;
6022 Some(ObjectLiteralField { name, value })
6023 }
6024
6025 fn parse_method_shorthand_body(&mut self, name_span: Span) -> Option<ExprId> {
6028 self.advance();
6029
6030 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
6031 Vec::new()
6032 } else {
6033 self.parse_arrow_param_list()?
6034 };
6035 if !matches!(self.peek().kind, TokenKind::RightParen) {
6036 self.error_at_peek("expected `)`");
6037 return None;
6038 }
6039 self.advance();
6040
6041 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
6042 self.advance();
6043 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
6044 } else {
6045 (None, None)
6046 };
6047
6048 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
6049 self.error_at_peek("expected `{` to open the method body");
6050 return None;
6051 }
6052 let block = self.parse_outer_this_boundary(Self::parse_block)?;
6053 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
6054 .span
6055 .end;
6056
6057 parse_arena_result(
6058 self.ast.try_push_expr(Expr {
6059 kind: ExprKind::Arrow {
6060 params,
6061 return_type,
6062 type_predicate,
6063 body: ArrowBody::Block(block),
6064 },
6065 span: self.span(name_span.start, end),
6066 }),
6067 &mut self.fatal,
6068 )
6069 }
6070
6071 fn parse_array_literal(&mut self) -> Option<ExprId> {
6072 let open = self.advance();
6073
6074 let mut elements: Vec<ArrayLiteralElement> = Vec::new();
6075 if !matches!(self.peek().kind, TokenKind::RightBracket) {
6076 loop {
6077 let element = if matches!(self.peek().kind, TokenKind::DotDotDot) {
6078 let dots = self.advance();
6079 let value = self.parse_expression()?;
6080 let value_span =
6081 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
6082 ArrayLiteralElement::Spread {
6083 value,
6084 span: self.span(dots.span.start, value_span.end),
6085 }
6086 } else {
6087 ArrayLiteralElement::Value(self.parse_expression()?)
6088 };
6089 elements.push(element);
6090 match self.peek().kind {
6091 TokenKind::Comma => {
6092 self.advance();
6093 if matches!(self.peek().kind, TokenKind::RightBracket) {
6094 break;
6095 }
6096 }
6097 TokenKind::RightBracket => break,
6098 _ => {
6099 self.error_at_peek("expected `,` or `]`");
6100 return None;
6101 }
6102 }
6103 }
6104 }
6105
6106 if !matches!(self.peek().kind, TokenKind::RightBracket) {
6107 self.error_at_peek("expected `]`");
6108 return None;
6109 }
6110 let close = self.advance();
6111
6112 parse_arena_result(
6113 self.ast.try_push_expr(Expr {
6114 kind: ExprKind::ArrayLiteral { elements },
6115 span: self.span(open.span.start, close.span.end),
6116 }),
6117 &mut self.fatal,
6118 )
6119 }
6120
6121 fn parse_paren(&mut self) -> Option<ExprId> {
6122 let open = self.advance();
6123 let inner = self.parse_expression()?;
6124 if !matches!(self.peek().kind, TokenKind::RightParen) {
6125 self.error_at_peek("expected `)`");
6126 return None;
6127 }
6128 let close = self.advance();
6129 let span = self.span(open.span.start, close.span.end);
6130 parse_arena_result(
6131 self.ast.try_push_expr(Expr {
6132 kind: ExprKind::Paren(inner),
6133 span,
6134 }),
6135 &mut self.fatal,
6136 )
6137 }
6138
6139 fn with_recursion_limit<T>(&mut self, parse: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
6140 if self.fatal.is_some() {
6141 return None;
6142 }
6143 if self.recursion_limit_span.is_some() || self.recursion_depth >= MAX_PARSE_DEPTH {
6144 self.stop_at_recursion_limit();
6145 return None;
6146 }
6147 self.recursion_depth += 1;
6148 let result = parse(self);
6149 self.recursion_depth -= 1;
6150 result
6151 }
6152
6153 fn stop_at_recursion_limit(&mut self) {
6154 self.recursion_limit_span.get_or_insert(self.peek().span);
6155 self.pos = self.tokens.len().saturating_sub(1);
6157 }
6158
6159 fn recover(&mut self) {
6160 while !self.is_at_eof() {
6161 match self.peek().kind {
6162 TokenKind::Semicolon => {
6163 self.advance();
6164 return;
6165 }
6166 TokenKind::RightBrace
6167 | TokenKind::Let
6168 | TokenKind::Const
6169 | TokenKind::Function
6170 | TokenKind::Export
6171 | TokenKind::If
6172 | TokenKind::While
6173 | TokenKind::Return
6174 | TokenKind::Try
6175 | TokenKind::Throw => return,
6176 _ => {
6177 self.advance();
6178 }
6179 }
6180 }
6181 }
6182
6183 fn peek(&self) -> &Token {
6184 self.tokens.get(self.pos).unwrap_or(&self.eof)
6185 }
6186
6187 fn peek_at(&self, offset: usize) -> &Token {
6188 self.pos
6189 .checked_add(offset)
6190 .and_then(|index| self.tokens.get(index))
6191 .unwrap_or(&self.eof)
6192 }
6193
6194 fn at_inserted_semicolon(&self) -> bool {
6196 let token = self.peek();
6197 matches!(token.kind, TokenKind::Semicolon) && token.span.start == token.span.end
6198 }
6199
6200 fn advance(&mut self) -> Token {
6201 let tok = self.peek().clone();
6202 if self.pos < self.tokens.len().saturating_sub(1) {
6203 self.pos += 1;
6204 }
6205 tok
6206 }
6207
6208 fn is_at_eof(&self) -> bool {
6209 self.fatal.is_some() || matches!(self.peek().kind, TokenKind::Eof)
6210 }
6211
6212 fn line_break_before_peek(&self) -> bool {
6214 self.source_has_line_break(self.prev_token_end(), self.peek().span.start)
6215 }
6216
6217 fn line_break_after_peek(&self) -> bool {
6219 self.source_has_line_break(self.peek().span.end, self.peek_at(1).span.start)
6220 }
6221
6222 fn source_has_line_break(&self, start: u32, end: u32) -> bool {
6225 self.source
6226 .get(start as usize..end as usize)
6227 .is_some_and(|gap| gap.contains(['\n', '\r']))
6228 }
6229
6230 fn at_statement_end(&self) -> bool {
6235 matches!(self.peek().kind, TokenKind::Semicolon)
6236 || (self.last_as_type_end == Some(self.prev_token_end())
6237 && self.line_break_before_peek())
6238 }
6239
6240 fn finish_statement(&mut self) -> u32 {
6243 if matches!(self.peek().kind, TokenKind::Semicolon) {
6244 return self.advance().span.end;
6245 }
6246 self.prev_token_end()
6247 }
6248
6249 fn prev_token_end(&self) -> u32 {
6252 self.tokens
6253 .get(self.pos.saturating_sub(1))
6254 .unwrap_or(&self.eof)
6255 .span
6256 .end
6257 }
6258
6259 fn take_leading_doc(&mut self) -> Option<crate::DocComment> {
6260 let parsed = self
6261 .peek()
6262 .leading_doc
6263 .as_ref()
6264 .map(crate::parse_doc_comment)
6265 .transpose();
6266 match parsed {
6267 Ok(doc) => doc,
6268 Err(error) => {
6269 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6270 None
6271 }
6272 }
6273 }
6274
6275 fn parse_doc(&mut self, raw: &crate::RawDoc) -> Option<crate::DocComment> {
6276 match crate::parse_doc_comment(raw) {
6277 Ok(doc) => Some(doc),
6278 Err(error) => {
6279 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6280 None
6281 }
6282 }
6283 }
6284
6285 fn reject_use_strict_with_non_simple_params(&mut self) {
6289 let mut functions: Vec<(Vec<Span>, StmtId)> = Vec::new();
6290 let non_simple = |param: &ParamDecl| {
6291 (param.default.is_some() || param.pattern.is_some() || param.rest).then(|| {
6292 param
6293 .pattern
6294 .as_ref()
6295 .map_or(param.name.span, Binding::span)
6296 })
6297 };
6298 for expr in self.ast.source_expressions() {
6299 if let ExprKind::Arrow {
6300 params,
6301 body: ArrowBody::Block(body),
6302 ..
6303 } = &expr.kind
6304 {
6305 functions.push((params.iter().filter_map(non_simple).collect(), *body));
6306 }
6307 }
6308 for stmt in self.ast.source_statements() {
6309 match &stmt.kind {
6310 StmtKind::Function { params, body, .. } => {
6311 functions.push((params.iter().filter_map(non_simple).collect(), *body));
6312 }
6313 StmtKind::ClassDecl { members, .. } => {
6314 for member in members {
6315 match member {
6316 ClassMember::Method { params, body, .. }
6317 | ClassMember::Constructor { params, body, .. } => {
6318 functions
6319 .push((params.iter().filter_map(non_simple).collect(), *body));
6320 }
6321 ClassMember::Accessor { param, body, .. } => {
6322 let params = param.as_deref().and_then(non_simple);
6323 functions.push((params.into_iter().collect(), *body));
6324 }
6325 _ => {}
6326 }
6327 }
6328 }
6329 _ => {}
6330 }
6331 }
6332 for (params, body) in functions {
6333 if params.is_empty() {
6334 continue;
6335 }
6336 let Some(directive) = self.use_strict_directive(body) else {
6337 continue;
6338 };
6339 for span in params {
6340 self.error_at(
6341 span,
6342 "this parameter is not allowed with a `\"use strict\"` directive",
6343 );
6344 }
6345 self.error_at_with_help(
6346 directive,
6347 "a `\"use strict\"` directive can't be used with a parameter list that has defaults, patterns, or a rest parameter",
6348 vec!["remove the directive; Submilli code is always strict".into()],
6349 );
6350 }
6351 }
6352
6353 fn use_strict_directive(&self, body: StmtId) -> Option<Span> {
6357 let StmtKind::Block(stmts) = &self.ast.try_stmt(body).ok()?.kind else {
6358 return None;
6359 };
6360 for stmt in stmts {
6361 let stmt = self.ast.try_stmt(*stmt).ok()?;
6362 let StmtKind::Expr(expr) = stmt.kind else {
6363 return None;
6364 };
6365 let expr = self.ast.try_expr(expr).ok()?;
6366 let ExprKind::String(value) = &expr.kind else {
6367 return None;
6368 };
6369 if self.source.as_bytes().get(expr.span.start as usize) == Some(&b'`') {
6370 return None;
6371 }
6372 if value == "use strict" {
6373 return Some(stmt.span);
6374 }
6375 }
6376 None
6377 }
6378
6379 fn error_at(&mut self, span: Span, message: impl Into<String>) {
6380 self.diagnostics.push(Diagnostic {
6381 severity: Severity::Error,
6382 span,
6383 message: message.into(),
6384 help: vec![],
6385 notes: vec![],
6386 });
6387 }
6388
6389 fn reject_loose_nullish_comparison(
6392 &mut self,
6393 op: BinOp,
6394 lhs: ExprId,
6395 rhs: ExprId,
6396 ) -> Option<()> {
6397 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
6398 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
6399 let (nullish, operand, nullish_first) =
6400 match (self.nullish_operand(lhs)?, self.nullish_operand(rhs)?) {
6401 (Some(left), Some(right)) => {
6402 self.reject_nullish_against_nullish(op, lhs_span, rhs_span, [left, right]);
6403 return Some(());
6404 }
6405 (None, Some(nullish)) => (nullish, lhs, false),
6406 (Some(nullish), None) => (nullish, rhs, true),
6407 (None, None) => return Some(()),
6408 };
6409 let comparison = LooseComparison {
6410 nullish,
6411 nullish_first,
6412 negated: op == BinOp::NotEq,
6413 };
6414 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
6415 let x = self.single_line_source(operand_span).unwrap_or("x");
6416 let strict = self.strict_suggestion(&comparison, x);
6417 let either = self.either_suggestion(&comparison, operand, x)?;
6418 let literal = comparison.nullish.literal.text();
6419 let written = self
6421 .single_line_source(self.span(lhs_span.start, rhs_span.end))
6422 .map_or_else(
6423 || {
6424 let written_op = if comparison.negated { "!=" } else { "==" };
6425 comparison.ordered(x, written_op, literal)
6426 },
6427 str::to_string,
6428 );
6429 self.error_at_with_help(
6430 self.span(lhs_span.start, rhs_span.end),
6431 format!(
6432 "`{written}` does not also match `{}`: `==` compares like `===`",
6433 comparison.nullish.literal.other().text()
6434 ),
6435 vec![
6436 either,
6437 format!("to test for `{literal}` alone, write `{strict}`"),
6438 ],
6439 );
6440 Some(())
6441 }
6442
6443 fn reject_nullish_against_nullish(
6447 &mut self,
6448 op: BinOp,
6449 lhs: Span,
6450 rhs: Span,
6451 sides: [NullishOperand; 2],
6452 ) {
6453 let span = self.span(lhs.start, rhs.end);
6454 let subject = self.single_line_source(span).map_or_else(
6457 || "this comparison".to_string(),
6458 |written| format!("`{written}`"),
6459 );
6460 let value = if op == BinOp::NotEq { "false" } else { "true" };
6461 let effects: Vec<String> = sides
6462 .iter()
6463 .filter_map(|side| side.effect)
6464 .map(|effect| {
6465 self.single_line_source(effect.operand).map_or_else(
6466 || "the `void` operand's expression".to_string(),
6467 |statement| format!("`{statement};`"),
6468 )
6469 })
6470 .collect();
6471 let help = if effects.is_empty() {
6472 format!("in TypeScript this is always `{value}`; write `{value}`")
6473 } else {
6474 format!(
6475 "in TypeScript this is always `{value}`; run {} {}, then use `{value}`",
6476 effects.join(" and "),
6477 if effects.len() > 1 {
6478 "each as its own statement"
6479 } else {
6480 "as its own statement"
6481 }
6482 )
6483 };
6484 self.error_at_with_help(
6485 span,
6486 format!("{subject} compares two nullish values: `==` compares like `===`"),
6487 vec![help],
6488 );
6489 }
6490
6491 fn strict_suggestion(&self, comparison: &LooseComparison, x: &str) -> String {
6494 let eq = comparison.strict_op();
6495 match comparison.nullish.effect {
6496 Some(effect) => {
6497 let void = self.single_line_source(effect.void).unwrap_or("void expr");
6498 comparison.ordered(x, eq, void)
6499 }
6500 None => format!("{x} {eq} {}", comparison.nullish.literal.text()),
6501 }
6502 }
6503
6504 fn either_suggestion(
6508 &mut self,
6509 comparison: &LooseComparison,
6510 operand: ExprId,
6511 x: &str,
6512 ) -> Option<String> {
6513 let mut steps = Vec::new();
6514 let tested = if self.repeats_without_effect(operand)? {
6515 x
6516 } else {
6517 steps.push(format!("bind `{x}` to a `const` (`const value = {x};`)"));
6518 "value"
6519 };
6520 if let Some(effect) = comparison.nullish.effect {
6521 let statement = self.single_line_source(effect.operand).unwrap_or("expr");
6522 let step = format!("run `{statement};` as its own statement");
6523 if comparison.nullish_first {
6524 steps.insert(0, step);
6525 } else {
6526 steps.push(step);
6527 }
6528 }
6529 let eq = comparison.strict_op();
6530 let joiner = if comparison.negated { "&&" } else { "||" };
6531 let both = format!("{tested} {eq} null {joiner} {tested} {eq} undefined");
6532 Some(if steps.is_empty() {
6533 format!("to test for either, write `{both}`")
6534 } else {
6535 format!(
6536 "to test for either, first {}, then write `{both}`",
6537 steps.join(", then ")
6538 )
6539 })
6540 }
6541
6542 fn repeats_without_effect(&mut self, id: ExprId) -> Option<bool> {
6545 let unwrapped = self.unparenthesized(id)?;
6546 let expr = parse_arena_result(self.ast.try_expr(unwrapped), &mut self.fatal)?;
6547 if is_literal(&expr.kind) {
6548 return Some(true);
6549 }
6550 if self
6551 .nullish_operand(unwrapped)?
6552 .is_some_and(|nullish| nullish.effect.is_none())
6553 {
6554 return Some(true);
6555 }
6556 self.is_reference_path(id)
6557 }
6558
6559 fn nullish_operand(&mut self, id: ExprId) -> Option<Option<NullishOperand>> {
6561 let id = self.unparenthesized(id)?;
6562 let expr = parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?;
6563 let span = expr.span;
6564 Some(match &expr.kind {
6565 ExprKind::Null => Some(NullishOperand {
6566 literal: Nullish::Null,
6567 effect: None,
6568 }),
6569 ExprKind::Identifier(ident) if ident.name == "undefined" => Some(NullishOperand {
6570 literal: Nullish::Undefined,
6571 effect: None,
6572 }),
6573 ExprKind::Void { operand } => {
6574 let operand_id = self.unparenthesized(*operand)?;
6575 let operand =
6576 parse_arena_result(self.ast.try_expr(operand_id), &mut self.fatal)?.clone();
6577 let effect_free =
6579 is_literal(&operand.kind) || self.is_reference_path(operand_id)?;
6580 let effect = (!effect_free).then_some(VoidEffect {
6581 void: span,
6582 operand: operand.span,
6583 });
6584 Some(NullishOperand {
6585 literal: Nullish::Undefined,
6586 effect,
6587 })
6588 }
6589 _ => None,
6590 })
6591 }
6592
6593 fn is_reference_path(&mut self, id: ExprId) -> Option<bool> {
6596 let mut id = id;
6597 loop {
6598 match &parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6599 ExprKind::Identifier(_) | ExprKind::This => return Some(true),
6600 ExprKind::FieldAccess { receiver, .. } => id = *receiver,
6601 ExprKind::Paren(inner) => id = *inner,
6602 _ => return Some(false),
6603 }
6604 }
6605 }
6606
6607 fn unparenthesized(&mut self, id: ExprId) -> Option<ExprId> {
6608 let mut id = id;
6609 loop {
6610 match parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6611 ExprKind::Paren(inner) => id = inner,
6612 _ => return Some(id),
6613 }
6614 }
6615 }
6616
6617 fn single_line_source(&self, span: Span) -> Option<&'a str> {
6618 self.source
6619 .get(span.start as usize..span.end as usize)
6620 .filter(|text| !text.contains('\n'))
6621 }
6622
6623 fn error_at_with_help(&mut self, span: Span, message: impl Into<String>, help: Vec<String>) {
6624 self.diagnostics.push(Diagnostic {
6625 severity: Severity::Error,
6626 span,
6627 message: message.into(),
6628 help,
6629 notes: vec![],
6630 });
6631 }
6632
6633 fn invariant_failure<T>(&mut self, message: &str) -> Option<T> {
6634 self.fatal.get_or_insert_with(|| CompilerFailure::Internal {
6635 stage: CompilerStage::Parse,
6636 span: None,
6637 message: message.into(),
6638 });
6639 self.pos = self.tokens.len();
6640 None
6641 }
6642
6643 fn error_at_peek(&mut self, message: impl Into<String>) {
6644 let span = self.peek().span;
6645 self.error_at(span, message);
6646 }
6647
6648 fn error_at_peek_with_help(&mut self, message: impl Into<String>, help: Vec<String>) {
6649 let span = self.peek().span;
6650 self.error_at_with_help(span, message, help);
6651 }
6652
6653 fn error_count(&self) -> usize {
6654 self.diagnostics
6655 .iter()
6656 .filter(|d| d.severity == Severity::Error)
6657 .count()
6658 }
6659}
6660
6661pub(crate) fn is_property_name(kind: &TokenKind) -> bool {
6662 matches!(
6663 kind,
6664 TokenKind::Identifier
6665 | TokenKind::BooleanLiteral(_)
6666 | TokenKind::NullLiteral
6667 | TokenKind::Let
6668 | TokenKind::Const
6669 | TokenKind::Function
6670 | TokenKind::If
6671 | TokenKind::Else
6672 | TokenKind::While
6673 | TokenKind::Do
6674 | TokenKind::For
6675 | TokenKind::Break
6676 | TokenKind::Continue
6677 | TokenKind::Return
6678 | TokenKind::Switch
6679 | TokenKind::Case
6680 | TokenKind::Default
6681 | TokenKind::Void
6682 | TokenKind::Interface
6683 | TokenKind::Enum
6684 | TokenKind::In
6685 | TokenKind::Typeof
6686 | TokenKind::Import
6687 | TokenKind::Export
6688 | TokenKind::New
6689 | TokenKind::Try
6690 | TokenKind::Catch
6691 | TokenKind::Finally
6692 | TokenKind::Throw
6693 | TokenKind::Class
6694 | TokenKind::Extends
6695 | TokenKind::Implements
6696 | TokenKind::Super
6697 | TokenKind::This
6698 )
6699}
6700
6701fn is_reserved_identifier_word(kind: &TokenKind) -> bool {
6702 matches!(
6703 kind,
6704 TokenKind::BooleanLiteral(_)
6705 | TokenKind::NullLiteral
6706 | TokenKind::Let
6707 | TokenKind::Const
6708 | TokenKind::Function
6709 | TokenKind::If
6710 | TokenKind::Else
6711 | TokenKind::While
6712 | TokenKind::Do
6713 | TokenKind::For
6714 | TokenKind::Break
6715 | TokenKind::Continue
6716 | TokenKind::Return
6717 | TokenKind::Switch
6718 | TokenKind::Case
6719 | TokenKind::Default
6720 | TokenKind::Export
6721 | TokenKind::Void
6722 | TokenKind::Interface
6723 | TokenKind::Enum
6724 | TokenKind::In
6725 | TokenKind::Typeof
6726 | TokenKind::Import
6727 | TokenKind::New
6728 | TokenKind::Try
6729 | TokenKind::Catch
6730 | TokenKind::Finally
6731 | TokenKind::Throw
6732 | TokenKind::Class
6733 | TokenKind::Extends
6734 | TokenKind::Implements
6735 | TokenKind::Super
6736 | TokenKind::This
6737 )
6738}
6739
6740#[derive(Clone, Copy, PartialEq, Eq)]
6744enum SpecifierList {
6745 Import,
6746 Export,
6747}
6748
6749impl SpecifierList {
6750 fn noun(self) -> &'static str {
6751 match self {
6752 SpecifierList::Import => "import",
6753 SpecifierList::Export => "export",
6754 }
6755 }
6756}
6757
6758const STRICT_MODE_RESERVED_WORDS: [&str; 6] = [
6762 "package",
6763 "private",
6764 "protected",
6765 "public",
6766 "static",
6767 "yield",
6768];
6769
6770fn reserved_keyword_rename_example(keyword: &str) -> &'static str {
6771 match keyword {
6772 "default" => "defaultValue",
6773 _ => "valueName",
6774 }
6775}
6776
6777fn mixed_logical(prev: BinOp, next: BinOp) -> bool {
6778 let prev_logical = matches!(prev, BinOp::Or | BinOp::And);
6779 let next_logical = matches!(next, BinOp::Or | BinOp::And);
6780 let prev_nullish = matches!(prev, BinOp::NullishCoalesce);
6781 let next_nullish = matches!(next, BinOp::NullishCoalesce);
6782 (prev_logical && next_nullish) || (prev_nullish && next_logical)
6783}
6784
6785fn mixed_logical_help(prev: BinOp, next: BinOp) -> String {
6788 let symbol = |op: BinOp| if op == BinOp::And { "&&" } else { "||" };
6789 let (first, second) = if prev == BinOp::NullishCoalesce {
6790 ("??", symbol(next))
6791 } else {
6792 (symbol(prev), "??")
6793 };
6794 format!(
6795 "add parentheses to choose the grouping: `(a {first} b) {second} c` or `a {first} (b {second} c)`"
6796 )
6797}
6798
6799const LOGICAL_AND_PREC: u8 = 2;
6800
6801#[derive(Clone, Copy)]
6803struct NullishOperand {
6804 literal: Nullish,
6805 effect: Option<VoidEffect>,
6807}
6808
6809#[derive(Clone, Copy, PartialEq, Eq)]
6810enum Nullish {
6811 Null,
6812 Undefined,
6813}
6814
6815impl Nullish {
6816 fn text(self) -> &'static str {
6817 match self {
6818 Nullish::Null => "null",
6819 Nullish::Undefined => "undefined",
6820 }
6821 }
6822
6823 fn other(self) -> Self {
6825 match self {
6826 Nullish::Null => Nullish::Undefined,
6827 Nullish::Undefined => Nullish::Null,
6828 }
6829 }
6830}
6831
6832struct LooseComparison {
6834 nullish: NullishOperand,
6835 nullish_first: bool,
6837 negated: bool,
6839}
6840
6841impl LooseComparison {
6842 fn strict_op(&self) -> &'static str {
6843 if self.negated { "!==" } else { "===" }
6844 }
6845
6846 fn ordered(&self, x: &str, op: &str, nullish: &str) -> String {
6848 if self.nullish_first {
6849 format!("{nullish} {op} {x}")
6850 } else {
6851 format!("{x} {op} {nullish}")
6852 }
6853 }
6854}
6855
6856#[derive(Clone, Copy)]
6857struct VoidEffect {
6858 void: Span,
6860 operand: Span,
6862}
6863
6864fn is_literal(kind: &ExprKind) -> bool {
6865 matches!(
6866 kind,
6867 ExprKind::Number(_)
6868 | ExprKind::BigInt(_)
6869 | ExprKind::String(_)
6870 | ExprKind::Boolean(_)
6871 | ExprKind::Null
6872 )
6873}
6874
6875struct ParamOrder<'p> {
6877 optional: bool,
6878 required: bool,
6880 name: Option<&'p str>,
6882 span: Span,
6883}
6884
6885impl<'p> ParamOrder<'p> {
6886 fn of_param(param: &'p ParamDecl) -> Self {
6887 let (name, span) = match ¶m.pattern {
6888 Some(binding) => (None, binding.span()),
6889 None => (Some(param.name.name.as_str()), param.name.span),
6890 };
6891 Self {
6892 optional: param.optional,
6893 required: !param.is_omittable() && !param.rest,
6894 name,
6895 span,
6896 }
6897 }
6898
6899 fn of_field(field: &'p TypeAnnotationField) -> Self {
6900 Self {
6901 optional: field.optional,
6902 required: !field.optional && !field.rest,
6903 name: Some(field.name.name.as_str()),
6904 span: field.name.span,
6905 }
6906 }
6907}
6908
6909struct BinaryToken {
6911 op: BinOp,
6912 precedence: u8,
6913 tokens: usize,
6914}
6915
6916struct ShiftToken {
6918 op: BinOp,
6919 tokens: usize,
6920 assignment: bool,
6922}
6923
6924impl ShiftToken {
6925 fn new(op: BinOp, tokens: usize, assignment: bool) -> Self {
6926 Self {
6927 op,
6928 tokens,
6929 assignment,
6930 }
6931 }
6932}
6933
6934const SHIFT_PRECEDENCE: u8 = 8;
6936
6937#[derive(Clone, Copy, PartialEq)]
6938enum TupleElementLabel {
6939 None,
6940 Required,
6942 Optional,
6944}
6945
6946fn peek_binop(kind: &TokenKind) -> Option<(BinOp, u8)> {
6947 Some(match kind {
6948 TokenKind::QuestionQuestion => (BinOp::NullishCoalesce, 0),
6949 TokenKind::PipePipe => (BinOp::Or, 1),
6950 TokenKind::AmpAmp => (BinOp::And, LOGICAL_AND_PREC),
6951 TokenKind::Pipe => (BinOp::BitOr, 3),
6952 TokenKind::Caret => (BinOp::BitXor, 4),
6953 TokenKind::Amp => (BinOp::BitAnd, 5),
6954 TokenKind::EqEqEq | TokenKind::EqEq => (BinOp::Eq, 6),
6955 TokenKind::BangEqEq | TokenKind::BangEq => (BinOp::NotEq, 6),
6956 TokenKind::LessThan => (BinOp::Lt, 7),
6957 TokenKind::GreaterThan => (BinOp::Gt, 7),
6958 TokenKind::LessEquals => (BinOp::Le, 7),
6959 TokenKind::GreaterEquals => (BinOp::Ge, 7),
6960 TokenKind::In => (BinOp::In, 7),
6961 TokenKind::Plus => (BinOp::Add, 9),
6964 TokenKind::Minus => (BinOp::Sub, 9),
6965 TokenKind::Star => (BinOp::Mul, 10),
6966 TokenKind::Slash => (BinOp::Div, 10),
6967 TokenKind::Percent => (BinOp::Rem, 10),
6968 TokenKind::StarStar => (BinOp::Pow, 11),
6970 _ => return None,
6971 })
6972}
6973
6974fn cannot_start_type(kind: &TokenKind) -> bool {
6978 matches!(
6979 kind,
6980 TokenKind::Dot
6981 | TokenKind::QuestionDot
6982 | TokenKind::LeftBracket
6983 | TokenKind::Bang
6984 | TokenKind::Semicolon
6985 | TokenKind::RightParen
6986 | TokenKind::RightBrace
6987 | TokenKind::Comma
6988 | TokenKind::Eof
6989 )
6990}
6991
6992fn right_operand_min_prec(op: BinOp, prec: u8) -> u8 {
6996 match op {
6997 BinOp::Pow => prec,
6998 BinOp::NullishCoalesce => LOGICAL_AND_PREC + 1,
6999 _ => prec + 1,
7000 }
7001}
7002
7003fn compound_op_for_token(kind: &TokenKind) -> Option<BinOp> {
7004 Some(match kind {
7005 TokenKind::PlusEquals => BinOp::Add,
7006 TokenKind::MinusEquals => BinOp::Sub,
7007 TokenKind::StarEquals => BinOp::Mul,
7008 TokenKind::SlashEquals => BinOp::Div,
7009 TokenKind::PercentEquals => BinOp::Rem,
7010 TokenKind::StarStarEquals => BinOp::Pow,
7011 TokenKind::AmpEquals => BinOp::BitAnd,
7012 TokenKind::PipeEquals => BinOp::BitOr,
7013 TokenKind::CaretEquals => BinOp::BitXor,
7014 _ => return None,
7015 })
7016}
7017
7018fn is_assign_lookahead(kind: &TokenKind) -> bool {
7019 matches!(kind, TokenKind::Equals) || compound_op_for_token(kind).is_some()
7020}
7021
7022fn parse_arena_result<T>(
7025 result: Result<T, crate::arena::ArenaError>,
7026 fatal: &mut Option<CompilerFailure>,
7027) -> Option<T> {
7028 match result {
7029 Ok(value) => Some(value),
7030 Err(error) => {
7031 fatal.get_or_insert_with(|| error.into_compiler_failure(CompilerStage::Parse));
7032 None
7033 }
7034 }
7035}
7036
7037fn is_rest_array_annotation(ty: &crate::TypeAnnotation) -> bool {
7039 use crate::ast::TypeAnnotationKind;
7040 match &ty.kind {
7041 TypeAnnotationKind::Array(_) => true,
7042 TypeAnnotationKind::Readonly(inner) => matches!(inner.kind, TypeAnnotationKind::Array(_)),
7043 _ => false,
7044 }
7045}
7046
7047#[cfg(test)]
7048mod tests {
7049 use super::{MAX_ERRORS, MAX_PARSE_DEPTH, Parser, parse};
7050 use crate::source::Sources;
7051 use crate::{Asi, Ast, ExprKind, FileId, ImportKind, StmtKind, Token, TokenKind, diagnostics};
7052
7053 const F: FileId = FileId(0);
7054
7055 #[test]
7056 fn malformed_direct_token_streams_return_typed_failures() {
7057 use crate::compiler_error::CompilerFailure;
7058 for tokens in [
7059 Vec::new(),
7060 vec![Token::new(
7061 TokenKind::Identifier,
7062 crate::Span::new(F, 0, 2).unwrap(),
7063 )],
7064 vec![
7065 Token::new(
7066 TokenKind::Identifier,
7067 crate::Span {
7068 file: F,
7069 start: 1,
7070 end: 2,
7071 },
7072 ),
7073 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
7074 ],
7075 vec![
7076 Token::new(TokenKind::Eof, crate::Span::at(F)),
7077 Token::new(TokenKind::Eof, crate::Span::at(F)),
7078 ],
7079 vec![
7080 Token::new(
7081 TokenKind::Identifier,
7082 crate::Span {
7083 file: F,
7084 start: 2,
7085 end: 0,
7086 },
7087 ),
7088 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
7089 ],
7090 vec![Token::new(TokenKind::Eof, crate::Span::at(FileId(9)))],
7091 ] {
7092 let error = super::parse_checked("é", tokens, F).expect_err("invalid token metadata");
7093 assert!(matches!(
7094 error.fatal,
7095 Some(CompilerFailure::Internal { .. })
7096 ));
7097 }
7098 }
7099
7100 #[test]
7101 fn malformed_documentation_metadata_returns_a_fatal_error() {
7102 let source = "/** hello */ function main(): void {}";
7103 let valid = tokens_of(source);
7104 for doc in [
7105 crate::RawDoc {
7106 text: "/** hi */".into(),
7107 span: crate::Span {
7108 file: F,
7109 start: u32::MAX,
7110 end: u32::MAX,
7111 },
7112 },
7113 crate::RawDoc {
7114 text: "/** different */".into(),
7115 span: crate::Span::new(F, 0, 12).unwrap(),
7116 },
7117 crate::RawDoc {
7118 text: "/** hello */".into(),
7119 span: crate::Span::new(FileId(9), 0, 12).unwrap(),
7120 },
7121 ] {
7122 let mut tokens = valid.clone();
7123 tokens[0].leading_doc = Some(doc);
7124 let error = super::parse_checked(source, tokens, F).expect_err("invalid doc metadata");
7125 assert!(error.to_string().contains("documentation metadata"));
7126 }
7127 assert!(super::parse_checked(source, valid, F).is_ok());
7128 }
7129
7130 #[test]
7131 fn invalid_parser_dispatch_survives_diagnostic_rollback() {
7132 let mut parser = parser_from_source("let x = 1;");
7133 assert!(parser.try_parse_type_args_only().is_none());
7134 parser.diagnostics.clear();
7135 assert!(parser.fatal.is_some());
7136 assert!(parser.parse_expression().is_none());
7137 }
7138
7139 #[test]
7140 fn invalid_arena_read_stops_parser_and_survives_diagnostic_rollback() {
7141 use crate::{
7142 ExprId,
7143 compiler_error::{CompilerFailure, CompilerStage},
7144 };
7145 let mut parser = parser_from_source("let x = 1;");
7146 assert!(parser.statement_kind_for(ExprId(u32::MAX)).is_none());
7147 assert!(matches!(
7148 parser.fatal,
7149 Some(CompilerFailure::Internal {
7150 stage: CompilerStage::Parse,
7151 span: None,
7152 ..
7153 })
7154 ));
7155 parser.diagnostics.clear();
7156 let position = parser.pos;
7157 assert!(parser.parse_statement().is_none());
7158 parser.recover();
7159 assert_eq!(parser.pos, position);
7160 assert!(parser.ast.expr_ids().unwrap().next().is_none());
7161 }
7162
7163 fn tokens_of(source: &str) -> Vec<Token> {
7164 let mut asi = Asi::new(source, crate::FileId(0));
7165 let mut tokens = Vec::new();
7166 loop {
7167 let tok = asi.next_token();
7168 let is_eof = matches!(tok.kind, TokenKind::Eof);
7169 tokens.push(tok);
7170 if is_eof {
7171 break;
7172 }
7173 }
7174 let diags = asi.into_diagnostics();
7175 assert!(diags.is_empty(), "unexpected lexer diagnostics: {diags:?}");
7176 tokens
7177 }
7178
7179 fn parse_str(source: &str) -> (Ast, Vec<crate::Diagnostic>) {
7180 parse(source, tokens_of(source), crate::FileId(0))
7181 }
7182
7183 fn parser_from_source(source: &str) -> Parser<'_> {
7184 Parser {
7185 source,
7186 tokens: tokens_of(source),
7187 file: F,
7188 pos: 0,
7189 ast: Ast::new(),
7190 diagnostics: Vec::new(),
7191 block_depth: 0,
7192 class_member_body_depth: 0,
7193 function_expression_body_depth: 0,
7194 recursion_depth: 0,
7195 recursion_limit_span: None,
7196 resume_at_next_member: false,
7197 last_as_type_end: None,
7198 trailing_declarators: Vec::new(),
7199 eof: Token::new(
7200 TokenKind::Eof,
7201 crate::Span::new(F, source.len() as u32, source.len() as u32).unwrap(),
7202 ),
7203 fatal: None,
7204 }
7205 }
7206
7207 #[test]
7208 fn recursion_budget_accepts_boundary_and_restores_depth() {
7209 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH - 1));
7210 let mut parser = parser_from_source(&source);
7211 assert!(parser.parse_unary().is_some());
7212 assert_eq!(parser.recursion_depth, 0);
7213 assert!(parser.recursion_limit_span.is_none());
7214
7215 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH));
7216 let mut parser = parser_from_source(&source);
7217 assert!(parser.parse_unary().is_none());
7218 assert_eq!(parser.recursion_depth, 0);
7219 assert!(parser.recursion_limit_span.is_some());
7220 }
7221
7222 #[test]
7223 fn parse_empty_input() {
7224 let (ast, diags) = parse_str("");
7225 assert!(diags.is_empty());
7226 assert!(ast.top_level.is_empty());
7227 }
7228
7229 #[test]
7230 fn any_type_is_rejected() {
7231 for src in [
7234 "function main(): void { const x: any = 1; }",
7235 "function main(): void { const x = 1 as any; }",
7236 "function main(): void { const x: any[] = []; }",
7237 ] {
7238 let (_ast, diags) = parse_str(src);
7239 assert!(
7240 diags
7241 .iter()
7242 .any(|d| d.message.contains("`any` is not supported")),
7243 "expected an `any` rejection for {src:?}, got: {diags:?}",
7244 );
7245 assert!(
7246 diags
7247 .iter()
7248 .any(|d| d.help.iter().any(|h| h.contains("`unknown`"))),
7249 "expected the fix-shape help for {src:?}, got: {diags:?}",
7250 );
7251 }
7252 }
7253
7254 #[test]
7255 fn any_catch_binding_help_suggests_catch_forms() {
7256 let (_ast, diags) = parse_str("function main(): void { try { } catch (e: any) { } }");
7259 let any_diags: Vec<_> = diags
7260 .iter()
7261 .filter(|d| d.message.contains("`any` is not supported"))
7262 .collect();
7263 assert_eq!(any_diags.len(), 1, "got: {diags:?}");
7264 assert!(
7265 any_diags[0].help.iter().any(|h| h.contains("`catch (e)`")),
7266 "got: {diags:?}"
7267 );
7268 assert!(
7269 !any_diags[0].help.iter().any(|h| h.contains("`unknown`")),
7270 "got: {diags:?}"
7271 );
7272 }
7273
7274 #[test]
7275 fn arrow_return_type_scanner_matches_type_grammar() {
7276 for ty in [
7281 "number",
7282 "void",
7283 "null",
7284 "ns.Foo",
7285 "Map<string, number>",
7286 "ns.Foo<string>[]",
7287 "number[]",
7288 "[number, number]",
7289 "[number, [string, boolean]]",
7290 "[number, string][]",
7291 "{ a: number }",
7292 "{ a: number }[]",
7293 "\"on\" | \"off\"",
7294 "1 | 2",
7295 "Point | null",
7296 "| number | string",
7297 "(n: number) => number",
7298 "(n: number) => [number, number]",
7299 "readonly number[]",
7300 "readonly number[][]",
7301 "readonly [number, string]",
7302 "readonly number[] | null",
7303 "[first: number, second: string]",
7304 "[new: number, string]",
7305 "keyof { a: number }",
7306 ] {
7307 let annotation = format!("function f(): void {{ const a: {ty} = x; }}");
7308 let (_ast, diags) = parse_str(&annotation);
7309 assert!(
7310 diags.is_empty(),
7311 "annotation grammar rejected {ty:?}: {diags:?}",
7312 );
7313
7314 let arrow = format!("function f(): void {{ const g = (): {ty} => x; }}");
7315 let (_ast, diags) = parse_str(&arrow);
7316 assert!(
7317 diags.is_empty(),
7318 "arrow lookahead rejected return type {ty:?}: {diags:?}",
7319 );
7320 }
7321
7322 let (_ast, diags) =
7324 parse_str("function f(): void { const g = (v: unknown): v is string => x; }");
7325 assert!(
7326 diags.is_empty(),
7327 "arrow lookahead rejected predicate return: {diags:?}",
7328 );
7329 }
7330
7331 #[test]
7332 fn bare_semicolons_are_silent_empty_statements() {
7333 let (ast, diags) = parse_str(";");
7334 assert!(diags.is_empty());
7335 assert!(ast.top_level.is_empty());
7336 }
7337
7338 #[test]
7339 fn parse_single_non_expression_token_errors() {
7340 let (ast, diags) = parse_str("}");
7341 assert_eq!(diags.len(), 1);
7342 assert_eq!(diags[0].message, "expected expression");
7343 assert!(ast.top_level.is_empty());
7344 }
7345
7346 #[test]
7347 fn recovery_across_many_bad_starts() {
7348 let (ast, diags) = parse_str("} } } x;");
7349 assert_eq!(diags.len(), 3);
7350 for d in &diags {
7351 assert_eq!(d.message, "expected expression");
7352 }
7353 assert_eq!(ast.top_level.len(), 1);
7354 }
7355
7356 #[test]
7357 fn recovery_consumes_semicolon() {
7358 let mut p = parser_from_source("foo ;");
7359 p.error_at_peek("test");
7360 p.recover();
7361 assert!(matches!(
7362 p.peek().kind,
7363 TokenKind::Semicolon | TokenKind::Eof
7364 ));
7365 assert!(matches!(p.peek().kind, TokenKind::Eof));
7366 }
7367
7368 #[test]
7369 fn recovery_stops_at_right_brace() {
7370 let mut p = parser_from_source("foo }");
7371 p.error_at_peek("test");
7372 p.recover();
7373 assert!(matches!(p.peek().kind, TokenKind::RightBrace));
7374 }
7375
7376 #[test]
7377 fn recovery_stops_at_let_keyword() {
7378 let mut p = parser_from_source("foo let x");
7379 p.error_at_peek("test");
7380 p.recover();
7381 assert!(matches!(p.peek().kind, TokenKind::Let));
7382 }
7383
7384 #[test]
7385 fn recovery_stops_at_function_keyword() {
7386 let mut p = parser_from_source("foo function bar");
7387 p.error_at_peek("test");
7388 p.recover();
7389 assert!(matches!(p.peek().kind, TokenKind::Function));
7390 }
7391
7392 #[test]
7393 fn error_cap_at_20() {
7394 let source = "} ".repeat(25);
7395 let (_, diags) = parse_str(&source);
7396 assert_eq!(diags.len(), MAX_ERRORS);
7397 }
7398
7399 #[test]
7400 fn driver_makes_progress_past_unmatched_brace() {
7401 let (ast, diags) = parse_str("} x;");
7402 assert_eq!(diags.len(), 1);
7403 assert_eq!(diags[0].message, "expected expression");
7404 assert_eq!(ast.top_level.len(), 1);
7405 }
7406
7407 #[test]
7408 fn snapshot_multi_error_render() {
7409 let source = "foo bar;\nbaz qux;\nhello world;";
7410 let (_, diags) = parse_str(source);
7411 let (sources, _) = Sources::single("script.subm", source).unwrap();
7412 let rendered: String = diags
7413 .iter()
7414 .map(|d| diagnostics::render(d, &sources))
7415 .collect::<Vec<_>>()
7416 .join("\n");
7417 insta::assert_snapshot!(rendered);
7418 }
7419
7420 fn single_stmt(ast: &Ast) -> &crate::Stmt {
7421 assert_eq!(ast.top_level.len(), 1);
7422 ast.try_stmt(ast.top_level[0]).unwrap()
7423 }
7424
7425 fn expr_of_single_stmt(ast: &Ast) -> &crate::Expr {
7426 let stmt = single_stmt(ast);
7427 match stmt.kind {
7428 crate::StmtKind::Expr(id) => ast.try_expr(id).unwrap(),
7429 _ => panic!("expected expression statement, got {:?}", stmt.kind),
7430 }
7431 }
7432
7433 #[test]
7434 fn parse_number_literal() {
7435 let (ast, diags) = parse_str("42;");
7436 assert!(diags.is_empty());
7437 let expr = expr_of_single_stmt(&ast);
7438 assert_eq!(expr.kind, crate::ExprKind::Number(42.0));
7439 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
7440 }
7441
7442 #[test]
7443 fn parse_string_literal() {
7444 let (ast, diags) = parse_str(r#""s";"#);
7445 assert!(diags.is_empty());
7446 let expr = expr_of_single_stmt(&ast);
7447 assert_eq!(expr.kind, crate::ExprKind::String("s".to_string()));
7448 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7449 }
7450
7451 #[test]
7452 fn parse_boolean_true() {
7453 let (ast, diags) = parse_str("true;");
7454 assert!(diags.is_empty());
7455 let expr = expr_of_single_stmt(&ast);
7456 assert_eq!(expr.kind, crate::ExprKind::Boolean(true));
7457 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7458 }
7459
7460 #[test]
7461 fn parse_boolean_false() {
7462 let (ast, diags) = parse_str("false;");
7463 assert!(diags.is_empty());
7464 let expr = expr_of_single_stmt(&ast);
7465 assert_eq!(expr.kind, crate::ExprKind::Boolean(false));
7466 assert_eq!(expr.span, crate::Span::new(F, 0, 5).unwrap());
7467 }
7468
7469 #[test]
7470 fn parse_null_literal() {
7471 let (ast, diags) = parse_str("null;");
7472 assert!(diags.is_empty());
7473 let expr = expr_of_single_stmt(&ast);
7474 assert_eq!(expr.kind, crate::ExprKind::Null);
7475 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7476 }
7477
7478 #[test]
7479 fn parse_identifier() {
7480 let (ast, diags) = parse_str("x;");
7481 assert!(diags.is_empty());
7482 let expr = expr_of_single_stmt(&ast);
7483 assert!(matches!(expr.kind, crate::ExprKind::Identifier(_)));
7484 assert_eq!(expr.span, crate::Span::new(F, 0, 1).unwrap());
7485 }
7486
7487 #[test]
7488 fn reserved_keywords_remain_invalid_declaration_names() {
7489 let (_ast, diags) = parse_str("function default(): void {}");
7490 assert_eq!(diags.len(), 1, "{diags:#?}");
7491 assert_eq!(
7492 diags[0].message,
7493 "`default` is a reserved keyword and can't be used as a name"
7494 );
7495 assert!(
7496 diags[0]
7497 .help
7498 .iter()
7499 .any(|h| h == "rename it, for example: `defaultValue`"),
7500 "expected rename help, got: {diags:?}"
7501 );
7502 }
7503
7504 #[test]
7505 fn eval_and_arguments_cannot_name_bindings() {
7506 let bindings = [
7507 ("let arguments = 1;", "arguments"),
7508 ("const [eval] = [1];", "eval"),
7509 ("const { a: eval } = { a: 1 };", "eval"),
7510 ("const { eval } = { eval: 1 };", "eval"),
7511 ("const [...arguments] = [1];", "arguments"),
7512 ("function eval(): void {}", "eval"),
7513 ("function f(arguments: number): void {}", "arguments"),
7514 ("const f = arguments => arguments;", "arguments"),
7515 ("const f = (...eval: number[]) => 1;", "eval"),
7516 ("const f = function eval(): void {};", "eval"),
7517 ("for (const eval of [1]) {}", "eval"),
7518 ("try {} catch (arguments) {}", "arguments"),
7519 ("type F = (eval: number) => void;", "eval"),
7520 ("type G = (...arguments: number[]) => void;", "arguments"),
7521 ("type H = { [eval: string]: number };", "eval"),
7522 ];
7523 for (source, word) in bindings {
7524 let (_ast, diags) = parse_str(source);
7525 assert!(
7526 diags
7527 .iter()
7528 .any(|d| d.message == format!("`{word}` can't name a binding in strict mode")),
7529 "{source}: {diags:#?}"
7530 );
7531 }
7532 }
7533
7534 #[test]
7535 fn eval_and_arguments_still_name_types_and_properties() {
7536 for source in [
7537 "class eval {}",
7538 "interface arguments { eval: number }",
7539 "type eval = number;",
7540 "enum arguments { eval }",
7541 "const o = { eval: 1, arguments: 2 };",
7542 "import { eval } from \"./m\";",
7543 ] {
7544 let (_ast, diags) = parse_str(source);
7545 assert!(diags.is_empty(), "{source}: {diags:#?}");
7546 }
7547 }
7548
7549 #[test]
7550 fn reserved_keywords_remain_invalid_parameter_names() {
7551 let (_ast, diags) = parse_str("function f(default: string): void {}");
7552 assert_eq!(diags.len(), 1, "{diags:#?}");
7553 assert_eq!(
7554 diags[0].message,
7555 "`default` is a reserved keyword and can't be used as a name"
7556 );
7557 assert!(
7558 diags[0]
7559 .help
7560 .iter()
7561 .any(|h| h == "rename it, for example: `defaultValue`"),
7562 "expected rename help, got: {diags:?}"
7563 );
7564 }
7565
7566 #[test]
7567 fn contextual_keywords_are_valid_names() {
7568 for src in [
7569 "const type = \"x\";",
7570 "const from = 1;",
7571 "let of = 2;",
7572 "const as = 3;",
7573 "const is = 4;",
7574 "function f(from: string): void {}",
7575 ] {
7576 let (_ast, diags) = parse_str(src);
7577 assert!(
7578 diags.is_empty(),
7579 "unexpected diagnostics for {src:?}: {diags:#?}"
7580 );
7581 }
7582 }
7583
7584 #[test]
7585 fn type_alias_dispatch_table() {
7586 let (ast, diags) = parse_str("type X = number;");
7587 assert!(diags.is_empty(), "{diags:#?}");
7588 assert!(matches!(
7589 ast.try_stmt(ast.top_level[0]).unwrap().kind,
7590 crate::StmtKind::TypeAliasDecl { .. }
7591 ));
7592
7593 for src in ["type = 5;", "type;", "type(1);", "type < 3;", "type.foo;"] {
7595 let (ast, diags) = parse_str(src);
7596 assert!(
7597 diags.is_empty(),
7598 "unexpected diagnostics for {src:?}: {diags:#?}"
7599 );
7600 assert!(
7601 !matches!(
7602 ast.try_stmt(ast.top_level[0]).unwrap().kind,
7603 crate::StmtKind::TypeAliasDecl { .. }
7604 ),
7605 "{src:?} must not parse as a type alias"
7606 );
7607 }
7608
7609 let (_ast, diags) = parse_str("type X;");
7610 assert!(
7611 diags
7612 .iter()
7613 .any(|d| d.message == "expected `=` after type alias name"),
7614 "bare `type X` should commit to the alias diagnostic, got: {diags:#?}"
7615 );
7616 }
7617
7618 #[test]
7619 fn export_type_alias_dispatch() {
7620 let (ast, diags) = parse_str("export type X = number;");
7621 assert!(diags.is_empty(), "{diags:#?}");
7622 assert_eq!(ast.exported_decls.len(), 1);
7623 assert!(matches!(
7624 ast.try_stmt(ast.exported_decls[0].stmt).unwrap().kind,
7625 crate::StmtKind::TypeAliasDecl { .. }
7626 ));
7627
7628 let (_ast, diags) = parse_str("export type = 5;");
7629 assert!(
7630 diags
7631 .iter()
7632 .any(|d| d.message == "expected a declaration after `export`"),
7633 "`export type = 5` must not become an exported assignment, got: {diags:#?}"
7634 );
7635 }
7636
7637 #[test]
7638 fn parse_paren_simple() {
7639 let (ast, diags) = parse_str("(x);");
7640 assert!(diags.is_empty());
7641 let expr = expr_of_single_stmt(&ast);
7642 let crate::ExprKind::Paren(inner_id) = expr.kind else {
7643 panic!("expected Paren, got {:?}", expr.kind);
7644 };
7645 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7646 let inner = ast.try_expr(inner_id).unwrap();
7647 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7648 assert_eq!(inner.span, crate::Span::new(F, 1, 2).unwrap());
7649 }
7650
7651 #[test]
7652 fn parse_paren_nested() {
7653 let (ast, diags) = parse_str("((x));");
7654 assert!(diags.is_empty());
7655 let outer = expr_of_single_stmt(&ast);
7656 let crate::ExprKind::Paren(mid_id) = outer.kind else {
7657 panic!("expected outer Paren");
7658 };
7659 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7660 let mid = ast.try_expr(mid_id).unwrap();
7661 let crate::ExprKind::Paren(inner_id) = mid.kind else {
7662 panic!("expected inner Paren");
7663 };
7664 assert_eq!(mid.span, crate::Span::new(F, 1, 4).unwrap());
7665 let inner = ast.try_expr(inner_id).unwrap();
7666 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7667 assert_eq!(inner.span, crate::Span::new(F, 2, 3).unwrap());
7668 }
7669
7670 #[test]
7671 fn parse_sequence_of_atoms() {
7672 let (ast, diags) = parse_str(r#"42; "hi"; true; null; x; (x);"#);
7673 assert!(diags.is_empty());
7674 assert_eq!(ast.top_level.len(), 6);
7675 insta::assert_debug_snapshot!(ast);
7676 }
7677
7678 #[test]
7679 fn missing_semicolon_after_expression() {
7680 let (ast, diags) = parse_str("42 43");
7681 assert_eq!(diags.len(), 1);
7682 assert_eq!(diags[0].message, "expected `;` after expression");
7683 assert!(ast.top_level.is_empty());
7684 }
7685
7686 #[test]
7687 fn empty_paren_diagnoses() {
7688 let (ast, diags) = parse_str("();");
7689 assert_eq!(diags.len(), 1);
7690 assert_eq!(diags[0].message, "expected expression");
7691 assert!(ast.top_level.is_empty());
7692 }
7693
7694 #[test]
7695 fn unterminated_paren_diagnoses() {
7696 let (ast, diags) = parse_str("(x;");
7697 assert_eq!(diags.len(), 1);
7698 assert_eq!(diags[0].message, "expected `)`");
7699 assert!(ast.top_level.is_empty());
7700 }
7701
7702 fn binary(e: &crate::Expr) -> (crate::BinOp, crate::ExprId, crate::ExprId) {
7703 match e.kind {
7704 crate::ExprKind::Binary { op, lhs, rhs } => (op, lhs, rhs),
7705 _ => panic!("expected Binary, got {:?}", e.kind),
7706 }
7707 }
7708
7709 #[test]
7710 fn parse_binary_add() {
7711 let (ast, diags) = parse_str("a + b;");
7712 assert!(diags.is_empty());
7713 let outer = expr_of_single_stmt(&ast);
7714 let (op, lhs, rhs) = binary(outer);
7715 assert_eq!(op, crate::BinOp::Add);
7716 assert!(matches!(
7717 ast.try_expr(lhs).unwrap().kind,
7718 crate::ExprKind::Identifier(_)
7719 ));
7720 assert!(matches!(
7721 ast.try_expr(rhs).unwrap().kind,
7722 crate::ExprKind::Identifier(_)
7723 ));
7724 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7725 }
7726
7727 #[test]
7728 fn bitwise_precedence_and_shift_adjacency() {
7729 let (ast, diags) = parse_str("a | b ^ c & d == e >> f + g;");
7730 assert!(diags.is_empty(), "{diags:?}");
7731 let (op, _, mut rhs) = binary(expr_of_single_stmt(&ast));
7732 assert_eq!(op, crate::BinOp::BitOr);
7733 for expected in [
7734 crate::BinOp::BitXor,
7735 crate::BinOp::BitAnd,
7736 crate::BinOp::Eq,
7737 crate::BinOp::Shr,
7738 crate::BinOp::Add,
7739 ] {
7740 let (op, _, next) = binary(ast.try_expr(rhs).unwrap());
7741 assert_eq!(op, expected);
7742 rhs = next;
7743 }
7744 for source in ["a > > b;", "a >/*gap*/> b;", "a < < b;"] {
7745 let (_, diags) = parse_str(source);
7746 assert!(!diags.is_empty(), "{source}");
7747 }
7748 }
7749
7750 #[test]
7751 fn bitwise_shift_assignment_span() {
7752 let (ast, diags) = parse_str("a >>>= b;");
7753 assert!(diags.is_empty(), "{diags:?}");
7754 let crate::StmtKind::CompoundAssign { op, op_span, .. } = single_stmt(&ast).kind else {
7755 panic!("expected assignment");
7756 };
7757 assert_eq!(op, crate::BinOp::UnsignedShr);
7758 assert_eq!(op_span, crate::Span::new(F, 2, 6).unwrap());
7759 }
7760
7761 #[test]
7762 fn precedence_mul_binds_tighter_than_add() {
7763 let (ast, diags) = parse_str("a + b * c;");
7764 assert!(diags.is_empty());
7765 let outer = expr_of_single_stmt(&ast);
7766 let (op, lhs, rhs) = binary(outer);
7767 assert_eq!(op, crate::BinOp::Add);
7768 assert!(matches!(
7769 ast.try_expr(lhs).unwrap().kind,
7770 crate::ExprKind::Identifier(_)
7771 ));
7772 let (rop, rlhs, rrhs) = binary(ast.try_expr(rhs).unwrap());
7773 assert_eq!(rop, crate::BinOp::Mul);
7774 assert!(matches!(
7775 ast.try_expr(rlhs).unwrap().kind,
7776 crate::ExprKind::Identifier(_)
7777 ));
7778 assert!(matches!(
7779 ast.try_expr(rrhs).unwrap().kind,
7780 crate::ExprKind::Identifier(_)
7781 ));
7782 }
7783
7784 #[test]
7785 fn precedence_mul_left_of_add() {
7786 let (ast, diags) = parse_str("a * b + c;");
7787 assert!(diags.is_empty());
7788 let outer = expr_of_single_stmt(&ast);
7789 let (op, lhs, rhs) = binary(outer);
7790 assert_eq!(op, crate::BinOp::Add);
7791 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7792 assert_eq!(lop, crate::BinOp::Mul);
7793 assert!(matches!(
7794 ast.try_expr(rhs).unwrap().kind,
7795 crate::ExprKind::Identifier(_)
7796 ));
7797 }
7798
7799 #[test]
7800 fn left_associative_same_precedence() {
7801 let (ast, diags) = parse_str("a - b - c;");
7802 assert!(diags.is_empty());
7803 let outer = expr_of_single_stmt(&ast);
7804 let (op, lhs, rhs) = binary(outer);
7805 assert_eq!(op, crate::BinOp::Sub);
7806 let (lop, llhs, lrhs) = binary(ast.try_expr(lhs).unwrap());
7807 assert_eq!(lop, crate::BinOp::Sub);
7808 assert!(matches!(
7809 ast.try_expr(llhs).unwrap().kind,
7810 crate::ExprKind::Identifier(_)
7811 ));
7812 assert!(matches!(
7813 ast.try_expr(lrhs).unwrap().kind,
7814 crate::ExprKind::Identifier(_)
7815 ));
7816 assert!(matches!(
7817 ast.try_expr(rhs).unwrap().kind,
7818 crate::ExprKind::Identifier(_)
7819 ));
7820 }
7821
7822 #[test]
7823 fn equality_inside_logical() {
7824 let (ast, diags) = parse_str("a === b && c === d;");
7825 assert!(diags.is_empty());
7826 let outer = expr_of_single_stmt(&ast);
7827 let (op, lhs, rhs) = binary(outer);
7828 assert_eq!(op, crate::BinOp::And);
7829 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7830 assert_eq!(lop, crate::BinOp::Eq);
7831 let (rop, ..) = binary(ast.try_expr(rhs).unwrap());
7832 assert_eq!(rop, crate::BinOp::Eq);
7833 }
7834
7835 #[test]
7836 fn double_equal_is_same_variant_as_triple() {
7837 let (ast, _) = parse_str("a == b;");
7838 let outer = expr_of_single_stmt(&ast);
7839 let (op, ..) = binary(outer);
7840 assert_eq!(op, crate::BinOp::Eq);
7841 }
7842
7843 #[test]
7844 fn bang_eq_is_not_eq() {
7845 let (ast, _) = parse_str("a != b;");
7846 let outer = expr_of_single_stmt(&ast);
7847 let (op, ..) = binary(outer);
7848 assert_eq!(op, crate::BinOp::NotEq);
7849 }
7850
7851 #[test]
7852 fn comparison_has_higher_prec_than_logical() {
7853 let (ast, _) = parse_str("a < b && c;");
7854 let outer = expr_of_single_stmt(&ast);
7855 let (op, lhs, _) = binary(outer);
7856 assert_eq!(op, crate::BinOp::And);
7857 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7858 assert_eq!(lop, crate::BinOp::Lt);
7859 }
7860
7861 #[test]
7862 fn full_precedence_chain_snapshot() {
7863 let (ast, diags) = parse_str("a || b && c === d < e + f * g;");
7864 assert!(diags.is_empty());
7865 insta::assert_debug_snapshot!(ast);
7866 }
7867
7868 #[test]
7869 fn paren_overrides_precedence() {
7870 let (ast, _) = parse_str("(a + b) * c;");
7871 let outer = expr_of_single_stmt(&ast);
7872 let (op, lhs, rhs) = binary(outer);
7873 assert_eq!(op, crate::BinOp::Mul);
7874 let crate::ExprKind::Paren(inner_id) = ast.try_expr(lhs).unwrap().kind else {
7875 panic!("expected Paren on lhs");
7876 };
7877 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7878 assert_eq!(inner_op, crate::BinOp::Add);
7879 assert!(matches!(
7880 ast.try_expr(rhs).unwrap().kind,
7881 crate::ExprKind::Identifier(_)
7882 ));
7883 }
7884
7885 #[test]
7886 fn binary_missing_rhs_diagnoses() {
7887 let (ast, diags) = parse_str("a +;");
7888 assert_eq!(diags.len(), 1);
7889 assert_eq!(diags[0].message, "expected expression");
7890 assert!(ast.top_level.is_empty());
7891 }
7892
7893 fn unary(e: &crate::Expr) -> (crate::UnOp, crate::ExprId) {
7894 match e.kind {
7895 crate::ExprKind::Unary { op, operand } => (op, operand),
7896 _ => panic!("expected Unary, got {:?}", e.kind),
7897 }
7898 }
7899
7900 #[test]
7901 fn parse_unary_not() {
7902 let (ast, _) = parse_str("!x;");
7903 let outer = expr_of_single_stmt(&ast);
7904 let (op, operand) = unary(outer);
7905 assert_eq!(op, crate::UnOp::Not);
7906 assert!(matches!(
7907 ast.try_expr(operand).unwrap().kind,
7908 crate::ExprKind::Identifier(_)
7909 ));
7910 assert_eq!(outer.span, crate::Span::new(F, 0, 2).unwrap());
7911 }
7912
7913 #[test]
7914 fn parse_unary_neg() {
7915 let (ast, _) = parse_str("-x;");
7916 let (op, _) = unary(expr_of_single_stmt(&ast));
7917 assert_eq!(op, crate::UnOp::Neg);
7918 }
7919
7920 #[test]
7921 fn parse_unary_pos() {
7922 let (ast, _) = parse_str("+x;");
7923 let (op, _) = unary(expr_of_single_stmt(&ast));
7924 assert_eq!(op, crate::UnOp::Pos);
7925 }
7926
7927 #[test]
7928 fn repeated_unary_not() {
7929 let (ast, _) = parse_str("!!x;");
7930 let outer = expr_of_single_stmt(&ast);
7931 let (op, operand) = unary(outer);
7932 assert_eq!(op, crate::UnOp::Not);
7933 let (inner_op, inner_operand) = unary(ast.try_expr(operand).unwrap());
7934 assert_eq!(inner_op, crate::UnOp::Not);
7935 assert!(matches!(
7936 ast.try_expr(inner_operand).unwrap().kind,
7937 crate::ExprKind::Identifier(_)
7938 ));
7939 }
7940
7941 #[test]
7942 fn unary_binds_tighter_than_binary() {
7943 let (ast, _) = parse_str("-x + 1;");
7944 let outer = expr_of_single_stmt(&ast);
7945 let (op, lhs, rhs) = binary(outer);
7946 assert_eq!(op, crate::BinOp::Add);
7947 let (unop, _) = unary(ast.try_expr(lhs).unwrap());
7948 assert_eq!(unop, crate::UnOp::Neg);
7949 assert_eq!(
7950 ast.try_expr(rhs).unwrap().kind,
7951 crate::ExprKind::Number(1.0)
7952 );
7953 }
7954
7955 #[test]
7956 fn unary_over_paren() {
7957 let (ast, _) = parse_str("-(x + 1);");
7958 let outer = expr_of_single_stmt(&ast);
7959 let (op, operand) = unary(outer);
7960 assert_eq!(op, crate::UnOp::Neg);
7961 let crate::ExprKind::Paren(inner_id) = ast.try_expr(operand).unwrap().kind else {
7962 panic!("expected Paren");
7963 };
7964 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7965 assert_eq!(inner_op, crate::BinOp::Add);
7966 }
7967
7968 #[test]
7969 fn parse_is_as_expression_rejected() {
7970 let (_, diags) = parse_str("x is number;");
7971 assert!(!diags.is_empty(), "expected parse error for `x is T`");
7972 }
7973
7974 fn as_cast(e: &crate::Expr) -> (crate::ExprId, &crate::TypeAnnotation) {
7975 match &e.kind {
7976 crate::ExprKind::As { expr, ty } => (*expr, ty),
7977 other => panic!("expected ExprKind::As, got {other:?}"),
7978 }
7979 }
7980
7981 #[test]
7982 fn parse_instanceof_class() {
7983 let (ast, diags) = parse_str("x instanceof Foo;");
7984 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7985 let outer = expr_of_single_stmt(&ast);
7986 let crate::ExprKind::InstanceOf { value, ty } = &outer.kind else {
7987 panic!("expected ExprKind::InstanceOf, got {:?}", outer.kind);
7988 };
7989 assert!(matches!(
7990 ast.try_expr(*value).unwrap().kind,
7991 crate::ExprKind::Identifier(_)
7992 ));
7993 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7994 }
7995
7996 #[test]
7997 fn parse_instanceof_binds_tighter_than_logical_and() {
7998 let (ast, diags) = parse_str("a instanceof B && c;");
8000 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8001 let outer = expr_of_single_stmt(&ast);
8002 let (op, lhs, rhs) = binary(outer);
8003 assert_eq!(op, crate::BinOp::And);
8004 assert!(matches!(
8005 ast.try_expr(lhs).unwrap().kind,
8006 crate::ExprKind::InstanceOf { .. }
8007 ));
8008 assert!(matches!(
8009 ast.try_expr(rhs).unwrap().kind,
8010 crate::ExprKind::Identifier(_)
8011 ));
8012 }
8013
8014 #[test]
8017 fn parse_angle_cast_lowers_to_the_as_node() {
8018 let (ast, diags) = parse_str("<number>x;");
8019 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8020 let outer = expr_of_single_stmt(&ast);
8021 let (inner, ty) = as_cast(outer);
8022 assert!(matches!(
8023 ast.try_expr(inner).unwrap().kind,
8024 crate::ExprKind::Identifier(_)
8025 ));
8026 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8027 }
8028
8029 #[test]
8033 fn a_less_than_comparison_is_not_read_as_a_cast() {
8034 let (ast, diags) = parse_str("a < b;");
8035 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8036 let e = expr_of_single_stmt(&ast);
8037 assert!(matches!(e.kind, crate::ExprKind::Binary { .. }));
8038 }
8039
8040 #[test]
8041 fn parse_typeof_in_type_position() {
8042 let (_ast, diags) = parse_str("let a: typeof b = 1;");
8043 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8044 }
8045
8046 #[test]
8047 fn parse_typeof_with_a_dotted_path() {
8048 let (_ast, diags) = parse_str("let a: typeof b.c.d = 1;");
8049 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8050 }
8051
8052 #[test]
8053 fn parse_as_primitive() {
8054 let (ast, diags) = parse_str("x as number;");
8055 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8056 let outer = expr_of_single_stmt(&ast);
8057 let (inner, ty) = as_cast(outer);
8058 assert!(matches!(
8059 ast.try_expr(inner).unwrap().kind,
8060 crate::ExprKind::Identifier(_)
8061 ));
8062 match &ty.kind {
8063 crate::TypeAnnotationKind::Name { args, .. } => {
8064 assert!(args.is_empty());
8065 }
8066 other => panic!("expected Name target, got {other:?}"),
8067 }
8068 assert_eq!(outer.span, crate::Span::new(F, 0, 11).unwrap());
8069 }
8070
8071 #[test]
8072 fn parse_as_object_literal_type() {
8073 let (ast, diags) = parse_str("x as { a: number };");
8074 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8075 let outer = expr_of_single_stmt(&ast);
8076 let (_, ty) = as_cast(outer);
8077 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Object { .. }));
8078 }
8079
8080 #[test]
8081 fn parse_as_array_short() {
8082 let (ast, diags) = parse_str("x as string[];");
8083 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8084 let outer = expr_of_single_stmt(&ast);
8085 let (_, ty) = as_cast(outer);
8086 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
8087 }
8088
8089 #[test]
8090 fn parse_as_array_generic() {
8091 let (ast, diags) = parse_str("x as T[];");
8092 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8093 let outer = expr_of_single_stmt(&ast);
8094 let (_, ty) = as_cast(outer);
8095 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
8096 }
8097
8098 #[test]
8099 fn parse_as_generic_instantiation() {
8100 let (ast, diags) = parse_str("x as Foo<Bar>;");
8101 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8102 let outer = expr_of_single_stmt(&ast);
8103 let (_, ty) = as_cast(outer);
8104 match &ty.kind {
8105 crate::TypeAnnotationKind::Name { args, .. } => {
8106 assert_eq!(args.len(), 1, "Foo<Bar> has one type arg");
8107 }
8108 other => panic!("expected Name with type args, got {other:?}"),
8109 }
8110 }
8111
8112 #[test]
8113 fn parse_as_precedence_higher_than_additive() {
8114 let (ast, diags) = parse_str("x as number + 1;");
8115 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8116 let outer = expr_of_single_stmt(&ast);
8117 let (op, lhs, rhs) = binary(outer);
8118 assert_eq!(op, crate::BinOp::Add);
8119 assert!(matches!(
8120 ast.try_expr(lhs).unwrap().kind,
8121 crate::ExprKind::As { .. }
8122 ));
8123 assert!(matches!(
8124 ast.try_expr(rhs).unwrap().kind,
8125 crate::ExprKind::Number(_)
8126 ));
8127 }
8128
8129 #[test]
8130 fn parse_as_precedence_lower_than_unary() {
8131 let (ast, diags) = parse_str("!x as boolean;");
8132 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8133 let outer = expr_of_single_stmt(&ast);
8134 let (inner, _) = as_cast(outer);
8135 assert!(matches!(
8136 ast.try_expr(inner).unwrap().kind,
8137 crate::ExprKind::Unary {
8138 op: crate::UnOp::Not,
8139 ..
8140 }
8141 ));
8142 }
8143
8144 #[test]
8145 fn parse_as_left_associative_chain() {
8146 let (ast, diags) = parse_str("x as A as B;");
8147 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8148 let outer = expr_of_single_stmt(&ast);
8149 let (inner, _outer_ty) = as_cast(outer);
8150 let (innermost, _inner_ty) = as_cast(ast.try_expr(inner).unwrap());
8151 assert!(matches!(
8152 ast.try_expr(innermost).unwrap().kind,
8153 crate::ExprKind::Identifier(_)
8154 ));
8155 }
8156
8157 #[test]
8158 fn parse_as_inside_call_arg() {
8159 let (ast, diags) = parse_str("f(x as T);");
8160 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8161 let outer = expr_of_single_stmt(&ast);
8162 match &outer.kind {
8163 crate::ExprKind::Call { args, .. } => {
8164 assert_eq!(args.len(), 1);
8165 assert!(matches!(
8166 ast.try_expr(args[0]).unwrap().kind,
8167 crate::ExprKind::As { .. }
8168 ));
8169 }
8170 other => panic!("expected Call, got {other:?}"),
8171 }
8172 }
8173
8174 #[test]
8175 fn parse_as_after_field_access() {
8176 let (ast, diags) = parse_str("obj.field as T;");
8177 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8178 let outer = expr_of_single_stmt(&ast);
8179 let (inner, _) = as_cast(outer);
8180 assert!(matches!(
8181 ast.try_expr(inner).unwrap().kind,
8182 crate::ExprKind::FieldAccess { .. }
8183 ));
8184 }
8185
8186 #[test]
8187 fn parse_type_guard_return_annotation_on_function() {
8188 let src = "function isCircle(s: Shape): s is Circle { return true; }";
8189 let (ast, diags) = parse_str(src);
8190 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8191 let stmt = single_stmt(&ast);
8192 match stmt.kind {
8193 crate::StmtKind::Function {
8194 ref return_type,
8195 ref type_predicate,
8196 ..
8197 } => {
8198 assert!(
8199 return_type.is_none(),
8200 "predicate form leaves return_type empty",
8201 );
8202 let tp = type_predicate.as_ref().expect("predicate parsed");
8203 assert_eq!(tp.param.name, "s");
8204 assert!(matches!(
8205 tp.asserted.kind,
8206 crate::TypeAnnotationKind::Name { .. }
8207 ));
8208 assert_eq!(tp.span, crate::Span::new(F, 29, 40).unwrap());
8209 }
8210 _ => panic!("expected Function"),
8211 }
8212 }
8213
8214 #[test]
8215 fn parse_type_guard_return_annotation_on_arrow() {
8216 let src = "const f = (s: Shape): s is Circle => true;";
8217 let (ast, diags) = parse_str(src);
8218 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8219 let value = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
8220 crate::StmtKind::Const { value, .. } => *value,
8221 _ => panic!("expected const"),
8222 };
8223 match &ast.try_expr(value).unwrap().kind {
8224 crate::ExprKind::Arrow {
8225 return_type,
8226 type_predicate,
8227 ..
8228 } => {
8229 assert!(return_type.is_none());
8230 let tp = type_predicate.as_ref().expect("predicate parsed");
8231 assert_eq!(tp.param.name, "s");
8232 }
8233 other => panic!("expected Arrow, got {other:?}"),
8234 }
8235 }
8236
8237 #[test]
8238 fn parse_plain_return_type_unaffected_by_predicate_parser() {
8239 let src = "function f(s: Shape): boolean { return true; }";
8240 let (ast, diags) = parse_str(src);
8241 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8242 let stmt = single_stmt(&ast);
8243 match stmt.kind {
8244 crate::StmtKind::Function {
8245 ref return_type,
8246 ref type_predicate,
8247 ..
8248 } => {
8249 assert!(return_type.is_some(), "plain form has return_type");
8250 assert!(type_predicate.is_none(), "plain form has no type_predicate",);
8251 }
8252 _ => panic!("expected Function"),
8253 }
8254 }
8255
8256 #[test]
8257 fn parse_type_guard_rejects_non_return_position() {
8258 let (_, diags) = parse_str("let x: y is Foo = null;");
8259 assert!(
8260 !diags.is_empty(),
8261 "expected parse error for `is` outside return-type position",
8262 );
8263 }
8264
8265 #[test]
8266 fn parse_typeof_prefix() {
8267 let (ast, diags) = parse_str("typeof x;");
8268 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8269 let outer = expr_of_single_stmt(&ast);
8270 let crate::ExprKind::Typeof { operand } = outer.kind else {
8271 panic!("expected Typeof, got {:?}", outer.kind);
8272 };
8273 assert!(matches!(
8274 ast.try_expr(operand).unwrap().kind,
8275 crate::ExprKind::Identifier(_)
8276 ));
8277 assert_eq!(outer.span, crate::Span::new(F, 0, 8).unwrap());
8278 }
8279
8280 #[test]
8281 fn parse_typeof_in_equality() {
8282 let (ast, diags) = parse_str(r#"typeof x === "number";"#);
8283 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8284 let outer = expr_of_single_stmt(&ast);
8285 let (op, lhs, rhs) = binary(outer);
8286 assert_eq!(op, crate::BinOp::Eq);
8287 assert!(matches!(
8288 ast.try_expr(lhs).unwrap().kind,
8289 crate::ExprKind::Typeof { .. }
8290 ));
8291 assert!(matches!(
8292 ast.try_expr(rhs).unwrap().kind,
8293 crate::ExprKind::String(ref s) if s == "number"
8294 ));
8295 }
8296
8297 #[test]
8298 fn parse_let_without_type() {
8299 let (ast, diags) = parse_str("let x = 1;");
8300 assert!(diags.is_empty());
8301 let stmt = single_stmt(&ast);
8302 assert_eq!(stmt.span, crate::Span::new(F, 0, 10).unwrap());
8303 match stmt.kind {
8304 crate::StmtKind::Let {
8305 ref name,
8306 ref ty,
8307 value,
8308 ..
8309 } => {
8310 assert_eq!(name.name, "x");
8311 assert_eq!(name.span, crate::Span::new(F, 4, 5).unwrap());
8312 assert!(ty.is_none());
8313 assert_eq!(
8314 ast.try_expr(value).unwrap().kind,
8315 crate::ExprKind::Number(1.0)
8316 );
8317 }
8318 _ => panic!("expected Let"),
8319 }
8320 }
8321
8322 #[test]
8323 fn parse_const_with_type_and_string() {
8324 let (ast, diags) = parse_str(r#"const y: string = "hi";"#);
8325 assert!(diags.is_empty());
8326 let stmt = single_stmt(&ast);
8327 match stmt.kind {
8328 crate::StmtKind::Const {
8329 ref name,
8330 ref ty,
8331 value,
8332 ..
8333 } => {
8334 assert_eq!(name.name, "y");
8335 assert_eq!(name.span, crate::Span::new(F, 6, 7).unwrap());
8336 let ty = ty.as_ref().expect("expected type annotation");
8337 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8338 assert_eq!(ty.span, crate::Span::new(F, 9, 15).unwrap());
8339 assert_eq!(
8340 ast.try_expr(value).unwrap().kind,
8341 crate::ExprKind::String("hi".to_string())
8342 );
8343 }
8344 _ => panic!("expected Const"),
8345 }
8346 }
8347
8348 #[test]
8349 fn parse_let_with_type_and_binary_initializer() {
8350 let (ast, diags) = parse_str("let z: number = 1 + 2;");
8351 assert!(diags.is_empty());
8352 let stmt = single_stmt(&ast);
8353 match stmt.kind {
8354 crate::StmtKind::Let { ref ty, value, .. } => {
8355 assert!(ty.is_some());
8356 let (op, ..) = binary(ast.try_expr(value).unwrap());
8357 assert_eq!(op, crate::BinOp::Add);
8358 }
8359 _ => panic!("expected Let"),
8360 }
8361 }
8362
8363 #[test]
8364 fn void_type_annotation_is_accepted_syntactically() {
8365 let (ast, diags) = parse_str("let v: void = null;");
8366 assert!(diags.is_empty());
8367 let stmt = single_stmt(&ast);
8368 match stmt.kind {
8369 crate::StmtKind::Let { ref ty, .. } => {
8370 let ty = ty.as_ref().unwrap();
8371 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8372 assert_eq!(ty.span, crate::Span::new(F, 7, 11).unwrap());
8373 }
8374 _ => panic!("expected Let"),
8375 }
8376 }
8377
8378 #[test]
8379 fn const_missing_initializer_diagnoses() {
8380 let (ast, diags) = parse_str("const x;");
8381 assert_eq!(diags.len(), 1);
8382 assert_eq!(
8383 diags[0].message,
8384 "`const` declaration requires an initializer"
8385 );
8386 assert!(ast.top_level.is_empty());
8387 }
8388
8389 #[test]
8390 fn parse_const_object_shorthand_pattern() {
8391 let (ast, diags) = parse_str("const { a, b } = obj;");
8392 assert!(diags.is_empty(), "unexpected: {diags:?}");
8393 let stmt = single_stmt(&ast);
8394 match stmt.kind {
8395 crate::StmtKind::ConstPattern {
8396 binding:
8397 crate::Binding::Object {
8398 ref fields,
8399 ref rest,
8400 ..
8401 },
8402 ..
8403 } => {
8404 assert_eq!(fields.len(), 2);
8405 assert_eq!(fields[0].source.name, "a");
8406 assert_eq!(fields[0].local.name, "a");
8407 assert_eq!(fields[1].source.name, "b");
8408 assert_eq!(fields[1].local.name, "b");
8409 assert!(rest.is_none());
8410 }
8411 _ => panic!("expected ConstPattern Object, got {:?}", stmt.kind),
8412 }
8413 }
8414
8415 #[test]
8416 fn parse_const_object_pattern_keyword_source_key() {
8417 let (ast, diags) = parse_str("const { type: statusType, default: fallback } = obj;");
8418 assert!(diags.is_empty(), "unexpected: {diags:?}");
8419 let stmt = single_stmt(&ast);
8420 let crate::StmtKind::ConstPattern {
8421 binding: crate::Binding::Object { ref fields, .. },
8422 ..
8423 } = stmt.kind
8424 else {
8425 panic!("expected object const pattern");
8426 };
8427
8428 let names: Vec<(&str, &str)> = fields
8429 .iter()
8430 .map(|f| (f.source.name.as_str(), f.local.name.as_str()))
8431 .collect();
8432 assert_eq!(names, vec![("type", "statusType"), ("default", "fallback")]);
8433 }
8434
8435 #[test]
8436 fn parse_const_object_pattern_keyword_shorthand_rejected() {
8437 let (_ast, diags) = parse_str("const { default } = obj;");
8438 assert!(
8439 diags
8440 .iter()
8441 .any(|d| d.message == "expected `:` after keyword field name in object pattern"),
8442 "expected keyword-shorthand diagnostic, got: {diags:?}"
8443 );
8444 }
8445
8446 #[test]
8447 fn parse_const_object_renamed_pattern() {
8448 let (ast, diags) = parse_str("const { a: x, b: y } = obj;");
8449 assert!(diags.is_empty(), "unexpected: {diags:?}");
8450 match single_stmt(&ast).kind {
8451 crate::StmtKind::ConstPattern {
8452 binding: crate::Binding::Object { ref fields, .. },
8453 ..
8454 } => {
8455 assert_eq!(fields[0].source.name, "a");
8456 assert_eq!(fields[0].local.name, "x");
8457 assert_eq!(fields[1].source.name, "b");
8458 assert_eq!(fields[1].local.name, "y");
8459 }
8460 ref other => panic!("expected ConstPattern Object, got {other:?}"),
8461 }
8462 }
8463
8464 #[test]
8465 fn parse_const_object_with_rest() {
8466 let (ast, diags) = parse_str("const { a, ...rest } = obj;");
8467 assert!(diags.is_empty(), "unexpected: {diags:?}");
8468 match single_stmt(&ast).kind {
8469 crate::StmtKind::ConstPattern {
8470 binding:
8471 crate::Binding::Object {
8472 ref fields,
8473 ref rest,
8474 ..
8475 },
8476 ..
8477 } => {
8478 assert_eq!(fields.len(), 1);
8479 assert_eq!(fields[0].source.name, "a");
8480 let r = rest.as_ref().expect("expected rest");
8481 assert_eq!(r.name, "rest");
8482 }
8483 ref other => panic!("expected ConstPattern Object, got {other:?}"),
8484 }
8485 }
8486
8487 #[test]
8488 fn parse_const_array_pattern() {
8489 let (ast, diags) = parse_str("const [x, y] = arr;");
8490 assert!(diags.is_empty(), "unexpected: {diags:?}");
8491 match single_stmt(&ast).kind {
8492 crate::StmtKind::ConstPattern {
8493 binding:
8494 crate::Binding::Array {
8495 ref elems,
8496 ref rest,
8497 ..
8498 },
8499 ..
8500 } => {
8501 assert_eq!(elems.len(), 2);
8502 assert_eq!(elems[0].as_ref().unwrap().name, "x");
8503 assert_eq!(elems[1].as_ref().unwrap().name, "y");
8504 assert!(rest.is_none());
8505 }
8506 ref other => panic!("expected ConstPattern Array, got {other:?}"),
8507 }
8508 }
8509
8510 #[test]
8511 fn parse_const_array_with_hole_and_rest() {
8512 let (ast, diags) = parse_str("const [, x, ...rest] = arr;");
8513 assert!(diags.is_empty(), "unexpected: {diags:?}");
8514 match single_stmt(&ast).kind {
8515 crate::StmtKind::ConstPattern {
8516 binding:
8517 crate::Binding::Array {
8518 ref elems,
8519 ref rest,
8520 ..
8521 },
8522 ..
8523 } => {
8524 assert_eq!(elems.len(), 2);
8525 assert!(elems[0].is_none(), "first slot should be a hole");
8526 assert_eq!(elems[1].as_ref().unwrap().name, "x");
8527 assert_eq!(rest.as_ref().unwrap().name, "rest");
8528 }
8529 ref other => panic!("expected ConstPattern Array, got {other:?}"),
8530 }
8531 }
8532
8533 #[test]
8534 fn parse_let_object_pattern() {
8535 let (ast, diags) = parse_str("let { a, b } = obj;");
8536 assert!(diags.is_empty(), "unexpected: {diags:?}");
8537 assert!(matches!(
8538 single_stmt(&ast).kind,
8539 crate::StmtKind::LetPattern {
8540 binding: crate::Binding::Object { .. },
8541 ..
8542 }
8543 ));
8544 }
8545
8546 #[test]
8547 fn parse_function_param_object_pattern() {
8548 let src = "function f({ a, b }: T): void { return; }";
8549 let (ast, diags) = parse_str(src);
8550 assert!(diags.is_empty(), "unexpected: {diags:?}");
8551 match single_stmt(&ast).kind {
8552 crate::StmtKind::Function { ref params, .. } => {
8553 assert_eq!(params.len(), 1);
8554 let p = ¶ms[0];
8555 assert!(p.pattern.is_some(), "expected pattern param");
8556 assert_eq!(p.name.name, "", "pattern placeholder uses empty name");
8557 match p.pattern.as_ref().unwrap() {
8558 crate::Binding::Object { fields, .. } => {
8559 assert_eq!(fields.len(), 2);
8560 assert_eq!(fields[0].source.name, "a");
8561 }
8562 other => panic!("expected Object binding, got {other:?}"),
8563 }
8564 }
8565 ref other => panic!("expected Function, got {other:?}"),
8566 }
8567 }
8568
8569 #[test]
8570 fn parse_arrow_param_array_pattern() {
8571 let src = "const g = ([x, y]: number[]): number => x + y;";
8572 let (_ast, diags) = parse_str(src);
8573 assert!(diags.is_empty(), "unexpected: {diags:?}");
8574 }
8575
8576 #[test]
8577 fn parse_destructure_nested_rejected() {
8578 let (_ast, diags) = parse_str("const { a: { b } } = obj;");
8579 assert!(
8580 diags
8581 .iter()
8582 .any(|d| d.message.contains("nested destructuring")),
8583 "expected nested-destructuring diagnostic, got: {diags:?}",
8584 );
8585 }
8586
8587 #[test]
8588 fn parse_destructure_array_nested_rejected() {
8589 let (_ast, diags) = parse_str("const [[a]] = arr;");
8590 assert!(
8591 diags
8592 .iter()
8593 .any(|d| d.message.contains("nested destructuring")),
8594 "expected nested-destructuring diagnostic, got: {diags:?}",
8595 );
8596 }
8597
8598 #[test]
8599 fn parse_destructure_pattern_defaults() {
8600 let (ast, diags) = parse_str("const { a = 1, b: c = 2 } = obj;");
8601 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8602 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
8603 panic!("expected pattern")
8604 };
8605 let crate::Binding::Object { fields, .. } = binding else {
8606 panic!("expected object")
8607 };
8608 assert_eq!(fields.len(), 2);
8609 assert!(fields.iter().all(|field| field.default.is_some()));
8610 assert_eq!(fields[1].local.name, "c");
8611 }
8612
8613 #[test]
8614 fn parse_destructure_param_default() {
8615 let (ast, diags) = parse_str("function f({ a = 1 }: T = obj): void { return; }");
8616 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8617 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
8618 panic!("expected function")
8619 };
8620 assert!(params[0].default.is_some());
8621 assert!(params[0].pattern.is_some());
8622 }
8623
8624 #[test]
8625 fn parse_destructure_rest_not_last_rejected() {
8626 let (_ast, diags) = parse_str("const { ...rest, a } = obj;");
8627 assert!(
8628 diags
8629 .iter()
8630 .any(|d| d.message.contains("rest element must be the last element")),
8631 "expected rest-not-last diagnostic, got: {diags:?}",
8632 );
8633 }
8634
8635 #[test]
8636 fn parse_destructure_empty_object_rejected() {
8637 let (_ast, diags) = parse_str("const { } = obj;");
8638 assert!(
8639 diags
8640 .iter()
8641 .any(|d| d.message.contains("empty object destructuring pattern")),
8642 "expected empty-pattern diagnostic, got: {diags:?}",
8643 );
8644 }
8645
8646 #[test]
8647 fn parse_ternary_simple() {
8648 let (ast, diags) = parse_str("const x: number = a ? 1 : 2;");
8649 assert!(diags.is_empty(), "unexpected: {diags:?}");
8650 let stmt = single_stmt(&ast);
8651 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8652 panic!("expected Const, got {:?}", stmt.kind);
8653 };
8654 assert!(matches!(
8655 ast.try_expr(value).unwrap().kind,
8656 crate::ExprKind::Ternary { .. }
8657 ));
8658 }
8659
8660 #[test]
8661 fn parse_ternary_nested_right_assoc() {
8662 let (ast, diags) = parse_str("const x: number = a ? 1 : b ? 2 : 3;");
8663 assert!(diags.is_empty(), "unexpected: {diags:?}");
8664 let stmt = single_stmt(&ast);
8665 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8666 panic!("expected Const");
8667 };
8668 let crate::ExprKind::Ternary { else_, .. } = ast.try_expr(value).unwrap().kind else {
8669 panic!("expected outer Ternary");
8670 };
8671 assert!(matches!(
8672 ast.try_expr(else_).unwrap().kind,
8673 crate::ExprKind::Ternary { .. }
8674 ));
8675 }
8676
8677 #[test]
8678 fn parse_ternary_missing_colon_diagnoses() {
8679 let (_, diags) = parse_str("const x: number = a ? 1;");
8680 assert!(
8681 diags
8682 .iter()
8683 .any(|d| d.message.contains("expected `:` to complete ternary")),
8684 "expected colon diagnostic, got: {diags:?}",
8685 );
8686 }
8687
8688 #[test]
8689 fn parse_nullish_coalesce_binop() {
8690 let (ast, diags) = parse_str("const x: number = a ?? 1;");
8691 assert!(diags.is_empty(), "unexpected: {diags:?}");
8692 let stmt = single_stmt(&ast);
8693 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8694 panic!("expected Const");
8695 };
8696 match ast.try_expr(value).unwrap().kind {
8697 crate::ExprKind::Binary { op, .. } => {
8698 assert_eq!(op, crate::BinOp::NullishCoalesce);
8699 }
8700 ref other => panic!("expected Binary NullishCoalesce, got {other:?}"),
8701 }
8702 }
8703
8704 #[test]
8705 fn parse_nullish_left_associative() {
8706 let (ast, diags) = parse_str("const x: number = a ?? b ?? c;");
8707 assert!(diags.is_empty(), "unexpected: {diags:?}");
8708 let stmt = single_stmt(&ast);
8709 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8710 panic!("expected Const");
8711 };
8712 let crate::ExprKind::Binary { lhs, .. } = ast.try_expr(value).unwrap().kind else {
8713 panic!("expected outer Binary");
8714 };
8715 match ast.try_expr(lhs).unwrap().kind {
8716 crate::ExprKind::Binary { op, .. } => {
8717 assert_eq!(op, crate::BinOp::NullishCoalesce);
8718 }
8719 ref other => panic!("expected inner Binary, got {other:?}"),
8720 }
8721 }
8722
8723 #[test]
8724 fn parse_pow_is_right_associative() {
8725 let (ast, diags) = parse_str("const x: number = 2 ** 3 ** 2;");
8726 assert!(diags.is_empty(), "unexpected: {diags:?}");
8727 let stmt = single_stmt(&ast);
8728 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8729 panic!("expected Const");
8730 };
8731 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8732 panic!("expected outer Binary");
8733 };
8734 assert_eq!(op, crate::BinOp::Pow);
8735 match ast.try_expr(rhs).unwrap().kind {
8736 crate::ExprKind::Binary { op: inner_op, .. } => {
8737 assert_eq!(inner_op, crate::BinOp::Pow);
8738 }
8739 ref other => panic!("expected inner Pow Binary on RHS, got {other:?}"),
8740 }
8741 }
8742
8743 #[test]
8744 fn parse_pow_higher_than_multiply() {
8745 let (ast, diags) = parse_str("const x: number = 2 * 3 ** 2;");
8746 assert!(diags.is_empty(), "unexpected: {diags:?}");
8747 let stmt = single_stmt(&ast);
8748 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8749 panic!("expected Const");
8750 };
8751 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8752 panic!("expected outer Binary");
8753 };
8754 assert_eq!(op, crate::BinOp::Mul);
8755 match ast.try_expr(rhs).unwrap().kind {
8756 crate::ExprKind::Binary { op: inner_op, .. } => {
8757 assert_eq!(inner_op, crate::BinOp::Pow);
8758 }
8759 ref other => panic!("expected Pow on the RHS of *, got {other:?}"),
8760 }
8761 }
8762
8763 #[test]
8764 fn parse_compound_assign_ident() {
8765 let (ast, diags) = parse_str("function main(): void { let x: number = 0; x += 5; }");
8766 assert!(diags.is_empty(), "unexpected: {diags:?}");
8767 let func = ast.top_level.iter().find_map(|sid| {
8768 if let crate::StmtKind::Function { body, .. } = &ast.try_stmt(*sid).unwrap().kind {
8769 Some(*body)
8770 } else {
8771 None
8772 }
8773 });
8774 let crate::StmtKind::Block(stmts) = &ast.try_stmt(func.expect("main")).unwrap().kind else {
8775 panic!("expected function body to be a block");
8776 };
8777 let target = stmts
8778 .iter()
8779 .find_map(|sid| match &ast.try_stmt(*sid).unwrap().kind {
8780 crate::StmtKind::CompoundAssign { target, op, .. } => {
8781 Some((target.name.clone(), *op))
8782 }
8783 _ => None,
8784 });
8785 let (name, op) = target.expect("expected a CompoundAssign in the body");
8786 assert_eq!(name, "x");
8787 assert_eq!(op, crate::BinOp::Add);
8788 }
8789
8790 #[test]
8791 fn parse_nullish_mixed_with_or_rejected() {
8792 let (_, diags) = parse_str("const x: number = a || b ?? c;");
8793 assert!(
8794 diags
8795 .iter()
8796 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8797 "expected mixing diagnostic, got: {diags:?}",
8798 );
8799 }
8800
8801 #[test]
8802 fn parse_nullish_mixed_with_and_rejected() {
8803 let (_, diags) = parse_str("const x: number = a && b ?? c;");
8804 assert!(
8805 diags
8806 .iter()
8807 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8808 "expected mixing diagnostic, got: {diags:?}",
8809 );
8810 }
8811
8812 #[test]
8813 fn parse_nullish_followed_by_logical_rejected() {
8814 for (source, help) in [
8815 (
8816 "const x: number = a ?? b || c;",
8817 "`(a ?? b) || c` or `a ?? (b || c)`",
8818 ),
8819 (
8820 "const x: number = a ?? b && c;",
8821 "`(a ?? b) && c` or `a ?? (b && c)`",
8822 ),
8823 (
8824 "const x: number = a ?? b ?? c || d;",
8825 "`(a ?? b) || c` or `a ?? (b || c)`",
8826 ),
8827 (
8829 "const x: number = a || b ?? c || d;",
8830 "`(a || b) ?? c` or `a || (b ?? c)`",
8831 ),
8832 ] {
8833 let (_, diags) = parse_str(source);
8834 assert_eq!(diags.len(), 1, "diags for {source:?}: {diags:?}");
8835 assert!(
8836 diags[0].message.contains("mixing `??` with `||` / `&&`"),
8837 "message for {source:?}: {diags:?}",
8838 );
8839 assert!(
8840 diags[0].help.iter().any(|h| h.contains(help)),
8841 "help for {source:?}: {diags:?}",
8842 );
8843 }
8844 }
8845
8846 #[test]
8847 fn parse_nullish_operand_keeps_tighter_operators() {
8848 let (ast, diags) = parse_str("const x: boolean = a ?? b == c;");
8850 assert!(diags.is_empty(), "unexpected: {diags:?}");
8851 let stmt = single_stmt(&ast);
8852 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8853 panic!("expected Const");
8854 };
8855 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8856 panic!("expected outer Binary");
8857 };
8858 assert_eq!(op, crate::BinOp::NullishCoalesce);
8859 assert!(matches!(
8860 ast.try_expr(rhs).unwrap().kind,
8861 crate::ExprKind::Binary {
8862 op: crate::BinOp::Eq,
8863 ..
8864 }
8865 ));
8866 }
8867
8868 #[test]
8869 fn parse_nullish_with_parens_ok() {
8870 let (_, diags) = parse_str("const x: number = (a || b) ?? c;");
8871 assert!(diags.is_empty(), "unexpected: {diags:?}");
8872 }
8873
8874 #[test]
8875 fn parse_optional_field_chain() {
8876 let (ast, diags) = parse_str("const x: number = a?.b;");
8877 assert!(diags.is_empty(), "unexpected: {diags:?}");
8878 let stmt = single_stmt(&ast);
8879 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8880 panic!("expected Const");
8881 };
8882 match ast.try_expr(value).unwrap().kind {
8883 crate::ExprKind::OptionalChain { ref parts, .. } => {
8884 assert_eq!(parts.len(), 1);
8885 match &parts[0] {
8886 crate::ChainPart::Field { name, optional, .. } => {
8887 assert_eq!(name.name, "b");
8888 assert!(*optional);
8889 }
8890 other => panic!("expected Field, got {other:?}"),
8891 }
8892 }
8893 ref other => panic!("expected OptionalChain, got {other:?}"),
8894 }
8895 }
8896
8897 #[test]
8898 fn parse_optional_chain_continues_after_first_dot() {
8899 let (ast, diags) = parse_str("const x: number = a?.b.c;");
8900 assert!(diags.is_empty(), "unexpected: {diags:?}");
8901 let stmt = single_stmt(&ast);
8902 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8903 panic!("expected Const");
8904 };
8905 match ast.try_expr(value).unwrap().kind {
8906 crate::ExprKind::OptionalChain { ref parts, .. } => {
8907 assert_eq!(parts.len(), 2);
8908 if let crate::ChainPart::Field { optional, .. } = &parts[0] {
8909 assert!(*optional);
8910 }
8911 if let crate::ChainPart::Field { optional, .. } = &parts[1] {
8912 assert!(!*optional);
8913 }
8914 }
8915 ref other => panic!("expected OptionalChain, got {other:?}"),
8916 }
8917 }
8918
8919 #[test]
8920 fn parse_optional_chain_multiple_short_circuits() {
8921 let (ast, diags) = parse_str("const x: number = a?.b?.c;");
8922 assert!(diags.is_empty(), "unexpected: {diags:?}");
8923 let stmt = single_stmt(&ast);
8924 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8925 panic!("expected Const");
8926 };
8927 let crate::ExprKind::OptionalChain { parts, .. } = &ast.try_expr(value).unwrap().kind
8928 else {
8929 panic!("expected OptionalChain");
8930 };
8931 assert_eq!(parts.len(), 2);
8932 for p in parts {
8933 if let crate::ChainPart::Field { optional, .. } = p {
8934 assert!(*optional, "both parts should be optional");
8935 }
8936 }
8937 }
8938
8939 #[test]
8940 fn parse_optional_call_and_index() {
8941 let (_, diags) = parse_str("const x: number = f?.();");
8942 assert!(diags.is_empty(), "unexpected: {diags:?}");
8943 let (_, diags) = parse_str("const y: number = arr?.[0];");
8944 assert!(diags.is_empty(), "unexpected: {diags:?}");
8945 }
8946
8947 #[test]
8948 fn parse_optional_chain_terminates_at_questionmark_for_ternary() {
8949 let (ast, diags) = parse_str("const x: number = a?.b ? c : d;");
8950 assert!(diags.is_empty(), "unexpected: {diags:?}");
8951 let stmt = single_stmt(&ast);
8952 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8953 panic!("expected Const");
8954 };
8955 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8956 panic!(
8957 "expected top-level Ternary, got {:?}",
8958 ast.try_expr(value).unwrap().kind
8959 );
8960 };
8961 assert!(matches!(
8962 ast.try_expr(cond).unwrap().kind,
8963 crate::ExprKind::OptionalChain { .. }
8964 ));
8965 }
8966
8967 #[test]
8968 fn parse_nullish_below_ternary() {
8969 let (ast, diags) = parse_str("const x: number = a ?? b ? c : d;");
8970 assert!(diags.is_empty(), "unexpected: {diags:?}");
8971 let stmt = single_stmt(&ast);
8972 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8973 panic!("expected Const");
8974 };
8975 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8976 panic!("expected top-level Ternary");
8977 };
8978 match ast.try_expr(cond).unwrap().kind {
8979 crate::ExprKind::Binary { op, .. } => {
8980 assert_eq!(op, crate::BinOp::NullishCoalesce);
8981 }
8982 ref other => panic!("expected Binary ??, got {other:?}"),
8983 }
8984 }
8985
8986 #[test]
8987 fn let_missing_initializer_diagnoses() {
8988 let (ast, diags) = parse_str("let x;");
8989 assert_eq!(diags.len(), 1);
8990 assert_eq!(
8991 diags[0].message,
8992 "`let` declaration requires an initializer"
8993 );
8994 assert!(ast.top_level.is_empty());
8995 }
8996
8997 #[test]
8998 fn missing_identifier_after_let_diagnoses() {
8999 let (ast, diags) = parse_str("let = 1;");
9000 assert_eq!(diags.len(), 1);
9001 assert_eq!(diags[0].message, "expected identifier after `let`/`const`");
9002 assert!(ast.top_level.is_empty());
9003 }
9004
9005 #[test]
9006 fn missing_type_after_colon_diagnoses() {
9007 let (ast, diags) = parse_str("let x: = 1;");
9008 assert_eq!(diags.len(), 1);
9009 assert_eq!(diags[0].message, "expected type");
9010 assert!(ast.top_level.is_empty());
9011 }
9012
9013 #[test]
9014 fn missing_semicolon_after_declaration_diagnoses() {
9015 let (ast, diags) = parse_str("let x = 1 y;");
9016 assert_eq!(diags.len(), 1);
9017 assert_eq!(diags[0].message, "expected `;` after declaration");
9018 assert!(ast.top_level.is_empty());
9019 }
9020
9021 #[test]
9022 fn expression_statements_still_parse() {
9023 let (ast, diags) = parse_str("42;");
9024 assert!(diags.is_empty());
9025 assert_eq!(ast.top_level.len(), 1);
9026 }
9027
9028 #[test]
9029 fn snapshot_mixed_declarations() {
9030 let (ast, diags) =
9031 parse_str(r#"let x = 1; const y: string = "hi"; let z: number = 1 + 2;"#);
9032 assert!(diags.is_empty());
9033 assert_eq!(ast.top_level.len(), 3);
9034 insta::assert_debug_snapshot!(ast);
9035 }
9036
9037 #[test]
9038 fn parse_function_typical() {
9039 let (ast, diags) = parse_str("function f(a: number, b: string): boolean { }");
9040 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9041 let stmt = single_stmt(&ast);
9042 match stmt.kind {
9043 crate::StmtKind::Function {
9044 ref name,
9045 ref generics,
9046 ref params,
9047 ref return_type,
9048 body,
9049 ..
9050 } => {
9051 assert_eq!(name.name, "f");
9052 assert_eq!(name.span, crate::Span::new(F, 9, 10).unwrap());
9053 assert!(generics.is_empty());
9054 assert_eq!(params.len(), 2);
9055 assert_eq!(params[0].name.name, "a");
9056 assert_eq!(params[0].name.span, crate::Span::new(F, 11, 12).unwrap());
9057 let p0_ty = params[0]
9058 .ty
9059 .as_ref()
9060 .expect("function-decl param requires annotation");
9061 assert!(matches!(p0_ty.kind, crate::TypeAnnotationKind::Name { .. }));
9062 assert_eq!(p0_ty.span, crate::Span::new(F, 14, 20).unwrap()); assert_eq!(params[1].name.name, "b");
9064 assert_eq!(params[1].name.span, crate::Span::new(F, 22, 23).unwrap());
9065 let p1_ty = params[1]
9066 .ty
9067 .as_ref()
9068 .expect("function-decl param requires annotation");
9069 assert_eq!(p1_ty.span, crate::Span::new(F, 25, 31).unwrap()); let return_type = return_type.as_ref().expect("plain return type");
9071 assert!(matches!(
9072 return_type.kind,
9073 crate::TypeAnnotationKind::Name { .. }
9074 ));
9075 assert_eq!(return_type.span, crate::Span::new(F, 34, 41).unwrap()); let block = ast.try_stmt(body).unwrap();
9077 assert!(matches!(block.kind, crate::StmtKind::Block(ref v) if v.is_empty()));
9078 }
9079 _ => panic!("expected Function"),
9080 }
9081 }
9082
9083 #[test]
9084 fn parse_function_with_doc_comment() {
9085 let (ast, diags) = parse_str(
9086 "/**\n * Sum two numbers.\n * @param a First.\n * @param b Second.\n */\nfunction add(a: number, b: number): number { return a + b; }",
9087 );
9088 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9089 let stmt = single_stmt(&ast);
9090 match &stmt.kind {
9091 crate::StmtKind::Function { doc, .. } => {
9092 let d = doc.as_ref().expect("doc attached");
9093 assert_eq!(d.summary, "Sum two numbers.");
9094 assert_eq!(d.params.len(), 2);
9095 assert_eq!(d.params[0].name, "a");
9096 assert_eq!(d.params[1].name, "b");
9097 }
9098 _ => panic!("expected Function"),
9099 }
9100 }
9101
9102 #[test]
9103 fn parse_interface_member_docs() {
9104 let (ast, diags) = parse_str(
9105 "interface Foo {\n /** First method. */ first(): void;\n /** A field. */ field: number;\n}",
9106 );
9107 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9108 let stmt = single_stmt(&ast);
9109 match &stmt.kind {
9110 crate::StmtKind::InterfaceDecl { members, .. } => {
9111 assert_eq!(members.len(), 2);
9112 match &members[0] {
9113 crate::InterfaceMember::Method { doc, .. } => {
9114 assert_eq!(doc.as_ref().unwrap().summary, "First method.");
9115 }
9116 _ => panic!("expected Method first"),
9117 }
9118 match &members[1] {
9119 crate::InterfaceMember::Property { doc, .. } => {
9120 assert_eq!(doc.as_ref().unwrap().summary, "A field.");
9121 }
9122 _ => panic!("expected Property second"),
9123 }
9124 }
9125 _ => panic!("expected InterfaceDecl"),
9126 }
9127 }
9128
9129 #[test]
9130 fn parse_numeric_enum_all_implicit() {
9131 let (ast, diags) = parse_str("enum Direction { Up, Down, Left, Right }");
9132 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9133 let stmt = single_stmt(&ast);
9134 match &stmt.kind {
9135 crate::StmtKind::EnumDecl { name, members, doc } => {
9136 assert_eq!(name.name, "Direction");
9137 assert!(doc.is_none());
9138 assert_eq!(members.len(), 4);
9139 for (m, expected) in members.iter().zip(["Up", "Down", "Left", "Right"]) {
9140 assert_eq!(m.name.name, expected);
9141 assert!(m.value.is_none(), "expected no initializer on {expected}");
9142 }
9143 }
9144 _ => panic!("expected EnumDecl"),
9145 }
9146 }
9147
9148 #[test]
9149 fn parse_string_enum_all_explicit() {
9150 let (ast, diags) =
9151 parse_str("enum Status { Active = \"active\", Inactive = \"inactive\" }");
9152 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9153 let stmt = single_stmt(&ast);
9154 match &stmt.kind {
9155 crate::StmtKind::EnumDecl { name, members, .. } => {
9156 assert_eq!(name.name, "Status");
9157 assert_eq!(members.len(), 2);
9158 match &members[0].value {
9159 Some(crate::EnumInitializer::String { value, .. }) => {
9160 assert_eq!(value, "active");
9161 }
9162 other => panic!("expected String init, got {other:?}"),
9163 }
9164 match &members[1].value {
9165 Some(crate::EnumInitializer::String { value, .. }) => {
9166 assert_eq!(value, "inactive");
9167 }
9168 other => panic!("expected String init, got {other:?}"),
9169 }
9170 }
9171 _ => panic!("expected EnumDecl"),
9172 }
9173 }
9174
9175 #[test]
9176 fn parse_numeric_enum_with_negative_init() {
9177 let (ast, diags) = parse_str("enum D { Up = 1, Down = -1 }");
9178 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9179 let stmt = single_stmt(&ast);
9180 match &stmt.kind {
9181 crate::StmtKind::EnumDecl { members, .. } => {
9182 assert_eq!(members.len(), 2);
9183 match &members[0].value {
9184 Some(crate::EnumInitializer::Number { value, .. }) => {
9185 assert_eq!(*value, 1.0);
9186 }
9187 other => panic!("expected Number init, got {other:?}"),
9188 }
9189 match &members[1].value {
9190 Some(crate::EnumInitializer::Number { value, .. }) => {
9191 assert_eq!(*value, -1.0);
9192 }
9193 other => panic!("expected Number init, got {other:?}"),
9194 }
9195 }
9196 _ => panic!("expected EnumDecl"),
9197 }
9198 }
9199
9200 #[test]
9201 fn parse_enum_mixed_kinds_is_parse_error() {
9202 let (_ast, diags) = parse_str("enum Mix { A = 1, B = \"two\" }");
9203 assert!(
9204 diags
9205 .iter()
9206 .any(|d| d.message.contains("mixed numeric and string")),
9207 "expected mixed-kind diagnostic, got: {diags:?}"
9208 );
9209 }
9210
9211 #[test]
9212 fn parse_enum_member_docs() {
9213 let (ast, diags) = parse_str(
9214 "enum D {\n /** The first one. */ A = 1,\n /** The second one. */ B = 2,\n}",
9215 );
9216 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9217 let stmt = single_stmt(&ast);
9218 match &stmt.kind {
9219 crate::StmtKind::EnumDecl { members, .. } => {
9220 assert_eq!(members.len(), 2);
9221 assert_eq!(
9222 members[0].doc.as_ref().expect("first doc").summary,
9223 "The first one."
9224 );
9225 assert_eq!(
9226 members[1].doc.as_ref().expect("second doc").summary,
9227 "The second one."
9228 );
9229 }
9230 _ => panic!("expected EnumDecl"),
9231 }
9232 }
9233
9234 #[test]
9235 fn parse_enum_trailing_comma_ok() {
9236 let (ast, diags) = parse_str("enum D { A, B, }");
9237 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9238 let stmt = single_stmt(&ast);
9239 match &stmt.kind {
9240 crate::StmtKind::EnumDecl { members, .. } => assert_eq!(members.len(), 2),
9241 _ => panic!("expected EnumDecl"),
9242 }
9243 }
9244
9245 #[test]
9246 fn parse_function_empty_params() {
9247 let (ast, diags) = parse_str("function noop(): void { }");
9248 assert!(diags.is_empty());
9249 let stmt = single_stmt(&ast);
9250 match stmt.kind {
9251 crate::StmtKind::Function { ref params, .. } => assert!(params.is_empty()),
9252 _ => panic!("expected Function"),
9253 }
9254 }
9255
9256 #[test]
9257 fn parse_generic_function_single_param() {
9258 let (ast, diags) = parse_str("function identity<T>(x: T): T { return x; }");
9259 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9260 let stmt = single_stmt(&ast);
9261 match stmt.kind {
9262 crate::StmtKind::Function {
9263 ref name,
9264 ref generics,
9265 ref params,
9266 ..
9267 } => {
9268 assert_eq!(name.name, "identity");
9269 assert_eq!(generics.len(), 1);
9270 assert_eq!(generics[0].name, "T");
9271 assert_eq!(params.len(), 1);
9272 assert_eq!(params[0].name.name, "x");
9273 }
9274 _ => panic!("expected Function"),
9275 }
9276 }
9277
9278 #[test]
9279 fn parse_generic_function_multiple_params() {
9280 let (ast, diags) = parse_str("function pair<T, U>(a: T, b: U): T { return a; }");
9281 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9282 let stmt = single_stmt(&ast);
9283 match stmt.kind {
9284 crate::StmtKind::Function { ref generics, .. } => {
9285 assert_eq!(generics.len(), 2);
9286 assert_eq!(generics[0].name, "T");
9287 assert_eq!(generics[1].name, "U");
9288 }
9289 _ => panic!("expected Function"),
9290 }
9291 }
9292
9293 #[test]
9294 fn parse_non_generic_function_has_empty_generics() {
9295 let (ast, diags) = parse_str("function noop(): void { }");
9296 assert!(diags.is_empty());
9297 let stmt = single_stmt(&ast);
9298 match stmt.kind {
9299 crate::StmtKind::Function { ref generics, .. } => assert!(generics.is_empty()),
9300 _ => panic!("expected Function"),
9301 }
9302 }
9303
9304 #[test]
9305 fn parse_empty_generic_list_diagnoses() {
9306 let (_ast, diags) = parse_str("function f<>(): void { }");
9307 assert!(
9308 diags
9309 .iter()
9310 .any(|d| d.message.contains("empty generic parameter list")),
9311 "expected empty-generic-list diagnostic, got: {diags:?}",
9312 );
9313 }
9314
9315 #[test]
9316 fn parse_trailing_comma_in_generic_list_ok() {
9317 let (ast, diags) = parse_str("function f<T,>(): void { }");
9318 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9319 match single_stmt(&ast).kind {
9320 crate::StmtKind::Function { ref generics, .. } => assert_eq!(generics.len(), 1),
9321 _ => panic!("expected Function"),
9322 }
9323 }
9324
9325 #[test]
9326 fn parse_generic_call_single_type_arg() {
9327 let (ast, diags) = parse_str("identity<number>(42);");
9328 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9329 let stmt = single_stmt(&ast);
9330 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9331 panic!("expected Expr stmt");
9332 };
9333 match &ast.try_expr(call_id).unwrap().kind {
9334 crate::ExprKind::Call {
9335 type_args, args, ..
9336 } => {
9337 let ta = type_args.as_ref().expect("type_args set for generic call");
9338 assert_eq!(ta.len(), 1);
9339 assert_eq!(args.len(), 1);
9340 }
9341 other => panic!("expected Call, got {other:?}"),
9342 }
9343 }
9344
9345 #[test]
9346 fn parse_generic_call_trailing_comma_ok() {
9347 let (ast, diags) = parse_str("identity<number,>(42);");
9348 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9349 let stmt = single_stmt(&ast);
9350 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9351 panic!("expected Expr stmt");
9352 };
9353 match &ast.try_expr(call_id).unwrap().kind {
9354 crate::ExprKind::Call {
9355 type_args, args, ..
9356 } => {
9357 assert_eq!(type_args.as_ref().expect("type_args set").len(), 1);
9358 assert_eq!(args.len(), 1);
9359 }
9360 other => panic!("expected Call, got {other:?}"),
9361 }
9362 }
9363
9364 #[test]
9365 fn parse_generic_call_multi_type_args() {
9366 let (ast, diags) = parse_str("pair<number, string>(1, \"x\");");
9367 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9368 let stmt = single_stmt(&ast);
9369 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9370 panic!("expected Expr stmt");
9371 };
9372 match &ast.try_expr(call_id).unwrap().kind {
9373 crate::ExprKind::Call { type_args, .. } => {
9374 let ta = type_args.as_ref().expect("type_args set for generic call");
9375 assert_eq!(ta.len(), 2);
9376 }
9377 other => panic!("expected Call, got {other:?}"),
9378 }
9379 }
9380
9381 #[test]
9382 fn parse_comparison_chain_unaffected_by_generic_lookahead() {
9383 let (ast, diags) = parse_str("let z = a < b > c;");
9384 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9385 assert!(
9386 !ast_contains_call_with_type_args(&ast),
9387 "should not have parsed any generic call",
9388 );
9389 }
9390
9391 #[test]
9392 fn parse_lt_with_paren_rhs_is_comparison_not_generic() {
9393 let (ast, diags) = parse_str("let z = a < (b);");
9394 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9395 assert!(!ast_contains_call_with_type_args(&ast));
9396 }
9397
9398 #[test]
9399 fn parse_generic_call_no_args() {
9400 let (ast, diags) = parse_str("none<number>();");
9401 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9402 let stmt = single_stmt(&ast);
9403 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9404 panic!("expected Expr stmt");
9405 };
9406 match &ast.try_expr(call_id).unwrap().kind {
9407 crate::ExprKind::Call {
9408 type_args, args, ..
9409 } => {
9410 assert_eq!(type_args.as_ref().unwrap().len(), 1);
9411 assert!(args.is_empty());
9412 }
9413 other => panic!("expected Call, got {other:?}"),
9414 }
9415 }
9416
9417 fn ast_contains_call_with_type_args(ast: &crate::Ast) -> bool {
9418 for id in 0..ast.top_level.len() {
9419 let stmt = ast.try_stmt(ast.top_level[id]).unwrap();
9420 if walk_stmt_for_typed_call(ast, stmt) {
9421 return true;
9422 }
9423 }
9424 false
9425 }
9426
9427 fn walk_stmt_for_typed_call(ast: &crate::Ast, stmt: &crate::Stmt) -> bool {
9428 match &stmt.kind {
9429 crate::StmtKind::Let { value, .. } | crate::StmtKind::Const { value, .. } => {
9430 walk_expr_for_typed_call(ast, *value)
9431 }
9432 crate::StmtKind::Expr(e) => walk_expr_for_typed_call(ast, *e),
9433 crate::StmtKind::Block(stmts) => stmts
9434 .iter()
9435 .any(|sid| walk_stmt_for_typed_call(ast, ast.try_stmt(*sid).unwrap())),
9436 _ => false,
9437 }
9438 }
9439
9440 fn walk_expr_for_typed_call(ast: &crate::Ast, id: crate::ExprId) -> bool {
9441 match &ast.try_expr(id).unwrap().kind {
9442 crate::ExprKind::Call {
9443 type_args: Some(_), ..
9444 } => true,
9445 crate::ExprKind::Call { callee, args, .. } => {
9446 walk_expr_for_typed_call(ast, *callee)
9447 || args.iter().any(|a| walk_expr_for_typed_call(ast, *a))
9448 }
9449 crate::ExprKind::Binary { lhs, rhs, .. } => {
9450 walk_expr_for_typed_call(ast, *lhs) || walk_expr_for_typed_call(ast, *rhs)
9451 }
9452 crate::ExprKind::Unary { operand, .. } => walk_expr_for_typed_call(ast, *operand),
9453 crate::ExprKind::Paren(e) => walk_expr_for_typed_call(ast, *e),
9454 crate::ExprKind::FieldAccess { receiver, .. } => {
9455 walk_expr_for_typed_call(ast, *receiver)
9456 }
9457 crate::ExprKind::IndexAccess { receiver, index } => {
9458 walk_expr_for_typed_call(ast, *receiver) || walk_expr_for_typed_call(ast, *index)
9459 }
9460 _ => false,
9461 }
9462 }
9463
9464 #[test]
9465 fn parse_function_body_with_statements() {
9466 let (ast, diags) = parse_str("function g(x: number): number { let y = x; y; }");
9467 assert!(diags.is_empty());
9468 let stmt = single_stmt(&ast);
9469 match stmt.kind {
9470 crate::StmtKind::Function { body, .. } => {
9471 let block = ast.try_stmt(body).unwrap();
9472 match block.kind {
9473 crate::StmtKind::Block(ref v) => assert_eq!(v.len(), 2),
9474 _ => panic!("expected Block"),
9475 }
9476 }
9477 _ => panic!("expected Function"),
9478 }
9479 }
9480
9481 #[test]
9482 fn function_missing_return_type_diagnoses() {
9483 let (ast, diags) = parse_str("function f() { }");
9484 assert!(!diags.is_empty());
9485 assert_eq!(diags[0].message, "expected `:` and return type");
9486 assert!(ast.top_level.is_empty());
9487 }
9488
9489 #[test]
9490 fn method_missing_return_type_still_reads_the_body() {
9491 let (_ast, diags) = parse_str(
9492 "class Box {\n async run() {\n const x = await f();\n return x;\n }\n ok(): void {}\n}",
9493 );
9494 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9495 assert_eq!(
9496 messages,
9497 [
9498 "`async` functions are not supported",
9499 "expected `:` and return type",
9500 "`await` is not supported"
9501 ],
9502 "{diags:?}"
9503 );
9504 assert_eq!(
9505 diags[1].help,
9506 ["write the return type: `run(…): T { … }`, with `void` when it returns nothing"]
9507 );
9508 }
9509
9510 #[test]
9511 fn an_unclosed_method_body_leaves_the_next_declaration_alone() {
9512 let (ast, diags) =
9513 parse_str("class Box {\n a() { return 1;\n}\nfunction main(): number { return 0; }");
9514 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9515 assert_eq!(
9516 messages,
9517 [
9518 "expected `:` and return type",
9519 "expected `}` to close class"
9520 ],
9521 "{diags:?}"
9522 );
9523 assert_eq!(ast.top_level.len(), 1, "`main` is still declared");
9524 }
9525
9526 #[test]
9527 fn every_declaration_after_an_unclosed_method_body_is_kept() {
9528 for next in [
9529 "class B { ok(): number { return 2; } }",
9530 "interface I { a: number; }",
9531 "enum E { A }",
9532 "type X = number;",
9533 "import * as fs from \"submilli:fs\";",
9534 "async function g(): number { return 1; }",
9535 ] {
9536 let source = format!(
9537 "class A {{\n bad() {{\n return 1;\n}}\n{next}\nfunction main(): void {{}}"
9538 );
9539 let (ast, diags) = parse_str(&source);
9540 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9541 let expected_async = next.starts_with("async");
9542 let mut expected = vec![
9543 "expected `:` and return type",
9544 "expected `}` to close class",
9545 ];
9546 if expected_async {
9547 expected.push("`async` functions are not supported");
9548 }
9549 assert_eq!(messages, expected, "{next}: {diags:?}");
9550 assert_eq!(
9551 ast.top_level.len(),
9552 2,
9553 "{next} and `main` are kept: {diags:?}"
9554 );
9555 }
9556 }
9557
9558 #[test]
9559 fn a_failed_statement_leaves_the_next_declaration_to_be_parsed() {
9560 for (source, kept) in [
9561 ("let x = 1 +\nimport { exists } from \"submilli:fs\";", 1),
9562 ("const y =\nclass C {}\nfunction main(): void {}", 2),
9563 (
9564 "interface I {\n a: number;\nimport * as fs from \"submilli:fs\";",
9565 1,
9566 ),
9567 ] {
9568 let (ast, diags) = parse_str(source);
9569 assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9570 assert_eq!(ast.top_level.len(), kept, "{source}: {diags:?}");
9571 }
9572 }
9573
9574 #[test]
9575 fn a_dynamic_import_after_a_failed_statement_reports_once() {
9576 for source in [
9577 "const m = import(\"submilli:fs\");\nclass C {}",
9578 "const d = import.meta.dirname;\nclass C {}",
9579 ] {
9580 let (_ast, diags) = parse_str(source);
9581 assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9582 }
9583 }
9584
9585 #[test]
9586 fn members_named_like_declarations_after_a_bad_method_stay_members() {
9587 let (_ast, diags) = parse_str(
9588 "class A {\n bad() {}\n type: number = 1;\n function(): void {}\n declare(): void {}\n async(): void {}\n}",
9589 );
9590 assert_eq!(diags.len(), 1, "{diags:?}");
9591 }
9592
9593 #[test]
9594 fn every_member_after_a_bad_method_is_still_read() {
9595 let (_ast, diags) = parse_str(
9596 "class A {\n a() { return 1; }\n x: number = 1;\n get g(): number { return 1; }\n constructor() {}\n b() {}\n ok(): void {}\n}\nclass B {\n c() {}\n}",
9597 );
9598 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9599 assert_eq!(messages, ["expected `:` and return type"; 3], "{diags:?}");
9600 }
9601
9602 #[test]
9603 fn a_member_failing_after_a_type_literal_reports_once() {
9604 let (_ast, diags) = parse_str("class A {\n x: { a: number } 5\n}");
9605 assert_eq!(diags.len(), 1, "{diags:?}");
9606 }
9607
9608 #[test]
9609 fn multiple_declarators_become_one_statement_each() {
9610 let (ast, diags) = parse_str("let a = 0, b: string = \"s\", [c] = [1];");
9611 assert!(diags.is_empty(), "{diags:?}");
9612 let kinds: Vec<&crate::StmtKind> = ast
9613 .top_level
9614 .iter()
9615 .map(|id| &ast.try_stmt(*id).unwrap().kind)
9616 .collect();
9617 assert!(
9618 matches!(
9619 kinds.as_slice(),
9620 [
9621 crate::StmtKind::Let { name: a, ty: None, .. },
9622 crate::StmtKind::Let { name: b, ty: Some(_), .. },
9623 crate::StmtKind::LetPattern { .. },
9624 ] if a.name == "a" && b.name == "b"
9625 ),
9626 "{kinds:?}"
9627 );
9628 }
9629
9630 #[test]
9631 fn declarators_keep_their_own_spans() {
9632 let source = "const a = 1, b = 2;";
9633 let (ast, diags) = parse_str(source);
9634 assert!(diags.is_empty(), "{diags:?}");
9635 let spans: Vec<&str> = ast
9636 .top_level
9637 .iter()
9638 .map(|id| {
9639 let span = ast.try_stmt(*id).unwrap().span;
9640 &source[span.start as usize..span.end as usize]
9641 })
9642 .collect();
9643 assert_eq!(spans, ["const a = 1", "b = 2;"]);
9644 }
9645
9646 #[test]
9647 fn a_declarator_missing_its_initializer_is_reported_at_it() {
9648 let (_ast, diags) = parse_str("const a = 1, b;");
9649 assert_eq!(diags.len(), 1, "{diags:?}");
9650 assert_eq!(
9651 diags[0].message,
9652 "`const` declaration requires an initializer"
9653 );
9654 assert_eq!(diags[0].span.start, 14, "{diags:?}");
9655 let (_ast, diags) = parse_str("let a = 1, ;");
9656 assert_eq!(
9657 diags[0].message, "expected identifier after `let`/`const`",
9658 "{diags:?}"
9659 );
9660 }
9661
9662 #[test]
9663 fn a_typed_declarator_without_a_value_may_precede_another() {
9664 let (ast, diags) = parse_str("let a: number | undefined, b = 1;");
9665 assert!(diags.is_empty(), "{diags:?}");
9666 assert_eq!(ast.top_level.len(), 2);
9667 }
9668
9669 #[test]
9670 fn declarators_stay_in_their_block() {
9671 let (ast, diags) = parse_str("function f(): void { let a = 1, b = 2; }\nlet c = 3;");
9672 assert!(diags.is_empty(), "{diags:?}");
9673 assert_eq!(
9674 ast.top_level.len(),
9675 2,
9676 "the block's declarators don't leak out"
9677 );
9678 }
9679
9680 #[test]
9681 fn every_exported_declarator_is_exported() {
9682 let (ast, diags) = parse_str("export const a = 1, b = 2;");
9683 assert!(diags.is_empty(), "{diags:?}");
9684 assert_eq!(ast.top_level.len(), 2);
9685 let exported: Vec<crate::StmtId> =
9686 ast.exported_decls.iter().map(|decl| decl.stmt).collect();
9687 assert_eq!(exported, ast.top_level);
9688 }
9689
9690 #[test]
9691 fn a_for_head_holds_every_declarator() {
9692 let (ast, diags) = parse_str("for (let i = 0, j = 10; i < j; i++, j--) {}");
9693 assert!(diags.is_empty(), "{diags:?}");
9694 let crate::StmtKind::For { init, update, .. } = &single_stmt(&ast).kind else {
9695 panic!("expected For");
9696 };
9697 assert_eq!(init.len(), 2);
9698 let (ast2, diags) = parse_str("for (i = 0, j = 10; i < j; i++) {}");
9699 assert!(diags.is_empty(), "{diags:?}");
9700 let crate::StmtKind::For { init: assigns, .. } = &single_stmt(&ast2).kind else {
9701 panic!("expected For");
9702 };
9703 assert_eq!(assigns.len(), 2);
9704 let update = ast.try_stmt(update.unwrap()).unwrap();
9705 assert!(
9706 matches!(&update.kind, crate::StmtKind::Block(stmts) if stmts.len() == 2),
9707 "{update:?}"
9708 );
9709 }
9710
9711 #[test]
9712 fn missing_return_type_still_reads_the_body() {
9713 let (_ast, diags) = parse_str(
9714 "async function main() {\n const x = await f();\n if (x) { return 1; }\n return 2;\n}\nfunction g(): void {}",
9715 );
9716 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9717 assert_eq!(
9718 messages,
9719 [
9720 "`async` functions are not supported",
9721 "expected `:` and return type",
9722 "`await` is not supported"
9723 ],
9724 "{diags:?}"
9725 );
9726 assert_eq!(
9727 diags[1].help,
9728 [
9729 "write the return type: `function main(…): T { … }`, with `void` when it returns nothing"
9730 ]
9731 );
9732 }
9733
9734 #[test]
9735 fn function_missing_param_type_diagnoses() {
9736 let (ast, diags) = parse_str("function f(a): void { }");
9737 assert!(!diags.is_empty());
9738 assert_eq!(diags[0].message, "parameter requires a type annotation");
9739 assert!(ast.top_level.is_empty());
9740 }
9741
9742 #[test]
9743 fn function_missing_name_diagnoses() {
9744 let (ast, diags) = parse_str("function (x: number): void { }");
9745 assert!(!diags.is_empty());
9746 assert_eq!(diags[0].message, "expected function name");
9747 assert!(ast.top_level.is_empty());
9748 }
9749
9750 #[test]
9751 fn function_trailing_param_comma_ok() {
9752 let (ast, diags) = parse_str("function f(a: number,): void { }");
9753 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9754 match single_stmt(&ast).kind {
9755 crate::StmtKind::Function { ref params, .. } => assert_eq!(params.len(), 1),
9756 _ => panic!("expected Function"),
9757 }
9758 }
9759
9760 #[test]
9761 fn function_missing_open_brace_diagnoses() {
9762 let (ast, diags) = parse_str("function f(): void");
9763 assert_eq!(diags.len(), 1);
9764 assert_eq!(diags[0].message, "expected `{`");
9765 assert!(ast.top_level.is_empty());
9766 }
9767
9768 #[test]
9769 fn snapshot_function_declaration() {
9770 let (ast, diags) =
9771 parse_str("function add(a: number, b: number): number { let sum = a + b; sum; }");
9772 assert!(diags.is_empty());
9773 insta::assert_debug_snapshot!(ast);
9774 }
9775
9776 #[test]
9777 fn rest_param_basic_parses() {
9778 let (_, diags) = parse_str("function sum(...nums: number[]): number { return 0; }");
9779 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9780 }
9781
9782 #[test]
9783 fn rest_param_with_fixed_prefix_parses() {
9784 let (_, diags) =
9785 parse_str("function tag(label: string, ...vals: number[]): string { return label; }");
9786 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9787 }
9788
9789 #[test]
9790 fn rest_param_in_arrow_parses() {
9791 let (_, diags) = parse_str("const f = (...n: number[]) => n.length;");
9792 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9793 }
9794
9795 #[test]
9796 fn rest_param_in_function_type_annotation_parses() {
9797 let (_, diags) =
9798 parse_str("let f: (a: number, ...rest: string[]) => void = (a, ...r) => {};");
9799 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9800 }
9801
9802 #[test]
9803 fn rest_param_not_last_rejected() {
9804 let (_, diags) = parse_str("function f(...xs: number[], y: number): void {}");
9805 assert!(
9806 diags.iter().any(|d| d
9807 .message
9808 .contains("rest parameter must be the last parameter")),
9809 "expected 'must be last' diagnostic, got {diags:?}",
9810 );
9811 }
9812
9813 #[test]
9814 fn rest_param_with_default_rejected() {
9815 let (_, diags) = parse_str("function f(...xs: number[] = []): void {}");
9816 assert!(
9817 diags.iter().any(|d| d
9818 .message
9819 .contains("rest parameter cannot have a default value")),
9820 "expected default-rejection diagnostic, got {diags:?}",
9821 );
9822 }
9823
9824 #[test]
9825 fn rest_param_without_annotation_rejected() {
9826 let (_, diags) = parse_str("function f(...xs): void {}");
9827 assert!(
9828 diags.iter().any(|d| d
9829 .message
9830 .contains("rest parameter requires a type annotation")),
9831 "expected missing-annotation diagnostic, got {diags:?}",
9832 );
9833 }
9834
9835 #[test]
9836 fn rest_param_destructured_rejected() {
9837 let (_, diags) = parse_str("function f(...{a, b}): void {}");
9838 assert!(
9839 diags
9840 .iter()
9841 .any(|d| d.message.contains("rest parameter cannot be destructured")),
9842 "expected destructured-rejection diagnostic, got {diags:?}",
9843 );
9844 }
9845
9846 #[test]
9847 fn rest_param_in_function_type_with_non_array_rejected() {
9848 let (_, diags) = parse_str("let f: (...xs: number) => void = () => {};");
9849 assert!(
9850 diags
9851 .iter()
9852 .any(|d| d.message.contains("rest parameter type must be an array")),
9853 "expected non-array-type diagnostic, got {diags:?}",
9854 );
9855 }
9856
9857 fn call(e: &crate::Expr) -> (crate::ExprId, &[crate::ExprId]) {
9858 match e.kind {
9859 crate::ExprKind::Call {
9860 callee, ref args, ..
9861 } => (callee, args.as_slice()),
9862 _ => panic!("expected Call, got {:?}", e.kind),
9863 }
9864 }
9865
9866 #[test]
9867 fn parse_call_no_args() {
9868 let (ast, diags) = parse_str("f();");
9869 assert!(diags.is_empty());
9870 let outer = expr_of_single_stmt(&ast);
9871 let (callee, args) = call(outer);
9872 assert!(matches!(
9873 ast.try_expr(callee).unwrap().kind,
9874 crate::ExprKind::Identifier(_)
9875 ));
9876 assert!(args.is_empty());
9877 assert_eq!(outer.span, crate::Span::new(F, 0, 3).unwrap());
9878 }
9879
9880 #[test]
9881 fn parse_call_one_arg() {
9882 let (ast, diags) = parse_str("f(1);");
9883 assert!(diags.is_empty());
9884 let outer = expr_of_single_stmt(&ast);
9885 let (callee, args) = call(outer);
9886 assert!(matches!(
9887 ast.try_expr(callee).unwrap().kind,
9888 crate::ExprKind::Identifier(_)
9889 ));
9890 assert_eq!(args.len(), 1);
9891 assert_eq!(
9892 ast.try_expr(args[0]).unwrap().kind,
9893 crate::ExprKind::Number(1.0)
9894 );
9895 }
9896
9897 #[test]
9898 fn parse_call_three_args() {
9899 let (ast, diags) = parse_str("f(1, 2, 3);");
9900 assert!(diags.is_empty());
9901 let outer = expr_of_single_stmt(&ast);
9902 let (_callee, args) = call(outer);
9903 assert_eq!(args.len(), 3);
9904 assert_eq!(
9905 ast.try_expr(args[0]).unwrap().kind,
9906 crate::ExprKind::Number(1.0)
9907 );
9908 assert_eq!(
9909 ast.try_expr(args[1]).unwrap().kind,
9910 crate::ExprKind::Number(2.0)
9911 );
9912 assert_eq!(
9913 ast.try_expr(args[2]).unwrap().kind,
9914 crate::ExprKind::Number(3.0)
9915 );
9916 }
9917
9918 #[test]
9919 fn parse_call_nested() {
9920 let (ast, diags) = parse_str("f(g(x));");
9921 assert!(diags.is_empty());
9922 let outer = expr_of_single_stmt(&ast);
9923 let (_callee, args) = call(outer);
9924 assert_eq!(args.len(), 1);
9925 let (_inner_callee, inner_args) = call(ast.try_expr(args[0]).unwrap());
9926 assert_eq!(inner_args.len(), 1);
9927 assert!(matches!(
9928 ast.try_expr(inner_args[0]).unwrap().kind,
9929 crate::ExprKind::Identifier(_)
9930 ));
9931 }
9932
9933 #[test]
9934 fn parse_curried_call() {
9935 let (ast, diags) = parse_str("f()(x);");
9936 assert!(diags.is_empty());
9937 let outer = expr_of_single_stmt(&ast);
9938 let (callee, args) = call(outer);
9939 assert_eq!(args.len(), 1);
9940 let (_inner_callee, inner_args) = call(ast.try_expr(callee).unwrap());
9941 assert!(inner_args.is_empty());
9942 }
9943
9944 #[test]
9945 fn unary_binds_looser_than_call() {
9946 let (ast, diags) = parse_str("-f();");
9947 assert!(diags.is_empty());
9948 let outer = expr_of_single_stmt(&ast);
9949 let (op, operand) = unary(outer);
9950 assert_eq!(op, crate::UnOp::Neg);
9951 let (_callee, args) = call(ast.try_expr(operand).unwrap());
9952 assert!(args.is_empty());
9953 }
9954
9955 #[test]
9956 fn call_inside_binary() {
9957 let (ast, diags) = parse_str("f() + 1;");
9958 assert!(diags.is_empty());
9959 let outer = expr_of_single_stmt(&ast);
9960 let (op, lhs, rhs) = binary(outer);
9961 assert_eq!(op, crate::BinOp::Add);
9962 let (_callee, args) = call(ast.try_expr(lhs).unwrap());
9963 assert!(args.is_empty());
9964 assert_eq!(
9965 ast.try_expr(rhs).unwrap().kind,
9966 crate::ExprKind::Number(1.0)
9967 );
9968 }
9969
9970 #[test]
9971 fn call_with_complex_args() {
9972 let (ast, diags) = parse_str("f(1 + 2, g(x));");
9973 assert!(diags.is_empty());
9974 let outer = expr_of_single_stmt(&ast);
9975 let (_callee, args) = call(outer);
9976 assert_eq!(args.len(), 2);
9977 let (op, ..) = binary(ast.try_expr(args[0]).unwrap());
9978 assert_eq!(op, crate::BinOp::Add);
9979 let (_, inner_args) = call(ast.try_expr(args[1]).unwrap());
9980 assert_eq!(inner_args.len(), 1);
9981 }
9982
9983 #[test]
9984 fn call_trailing_comma_ok() {
9985 let (ast, diags) = parse_str("f(1, 2,);");
9986 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9987 let (_callee, args) = call(expr_of_single_stmt(&ast));
9988 assert_eq!(args.len(), 2);
9989 }
9990
9991 #[test]
9992 fn unclosed_call_diagnoses() {
9993 let (ast, diags) = parse_str("f(1, 2");
9994 assert!(!diags.is_empty());
9995 assert!(diags[0].message == "expected `,` or `)`" || diags[0].message == "expected `)`");
9996 assert!(ast.top_level.is_empty());
9997 }
9998
9999 #[test]
10000 fn call_expression_statement() {
10001 let (ast, diags) = parse_str(r#"greet("world");"#);
10002 assert!(diags.is_empty());
10003 let outer = expr_of_single_stmt(&ast);
10004 let (callee, args) = call(outer);
10005 assert!(matches!(
10006 ast.try_expr(callee).unwrap().kind,
10007 crate::ExprKind::Identifier(_)
10008 ));
10009 assert_eq!(args.len(), 1);
10010 assert_eq!(
10011 ast.try_expr(args[0]).unwrap().kind,
10012 crate::ExprKind::String("world".to_string())
10013 );
10014 }
10015
10016 #[test]
10017 fn snapshot_multi_call_program() {
10018 let (ast, diags) = parse_str("f(); g(1); h(f(2), 3);");
10019 assert!(diags.is_empty());
10020 assert_eq!(ast.top_level.len(), 3);
10021 insta::assert_debug_snapshot!(ast);
10022 }
10023
10024 #[test]
10025 fn parse_if_simple() {
10026 let (ast, diags) = parse_str("if (a) { }");
10027 assert!(diags.is_empty());
10028 let stmt = single_stmt(&ast);
10029 match stmt.kind {
10030 crate::StmtKind::If {
10031 condition,
10032 then_block,
10033 else_block,
10034 } => {
10035 assert!(matches!(
10036 ast.try_expr(condition).unwrap().kind,
10037 crate::ExprKind::Identifier(_)
10038 ));
10039 assert!(matches!(
10040 ast.try_stmt(then_block).unwrap().kind,
10041 crate::StmtKind::Block(ref v) if v.is_empty()
10042 ));
10043 assert!(else_block.is_none());
10044 }
10045 _ => panic!("expected If"),
10046 }
10047 }
10048
10049 #[test]
10050 fn parse_if_else() {
10051 let (ast, diags) = parse_str("if (a) { } else { }");
10052 assert!(diags.is_empty());
10053 let stmt = single_stmt(&ast);
10054 match stmt.kind {
10055 crate::StmtKind::If { else_block, .. } => {
10056 let else_id = else_block.expect("expected else block");
10057 assert!(matches!(
10058 ast.try_stmt(else_id).unwrap().kind,
10059 crate::StmtKind::Block(ref v) if v.is_empty()
10060 ));
10061 }
10062 _ => panic!("expected If"),
10063 }
10064 }
10065
10066 #[test]
10067 fn parse_else_if_chain() {
10068 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10069 assert!(diags.is_empty());
10070 let stmt = single_stmt(&ast);
10071 let crate::StmtKind::If {
10072 else_block: Some(inner_id),
10073 ..
10074 } = stmt.kind
10075 else {
10076 panic!("expected outer If with else_block");
10077 };
10078 let inner = ast.try_stmt(inner_id).unwrap();
10079 let crate::StmtKind::If {
10080 else_block: Some(tail_id),
10081 ..
10082 } = inner.kind
10083 else {
10084 panic!("expected inner If for `else if`");
10085 };
10086 assert!(matches!(
10087 ast.try_stmt(tail_id).unwrap().kind,
10088 crate::StmtKind::Block(_)
10089 ));
10090 }
10091
10092 #[test]
10093 fn parse_if_with_body_statements() {
10094 let (ast, diags) = parse_str("if (a) { let x = 1; }");
10095 assert!(diags.is_empty());
10096 let stmt = single_stmt(&ast);
10097 match stmt.kind {
10098 crate::StmtKind::If { then_block, .. } => {
10099 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind
10100 else {
10101 panic!("expected Block")
10102 };
10103 assert_eq!(body.len(), 1);
10104 }
10105 _ => panic!("expected If"),
10106 }
10107 }
10108
10109 #[test]
10110 fn parse_if_braceless_body() {
10111 let (ast, diags) = parse_str("if (a) return b;");
10112 assert!(diags.is_empty(), "{diags:?}");
10113 let stmt = single_stmt(&ast);
10114 let crate::StmtKind::If {
10115 then_block,
10116 else_block,
10117 ..
10118 } = stmt.kind
10119 else {
10120 panic!("expected If");
10121 };
10122 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10123 panic!("braceless body should wrap in a Block");
10124 };
10125 assert_eq!(body.len(), 1);
10126 assert!(matches!(
10127 ast.try_stmt(body[0]).unwrap().kind,
10128 crate::StmtKind::Return(_)
10129 ));
10130 assert!(else_block.is_none());
10131 }
10132
10133 #[test]
10134 fn parse_braceless_dangling_else_binds_nearest_if() {
10135 let (ast, diags) = parse_str("if (a) if (b) x(); else y();");
10137 assert!(diags.is_empty(), "{diags:?}");
10138 let crate::StmtKind::If {
10139 then_block,
10140 else_block: outer_else,
10141 ..
10142 } = single_stmt(&ast).kind
10143 else {
10144 panic!("expected outer If");
10145 };
10146 assert!(outer_else.is_none(), "else must bind to the inner if");
10147 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10148 panic!("expected wrapped Block");
10149 };
10150 let crate::StmtKind::If {
10151 else_block: Some(_),
10152 ..
10153 } = ast.try_stmt(body[0]).unwrap().kind
10154 else {
10155 panic!("inner If should own the else");
10156 };
10157 }
10158
10159 #[test]
10160 fn parse_while_braceless_body() {
10161 let (ast, diags) = parse_str("while (a) x();");
10162 assert!(diags.is_empty(), "{diags:?}");
10163 let crate::StmtKind::While { body, .. } = single_stmt(&ast).kind else {
10164 panic!("expected While");
10165 };
10166 assert!(matches!(
10167 ast.try_stmt(body).unwrap().kind,
10168 crate::StmtKind::Block(ref v) if v.len() == 1
10169 ));
10170 }
10171
10172 #[test]
10173 fn parse_loop_with_empty_body() {
10174 for src in [
10175 "while (a());",
10176 "for (const x of xs);",
10177 "for (let i = 0; i < 3; i++);",
10178 "do ; while (a());",
10179 ] {
10180 let (ast, diags) = parse_str(src);
10181 assert!(diags.is_empty(), "{src}: {diags:?}");
10182 let body = match single_stmt(&ast).kind {
10183 crate::StmtKind::While { body, .. }
10184 | crate::StmtKind::DoWhile { body, .. }
10185 | crate::StmtKind::For { body, .. }
10186 | crate::StmtKind::ForOf { body, .. } => body,
10187 ref other => panic!("{src}: expected a loop, got {other:?}"),
10188 };
10189 assert!(
10190 matches!(ast.try_stmt(body).unwrap().kind, crate::StmtKind::Block(ref v) if v.is_empty()),
10191 "{src}"
10192 );
10193 }
10194 }
10195
10196 #[test]
10197 fn parse_if_with_empty_body_is_an_error() {
10198 let (_ast, diags) = parse_str("if (a);");
10199 assert_eq!(diags[0].message, "`if` has an empty body");
10200 }
10201
10202 #[test]
10203 fn parse_empty_branch_body_reports_once() {
10204 let (_ast, diags) = parse_str("if (a) ; else b();");
10205 assert_eq!(diags.len(), 1, "{diags:?}");
10206 assert_eq!(diags[0].message, "`if` has an empty body");
10207 let (_ast, diags) = parse_str("if (a) b(); else ;");
10208 assert_eq!(diags.len(), 1, "{diags:?}");
10209 assert_eq!(diags[0].message, "`else` has an empty body");
10210 let (_ast, diags) = parse_str("if (a) ; else if (b) ; else ;");
10212 let messages: Vec<_> = diags.iter().map(|d| d.message.as_str()).collect();
10213 assert_eq!(
10214 messages,
10215 [
10216 "`if` has an empty body",
10217 "`if` has an empty body",
10218 "`else` has an empty body"
10219 ]
10220 );
10221 }
10222
10223 #[test]
10224 fn parse_for_of_braceless_body() {
10225 let (ast, diags) = parse_str("for (const x of xs) f(x);");
10226 assert!(diags.is_empty(), "{diags:?}");
10227 let crate::StmtKind::ForOf { body, .. } = single_stmt(&ast).kind else {
10228 panic!("expected ForOf");
10229 };
10230 assert!(matches!(
10231 ast.try_stmt(body).unwrap().kind,
10232 crate::StmtKind::Block(ref v) if v.len() == 1
10233 ));
10234 }
10235
10236 #[test]
10237 fn parse_nested_if() {
10238 let (ast, diags) = parse_str("if (a) { if (b) { } }");
10239 assert!(diags.is_empty());
10240 let outer = single_stmt(&ast);
10241 let crate::StmtKind::If { then_block, .. } = outer.kind else {
10242 panic!("expected outer If");
10243 };
10244 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10245 panic!("expected Block")
10246 };
10247 assert_eq!(body.len(), 1);
10248 assert!(matches!(
10249 ast.try_stmt(body[0]).unwrap().kind,
10250 crate::StmtKind::If { .. }
10251 ));
10252 }
10253
10254 #[test]
10255 fn if_missing_open_paren_diagnoses() {
10256 let (ast, diags) = parse_str("if a { }");
10257 assert!(!diags.is_empty());
10258 assert_eq!(diags[0].message, "expected `(`");
10259 assert!(ast.top_level.is_empty());
10260 }
10261
10262 #[test]
10263 fn if_missing_close_paren_diagnoses() {
10264 let (ast, diags) = parse_str("if (a { }");
10265 assert!(!diags.is_empty());
10266 assert_eq!(diags[0].message, "expected `)`");
10267 assert!(ast.top_level.is_empty());
10268 }
10269
10270 #[test]
10271 fn if_braceless_body_parses() {
10272 let (ast, diags) = parse_str("if (a) x;");
10273 assert!(diags.is_empty(), "{diags:?}");
10274 let crate::StmtKind::If { then_block, .. } = single_stmt(&ast).kind else {
10275 panic!("expected If");
10276 };
10277 assert!(matches!(
10278 ast.try_stmt(then_block).unwrap().kind,
10279 crate::StmtKind::Block(ref v) if v.len() == 1
10280 ));
10281 }
10282
10283 #[test]
10284 fn snapshot_else_if_chain() {
10285 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10286 assert!(diags.is_empty());
10287 insta::assert_debug_snapshot!(ast);
10288 }
10289
10290 #[test]
10291 fn parse_while_simple() {
10292 let (ast, diags) = parse_str("while (a) { }");
10293 assert!(diags.is_empty());
10294 let stmt = single_stmt(&ast);
10295 match stmt.kind {
10296 crate::StmtKind::While { condition, body } => {
10297 assert!(matches!(
10298 ast.try_expr(condition).unwrap().kind,
10299 crate::ExprKind::Identifier(_)
10300 ));
10301 assert!(matches!(
10302 ast.try_stmt(body).unwrap().kind,
10303 crate::StmtKind::Block(ref v) if v.is_empty()
10304 ));
10305 }
10306 _ => panic!("expected While"),
10307 }
10308 }
10309
10310 #[test]
10311 fn parse_while_with_body() {
10312 let (ast, diags) = parse_str("while (a) { x; y; }");
10313 assert!(diags.is_empty());
10314 let stmt = single_stmt(&ast);
10315 let crate::StmtKind::While { body, .. } = stmt.kind else {
10316 panic!("expected While");
10317 };
10318 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10319 panic!("expected Block")
10320 };
10321 assert_eq!(stmts.len(), 2);
10322 }
10323
10324 #[test]
10325 fn while_missing_paren_diagnoses() {
10326 let (ast, diags) = parse_str("while a { }");
10327 assert!(!diags.is_empty());
10328 assert_eq!(diags[0].message, "expected `(`");
10329 assert!(ast.top_level.is_empty());
10330 }
10331
10332 #[test]
10333 fn parse_for_all_slots() {
10334 let (ast, diags) = parse_str("for (let i = 0; i < 10; i = i + 1) { x; }");
10335 assert!(diags.is_empty(), "{diags:?}");
10336 let stmt = single_stmt(&ast);
10337 let crate::StmtKind::For {
10338 init,
10339 condition,
10340 update,
10341 body,
10342 } = &stmt.kind
10343 else {
10344 panic!("expected For");
10345 };
10346 assert!(!init.is_empty(), "init missing");
10347 assert!(condition.is_some(), "condition missing");
10348 assert!(update.is_some(), "update missing");
10349 assert!(matches!(
10350 ast.try_stmt(*body).unwrap().kind,
10351 crate::StmtKind::Block(_)
10352 ));
10353 }
10354
10355 #[test]
10356 fn parse_for_empty_slots() {
10357 let (ast, diags) = parse_str("for (;;) { break; }");
10358 assert!(diags.is_empty(), "{diags:?}");
10359 let stmt = single_stmt(&ast);
10360 let crate::StmtKind::For {
10361 init,
10362 condition,
10363 update,
10364 ..
10365 } = &stmt.kind
10366 else {
10367 panic!("expected For");
10368 };
10369 assert!(init.is_empty() && condition.is_none() && update.is_none());
10370 }
10371
10372 #[test]
10373 fn parse_for_of_const_binding() {
10374 let (ast, diags) = parse_str("for (const x of arr) { y; }");
10375 assert!(diags.is_empty(), "{diags:?}");
10376 let stmt = single_stmt(&ast);
10377 let crate::StmtKind::ForOf {
10378 binding_kind,
10379 ref name,
10380 ref ty,
10381 ..
10382 } = stmt.kind
10383 else {
10384 panic!("expected ForOf");
10385 };
10386 assert!(matches!(binding_kind, crate::BindingKind::Const));
10387 assert_eq!(name.name, "x");
10388 assert!(ty.is_none());
10389 }
10390
10391 #[test]
10392 fn parse_for_of_let_with_annotation() {
10393 let (ast, diags) = parse_str("for (let x: number of arr) { y; }");
10394 assert!(diags.is_empty(), "{diags:?}");
10395 let stmt = single_stmt(&ast);
10396 let crate::StmtKind::ForOf {
10397 binding_kind,
10398 ref ty,
10399 ..
10400 } = stmt.kind
10401 else {
10402 panic!("expected ForOf");
10403 };
10404 assert!(matches!(binding_kind, crate::BindingKind::Let));
10405 assert!(ty.is_some());
10406 }
10407
10408 #[test]
10409 fn parse_do_while_simple() {
10410 let (ast, diags) = parse_str("do { x; } while (a);");
10411 assert!(diags.is_empty(), "{diags:?}");
10412 let stmt = single_stmt(&ast);
10413 assert!(matches!(stmt.kind, crate::StmtKind::DoWhile { .. }));
10414 }
10415
10416 #[test]
10417 fn parse_break_and_continue() {
10418 let (ast, diags) = parse_str("while (a) { break; continue; }");
10419 assert!(diags.is_empty(), "{diags:?}");
10420 let stmt = single_stmt(&ast);
10421 let crate::StmtKind::While { body, .. } = stmt.kind else {
10422 panic!("expected While");
10423 };
10424 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10425 panic!("expected Block");
10426 };
10427 assert!(matches!(
10428 ast.try_stmt(stmts[0]).unwrap().kind,
10429 crate::StmtKind::Break
10430 ));
10431 assert!(matches!(
10432 ast.try_stmt(stmts[1]).unwrap().kind,
10433 crate::StmtKind::Continue
10434 ));
10435 }
10436
10437 #[test]
10438 fn parse_bare_return_in_function() {
10439 let (ast, diags) = parse_str("function f(): void { return; }");
10440 assert!(diags.is_empty());
10441 let f = single_stmt(&ast);
10442 let crate::StmtKind::Function { body, .. } = f.kind else {
10443 panic!("expected Function");
10444 };
10445 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10446 panic!("expected Block")
10447 };
10448 assert_eq!(stmts.len(), 1);
10449 match ast.try_stmt(stmts[0]).unwrap().kind {
10450 crate::StmtKind::Return(None) => {}
10451 _ => panic!("expected Return(None)"),
10452 }
10453 }
10454
10455 #[test]
10456 fn parse_return_with_value() {
10457 let (ast, diags) = parse_str("function f(): number { return 1; }");
10458 assert!(diags.is_empty());
10459 let f = single_stmt(&ast);
10460 let crate::StmtKind::Function { body, .. } = f.kind else {
10461 panic!("expected Function");
10462 };
10463 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10464 panic!("expected Block")
10465 };
10466 match ast.try_stmt(stmts[0]).unwrap().kind {
10467 crate::StmtKind::Return(Some(value)) => {
10468 assert_eq!(
10469 ast.try_expr(value).unwrap().kind,
10470 crate::ExprKind::Number(1.0)
10471 );
10472 }
10473 _ => panic!("expected Return(Some)"),
10474 }
10475 }
10476
10477 #[test]
10478 fn parse_return_with_expression() {
10479 let (ast, diags) = parse_str("function f(): number { return 1 + 2; }");
10480 assert!(diags.is_empty());
10481 let f = single_stmt(&ast);
10482 let crate::StmtKind::Function { body, .. } = f.kind else {
10483 panic!("expected Function");
10484 };
10485 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10486 panic!("expected Block")
10487 };
10488 match ast.try_stmt(stmts[0]).unwrap().kind {
10489 crate::StmtKind::Return(Some(value)) => {
10490 let (op, ..) = binary(ast.try_expr(value).unwrap());
10491 assert_eq!(op, crate::BinOp::Add);
10492 }
10493 _ => panic!("expected Return(Some(Binary))"),
10494 }
10495 }
10496
10497 #[test]
10498 fn return_missing_semicolon_diagnoses() {
10499 let (_, diags) = parse_str("function f(): number { return x x; }");
10500 assert!(!diags.is_empty());
10501 assert_eq!(diags[0].message, "expected `;` after return");
10502 }
10503
10504 #[test]
10505 fn parse_bare_block() {
10506 let (ast, diags) = parse_str("{ x; y; }");
10507 assert!(diags.is_empty());
10508 let stmt = single_stmt(&ast);
10509 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10510 panic!("expected Block");
10511 };
10512 assert_eq!(stmts.len(), 2);
10513 }
10514
10515 #[test]
10516 fn parse_empty_bare_block() {
10517 let (ast, diags) = parse_str("{ }");
10518 assert!(diags.is_empty());
10519 let stmt = single_stmt(&ast);
10520 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10521 panic!("expected Block");
10522 };
10523 assert!(stmts.is_empty());
10524 }
10525
10526 #[test]
10527 fn parse_nested_bare_blocks() {
10528 let (ast, diags) = parse_str("{ { } }");
10529 assert!(diags.is_empty());
10530 let outer = single_stmt(&ast);
10531 let crate::StmtKind::Block(ref stmts) = outer.kind else {
10532 panic!("expected outer Block");
10533 };
10534 assert_eq!(stmts.len(), 1);
10535 assert!(matches!(
10536 ast.try_stmt(stmts[0]).unwrap().kind,
10537 crate::StmtKind::Block(ref v) if v.is_empty()
10538 ));
10539 }
10540
10541 #[test]
10542 fn snapshot_function_with_if_and_return() {
10543 let source = "function isPositive(n: number): boolean {\n if (n > 0) {\n return true;\n } else {\n return false;\n }\n}";
10544 let (ast, diags) = parse_str(source);
10545 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10546 insta::assert_debug_snapshot!(ast);
10547 }
10548
10549 fn object_literal(e: &crate::Expr) -> &[crate::ObjectLiteralMember] {
10550 match e.kind {
10551 crate::ExprKind::ObjectLiteral { ref members } => members.as_slice(),
10552 _ => panic!("expected ObjectLiteral, got {:?}", e.kind),
10553 }
10554 }
10555
10556 fn object_literal_field(m: &crate::ObjectLiteralMember) -> &crate::ObjectLiteralField {
10557 match m {
10558 crate::ObjectLiteralMember::Field(f) => f,
10559 crate::ObjectLiteralMember::Spread { .. }
10560 | crate::ObjectLiteralMember::Computed { .. } => {
10561 panic!("expected Field member, got Spread")
10562 }
10563 }
10564 }
10565
10566 fn array_literal(e: &crate::Expr) -> &[crate::ArrayLiteralElement] {
10567 match e.kind {
10568 crate::ExprKind::ArrayLiteral { ref elements } => elements.as_slice(),
10569 _ => panic!("expected ArrayLiteral, got {:?}", e.kind),
10570 }
10571 }
10572
10573 fn array_literal_value(el: &crate::ArrayLiteralElement) -> crate::ExprId {
10574 match el {
10575 crate::ArrayLiteralElement::Value(id) => *id,
10576 crate::ArrayLiteralElement::Spread { .. } => {
10577 panic!("expected Value element, got Spread")
10578 }
10579 }
10580 }
10581
10582 #[test]
10583 fn parse_empty_object_literal() {
10584 let (ast, diags) = parse_str("let x = {};");
10585 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10586 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10587 panic!("expected Let");
10588 };
10589 assert!(object_literal(ast.try_expr(value).unwrap()).is_empty());
10590 }
10591
10592 #[test]
10593 fn parse_object_literal_simple() {
10594 let (ast, diags) = parse_str("let p = { x: 1, y: 2 };");
10595 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10596 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10597 panic!("expected Let");
10598 };
10599 let members = object_literal(ast.try_expr(value).unwrap());
10600 assert_eq!(members.len(), 2);
10601 let f0 = object_literal_field(&members[0]);
10602 let f1 = object_literal_field(&members[1]);
10603 assert_eq!(f0.name.name, "x");
10604 assert_eq!(f1.name.name, "y");
10605 assert_eq!(
10606 ast.try_expr(f0.value).unwrap().kind,
10607 crate::ExprKind::Number(1.0)
10608 );
10609 assert_eq!(
10610 ast.try_expr(f1.value).unwrap().kind,
10611 crate::ExprKind::Number(2.0)
10612 );
10613 }
10614
10615 #[test]
10616 fn parse_object_literal_keyword_keys() {
10617 let (ast, diags) = parse_str("let p = { type: 1, default: 2, null: 3, true: 4 };");
10618 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10619 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10620 panic!("expected Let");
10621 };
10622 let names: Vec<&str> = object_literal(ast.try_expr(value).unwrap())
10623 .iter()
10624 .map(object_literal_field)
10625 .map(|f| f.name.name.as_str())
10626 .collect();
10627 assert_eq!(names, vec!["type", "default", "null", "true"]);
10628 }
10629
10630 #[test]
10631 fn parse_object_literal_string_key() {
10632 let (ast, diags) = parse_str(r#"let p = { "hello": 1 };"#);
10633 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10634 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10635 panic!("expected Let");
10636 };
10637 let members = object_literal(ast.try_expr(value).unwrap());
10638 assert_eq!(members.len(), 1);
10639 let f0 = object_literal_field(&members[0]);
10640 assert_eq!(f0.name.name, "hello");
10641 }
10642
10643 #[test]
10644 fn parse_object_literal_string_key_decodes_escape() {
10645 let (ast, diags) = parse_str(r#"let p = { "content\u002dtype": 1 };"#);
10646 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10647 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10648 panic!("expected Let");
10649 };
10650 let members = object_literal(ast.try_expr(value).unwrap());
10651 assert_eq!(members.len(), 1);
10652 let f0 = object_literal_field(&members[0]);
10653 assert_eq!(f0.name.name, "content-type");
10654 }
10655
10656 #[test]
10657 fn parse_object_literal_shorthand() {
10658 let (ast, diags) = parse_str("let p = { x, y: 2 };");
10659 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10660 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10661 panic!("expected Let");
10662 };
10663 let members = object_literal(ast.try_expr(value).unwrap());
10664 assert_eq!(members.len(), 2);
10665 let f0 = object_literal_field(&members[0]);
10666 assert_eq!(f0.name.name, "x");
10667 assert_eq!(
10669 ast.try_expr(f0.value).unwrap().kind,
10670 crate::ExprKind::Identifier(crate::Ident {
10671 name: "x".into(),
10672 span: f0.name.span,
10673 })
10674 );
10675 let f1 = object_literal_field(&members[1]);
10676 assert_eq!(f1.name.name, "y");
10677 assert_eq!(
10678 ast.try_expr(f1.value).unwrap().kind,
10679 crate::ExprKind::Number(2.0)
10680 );
10681 }
10682
10683 #[test]
10684 fn parse_object_literal_string_key_shorthand_rejected() {
10685 let (_ast, diags) = parse_str(r#"let p = { "x" };"#);
10686 assert!(
10687 diags
10688 .iter()
10689 .any(|d| d.message.contains("expected `:` after field name")),
10690 "string-key shorthand should be rejected: {diags:?}"
10691 );
10692 }
10693
10694 #[test]
10695 fn parse_object_literal_trailing_comma() {
10696 let (ast, diags) = parse_str("let p = { x: 1, y: 2, };");
10697 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10698 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10699 panic!("expected Let");
10700 };
10701 assert_eq!(object_literal(ast.try_expr(value).unwrap()).len(), 2);
10702 }
10703
10704 #[test]
10705 fn parse_object_literal_duplicate_key_diagnoses() {
10706 let (_, diags) = parse_str("let p = { x: 1, x: 2 };");
10707 assert_eq!(diags.len(), 1);
10708 assert_eq!(diags[0].message, "duplicate field `x` in object literal");
10709 }
10710
10711 #[test]
10712 fn parse_empty_array_literal() {
10713 let (ast, diags) = parse_str("let xs = [];");
10714 assert!(diags.is_empty());
10715 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10716 panic!("expected Let");
10717 };
10718 assert!(array_literal(ast.try_expr(value).unwrap()).is_empty());
10719 }
10720
10721 #[test]
10722 fn parse_array_literal_simple() {
10723 let (ast, diags) = parse_str("let xs = [1, 2, 3];");
10724 assert!(diags.is_empty());
10725 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10726 panic!("expected Let");
10727 };
10728 let elements = array_literal(ast.try_expr(value).unwrap());
10729 assert_eq!(elements.len(), 3);
10730 assert_eq!(
10731 ast.try_expr(array_literal_value(&elements[0]))
10732 .unwrap()
10733 .kind,
10734 crate::ExprKind::Number(1.0)
10735 );
10736 assert_eq!(
10737 ast.try_expr(array_literal_value(&elements[2]))
10738 .unwrap()
10739 .kind,
10740 crate::ExprKind::Number(3.0)
10741 );
10742 }
10743
10744 #[test]
10745 fn parse_array_literal_trailing_comma() {
10746 let (ast, diags) = parse_str("let xs = [1, 2,];");
10747 assert!(diags.is_empty());
10748 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10749 panic!("expected Let");
10750 };
10751 assert_eq!(array_literal(ast.try_expr(value).unwrap()).len(), 2);
10752 }
10753
10754 #[test]
10755 fn parse_nested_array_in_object() {
10756 let (ast, diags) = parse_str("let p = { items: [1, 2] };");
10757 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10758 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10759 panic!("expected Let");
10760 };
10761 let members = object_literal(ast.try_expr(value).unwrap());
10762 let f0 = object_literal_field(&members[0]);
10763 assert_eq!(f0.name.name, "items");
10764 let inner = array_literal(ast.try_expr(f0.value).unwrap());
10765 assert_eq!(inner.len(), 2);
10766 }
10767
10768 #[test]
10769 fn parse_nested_object_in_array() {
10770 let (ast, diags) = parse_str("let xs = [{ x: 1 }, { x: 2 }];");
10771 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10772 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10773 panic!("expected Let");
10774 };
10775 let elements = array_literal(ast.try_expr(value).unwrap());
10776 assert_eq!(elements.len(), 2);
10777 assert_eq!(
10778 object_literal(ast.try_expr(array_literal_value(&elements[0])).unwrap()).len(),
10779 1
10780 );
10781 assert_eq!(
10782 object_literal(ast.try_expr(array_literal_value(&elements[1])).unwrap()).len(),
10783 1
10784 );
10785 }
10786
10787 #[test]
10788 fn brace_at_statement_start_is_block_not_object() {
10789 let (_, diags) = parse_str("{ x: 1 }");
10790 assert!(
10791 !diags.is_empty(),
10792 "expected a diagnostic for `x:` inside block"
10793 );
10794 }
10795
10796 fn field_access(e: &crate::Expr) -> (crate::ExprId, &str) {
10797 match e.kind {
10798 crate::ExprKind::FieldAccess { receiver, ref name } => (receiver, name.name.as_str()),
10799 _ => panic!("expected FieldAccess, got {:?}", e.kind),
10800 }
10801 }
10802
10803 fn index_access(e: &crate::Expr) -> (crate::ExprId, crate::ExprId) {
10804 match e.kind {
10805 crate::ExprKind::IndexAccess { receiver, index } => (receiver, index),
10806 _ => panic!("expected IndexAccess, got {:?}", e.kind),
10807 }
10808 }
10809
10810 #[test]
10811 fn parse_field_access_simple() {
10812 let (ast, diags) = parse_str("a.b;");
10813 assert!(diags.is_empty());
10814 let outer = expr_of_single_stmt(&ast);
10815 let (receiver, name) = field_access(outer);
10816 assert!(matches!(
10817 ast.try_expr(receiver).unwrap().kind,
10818 crate::ExprKind::Identifier(_)
10819 ));
10820 assert_eq!(name, "b");
10821 }
10822
10823 #[test]
10824 fn parse_field_access_keyword_name() {
10825 let (ast, diags) = parse_str("status.type.default.null;");
10826 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10827 let outer = expr_of_single_stmt(&ast);
10828 let (receiver, name) = field_access(outer);
10829 assert_eq!(name, "null");
10830 let (receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10831 assert_eq!(name, "default");
10832 let (_receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10833 assert_eq!(name, "type");
10834 }
10835
10836 #[test]
10837 fn parse_field_access_chain_left_assoc() {
10838 let (ast, diags) = parse_str("a.b.c;");
10839 assert!(diags.is_empty());
10840 let outer = expr_of_single_stmt(&ast);
10841 let (mid_id, c) = field_access(outer);
10842 assert_eq!(c, "c");
10843 let (a_id, b) = field_access(ast.try_expr(mid_id).unwrap());
10844 assert_eq!(b, "b");
10845 assert!(matches!(
10846 ast.try_expr(a_id).unwrap().kind,
10847 crate::ExprKind::Identifier(_)
10848 ));
10849 }
10850
10851 #[test]
10852 fn parse_index_access_simple() {
10853 let (ast, diags) = parse_str("a[0];");
10854 assert!(diags.is_empty());
10855 let outer = expr_of_single_stmt(&ast);
10856 let (receiver, idx) = index_access(outer);
10857 assert!(matches!(
10858 ast.try_expr(receiver).unwrap().kind,
10859 crate::ExprKind::Identifier(_)
10860 ));
10861 assert_eq!(
10862 ast.try_expr(idx).unwrap().kind,
10863 crate::ExprKind::Number(0.0)
10864 );
10865 }
10866
10867 #[test]
10868 fn parse_index_access_chain() {
10869 let (ast, diags) = parse_str("a[0][1];");
10870 assert!(diags.is_empty());
10871 let outer = expr_of_single_stmt(&ast);
10872 let (mid_id, one) = index_access(outer);
10873 assert_eq!(
10874 ast.try_expr(one).unwrap().kind,
10875 crate::ExprKind::Number(1.0)
10876 );
10877 let (a_id, zero) = index_access(ast.try_expr(mid_id).unwrap());
10878 assert_eq!(
10879 ast.try_expr(zero).unwrap().kind,
10880 crate::ExprKind::Number(0.0)
10881 );
10882 assert!(matches!(
10883 ast.try_expr(a_id).unwrap().kind,
10884 crate::ExprKind::Identifier(_)
10885 ));
10886 }
10887
10888 #[test]
10889 fn parse_mixed_access_and_call_chain() {
10890 let (ast, diags) = parse_str("a.b[c].d();");
10891 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10892 let outer = expr_of_single_stmt(&ast);
10893 let (callee, args) = call(outer);
10894 assert!(args.is_empty());
10895 let (idx_id, d_name) = field_access(ast.try_expr(callee).unwrap());
10896 assert_eq!(d_name, "d");
10897 let (b_id, c_idx) = index_access(ast.try_expr(idx_id).unwrap());
10898 assert!(matches!(
10899 ast.try_expr(c_idx).unwrap().kind,
10900 crate::ExprKind::Identifier(_)
10901 ));
10902 let (a_id, b_name) = field_access(ast.try_expr(b_id).unwrap());
10903 assert_eq!(b_name, "b");
10904 assert!(matches!(
10905 ast.try_expr(a_id).unwrap().kind,
10906 crate::ExprKind::Identifier(_)
10907 ));
10908 }
10909
10910 #[test]
10911 fn parse_call_then_field() {
10912 let (ast, diags) = parse_str("f().x;");
10913 assert!(diags.is_empty());
10914 let outer = expr_of_single_stmt(&ast);
10915 let (call_id, x_name) = field_access(outer);
10916 assert_eq!(x_name, "x");
10917 let (_callee, args) = call(ast.try_expr(call_id).unwrap());
10918 assert!(args.is_empty());
10919 }
10920
10921 #[test]
10922 fn parse_index_with_complex_expression() {
10923 let (ast, diags) = parse_str("a[i + 1];");
10924 assert!(diags.is_empty());
10925 let outer = expr_of_single_stmt(&ast);
10926 let (_, idx) = index_access(outer);
10927 let (op, ..) = binary(ast.try_expr(idx).unwrap());
10928 assert_eq!(op, crate::BinOp::Add);
10929 }
10930
10931 #[test]
10932 fn field_access_missing_name_diagnoses() {
10933 let (_, diags) = parse_str("a.;");
10934 assert!(!diags.is_empty());
10935 assert_eq!(diags[0].message, "expected field name after `.`");
10936 }
10937
10938 #[test]
10939 fn parse_postfix_increment() {
10940 let (ast, diags) = parse_str("x++;");
10941 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10942 let expr = expr_of_single_stmt(&ast);
10943 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10944 panic!("expected PostfixUnary, got {:?}", expr.kind);
10945 };
10946 assert_eq!(op, crate::PostfixOp::Inc);
10947 assert!(matches!(
10948 ast.try_expr(operand).unwrap().kind,
10949 crate::ExprKind::Identifier(_)
10950 ));
10951 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
10952 }
10953
10954 #[test]
10955 fn parse_postfix_decrement() {
10956 let (ast, diags) = parse_str("x--;");
10957 assert!(diags.is_empty());
10958 let expr = expr_of_single_stmt(&ast);
10959 let crate::ExprKind::PostfixUnary { op, .. } = expr.kind else {
10960 panic!("expected PostfixUnary");
10961 };
10962 assert_eq!(op, crate::PostfixOp::Dec);
10963 }
10964
10965 #[test]
10966 fn parse_postfix_non_null_assertion() {
10967 let (ast, diags) = parse_str("x!;");
10968 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10969 let expr = expr_of_single_stmt(&ast);
10970 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10971 panic!("expected PostfixUnary, got {:?}", expr.kind);
10972 };
10973 assert_eq!(op, crate::PostfixOp::NonNullAssert);
10974 assert!(matches!(
10975 ast.try_expr(operand).unwrap().kind,
10976 crate::ExprKind::Identifier(_)
10977 ));
10978 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
10979 }
10980
10981 #[test]
10982 fn parse_non_null_assertion_continues_chain() {
10983 let (ast, diags) = parse_str("x!.y;");
10984 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10985 let expr = expr_of_single_stmt(&ast);
10986 let crate::ExprKind::FieldAccess { receiver, name } = &expr.kind else {
10987 panic!("expected FieldAccess, got {:?}", expr.kind);
10988 };
10989 assert_eq!(name.name, "y");
10990 assert!(matches!(
10991 ast.try_expr(*receiver).unwrap().kind,
10992 crate::ExprKind::PostfixUnary {
10993 op: crate::PostfixOp::NonNullAssert,
10994 ..
10995 }
10996 ));
10997 }
10998
10999 #[test]
11000 fn parse_postfix_on_field_access() {
11001 let (ast, diags) = parse_str("o.f++;");
11002 assert!(diags.is_empty());
11003 let expr = expr_of_single_stmt(&ast);
11004 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
11005 panic!("expected PostfixUnary");
11006 };
11007 assert!(matches!(
11008 ast.try_expr(operand).unwrap().kind,
11009 crate::ExprKind::FieldAccess { .. }
11010 ));
11011 }
11012
11013 #[test]
11014 fn parse_postfix_on_index_access() {
11015 let (ast, diags) = parse_str("xs[0]++;");
11016 assert!(diags.is_empty());
11017 let expr = expr_of_single_stmt(&ast);
11018 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
11019 panic!("expected PostfixUnary");
11020 };
11021 assert!(matches!(
11022 ast.try_expr(operand).unwrap().kind,
11023 crate::ExprKind::IndexAccess { .. }
11024 ));
11025 }
11026
11027 #[test]
11028 fn parse_postfix_terminates_chain() {
11029 let (_, diags) = parse_str("x++.y;");
11030 assert!(!diags.is_empty());
11031 assert_eq!(diags[0].message, "expected `;` after expression");
11032 }
11033
11034 #[test]
11035 fn parse_postfix_in_binary_continues() {
11036 let (ast, diags) = parse_str("let y = x++ + 1;");
11037 assert!(diags.is_empty(), "unexpected: {diags:?}");
11038 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11039 panic!("expected Let");
11040 };
11041 let (op, lhs, _) = binary(ast.try_expr(value).unwrap());
11042 assert_eq!(op, crate::BinOp::Add);
11043 assert!(matches!(
11044 ast.try_expr(lhs).unwrap().kind,
11045 crate::ExprKind::PostfixUnary { .. }
11046 ));
11047 }
11048
11049 #[test]
11050 fn parse_index_assignment() {
11051 let (ast, diags) = parse_str("xs[0] = 1;");
11052 assert!(diags.is_empty(), "unexpected: {diags:?}");
11053 let stmt = single_stmt(&ast);
11054 let crate::StmtKind::AssignIndex {
11055 receiver,
11056 index,
11057 value,
11058 } = stmt.kind
11059 else {
11060 panic!("expected AssignIndex, got {:?}", stmt.kind);
11061 };
11062 assert!(matches!(
11063 ast.try_expr(receiver).unwrap().kind,
11064 crate::ExprKind::Identifier(_)
11065 ));
11066 assert_eq!(
11067 ast.try_expr(index).unwrap().kind,
11068 crate::ExprKind::Number(0.0)
11069 );
11070 assert_eq!(
11071 ast.try_expr(value).unwrap().kind,
11072 crate::ExprKind::Number(1.0)
11073 );
11074 }
11075
11076 #[test]
11077 fn parse_for_update_postfix() {
11078 let (ast, diags) = parse_str("function main(): void { for (let i = 0; i < 3; i++) {} }");
11079 assert!(diags.is_empty(), "unexpected: {diags:?}");
11080 assert_eq!(ast.top_level.len(), 1);
11081 }
11082
11083 #[test]
11084 fn parse_for_update_index_assignment() {
11085 let (ast, diags) =
11086 parse_str("function main(): void { for (let i = 0; i < 3; xs[i] = i) {} }");
11087 assert!(diags.is_empty(), "unexpected: {diags:?}");
11088 assert_eq!(ast.top_level.len(), 1);
11089 }
11090
11091 #[test]
11092 fn index_access_missing_close_bracket_diagnoses() {
11093 let (_, diags) = parse_str("a[1;");
11094 assert!(!diags.is_empty());
11095 assert_eq!(diags[0].message, "expected `]`");
11096 }
11097
11098 #[test]
11099 fn field_access_inside_object_literal_value_does_not_steal_dot() {
11100 let (ast, diags) = parse_str("let x = { a: 1 }.foo;");
11101 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11102 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11103 panic!("expected Let");
11104 };
11105 let (recv_id, name) = field_access(ast.try_expr(value).unwrap());
11106 assert_eq!(name, "foo");
11107 assert_eq!(object_literal(ast.try_expr(recv_id).unwrap()).len(), 1);
11108 }
11109
11110 fn type_of_let(stmt: &crate::Stmt) -> &crate::TypeAnnotation {
11111 match stmt.kind {
11112 crate::StmtKind::Let { ref ty, .. } => ty.as_ref().expect("expected type annotation"),
11113 _ => panic!("expected Let"),
11114 }
11115 }
11116
11117 #[test]
11118 fn parse_array_type_annotation() {
11119 let (ast, diags) = parse_str("let xs: number[] = null;");
11120 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11121 let ty = type_of_let(single_stmt(&ast));
11122 match ty.kind {
11123 crate::TypeAnnotationKind::Array(ref inner) => {
11124 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Name { .. }));
11125 }
11126 _ => panic!("expected Array, got {:?}", ty.kind),
11127 }
11128 }
11129
11130 #[test]
11131 fn parse_nested_array_type_annotation() {
11132 let (ast, diags) = parse_str("let xs: number[][] = null;");
11133 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11134 let ty = type_of_let(single_stmt(&ast));
11135 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11136 panic!("expected outer Array");
11137 };
11138 let crate::TypeAnnotationKind::Array(ref inner2) = inner.kind else {
11139 panic!("expected inner Array");
11140 };
11141 assert!(matches!(
11142 inner2.kind,
11143 crate::TypeAnnotationKind::Name { .. }
11144 ));
11145 }
11146
11147 #[test]
11148 fn parse_object_type_annotation_simple() {
11149 let (ast, diags) = parse_str("let p: { x: number; y: number } = null;");
11150 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11151 let ty = type_of_let(single_stmt(&ast));
11152 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11153 panic!("expected Object, got {:?}", ty.kind);
11154 };
11155 assert_eq!(fields.len(), 2);
11156 assert_eq!(fields[0].name.name, "x");
11157 assert_eq!(fields[1].name.name, "y");
11158 }
11159
11160 #[test]
11161 fn readonly_is_rejected_on_methods_accessors_and_method_signatures() {
11162 let source = "class K {\n\
11163 readonly m(): number { return 1; }\n\
11164 public readonly n<T>(): number { return 1; }\n\
11165 readonly get g(): number { return 1; }\n\
11166 readonly p: number = 1;\n\
11167 }\n\
11168 type T = { readonly r(a: number): boolean; readonly f: () => void };\n\
11169 interface I { readonly s(): void; readonly q: number; }\n";
11170 let (_, diags) = parse_str(source);
11171 let lines: Vec<usize> = diags
11172 .iter()
11173 .map(|d| {
11174 assert_eq!(
11175 d.message,
11176 "`readonly` can only modify a property or index signature"
11177 );
11178 source[..d.span.start as usize].matches('\n').count() + 1
11179 })
11180 .collect();
11181 assert_eq!(lines, vec![2, 3, 4, 7, 8]);
11182 }
11183
11184 #[test]
11185 fn parse_object_type_method_members() {
11186 let (ast, diags) =
11189 parse_str("let p: { m(): number; opt?(): string; r(a: number): boolean } = null;");
11190 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11191 let ty = type_of_let(single_stmt(&ast));
11192 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11193 panic!("expected Object, got {:?}", ty.kind);
11194 };
11195 assert_eq!(fields.len(), 3);
11196 for field in fields {
11197 assert!(
11198 matches!(field.ty.kind, crate::TypeAnnotationKind::Function { .. }),
11199 "member `{}` should be function-typed, got {:?}",
11200 field.name.name,
11201 field.ty.kind,
11202 );
11203 }
11204 assert!(fields[1].optional, "`opt?()` is an optional member");
11205 let crate::TypeAnnotationKind::Function { ref params, .. } = fields[2].ty.kind else {
11206 unreachable!("checked above");
11207 };
11208 assert_eq!(params.len(), 1);
11209 assert_eq!(params[0].name.name, "a");
11210 }
11211
11212 #[test]
11213 fn parse_object_type_annotation_with_commas() {
11214 let (ast, diags) = parse_str("let p: { x: number, y: number } = null;");
11215 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11216 let ty = type_of_let(single_stmt(&ast));
11217 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11218 panic!("expected Object");
11219 };
11220 assert_eq!(fields.len(), 2);
11221 }
11222
11223 #[test]
11224 fn parse_object_type_annotation_empty() {
11225 let (ast, diags) = parse_str("let p: {} = null;");
11226 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11227 let ty = type_of_let(single_stmt(&ast));
11228 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11229 panic!("expected Object");
11230 };
11231 assert!(fields.is_empty());
11232 }
11233
11234 #[test]
11235 fn parse_object_type_with_optional_field() {
11236 let (ast, diags) = parse_str("let p: { id: number; nick?: string } = null;");
11237 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11238 let ty = type_of_let(single_stmt(&ast));
11239 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11240 panic!("expected Object");
11241 };
11242 assert_eq!(fields.len(), 2);
11243 assert_eq!(fields[0].name.name, "id");
11244 assert!(!fields[0].optional);
11245 assert_eq!(fields[1].name.name, "nick");
11246 assert!(fields[1].optional);
11247 }
11248
11249 #[test]
11250 fn parse_object_type_with_readonly_fields() {
11251 let (ast, diags) = parse_str(
11252 "let issue: { readonly id: string; readonly title?: string; body: string } = null;",
11253 );
11254 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11255 let ty = type_of_let(single_stmt(&ast));
11256 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11257 panic!("expected Object");
11258 };
11259 let fields: Vec<(&str, bool, bool)> = fields
11260 .iter()
11261 .map(|field| (field.name.name.as_str(), field.optional, field.readonly))
11262 .collect();
11263 assert_eq!(
11264 fields,
11265 vec![
11266 ("id", false, true),
11267 ("title", true, true),
11268 ("body", false, false),
11269 ]
11270 );
11271 }
11272
11273 #[test]
11274 fn parse_object_type_mixed_optional_and_required() {
11275 let (ast, diags) = parse_str("let p: { a?: number; b: string; c?: boolean } = null;");
11276 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11277 let ty = type_of_let(single_stmt(&ast));
11278 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11279 panic!("expected Object");
11280 };
11281 let opt: Vec<(&str, bool)> = fields
11282 .iter()
11283 .map(|f| (f.name.name.as_str(), f.optional))
11284 .collect();
11285 assert_eq!(opt, vec![("a", true), ("b", false), ("c", true)]);
11286 }
11287
11288 #[test]
11289 fn parse_object_type_keyword_fields() {
11290 let (ast, diags) =
11291 parse_str("let p: { type: string; default?: number; null: boolean } = null;");
11292 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11293 let ty = type_of_let(single_stmt(&ast));
11294 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11295 panic!("expected Object");
11296 };
11297 let opt: Vec<(&str, bool)> = fields
11298 .iter()
11299 .map(|f| (f.name.name.as_str(), f.optional))
11300 .collect();
11301 assert_eq!(
11302 opt,
11303 vec![("type", false), ("default", true), ("null", false)]
11304 );
11305 }
11306
11307 #[test]
11308 fn parse_object_type_quoted_fields() {
11309 let (ast, diags) =
11310 parse_str(r#"let p: { "content-type": string; "x\u002drequest-id"?: string } = null;"#);
11311 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11312 let ty = type_of_let(single_stmt(&ast));
11313 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11314 panic!("expected Object");
11315 };
11316 let opt: Vec<(&str, bool)> = fields
11317 .iter()
11318 .map(|f| (f.name.name.as_str(), f.optional))
11319 .collect();
11320 assert_eq!(opt, vec![("content-type", false), ("x-request-id", true)]);
11321 }
11322
11323 #[test]
11324 fn parse_interface_with_optional_property() {
11325 let (ast, diags) = parse_str("interface User { id: number; deletedAt?: string; }");
11326 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11327 let stmt = single_stmt(&ast);
11328 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11329 panic!("expected interface decl");
11330 };
11331 let opt: Vec<(&str, bool)> = members
11332 .iter()
11333 .map(|m| match m {
11334 crate::InterfaceMember::Property { name, optional, .. } => {
11335 (name.name.as_str(), *optional)
11336 }
11337 _ => panic!("expected property"),
11338 })
11339 .collect();
11340 assert_eq!(opt, vec![("id", false), ("deletedAt", true)]);
11341 }
11342
11343 #[test]
11344 fn parse_interface_with_readonly_properties() {
11345 let (ast, diags) = parse_str(
11346 "interface Issue { readonly id: string; readonly title?: string; body: string; }",
11347 );
11348 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11349 let stmt = single_stmt(&ast);
11350 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11351 panic!("expected interface decl");
11352 };
11353 let opt: Vec<(&str, bool, bool)> = members
11354 .iter()
11355 .map(|member| match member {
11356 crate::InterfaceMember::Property {
11357 name,
11358 optional,
11359 readonly,
11360 ..
11361 } => (name.name.as_str(), *optional, *readonly),
11362 _ => panic!("expected property"),
11363 })
11364 .collect();
11365 assert_eq!(
11366 opt,
11367 vec![
11368 ("id", false, true),
11369 ("title", true, true),
11370 ("body", false, false),
11371 ]
11372 );
11373 }
11374
11375 #[test]
11376 fn parse_readonly_property_name_without_modifier() {
11377 let (ast, diags) = parse_str(
11378 "interface Meta { readonly: boolean; }\nlet meta: { readonly: boolean } = null;",
11379 );
11380 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11381 let crate::StmtKind::InterfaceDecl { ref members, .. } =
11382 ast.try_stmt(ast.top_level[0]).unwrap().kind
11383 else {
11384 panic!("expected interface decl");
11385 };
11386 let crate::InterfaceMember::Property {
11387 ref name, readonly, ..
11388 } = members[0]
11389 else {
11390 panic!("expected property");
11391 };
11392 assert_eq!(name.name, "readonly");
11393 assert!(
11394 !readonly,
11395 "`readonly` here is the property name, not a modifier"
11396 );
11397
11398 let ty = type_of_let(ast.try_stmt(ast.top_level[1]).unwrap());
11399 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11400 panic!("expected Object");
11401 };
11402 assert_eq!(fields[0].name.name, "readonly");
11403 assert!(!fields[0].readonly);
11404 }
11405
11406 #[test]
11407 fn parse_interface_keyword_members() {
11408 let (ast, diags) =
11409 parse_str("interface IssueStatus { type: string; default(): string; null: boolean; }");
11410 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11411 let stmt = single_stmt(&ast);
11412 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11413 panic!("expected interface decl");
11414 };
11415 let names: Vec<&str> = members
11416 .iter()
11417 .map(|m| match m {
11418 crate::InterfaceMember::Method { name, .. }
11419 | crate::InterfaceMember::Property { name, .. } => name.name.as_str(),
11420 crate::InterfaceMember::IndexSignature(_) => panic!("unexpected index signature"),
11421 })
11422 .collect();
11423 assert_eq!(names, vec!["type", "default", "null"]);
11424 }
11425
11426 #[test]
11427 fn parse_interface_quoted_property_members() {
11428 let (ast, diags) = parse_str(
11429 r#"interface Headers { "content-type": string; "x\u002drequest-id"?: string; }"#,
11430 );
11431 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11432 let stmt = single_stmt(&ast);
11433 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11434 panic!("expected interface decl");
11435 };
11436 let names: Vec<(&str, bool)> = members
11437 .iter()
11438 .map(|m| match m {
11439 crate::InterfaceMember::Property { name, optional, .. } => {
11440 (name.name.as_str(), *optional)
11441 }
11442 _ => panic!("expected property"),
11443 })
11444 .collect();
11445 assert_eq!(names, vec![("content-type", false), ("x-request-id", true)]);
11446 }
11447
11448 #[test]
11449 fn parse_param_optional_marker() {
11450 let (ast, diags) = parse_str("function f(x?: number): void {}");
11451 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11452 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
11453 panic!("expected function")
11454 };
11455 assert!(params[0].optional);
11456 assert!(params[0].default.is_none());
11457 }
11458
11459 #[test]
11460 fn parse_interface_optional_method() {
11461 let (ast, diags) = parse_str("interface F { foo?(x?: number): void; }");
11462 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11463 let crate::StmtKind::InterfaceDecl { ref members, .. } = single_stmt(&ast).kind else {
11464 panic!("expected interface")
11465 };
11466 let crate::InterfaceMember::Property { optional, ty, .. } = &members[0] else {
11467 panic!("expected callable property")
11468 };
11469 assert!(*optional);
11470 let crate::TypeAnnotationKind::Function { params, .. } = &ty.kind else {
11471 panic!("expected function type")
11472 };
11473 assert!(params[0].optional);
11474 }
11475
11476 #[test]
11477 fn parse_interface_with_call_signature() {
11478 let (ast, diags) = parse_str("interface F { (s: string): number; }");
11479 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11480 let stmt = single_stmt(&ast);
11481 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11482 panic!("expected interface decl");
11483 };
11484 assert_eq!(members.len(), 1);
11485 match &members[0] {
11486 crate::InterfaceMember::Method { name, params, .. } => {
11487 assert_eq!(name.name, "@call");
11488 assert_eq!(params.len(), 1);
11489 assert_eq!(params[0].name.name, "s");
11490 }
11491 other => panic!("expected method (call signature), got {other:?}"),
11492 }
11493 }
11494
11495 #[test]
11496 fn parse_interface_mixed_call_signature_and_method() {
11497 let (ast, diags) = parse_str("interface F { greet(): string; (x: number): number; }");
11498 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11499 let stmt = single_stmt(&ast);
11500 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11501 panic!("expected interface decl");
11502 };
11503 let names: Vec<&str> = members
11504 .iter()
11505 .map(|m| match m {
11506 crate::InterfaceMember::Method { name, .. } => name.name.as_str(),
11507 _ => "<property>",
11508 })
11509 .collect();
11510 assert_eq!(names, vec!["greet", "@call"]);
11511 }
11512
11513 #[test]
11514 fn parse_interface_rejects_generic_call_signature() {
11515 let (_ast, diags) = parse_str("interface F { <T>(x: T): T; }");
11516 assert!(
11517 diags
11518 .iter()
11519 .any(|d| d.message.contains("generic call signatures")),
11520 "expected generic-call-signature diagnostic, got {diags:?}"
11521 );
11522 }
11523
11524 #[test]
11525 fn parse_object_type_with_array_field() {
11526 let (ast, diags) = parse_str("let p: { xs: number[] } = null;");
11527 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11528 let ty = type_of_let(single_stmt(&ast));
11529 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11530 panic!("expected Object");
11531 };
11532 assert_eq!(fields[0].name.name, "xs");
11533 assert!(matches!(
11534 fields[0].ty.kind,
11535 crate::TypeAnnotationKind::Array(_)
11536 ));
11537 }
11538
11539 #[test]
11540 fn parse_array_of_objects() {
11541 let (ast, diags) = parse_str("let xs: { x: number }[] = null;");
11542 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11543 let ty = type_of_let(single_stmt(&ast));
11544 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11545 panic!("expected Array");
11546 };
11547 assert!(matches!(
11548 inner.kind,
11549 crate::TypeAnnotationKind::Object { .. }
11550 ));
11551 }
11552
11553 #[test]
11554 fn parse_tuple_type_annotation() {
11555 let (ast, diags) = parse_str("let p: [string, number] = null;");
11556 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11557 let ty = type_of_let(single_stmt(&ast));
11558 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11559 panic!("expected Tuple, got {:?}", ty.kind);
11560 };
11561 assert_eq!(elems.len(), 2);
11562 assert!(matches!(
11563 elems[0].kind,
11564 crate::TypeAnnotationKind::Name { .. }
11565 ));
11566 assert!(matches!(
11567 elems[1].kind,
11568 crate::TypeAnnotationKind::Name { .. }
11569 ));
11570 }
11571
11572 #[test]
11573 fn parse_singleton_tuple_type() {
11574 let (ast, diags) = parse_str("let p: [number] = null;");
11575 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11576 let ty = type_of_let(single_stmt(&ast));
11577 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11578 panic!("expected Tuple");
11579 };
11580 assert_eq!(elems.len(), 1);
11581 }
11582
11583 #[test]
11584 fn parse_nested_tuple_type() {
11585 let (ast, diags) = parse_str("let p: [[number, string], boolean] = null;");
11586 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11587 let ty = type_of_let(single_stmt(&ast));
11588 let crate::TypeAnnotationKind::Tuple(ref outer) = ty.kind else {
11589 panic!("expected outer Tuple");
11590 };
11591 assert_eq!(outer.len(), 2);
11592 let crate::TypeAnnotationKind::Tuple(ref inner) = outer[0].kind else {
11593 panic!("expected inner Tuple");
11594 };
11595 assert_eq!(inner.len(), 2);
11596 }
11597
11598 #[test]
11599 fn parse_array_of_tuple_type() {
11600 let (ast, diags) = parse_str("let xs: [string, number][] = null;");
11601 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11602 let ty = type_of_let(single_stmt(&ast));
11603 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11604 panic!("expected outer Array, got {:?}", ty.kind);
11605 };
11606 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Tuple(_)));
11607 }
11608
11609 #[test]
11610 fn parse_trailing_comma_tuple_type() {
11611 let (ast, diags) = parse_str("let p: [number, string,] = null;");
11612 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11613 let ty = type_of_let(single_stmt(&ast));
11614 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11615 panic!("expected Tuple");
11616 };
11617 assert_eq!(elems.len(), 2);
11618 }
11619
11620 #[test]
11621 fn parse_empty_tuple_type_rejected() {
11622 let (_ast, diags) = parse_str("let p: [] = null;");
11623 assert!(
11624 diags.iter().any(|d| d
11625 .message
11626 .contains("tuple types must have at least one element")),
11627 "expected empty-tuple diagnostic, got: {diags:?}"
11628 );
11629 }
11630
11631 #[test]
11632 fn parse_labeled_tuple_drops_labels() {
11633 let (ast, diags) = parse_str("let p: [first: number, string] = null;");
11634 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11635 let ty = type_of_let(single_stmt(&ast));
11636 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11637 panic!("expected Tuple, got {:?}", ty.kind);
11638 };
11639 assert_eq!(elems.len(), 2);
11640 assert!(
11641 elems
11642 .iter()
11643 .all(|e| matches!(e.kind, crate::TypeAnnotationKind::Name { .. }))
11644 );
11645 }
11646
11647 #[test]
11648 fn parse_labeled_optional_tuple_element() {
11649 let (ast, diags) = parse_str("let p: [first: number, second?: string] = null;");
11650 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11651 let ty = type_of_let(single_stmt(&ast));
11652 let crate::TypeAnnotationKind::Tuple(elements) = &ty.kind else {
11653 panic!("expected tuple")
11654 };
11655 assert!(matches!(
11656 elements[1].kind,
11657 crate::TypeAnnotationKind::Optional(_)
11658 ));
11659 }
11660
11661 #[test]
11662 fn parse_readonly_binds_to_the_postfix_type() {
11663 let (ast, diags) = parse_str("let p: readonly number[][] | null = null;");
11664 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11665 let ty = type_of_let(single_stmt(&ast));
11666 let crate::TypeAnnotationKind::Union(ref members) = ty.kind else {
11667 panic!("expected Union, got {:?}", ty.kind);
11668 };
11669 let crate::TypeAnnotationKind::Readonly(ref operand) = members[0].kind else {
11670 panic!("expected Readonly, got {:?}", members[0].kind);
11671 };
11672 let crate::TypeAnnotationKind::Array(ref inner) = operand.kind else {
11673 panic!("expected Array, got {:?}", operand.kind);
11674 };
11675 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Array(_)));
11676 }
11677
11678 #[test]
11679 fn parse_readonly_as_a_type_name_is_not_the_operator() {
11680 let (ast, diags) = parse_str("let p: readonly = null;");
11681 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11682 let ty = type_of_let(single_stmt(&ast));
11683 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11684 }
11685
11686 #[test]
11687 fn parse_generic_one_arg() {
11688 let (ast, diags) = parse_str("let x: Foo<number> = null;");
11689 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11690 let ty = type_of_let(single_stmt(&ast));
11691 let crate::TypeAnnotationKind::Name { ref name, ref args } = ty.kind else {
11692 panic!("expected Name, got {:?}", ty.kind);
11693 };
11694 assert_eq!(name.span, crate::Span::new(F, 7, 10).unwrap());
11695 assert_eq!(ty.span, crate::Span::new(F, 7, 18).unwrap());
11696 assert_eq!(args.len(), 1);
11697 assert!(matches!(
11698 args[0].kind,
11699 crate::TypeAnnotationKind::Name { .. }
11700 ));
11701 }
11702
11703 #[test]
11704 fn parse_generic_two_args() {
11705 let (ast, diags) = parse_str("let m: Map<string, number> = null;");
11706 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11707 let ty = type_of_let(single_stmt(&ast));
11708 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11709 panic!("expected Name");
11710 };
11711 assert_eq!(args.len(), 2);
11712 }
11713
11714 #[test]
11715 fn parse_generic_nested() {
11716 let (ast, diags) = parse_str("let b: Box<Box<T>> = null;");
11717 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11718 let ty = type_of_let(single_stmt(&ast));
11719 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11720 panic!("expected outer Name");
11721 };
11722 assert_eq!(args.len(), 1);
11723 let crate::TypeAnnotationKind::Name {
11724 args: ref inner, ..
11725 } = args[0].kind
11726 else {
11727 panic!("expected inner Name");
11728 };
11729 assert_eq!(inner.len(), 1);
11730 }
11731
11732 #[test]
11733 fn parse_generic_array_postfix() {
11734 let (ast, diags) = parse_str("let xs: Foo<number>[] = null;");
11735 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11736 let ty = type_of_let(single_stmt(&ast));
11737 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11738 panic!("expected Array");
11739 };
11740 assert!(
11741 matches!(inner.kind, crate::TypeAnnotationKind::Name { ref args, .. } if args.len() == 1)
11742 );
11743 }
11744
11745 #[test]
11746 fn parse_generic_empty_diagnoses() {
11747 let (_ast, diags) = parse_str("let x: Foo<> = null;");
11748 assert!(
11749 diags
11750 .iter()
11751 .any(|d| d.message.contains("empty generic argument list")),
11752 "expected empty-generic-args diagnostic, got: {diags:?}"
11753 );
11754 }
11755
11756 #[test]
11757 fn parse_generic_trailing_comma_ok() {
11758 let (_ast, diags) = parse_str("let x: Foo<number,> = null;");
11759 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11760 }
11761
11762 #[test]
11763 fn parse_generic_missing_close_diagnoses() {
11764 let (_ast, diags) = parse_str("let x: Foo<number = null;");
11765 assert!(
11766 diags
11767 .iter()
11768 .any(|d| d.message.contains("expected `,` or `>`")),
11769 "expected unterminated-generic diagnostic, got: {diags:?}"
11770 );
11771 }
11772
11773 #[test]
11774 fn parse_qualified_two_segments() {
11775 let (ast, diags) = parse_str("let x: Foo.Bar = null;");
11776 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11777 let ty = type_of_let(single_stmt(&ast));
11778 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11779 panic!("expected Qualified, got {:?}", ty.kind);
11780 };
11781 assert_eq!(path.len(), 2);
11782 assert_eq!(path[0].span, crate::Span::new(F, 7, 10).unwrap());
11784 assert_eq!(path[1].span, crate::Span::new(F, 11, 14).unwrap());
11785 assert!(args.is_empty());
11786 assert_eq!(ty.span, crate::Span::new(F, 7, 14).unwrap());
11787 }
11788
11789 #[test]
11790 fn parse_qualified_three_segments() {
11791 let (ast, diags) = parse_str("let x: Foo.Bar.Baz = null;");
11792 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11793 let ty = type_of_let(single_stmt(&ast));
11794 let crate::TypeAnnotationKind::Qualified { ref path, .. } = ty.kind else {
11795 panic!("expected Qualified, got {:?}", ty.kind);
11796 };
11797 assert_eq!(path.len(), 3);
11798 }
11799
11800 #[test]
11801 fn parse_qualified_with_generic_args() {
11802 let (ast, diags) = parse_str("let x: Foo.Box<number> = null;");
11803 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11804 let ty = type_of_let(single_stmt(&ast));
11805 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11806 panic!("expected Qualified, got {:?}", ty.kind);
11807 };
11808 assert_eq!(path.len(), 2);
11809 assert_eq!(args.len(), 1);
11810 assert!(matches!(
11811 args[0].kind,
11812 crate::TypeAnnotationKind::Name { .. }
11813 ));
11814 }
11815
11816 #[test]
11817 fn parse_qualified_with_array_postfix() {
11818 let (ast, diags) = parse_str("let xs: Foo.Bar[] = null;");
11819 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11820 let ty = type_of_let(single_stmt(&ast));
11821 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11822 panic!("expected Array, got {:?}", ty.kind);
11823 };
11824 assert!(matches!(
11825 inner.kind,
11826 crate::TypeAnnotationKind::Qualified { .. }
11827 ));
11828 }
11829
11830 #[test]
11831 fn parse_qualified_trailing_dot_diagnoses() {
11832 let (_ast, diags) = parse_str("let x: Foo. = null;");
11833 assert!(
11834 diags.iter().any(|d| d
11835 .message
11836 .contains("expected identifier after `.` in type name")),
11837 "expected dotted-name diagnostic, got: {diags:?}"
11838 );
11839 }
11840
11841 #[test]
11842 fn parse_qualified_void_root_rejected_dot() {
11843 let (ast, diags) = parse_str("function f(): void.Bar { }");
11844 assert!(!diags.is_empty(), "expected a diagnostic for `void.Bar`");
11845 let _ = ast;
11846 }
11847
11848 fn union_members(ty: &crate::TypeAnnotation) -> &[crate::TypeAnnotation] {
11849 match &ty.kind {
11850 crate::TypeAnnotationKind::Union(members) => members,
11851 other => panic!("expected Union, got {other:?}"),
11852 }
11853 }
11854
11855 #[test]
11856 fn parse_union_two_members() {
11857 let (ast, diags) = parse_str("let x: number | string = null;");
11858 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11859 let ty = type_of_let(single_stmt(&ast));
11860 let members = union_members(ty);
11861 assert_eq!(members.len(), 2);
11862 assert!(matches!(
11863 members[0].kind,
11864 crate::TypeAnnotationKind::Name { .. }
11865 ));
11866 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
11868 members[1].kind,
11869 crate::TypeAnnotationKind::Name { .. }
11870 ));
11871 assert_eq!(members[1].span, crate::Span::new(F, 16, 22).unwrap()); assert_eq!(ty.span, crate::Span::new(F, 7, 22).unwrap());
11873 }
11874
11875 #[test]
11876 fn parse_union_three_members() {
11877 let (ast, diags) = parse_str("let x: A | B | C = null;");
11878 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11879 let ty = type_of_let(single_stmt(&ast));
11880 let members = union_members(ty);
11881 assert_eq!(members.len(), 3);
11882 for m in members {
11883 assert!(matches!(m.kind, crate::TypeAnnotationKind::Name { .. }));
11884 }
11885 }
11886
11887 #[test]
11888 fn parse_union_lower_than_array() {
11889 let (ast, diags) = parse_str("let x: number[] | string = null;");
11890 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11891 let ty = type_of_let(single_stmt(&ast));
11892 let members = union_members(ty);
11893 assert_eq!(members.len(), 2);
11894 assert!(
11895 matches!(members[0].kind, crate::TypeAnnotationKind::Array(_)),
11896 "expected first member to be Array, got {:?}",
11897 members[0].kind
11898 );
11899 assert!(matches!(
11900 members[1].kind,
11901 crate::TypeAnnotationKind::Name { .. }
11902 ));
11903 }
11904
11905 #[test]
11906 fn parse_union_with_null_member() {
11907 let (ast, diags) = parse_str("let x: number | null = null;");
11908 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11909 let ty = type_of_let(single_stmt(&ast));
11910 let members = union_members(ty);
11911 assert_eq!(members.len(), 2);
11912 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
11914 members[1].kind,
11915 crate::TypeAnnotationKind::Name { .. }
11916 ));
11917 assert_eq!(members[1].span, crate::Span::new(F, 16, 20).unwrap()); }
11919
11920 #[test]
11921 fn parse_union_in_function_return_type() {
11922 let (ast, diags) = parse_str("function f(): number | string { }");
11923 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11924 let stmt = single_stmt(&ast);
11925 match stmt.kind {
11926 crate::StmtKind::Function {
11927 ref return_type, ..
11928 } => {
11929 let return_type = return_type.as_ref().expect("plain return type");
11930 let members = union_members(return_type);
11931 assert_eq!(members.len(), 2);
11932 }
11933 _ => panic!("expected Function"),
11934 }
11935 }
11936
11937 #[test]
11938 fn parse_union_in_function_param_type() {
11939 let (ast, diags) = parse_str("function f(x: number | string): void { }");
11940 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11941 let stmt = single_stmt(&ast);
11942 match stmt.kind {
11943 crate::StmtKind::Function { ref params, .. } => {
11944 let p_ty = params[0].ty.as_ref().expect("param type");
11945 let members = union_members(p_ty);
11946 assert_eq!(members.len(), 2);
11947 }
11948 _ => panic!("expected Function"),
11949 }
11950 }
11951
11952 #[test]
11953 fn parse_union_in_object_field_type() {
11954 let (ast, diags) = parse_str("let p: { x: number | string } = null;");
11955 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11956 let ty = type_of_let(single_stmt(&ast));
11957 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11958 panic!("expected Object, got {:?}", ty.kind);
11959 };
11960 assert_eq!(fields.len(), 1);
11961 let members = union_members(&fields[0].ty);
11962 assert_eq!(members.len(), 2);
11963 }
11964
11965 #[test]
11966 fn parse_union_leading_pipe_is_forgiven() {
11967 let (ast, diags) = parse_str("let x: | number | string = null;");
11968 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11969 let ty = type_of_let(single_stmt(&ast));
11970 let members = union_members(ty);
11971 assert_eq!(members.len(), 2);
11972 }
11973
11974 #[test]
11975 fn parse_union_leading_pipe_single_member_unwraps() {
11976 let (ast, diags) = parse_str("let x: | number = null;");
11977 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11978 let ty = type_of_let(single_stmt(&ast));
11979 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11980 }
11981
11982 #[test]
11983 fn parse_union_trailing_pipe_diagnoses() {
11984 let (_, diags) = parse_str("let x: number | = null;");
11985 assert!(!diags.is_empty());
11986 assert_eq!(diags[0].message, "expected type");
11987 }
11988
11989 #[test]
11990 fn snapshot_union_type_mix() {
11991 let (ast, diags) = parse_str("let xs: number[] | string | null = null;");
11992 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11993 insta::assert_debug_snapshot!(ast);
11994 }
11995
11996 #[test]
11997 fn parse_object_type_duplicate_field_diagnoses() {
11998 let (_, diags) = parse_str("let p: { readonly x: number; x: string } = null;");
11999 assert!(!diags.is_empty());
12000 assert!(
12001 diags
12002 .iter()
12003 .any(|d| d.message == "duplicate field `x` in object type"),
12004 "expected duplicate-field diagnostic, got: {diags:?}"
12005 );
12006 }
12007
12008 #[test]
12009 fn snapshot_object_and_array_literals() {
12010 let (ast, diags) = parse_str(r#"let mix = [{ a: 1, b: "hi" }, {}];"#);
12011 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12012 insta::assert_debug_snapshot!(ast);
12013 }
12014
12015 #[test]
12016 fn snapshot_function_with_while() {
12017 let source = "function loop(n: number): void {\n while (n > 0) {\n log(n);\n }\n}";
12018 let (ast, diags) = parse_str(source);
12019 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12020 insta::assert_debug_snapshot!(ast);
12021 }
12022
12023 fn parse_arrow_const(
12024 source: &str,
12025 ) -> (
12026 Ast,
12027 Vec<crate::ParamDecl>,
12028 Option<crate::TypeAnnotation>,
12029 crate::ArrowBody,
12030 ) {
12031 let (ast, diags) = parse_str(source);
12032 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12033 assert_eq!(ast.top_level.len(), 1);
12034 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12035 let value = match &stmt.kind {
12036 StmtKind::Const { value, .. } => *value,
12037 _ => panic!("expected const declaration"),
12038 };
12039 let value = match ast.try_expr(value).unwrap().kind {
12040 ExprKind::FunctionExpression { function, .. } => function,
12041 _ => value,
12042 };
12043 let expr = ast.try_expr(value).unwrap().clone();
12044 let (params, return_type, body) = match expr.kind {
12045 ExprKind::Arrow {
12046 params,
12047 return_type,
12048 body,
12049 ..
12050 } => (params, return_type, body),
12051 other => panic!("expected Arrow, got {other:?}"),
12052 };
12053 (ast, params, return_type, body)
12054 }
12055
12056 #[test]
12057 fn parses_anonymous_function_expression_as_arrow() {
12058 let (ast, params, return_type, body) =
12059 parse_arrow_const("const f = function (x: number): number { return x + 1; };");
12060 assert_eq!(params.len(), 1);
12061 assert_eq!(params[0].name.name, "x");
12062 assert!(return_type.is_some());
12063 assert!(matches!(body, crate::ArrowBody::Block(_)));
12064 let _ = ast;
12065 }
12066
12067 #[test]
12068 fn parses_function_expression_with_no_params() {
12069 let (_ast, params, _return_type, body) =
12070 parse_arrow_const("const f = function (): void {};");
12071 assert!(params.is_empty());
12072 assert!(matches!(body, crate::ArrowBody::Block(_)));
12073 }
12074
12075 #[test]
12077 fn parses_named_function_expression_signature() {
12078 let (_ast, params, _return_type, _body) =
12079 parse_arrow_const("const f = function named(x: number): number { return x + 1; };");
12080 assert_eq!(params.len(), 1);
12081 }
12082
12083 #[test]
12085 fn accepts_a_named_function_expression_that_calls_itself() {
12086 let (_ast, diags) =
12087 parse_str("const f = function bar(x: number): number { return bar(x); };");
12088 assert!(diags.is_empty(), "{diags:?}");
12089 }
12090
12091 #[test]
12093 fn accepts_a_named_function_expression_using_the_name_as_a_property() {
12094 let (_ast, diags) = parse_str("const f = function bar(o: O): number { return o.bar; };");
12095 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12096 }
12097
12098 #[test]
12100 fn accepts_a_named_function_expression_using_the_name_as_an_object_key() {
12101 let (_ast, diags) = parse_str(
12102 "const f = function bar(x: number): number { const o = { bar: 1 }; return o.bar + x; };",
12103 );
12104 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12105 }
12106
12107 #[test]
12109 fn accepts_a_named_function_expression_with_its_name_in_a_string() {
12110 let (_ast, diags) =
12111 parse_str("const f = function bar(x: number): string { return \"bar\"; };");
12112 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12113 }
12114
12115 #[test]
12117 fn accepts_a_self_call_inside_a_template_substitution() {
12118 let (_ast, diags) =
12119 parse_str("const f = function bar(x: number): string { return `${bar(0)}`; };");
12120 assert!(diags.is_empty(), "{diags:?}");
12121 }
12122
12123 #[test]
12124 fn rejects_a_generic_function_expression() {
12125 let (_ast, diags) = parse_str("const f = function <T>(x: T): T { return x; };");
12126 assert!(
12127 diags
12128 .iter()
12129 .any(|d| d.message.contains("generic function expressions")),
12130 "generic function expression should be rejected: {diags:?}"
12131 );
12132 }
12133
12134 #[test]
12135 fn accepts_this_inside_a_function_expression_in_a_class_method() {
12136 let (_ast, diags) = parse_str(
12137 "class C { x: number = 1; m(): number { \
12138 const f = function (this: { x: number }): number { return this.x; }; return f(); } }",
12139 );
12140 assert!(diags.is_empty(), "{diags:?}");
12141 }
12142
12143 #[test]
12144 fn retains_this_boundary_inside_method_shorthand() {
12145 let (ast, diags) = parse_str(
12146 "class C { x: number = 1; m(): number { \
12147 const o = { x: 2, g(): number { return this.x; } }; return o.g(); } }",
12148 );
12149 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12150 assert!((0..ast.exprs_len()).any(|index| matches!(
12151 ast.try_expr(crate::ExprId(index as u32)).unwrap().kind,
12152 ExprKind::ThisOutsideReceiver
12153 )));
12154 }
12155
12156 #[test]
12157 fn this_still_parses_in_an_ordinary_class_method() {
12158 let (_ast, diags) = parse_str("class C { x: number = 1; m(): number { return this.x; } }");
12159 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12160 }
12161
12162 #[test]
12164 fn function_keyword_still_parses_as_an_object_key() {
12165 let (_ast, diags) = parse_str("let o = { function: 1 };");
12166 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12167 }
12168
12169 fn object_literal_member_values(source: &str) -> (Ast, Vec<crate::ExprId>) {
12170 let (ast, diags) = parse_str(source);
12171 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12172 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12173 let value = match &stmt.kind {
12174 StmtKind::Const { value, .. } => *value,
12175 other => panic!("expected a const declaration, got {other:?}"),
12176 };
12177 let members = match &ast.try_expr(value).unwrap().kind {
12178 ExprKind::ObjectLiteral { members } => members
12179 .iter()
12180 .map(crate::ObjectLiteralMember::value)
12181 .collect::<Vec<_>>(),
12182 other => panic!("expected ObjectLiteral, got {other:?}"),
12183 };
12184 (ast, members)
12185 }
12186
12187 #[test]
12188 fn parses_object_literal_method_shorthand_as_arrow() {
12189 let (ast, members) =
12190 object_literal_member_values("const o = { m(x: number): number { return x; } };");
12191 assert_eq!(members.len(), 1);
12192 match &ast.try_expr(members[0]).unwrap().kind {
12193 ExprKind::Arrow { params, body, .. } => {
12194 assert_eq!(params.len(), 1);
12195 assert_eq!(params[0].name.name, "x");
12196 assert!(matches!(body, crate::ArrowBody::Block(_)));
12197 }
12198 other => panic!("expected Arrow, got {other:?}"),
12199 }
12200 }
12201
12202 #[test]
12204 fn parses_method_shorthand_with_a_keyword_name() {
12205 let (ast, members) =
12206 object_literal_member_values("const o = { if(x: number): number { return x; } };");
12207 assert!(matches!(
12208 ast.try_expr(members[0]).unwrap().kind,
12209 ExprKind::Arrow { .. }
12210 ));
12211 }
12212
12213 #[test]
12214 fn parses_method_shorthand_alongside_plain_and_shorthand_properties() {
12215 let (ast, members) =
12216 object_literal_member_values("const o = { a: 1, m(): void {}, b: 2 };");
12217 assert_eq!(members.len(), 3);
12218 assert!(matches!(
12219 ast.try_expr(members[1]).unwrap().kind,
12220 ExprKind::Arrow { .. }
12221 ));
12222 }
12223
12224 #[test]
12225 fn parses_bare_ident_arrow_expression_body() {
12226 let (_ast, params, return_type, body) = parse_arrow_const("const f = x => x + 1;");
12227 assert_eq!(params.len(), 1);
12228 assert_eq!(params[0].name.name, "x");
12229 assert!(params[0].ty.is_none());
12230 assert!(return_type.is_none());
12231 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12232 }
12233
12234 #[test]
12235 fn parses_zero_param_arrow() {
12236 let (_ast, params, return_type, body) = parse_arrow_const("const f = () => 0;");
12237 assert!(params.is_empty());
12238 assert!(return_type.is_none());
12239 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12240 }
12241
12242 #[test]
12243 fn parses_paren_unannotated_arrow() {
12244 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x) => x;");
12245 assert_eq!(params.len(), 1);
12246 assert_eq!(params[0].name.name, "x");
12247 assert!(params[0].ty.is_none());
12248 }
12249
12250 #[test]
12251 fn parses_typed_param_arrow() {
12252 let (_ast, params, return_type, body) =
12253 parse_arrow_const("const f = (x: number) => x * 2;");
12254 assert_eq!(params.len(), 1);
12255 assert!(params[0].ty.is_some());
12256 assert!(return_type.is_none());
12257 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12258 }
12259
12260 #[test]
12261 fn parses_typed_param_and_return_arrow() {
12262 let (_ast, params, return_type, body) =
12263 parse_arrow_const("const f = (x: number): number => x * 2;");
12264 assert_eq!(params.len(), 1);
12265 assert!(params[0].ty.is_some());
12266 assert!(return_type.is_some());
12267 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12268 }
12269
12270 #[test]
12271 fn parses_multi_param_unannotated_arrow() {
12272 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x, y) => x + y;");
12273 assert_eq!(params.len(), 2);
12274 assert_eq!(params[0].name.name, "x");
12275 assert_eq!(params[1].name.name, "y");
12276 assert!(params[0].ty.is_none());
12277 assert!(params[1].ty.is_none());
12278 }
12279
12280 #[test]
12281 fn parses_multi_param_typed_arrow() {
12282 let (_ast, params, _return_type, _body) =
12283 parse_arrow_const("const f = (x: number, y: number) => x + y;");
12284 assert_eq!(params.len(), 2);
12285 assert!(params[0].ty.is_some());
12286 assert!(params[1].ty.is_some());
12287 }
12288
12289 #[test]
12290 fn parses_block_body_arrow() {
12291 let (_ast, params, _return_type, body) =
12292 parse_arrow_const("const f = (x: number) => { return x; };");
12293 assert_eq!(params.len(), 1);
12294 assert!(matches!(body, crate::ArrowBody::Block(_)));
12295 }
12296
12297 #[test]
12298 fn rejects_typed_bare_ident_arrow() {
12299 let (_ast, diags) = parse_str("const f = x: number => x;");
12300 assert!(
12301 !diags.is_empty(),
12302 "expected diagnostics for `x: number => …`",
12303 );
12304 }
12305
12306 #[test]
12307 fn arrow_inside_call_argument_parses() {
12308 let (ast, diags) = parse_str("f((x: number) => x + 1, 2);");
12309 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12310 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12311 let call = match &stmt.kind {
12312 StmtKind::Expr(eid) => ast.try_expr(*eid).unwrap(),
12313 _ => panic!("expected expression statement"),
12314 };
12315 let args = match &call.kind {
12316 ExprKind::Call { args, .. } => args.clone(),
12317 other => panic!("expected Call, got {other:?}"),
12318 };
12319 assert_eq!(args.len(), 2);
12320 assert!(matches!(
12321 ast.try_expr(args[0]).unwrap().kind,
12322 ExprKind::Arrow { .. }
12323 ));
12324 }
12325
12326 #[test]
12327 fn function_decl_param_still_requires_annotation() {
12328 let (_ast, diags) = parse_str("function f(x): number { return x; }");
12329 assert!(
12330 diags.iter().any(|d| d.message.contains("type annotation")),
12331 "expected `parameter requires a type annotation` diagnostic, got {diags:?}",
12332 );
12333 }
12334
12335 #[test]
12336 fn paren_expression_still_parses_when_no_arrow() {
12337 let (ast, diags) = parse_str("const a = (1 + 2);");
12338 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12339 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12340 let value = match &stmt.kind {
12341 StmtKind::Const { value, .. } => *value,
12342 _ => panic!("expected const declaration"),
12343 };
12344 assert!(matches!(
12345 ast.try_expr(value).unwrap().kind,
12346 ExprKind::Paren(_)
12347 ));
12348 }
12349
12350 #[test]
12351 fn parse_named_import_single() {
12352 let (ast, diags) = parse_str("import { v4 } from \"submilli:uuid\";");
12353 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12354 assert_eq!(ast.top_level.len(), 1);
12355 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12356 let (module, kind) = match &stmt.kind {
12357 StmtKind::Import { module, kind, .. } => (module, kind),
12358 other => panic!("expected Import, got {other:?}"),
12359 };
12360 assert_eq!(module, "submilli:uuid");
12361 let specs = match kind {
12362 ImportKind::Named(specs) => specs,
12363 other => panic!("expected Named, got {other:?}"),
12364 };
12365 assert_eq!(specs.len(), 1);
12366 assert_eq!(specs[0].imported_name.name, "v4");
12367 assert_eq!(specs[0].local_name.name, "v4");
12368 }
12369
12370 #[test]
12371 fn parse_named_import_multi_and_alias() {
12372 let (ast, diags) =
12373 parse_str("import { v4, v7 as makeId, validate } from \"submilli:uuid\";");
12374 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12375 let specs = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12376 StmtKind::Import {
12377 kind: ImportKind::Named(s),
12378 ..
12379 } => s.clone(),
12380 other => panic!("expected named import, got {other:?}"),
12381 };
12382 assert_eq!(specs.len(), 3);
12383 assert_eq!(specs[0].imported_name.name, "v4");
12384 assert_eq!(specs[0].local_name.name, "v4");
12385 assert_eq!(specs[1].imported_name.name, "v7");
12386 assert_eq!(specs[1].local_name.name, "makeId");
12387 assert_eq!(specs[2].imported_name.name, "validate");
12388 assert_eq!(specs[2].local_name.name, "validate");
12389 }
12390
12391 #[test]
12392 fn parse_namespace_import() {
12393 let (ast, diags) = parse_str("import uuid from \"submilli:uuid\";");
12394 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12395 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12396 StmtKind::Import { kind, .. } => kind.clone(),
12397 other => panic!("expected import, got {other:?}"),
12398 };
12399 let local = match kind {
12400 ImportKind::Namespace { local_name } => local_name,
12401 other => panic!("expected Namespace, got {other:?}"),
12402 };
12403 assert_eq!(local.name, "uuid");
12404 }
12405
12406 #[test]
12407 fn parse_wildcard_namespace_import_synonym() {
12408 let (ast, diags) = parse_str("import * as uuid from \"submilli:uuid\";");
12409 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12410 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12411 StmtKind::Import { kind, .. } => kind.clone(),
12412 other => panic!("expected import, got {other:?}"),
12413 };
12414 let local = match kind {
12415 ImportKind::Namespace { local_name } => local_name,
12416 other => panic!("expected Namespace, got {other:?}"),
12417 };
12418 assert_eq!(local.name, "uuid");
12419 }
12420
12421 #[test]
12422 fn parse_wildcard_namespace_import_without_as_rejected() {
12423 let (_ast, diags) = parse_str("import * from \"submilli:uuid\";");
12424 assert!(
12425 diags
12426 .iter()
12427 .any(|d| d.message.contains("expected `as <name>`")),
12428 "expected missing-as diagnostic, got: {diags:?}",
12429 );
12430 }
12431
12432 #[test]
12433 fn parse_combined_default_and_named_rejected() {
12434 let (_ast, diags) = parse_str("import uuid, { v4 } from \"submilli:uuid\";");
12435 assert!(
12436 diags
12437 .iter()
12438 .any(|d| d.message.contains("combining default and named")),
12439 "expected combined-import diagnostic, got: {diags:?}",
12440 );
12441 }
12442
12443 #[test]
12444 fn parse_side_effect_import_rejected() {
12445 let (_ast, diags) = parse_str("import \"submilli:uuid\";");
12446 assert!(
12447 diags
12448 .iter()
12449 .any(|d| d.message.contains("side-effect-only imports")),
12450 "expected side-effect-import diagnostic, got: {diags:?}",
12451 );
12452 }
12453
12454 #[test]
12455 fn parse_empty_specifier_list_rejected() {
12456 let (_ast, diags) = parse_str("import {} from \"submilli:uuid\";");
12457 assert!(
12458 diags
12459 .iter()
12460 .any(|d| d.message.contains("empty import specifier list")),
12461 "expected empty-specifier diagnostic, got: {diags:?}",
12462 );
12463 }
12464
12465 #[test]
12466 fn parse_import_inside_function_body_rejected() {
12467 let (_ast, diags) =
12468 parse_str("function main(): void { import { v4 } from \"submilli:uuid\"; }");
12469 assert!(
12470 diags
12471 .iter()
12472 .any(|d| d.message.contains("must appear at the top of the file")),
12473 "expected top-level diagnostic, got: {diags:?}",
12474 );
12475 }
12476
12477 #[test]
12478 fn parse_missing_from_rejected() {
12479 let (_ast, diags) = parse_str("import { v4 } \"submilli:uuid\";");
12480 assert!(
12481 diags.iter().any(|d| d.message.contains("expected `from`")),
12482 "expected missing-from diagnostic, got: {diags:?}",
12483 );
12484 }
12485
12486 #[test]
12487 fn parse_missing_module_specifier_rejected() {
12488 let (_ast, diags) = parse_str("import { v4 } from;");
12489 assert!(
12490 diags
12491 .iter()
12492 .any(|d| d.message.contains("expected module specifier")),
12493 "expected missing-module diagnostic, got: {diags:?}",
12494 );
12495 }
12496
12497 #[test]
12498 fn parse_export_function_marks_exported() {
12499 let (ast, diags) = parse_str("export function main(): void {}");
12502 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12503 let stmt = single_stmt(&ast);
12504 assert!(
12505 matches!(stmt.kind, crate::StmtKind::Function { .. }),
12506 "expected Function decl, got {:?}",
12507 stmt.kind,
12508 );
12509 assert_eq!(ast.exported_decls.len(), 1, "one exported decl");
12510 assert_eq!(ast.exported_decls[0].stmt, ast.top_level[0]);
12511 }
12512
12513 #[test]
12514 fn parse_export_const_type_interface_enum_are_marked() {
12515 for src in [
12516 "export const X: number = 1;",
12517 "export type Id = string;",
12518 "export interface P { x: number; }",
12519 "export enum E { A, B }",
12520 ] {
12521 let (ast, diags) = parse_str(src);
12522 assert!(diags.is_empty(), "unexpected diags for {src:?}: {diags:?}");
12523 assert_eq!(ast.top_level.len(), 1, "one decl for {src:?}");
12524 assert_eq!(ast.exported_decls.len(), 1, "one exported decl for {src:?}");
12525 }
12526 }
12527
12528 #[test]
12529 fn parse_export_default_is_rejected_with_focused_message() {
12530 let (_ast, diags) = parse_str("export default function main(): void {}");
12531 assert!(
12532 diags
12533 .iter()
12534 .any(|d| d.message.contains("`export default` is not supported")),
12535 "expected focused export-default diagnostic, got: {diags:?}",
12536 );
12537 assert!(
12538 !diags.iter().any(|d| d.message.contains("expected `;`")),
12539 "the spurious missing-semicolon error must be gone: {diags:?}",
12540 );
12541 }
12542
12543 #[test]
12544 fn parse_export_list_parses_as_export_from() {
12545 let (ast, diags) = parse_str("function main(): void {}\nexport { main };");
12548 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12549 assert_eq!(ast.top_level.len(), 2, "function + export-from");
12550 let export = ast.try_stmt(ast.top_level[1]).unwrap();
12551 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12552 panic!("expected ExportFrom, got {:?}", export.kind);
12553 };
12554 assert!(source.is_none(), "bare `export {{ x }}` has no source");
12555 assert_eq!(specs.len(), 1);
12556 assert_eq!(specs[0].imported_name.name, "main");
12557 assert_eq!(specs[0].local_name.name, "main");
12558 }
12559
12560 #[test]
12561 fn parse_export_from_with_source_and_alias_parses() {
12562 let (ast, diags) = parse_str("export { foo, bar as baz } from \"./util\";");
12563 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12564 let export = ast.try_stmt(ast.top_level[0]).unwrap();
12565 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12566 panic!("expected ExportFrom, got {:?}", export.kind);
12567 };
12568 let Some((module, _)) = source else {
12569 panic!("expected a `from` source");
12570 };
12571 assert_eq!(module, "./util");
12572 assert_eq!(specs.len(), 2);
12573 assert_eq!(specs[1].imported_name.name, "bar");
12574 assert_eq!(specs[1].local_name.name, "baz");
12575 }
12576
12577 #[test]
12578 fn parse_export_inside_function_body_rejected() {
12579 let (_ast, diags) = parse_str("function main(): void { export const X: number = 1; }");
12580 assert!(
12581 diags
12582 .iter()
12583 .any(|d| d.message.contains("must appear at the top of the file")),
12584 "expected top-level diagnostic, got: {diags:?}",
12585 );
12586 }
12587
12588 #[test]
12589 fn parse_throw_with_expression() {
12590 let (ast, diags) = parse_str(r#"throw new Error("oops");"#);
12591 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12592 let stmt = single_stmt(&ast);
12593 let crate::StmtKind::Throw { value } = stmt.kind else {
12594 panic!("expected Throw, got {:?}", stmt.kind);
12595 };
12596 assert!(matches!(
12597 ast.try_expr(value).unwrap().kind,
12598 crate::ExprKind::New { .. }
12599 ));
12600 }
12601
12602 #[test]
12603 fn parse_new_with_dotted_callee() {
12604 let (ast, diags) = parse_str("new Temporal.Duration({});");
12605 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12606 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12607 panic!("expected expression statement");
12608 };
12609 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12610 panic!("expected New");
12611 };
12612 let crate::ExprKind::FieldAccess { name, .. } = &ast.try_expr(*callee).unwrap().kind else {
12613 panic!("expected FieldAccess callee");
12614 };
12615 assert_eq!(name.name, "Duration");
12616 }
12617
12618 #[test]
12619 fn parse_new_with_three_level_dotted_callee() {
12620 let (ast, diags) = parse_str("new Foo.Bar.Baz();");
12621 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12622 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12623 panic!("expected expression statement");
12624 };
12625 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12626 panic!("expected New");
12627 };
12628 let crate::ExprKind::FieldAccess { receiver, name } = &ast.try_expr(*callee).unwrap().kind
12629 else {
12630 panic!("expected outer FieldAccess");
12631 };
12632 assert_eq!(name.name, "Baz");
12633 let crate::ExprKind::FieldAccess { name: mid_name, .. } =
12634 &ast.try_expr(*receiver).unwrap().kind
12635 else {
12636 panic!("expected inner FieldAccess");
12637 };
12638 assert_eq!(mid_name.name, "Bar");
12639 }
12640
12641 #[test]
12642 fn parse_new_with_trailing_dot_diagnoses() {
12643 let (_ast, diags) = parse_str("new Foo.;");
12644 assert!(
12645 diags.iter().any(|d| d
12646 .message
12647 .contains("expected identifier after `.` in constructor name")),
12648 "expected trailing-dot diagnostic, got: {diags:?}",
12649 );
12650 }
12651
12652 #[test]
12653 fn throw_without_expression_diagnoses() {
12654 let (_ast, diags) = parse_str("throw;");
12655 assert!(
12656 diags
12657 .iter()
12658 .any(|d| d.message == "expected expression after `throw`"),
12659 "expected bare-throw diagnostic, got: {diags:?}",
12660 );
12661 }
12662
12663 #[test]
12664 fn parse_try_catch() {
12665 let (ast, diags) = parse_str("try { } catch (e: Error) { }");
12666 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12667 let stmt = single_stmt(&ast);
12668 let crate::StmtKind::Try {
12669 body,
12670 ref catches,
12671 ref finally,
12672 } = stmt.kind
12673 else {
12674 panic!("expected Try, got {:?}", stmt.kind);
12675 };
12676 assert!(matches!(
12677 ast.try_stmt(body).unwrap().kind,
12678 crate::StmtKind::Block(ref v) if v.is_empty()
12679 ));
12680 let [clause] = catches.as_slice() else {
12681 panic!("expected one catch clause, got {catches:?}");
12682 };
12683 assert_eq!(clause.binding.name, "e");
12684 assert!(clause.ty.is_some());
12685 assert!(finally.is_none());
12686 }
12687
12688 #[test]
12689 fn parse_try_finally() {
12690 let (ast, diags) = parse_str("try { } finally { }");
12691 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12692 let stmt = single_stmt(&ast);
12693 let crate::StmtKind::Try {
12694 ref catches,
12695 ref finally,
12696 ..
12697 } = stmt.kind
12698 else {
12699 panic!("expected Try");
12700 };
12701 assert!(catches.is_empty());
12702 let finally_id = finally.expect("finally block");
12703 assert!(matches!(
12704 ast.try_stmt(finally_id).unwrap().kind,
12705 crate::StmtKind::Block(_)
12706 ));
12707 }
12708
12709 #[test]
12710 fn parse_try_catch_finally() {
12711 let (ast, diags) = parse_str("try { } catch (e: Error) { } finally { }");
12712 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12713 let stmt = single_stmt(&ast);
12714 let crate::StmtKind::Try {
12715 ref catches,
12716 ref finally,
12717 ..
12718 } = stmt.kind
12719 else {
12720 panic!("expected Try");
12721 };
12722 assert_eq!(catches.len(), 1);
12723 assert!(finally.is_some());
12724 }
12725
12726 #[test]
12727 fn bare_try_diagnoses() {
12728 let (_ast, diags) = parse_str("try { }");
12729 assert!(
12730 diags
12731 .iter()
12732 .any(|d| d.message == "try requires at least one of `catch` or `finally`"),
12733 "expected bare-try diagnostic, got: {diags:?}",
12734 );
12735 }
12736
12737 #[test]
12738 fn parse_untyped_catch() {
12739 let (ast, diags) = parse_str("try { } catch (e) { }");
12740 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12741 let stmt = single_stmt(&ast);
12742 let crate::StmtKind::Try {
12743 ref catches,
12744 ref finally,
12745 ..
12746 } = stmt.kind
12747 else {
12748 panic!("expected Try");
12749 };
12750 let [clause] = catches.as_slice() else {
12751 panic!("expected one catch clause, got {catches:?}");
12752 };
12753 assert_eq!(clause.binding.name, "e");
12754 assert!(clause.ty.is_none());
12755 assert!(finally.is_none());
12756 }
12757
12758 #[test]
12759 fn parse_multiple_catch_clauses() {
12760 let (ast, diags) = parse_str("try { } catch (e: HttpError) { } catch (f) { }");
12761 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12762 let stmt = single_stmt(&ast);
12763 let crate::StmtKind::Try {
12764 ref catches,
12765 ref finally,
12766 ..
12767 } = stmt.kind
12768 else {
12769 panic!("expected Try");
12770 };
12771 let [first, second] = catches.as_slice() else {
12772 panic!("expected two catch clauses, got {catches:?}");
12773 };
12774 assert_eq!(first.binding.name, "e");
12775 assert!(first.ty.is_some());
12776 assert_eq!(second.binding.name, "f");
12777 assert!(second.ty.is_none());
12778 assert!(finally.is_none());
12779 }
12780
12781 #[test]
12782 fn catch_after_finally_diagnoses() {
12783 let (_ast, diags) = parse_str("try { } finally { } catch (e: Error) { }");
12784 assert!(
12785 !diags.is_empty(),
12786 "expected diagnostic for catch-after-finally"
12787 );
12788 }
12789
12790 #[test]
12791 fn catch_missing_open_paren_diagnoses() {
12792 let (_ast, diags) = parse_str("try { } catch e: Error) { }");
12793 assert!(
12794 diags
12795 .iter()
12796 .any(|d| d.message == "expected `(` after `catch`"),
12797 "expected open-paren diagnostic, got: {diags:?}",
12798 );
12799 }
12800
12801 fn class_members(ast: &Ast) -> &[crate::ClassMember] {
12802 match &single_stmt(ast).kind {
12803 StmtKind::ClassDecl { members, .. } => members,
12804 other => panic!("expected ClassDecl, got {other:?}"),
12805 }
12806 }
12807
12808 #[test]
12809 fn parse_animal_class() {
12810 let src = r#"
12811class Animal {
12812 name: string;
12813 private sound: string;
12814 readonly species: string;
12815
12816 constructor(name: string, sound: string, species: string) {
12817 this.name = name;
12818 this.sound = sound;
12819 this.species = species;
12820 }
12821
12822 speak(): string {
12823 return this.name + " says " + this.sound;
12824 }
12825}
12826"#;
12827 let (ast, diags) = parse_str(src);
12828 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12829 let StmtKind::ClassDecl {
12830 name,
12831 generics,
12832 extends,
12833 implements,
12834 members,
12835 ..
12836 } = &single_stmt(&ast).kind
12837 else {
12838 panic!("expected ClassDecl");
12839 };
12840 assert_eq!(name.name, "Animal");
12841 assert!(generics.is_empty());
12842 assert!(extends.is_none());
12843 assert!(implements.is_empty());
12844 assert_eq!(members.len(), 5);
12845
12846 let crate::ClassMember::Field {
12847 name, modifiers, ..
12848 } = &members[0]
12849 else {
12850 panic!("member 0 should be a field");
12851 };
12852 assert_eq!(name.name, "name");
12853 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12854 assert!(modifiers.readonly.is_none());
12855
12856 let crate::ClassMember::Field { modifiers, .. } = &members[1] else {
12857 panic!("member 1 should be a field");
12858 };
12859 assert_eq!(modifiers.visibility, crate::Visibility::Private);
12860
12861 let crate::ClassMember::Field { modifiers, .. } = &members[2] else {
12862 panic!("member 2 should be a field");
12863 };
12864 assert!(modifiers.readonly.is_some());
12865 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12866
12867 assert!(matches!(
12868 &members[3],
12869 crate::ClassMember::Constructor { .. }
12870 ));
12871
12872 let crate::ClassMember::Method { name, .. } = &members[4] else {
12873 panic!("member 4 should be a method");
12874 };
12875 assert_eq!(name.name, "speak");
12876 }
12877
12878 #[test]
12879 fn parse_dog_class_extends_super_and_void_method() {
12880 let src = r#"
12881class Dog extends Animal {
12882 private tricks: string[];
12883
12884 constructor(name: string) {
12885 super(name, "woof", "canis familiaris");
12886 this.tricks = [];
12887 }
12888
12889 learn(trick: string): void {
12890 this.tricks.push(trick);
12891 }
12892}
12893"#;
12894 let (ast, diags) = parse_str(src);
12895 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12896 let StmtKind::ClassDecl {
12897 extends, members, ..
12898 } = &single_stmt(&ast).kind
12899 else {
12900 panic!("expected ClassDecl");
12901 };
12902 assert!(extends.is_some(), "Dog should extend Animal");
12903 assert_eq!(members.len(), 3);
12904 assert!(matches!(&members[0], crate::ClassMember::Field { .. }));
12905 assert!(matches!(
12906 &members[1],
12907 crate::ClassMember::Constructor { .. }
12908 ));
12909 assert!(matches!(&members[2], crate::ClassMember::Method { .. }));
12910 }
12911
12912 #[test]
12913 fn parse_class_implements_list() {
12914 let (ast, diags) = parse_str("class C implements I, J { run(): void {} }");
12915 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12916 let StmtKind::ClassDecl { implements, .. } = &single_stmt(&ast).kind else {
12917 panic!("expected ClassDecl");
12918 };
12919 assert_eq!(implements.len(), 2);
12920 }
12921
12922 #[test]
12923 fn parse_class_extends_and_implements() {
12924 let (ast, diags) = parse_str("class C extends B implements I { run(): void {} }");
12925 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12926 let StmtKind::ClassDecl {
12927 extends,
12928 implements,
12929 ..
12930 } = &single_stmt(&ast).kind
12931 else {
12932 panic!("expected ClassDecl");
12933 };
12934 assert!(extends.is_some());
12935 assert_eq!(implements.len(), 1);
12936 }
12937
12938 #[test]
12939 fn parse_class_generics_optional_field_and_initializer() {
12940 let (ast, diags) = parse_str("class Box<T> { value?: T; items: T[] = []; }");
12941 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12942 let StmtKind::ClassDecl {
12943 generics, members, ..
12944 } = &single_stmt(&ast).kind
12945 else {
12946 panic!("expected ClassDecl");
12947 };
12948 assert_eq!(generics.len(), 1);
12949 assert_eq!(generics[0].name, "T");
12950
12951 let crate::ClassMember::Field {
12952 optional,
12953 initializer,
12954 ..
12955 } = &members[0]
12956 else {
12957 panic!("member 0 should be a field");
12958 };
12959 assert!(*optional);
12960 assert!(initializer.is_none());
12961
12962 let crate::ClassMember::Field { initializer, .. } = &members[1] else {
12963 panic!("member 1 should be a field");
12964 };
12965 assert!(initializer.is_some());
12966 }
12967
12968 #[test]
12969 fn export_class_is_marked() {
12970 let (ast, diags) = parse_str("export class Foo {}");
12971 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12972 assert_eq!(ast.top_level.len(), 1);
12973 assert_eq!(ast.exported_decls.len(), 1);
12974 assert!(matches!(
12975 &single_stmt(&ast).kind,
12976 StmtKind::ClassDecl { .. }
12977 ));
12978 }
12979
12980 #[test]
12981 fn parameterless_index_signature_is_reported_from_its_member_start() {
12982 for (source, expected) in [
12983 ("interface P { readonly []: number }", "readonly []"),
12984 ("let p: { readonly []: number } = {};", "readonly []"),
12985 ("let q: { []: number } = {};", "[]"),
12986 ] {
12987 let (_ast, diags) = parse_str(source);
12988 let diag = diags
12989 .iter()
12990 .find(|d| d.message.contains("must declare exactly one parameter"))
12991 .unwrap_or_else(|| panic!("no diagnostic for {source}"));
12992 let reported = &source[diag.span.start as usize..diag.span.end as usize];
12993 assert_eq!(reported, expected, "{source}");
12994 }
12995 }
12996
12997 #[test]
12998 fn unterminated_object_type_expects_closing_brace() {
12999 let (_ast, diags) = parse_str("let p: { a: number\n");
13000 assert!(
13001 diags.iter().any(|d| d.message == "expected `}`"),
13002 "{diags:?}"
13003 );
13004 }
13005
13006 #[test]
13007 fn conflicting_visibility_modifiers_diagnose() {
13008 let (_ast, diags) = parse_str("class C { public private x: number; }");
13009 assert!(
13010 diags
13011 .iter()
13012 .any(|d| d.message.contains("at most one visibility modifier")),
13013 "got: {diags:?}",
13014 );
13015 }
13016
13017 #[test]
13018 fn duplicate_readonly_modifier_diagnoses() {
13019 let (_ast, diags) = parse_str("class C { readonly readonly x: number; }");
13020 assert!(
13021 diags
13022 .iter()
13023 .any(|d| d.message.contains("duplicate `readonly`")),
13024 "got: {diags:?}",
13025 );
13026 }
13027
13028 #[test]
13029 fn field_named_like_a_modifier_is_allowed() {
13030 let (ast, diags) = parse_str("class C { private: number; }");
13031 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13032 let crate::ClassMember::Field {
13033 name, modifiers, ..
13034 } = &class_members(&ast)[0]
13035 else {
13036 panic!("expected a field");
13037 };
13038 assert_eq!(name.name, "private");
13039 assert_eq!(modifiers.visibility, crate::Visibility::Public);
13040 }
13041
13042 #[test]
13043 fn method_named_get_is_allowed() {
13044 let (ast, diags) = parse_str("class C { get(): number { return 1; } }");
13045 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13046 let crate::ClassMember::Method { name, .. } = &class_members(&ast)[0] else {
13047 panic!("expected a method");
13048 };
13049 assert_eq!(name.name, "get");
13050 }
13051
13052 #[test]
13053 fn static_method_parses() {
13054 let (ast, diags) = parse_str("class C { static f(): number { return 1; } }");
13055 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13056 let crate::ClassMember::Method {
13057 name, modifiers, ..
13058 } = &class_members(&ast)[0]
13059 else {
13060 panic!("expected a method");
13061 };
13062 assert_eq!(name.name, "f");
13063 assert!(modifiers.static_span.is_some());
13064 }
13065
13066 #[test]
13067 fn static_readonly_field_parses() {
13068 let (ast, diags) = parse_str("class C { static readonly x: number = 1; }");
13069 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13070 let crate::ClassMember::Field {
13071 name,
13072 modifiers,
13073 initializer,
13074 ..
13075 } = &class_members(&ast)[0]
13076 else {
13077 panic!("expected a field");
13078 };
13079 assert_eq!(name.name, "x");
13080 assert!(modifiers.static_span.is_some());
13081 assert!(modifiers.readonly.is_some());
13082 assert!(initializer.is_some());
13083 }
13084
13085 #[test]
13086 fn private_static_method_parses() {
13087 let (ast, diags) = parse_str("class C { private static f(): void {} }");
13088 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13089 let crate::ClassMember::Method { modifiers, .. } = &class_members(&ast)[0] else {
13090 panic!("expected a method");
13091 };
13092 assert_eq!(modifiers.visibility, crate::Visibility::Private);
13093 assert!(modifiers.static_span.is_some());
13094 }
13095
13096 #[test]
13097 fn method_named_static_is_allowed() {
13098 let (ast, diags) = parse_str("class C { static(): number { return 1; } }");
13099 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13100 let crate::ClassMember::Method {
13101 name, modifiers, ..
13102 } = &class_members(&ast)[0]
13103 else {
13104 panic!("expected a method");
13105 };
13106 assert_eq!(name.name, "static");
13107 assert!(modifiers.static_span.is_none());
13108 }
13109
13110 #[test]
13111 fn visibility_after_static_diagnoses() {
13112 let (_ast, diags) = parse_str("class C { static private f(): void {} }");
13113 assert!(
13114 diags
13115 .iter()
13116 .any(|d| d.message.contains("`private` must come before `static`")),
13117 "got: {diags:?}",
13118 );
13119 }
13120
13121 #[test]
13122 fn duplicate_static_modifier_diagnoses() {
13123 let (_ast, diags) = parse_str("class C { static static f(): void {} }");
13124 assert!(
13125 diags
13126 .iter()
13127 .any(|d| d.message.contains("duplicate `static` modifier")),
13128 "got: {diags:?}",
13129 );
13130 }
13131
13132 #[test]
13133 fn static_accessor_is_rejected() {
13134 let (_ast, diags) = parse_str("class C { static get x(): number { return 1; } }");
13135 assert!(
13136 diags
13137 .iter()
13138 .any(|d| d.message.contains("static accessors are not supported")),
13139 "got: {diags:?}",
13140 );
13141 }
13142
13143 #[test]
13144 fn static_constructor_is_rejected() {
13145 let (_ast, diags) = parse_str("class C { static constructor() {} }");
13146 assert!(
13147 diags
13148 .iter()
13149 .any(|d| d.message.contains("a constructor cannot be `static`")),
13150 "got: {diags:?}",
13151 );
13152 }
13153
13154 #[test]
13155 fn optional_static_field_is_rejected() {
13156 let (_ast, diags) = parse_str("class C { static x?: number; }");
13157 assert!(
13158 diags
13159 .iter()
13160 .any(|d| d.message.contains("a static field cannot be optional")),
13161 "got: {diags:?}",
13162 );
13163 }
13164
13165 #[test]
13166 fn async_function_names_the_synchronous_rewrite() {
13167 let (_ast, diags) = parse_str(
13168 "async function main(): Promise<string> {\n const x = await f();\n return x;\n}",
13169 );
13170 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13171 assert_eq!(
13172 messages,
13173 [
13174 "`async` functions are not supported",
13175 "`await` is not supported"
13176 ],
13177 "{diags:?}"
13178 );
13179 assert_eq!(
13180 diags[0].help,
13181 [
13182 "calls return their value directly: remove `async`, write the return type `T` \
13183 instead of `Promise<T>`, and drop `await`"
13184 ]
13185 );
13186 assert_eq!(
13187 diags[1].help,
13188 ["calls return their value directly: remove `await`"]
13189 );
13190 }
13191
13192 #[test]
13193 fn async_is_rejected_on_every_function_form() {
13194 for source in [
13195 "export async function f(): void {}",
13196 "const f = async (): void => {};",
13197 "const f = async x => x;",
13198 "const f = async function (): void {};",
13199 "class C { async run(): void {} }",
13200 "class C { static async run(): void {} }",
13201 ] {
13202 let (_ast, diags) = parse_str(source);
13203 assert_eq!(
13204 diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13205 ["`async` functions are not supported"],
13206 "{source}: {diags:?}"
13207 );
13208 }
13209 }
13210
13211 #[test]
13212 fn await_before_a_template_is_reported() {
13213 for source in ["const a = await `hi`;", "const a = await `a${1}b`;"] {
13214 let (_ast, diags) = parse_str(source);
13215 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13216 assert_eq!(
13217 messages,
13218 ["`await` is not supported"],
13219 "{source}: {diags:?}"
13220 );
13221 }
13222 }
13223
13224 #[test]
13225 fn async_and_await_stay_ordinary_names() {
13226 for source in [
13227 "const async = 1; const y = async + 1;",
13228 "function async(n: number): number { return n; } async(1);",
13229 "const await = 2; const z = await * 2;",
13230 "const await = (n: number): number => n; const a = await(1);",
13231 "const await = [1, 2]; const b = await[0];",
13232 "const async = { k: 1 }; const c = \"k\" in async; const d = async in ({ o: 1 });",
13233 "const async: unknown = 1; const n = async as (number);",
13234 "const await: unknown = 1; const m = await as number;",
13235 "const await = 1; const t = `${await}`;",
13236 "class C { async(): void {} async: number = 1; }",
13237 ] {
13238 let (_ast, diags) = parse_str(source);
13239 assert!(diags.is_empty(), "{source}: {diags:?}");
13240 }
13241 }
13242
13243 #[test]
13244 fn an_async_object_literal_method_names_the_rewrite_once() {
13245 assert_eq!(
13246 crate::typechecker::rules::test_util::compile_messages(
13247 "function main(): string {\n const o = { async run(): string { return \"a\"; } };\n return o.run();\n}\n"
13248 ),
13249 ["`async` functions are not supported"]
13250 );
13251 let (_ast, diags) = parse_str("const o = { async run(): string { return \"a\"; } };");
13252 assert_eq!(
13253 diags[0].help,
13254 [
13255 "calls return their value directly: remove `async`, write the return type `T` \
13256 instead of `Promise<T>`, and drop `await`"
13257 ]
13258 );
13259 }
13260
13261 #[test]
13262 fn await_in_an_async_object_literal_method_is_reported_too() {
13263 let (_ast, diags) =
13264 parse_str("const o = { async run(): string { const x = await f(); return x; } };");
13265 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13266 assert_eq!(
13267 messages,
13268 [
13269 "`async` functions are not supported",
13270 "`await` is not supported"
13271 ],
13272 "{diags:?}"
13273 );
13274 }
13275
13276 #[test]
13277 fn every_async_member_form_reports_async() {
13278 for source in [
13279 "const o = { async *run(): void {} };",
13280 "class C { async *run(): void {} }",
13281 "const o = { async [k](): void {} };",
13282 "const o = { async in(): void {} };",
13283 "class C { async as(): void {} }",
13284 ] {
13285 let (_ast, diags) = parse_str(source);
13286 assert_eq!(
13287 diags[0].message, "`async` functions are not supported",
13288 "{source}: {diags:?}"
13289 );
13290 }
13291 }
13292
13293 #[test]
13294 fn async_string_key_methods_report_only_async() {
13295 for source in [
13296 "const o = { async \"s\"(): string { return \"a\"; } };",
13297 "class C { async \"s\"(): string { return \"a\"; } }",
13298 ] {
13299 let (_ast, diags) = parse_str(source);
13300 assert_eq!(
13301 diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13302 ["`async` functions are not supported"],
13303 "{source}: {diags:?}"
13304 );
13305 }
13306 }
13307
13308 #[test]
13309 fn async_before_a_line_break_is_a_member_of_its_own() {
13310 for source in [
13311 "class C {\n async\n run(): void {}\n}",
13312 "const o = { async\n run(): void {} };",
13313 ] {
13314 let (_ast, diags) = parse_str(source);
13315 assert!(
13316 diags
13317 .iter()
13318 .all(|d| d.message != "`async` functions are not supported"),
13319 "{source}: {diags:?}"
13320 );
13321 }
13322 }
13323
13324 #[test]
13325 fn async_stays_an_object_literal_key() {
13326 let messages = crate::typechecker::rules::test_util::compile_messages(
13327 "function main(): number {\n const async = 2;\n const a = { async: 1 };\n const b = { async };\n const c = { async(): number { return 3; } };\n const d = { async, e: async * 2 };\n return a.async + b.async + c.async() + d.e;\n}\n",
13328 );
13329 assert!(messages.is_empty(), "{messages:?}");
13330 }
13331
13332 #[test]
13333 fn for_await_names_the_rewrite_once() {
13334 assert_eq!(
13335 crate::typechecker::rules::test_util::compile_messages(
13336 "import * as fs from \"submilli:fs\";\nfunction main(): string {\n for await (const e of fs.list(\".\", false)) { }\n return \"x\";\n}\n"
13337 ),
13338 ["`for await` is not supported"]
13339 );
13340 let (ast, diags) = parse_str("for await (const e of [1]) { }");
13341 assert_eq!(
13342 diags[0].help,
13343 ["calls return their values directly: remove `await` and write `for (const e of …)`"]
13344 );
13345 assert_eq!(ast.top_level.len(), 1, "the loop is still read");
13346 }
13347
13348 #[test]
13349 fn a_broken_switch_leaves_later_declarations_at_the_top_level() {
13350 assert_eq!(
13351 crate::typechecker::rules::test_util::compile_messages(
13352 "function g(): void { switch (1) { case 1 } }\nclass C {}\nfunction main() { return \"x\"; }\n"
13353 ),
13354 [
13355 "expected `:` after `case` label",
13356 "expected expression",
13357 "expected `:` and return type"
13358 ]
13359 );
13360 }
13361
13362 #[test]
13363 fn every_switch_arm_error_restores_the_nesting() {
13364 for switch in [
13365 "switch (1) { case 1 }",
13366 "switch (1) { case : break; }",
13367 "switch (1) { default: break; default: break; }",
13368 "switch (1) { default }",
13369 "switch (1) { x; }",
13370 "switch (1) { case 1: }",
13371 ] {
13372 let source = format!("function g(): void {{ {switch} }}\nclass C {{}}\n");
13373 let (_ast, diags) = parse_str(&source);
13374 assert!(
13375 diags.iter().all(|d| !d.message.contains("top level")),
13376 "{switch}: {diags:?}"
13377 );
13378 }
13379 }
13380
13381 #[test]
13382 fn protected_member_is_rejected() {
13383 let (_ast, diags) = parse_str("class C { protected x: number; }");
13384 assert!(
13385 diags
13386 .iter()
13387 .any(|d| d.message.contains("`protected` is not supported")),
13388 "got: {diags:?}",
13389 );
13390 }
13391
13392 #[test]
13393 fn abstract_member_is_rejected() {
13394 let (_ast, diags) = parse_str("class C { abstract run(): void; }");
13395 assert!(
13396 diags
13397 .iter()
13398 .any(|d| d.message.contains("abstract classes are not supported")),
13399 "got: {diags:?}",
13400 );
13401 }
13402
13403 #[test]
13404 fn getter_parses_as_accessor() {
13405 let (ast, diags) = parse_str("class C { get value(): number { return 1; } }");
13406 assert!(diags.is_empty(), "got: {diags:?}");
13407 let crate::ClassMember::Accessor { kind, name, .. } = &class_members(&ast)[0] else {
13408 panic!("expected an accessor member");
13409 };
13410 assert_eq!(*kind, crate::AccessorKind::Get);
13411 assert_eq!(name.name, "value");
13412 }
13413
13414 #[test]
13415 fn getter_without_return_type_is_rejected() {
13416 let (ast, diags) = parse_str("class C { get value() { return 1; } }");
13417 assert_eq!(diags.len(), 1, "got: {diags:?}");
13418 assert_eq!(diags[0].message, "expected `:` and return type");
13419 assert_eq!(diags[0].help, vec!["get name(): T { … }".to_string()]);
13420 assert!(
13421 matches!(
13422 &class_members(&ast)[0],
13423 crate::ClassMember::Accessor {
13424 return_type: None,
13425 ..
13426 }
13427 ),
13428 "the getter is still recovered as a member"
13429 );
13430 }
13431
13432 #[test]
13433 fn setter_parses_as_accessor() {
13434 let (ast, diags) = parse_str("class C { set value(v: number) {} }");
13435 assert!(diags.is_empty(), "got: {diags:?}");
13436 let crate::ClassMember::Accessor { kind, param, .. } = &class_members(&ast)[0] else {
13437 panic!("expected an accessor member");
13438 };
13439 assert_eq!(*kind, crate::AccessorKind::Set);
13440 assert!(param.is_some(), "setter should have a parameter");
13441 }
13442
13443 #[test]
13444 fn param_property_modifier_outside_constructor_is_rejected() {
13445 let (_ast, diags) = parse_str("function f(public x: number): void {}");
13446 assert!(
13447 diags.iter().any(|d| d
13448 .message
13449 .contains("parameter properties are only allowed in a constructor")),
13450 "got: {diags:?}",
13451 );
13452 }
13453
13454 #[test]
13455 fn constructor_return_type_is_rejected() {
13456 let (_ast, diags) = parse_str("class C { constructor(): void {} }");
13457 assert!(
13458 diags.iter().any(|d| d
13459 .message
13460 .contains("constructor cannot declare a return type")),
13461 "got: {diags:?}",
13462 );
13463 }
13464
13465 #[test]
13466 fn field_initializer_without_type_is_rejected() {
13467 let (_ast, diags) = parse_str("class C { x = 1; }");
13468 assert!(
13469 diags
13470 .iter()
13471 .any(|d| d.message.contains("class fields require a type annotation")),
13472 "got: {diags:?}",
13473 );
13474 }
13475
13476 #[test]
13477 fn this_outside_method_is_left_to_typechecker() {
13478 let (_ast, diags) = parse_str("function f(): string { return this.name; }");
13479 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13480 }
13481
13482 #[test]
13483 fn super_outside_method_is_rejected() {
13484 let (_ast, diags) = parse_str("function f(): void { super(1); }");
13485 assert!(
13486 diags.iter().any(|d| d
13487 .message
13488 .contains("`super` is only valid inside a class method or constructor body")),
13489 "got: {diags:?}",
13490 );
13491 }
13492
13493 #[test]
13494 fn this_inside_method_is_accepted_by_parser() {
13495 let (_ast, diags) = parse_str("class C { x: number; read(): number { return this.x; } }");
13496 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13497 }
13498 #[test]
13499 fn parse_void_expression_preserves_operand_and_precedence() {
13500 let (ast, diags) = parse_str("void effect() === undefined;");
13501 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13502 let expression = expr_of_single_stmt(&ast);
13503 let crate::ExprKind::Binary { lhs, rhs, .. } = expression.kind else {
13504 panic!("expected equality")
13505 };
13506 let crate::ExprKind::Void { operand } = ast.try_expr(lhs).unwrap().kind else {
13507 panic!("expected void")
13508 };
13509 assert!(matches!(
13510 ast.try_expr(operand).unwrap().kind,
13511 crate::ExprKind::Call { .. }
13512 ));
13513 assert!(matches!(
13514 ast.try_expr(rhs).unwrap().kind,
13515 crate::ExprKind::Identifier(_)
13516 ));
13517 }
13518
13519 #[test]
13520 fn parse_void_newline_and_object_operands() {
13521 for source in [
13522 "void\n0;",
13523 "void { value: 1 };",
13524 "void\n{ value: 1 };",
13525 "void /x/;",
13526 "void void 0;",
13527 ] {
13528 let (_, diags) = parse_str(source);
13529 assert!(diags.is_empty(), "{source}: {diags:?}");
13530 }
13531 let (_, diags) = parse_str("void; 0;");
13532 assert!(
13533 !diags.is_empty(),
13534 "an explicit semicolon cannot split void from its operand"
13535 );
13536 }
13537
13538 #[test]
13539 fn parse_optional_parameters_and_defaults_in_callable_forms() {
13540 for source in [
13541 "function f(x = 1): number { return x; }",
13542 "function f(x: number = 1, y: number): number { return y; }",
13543 "function f({ x } = { x: 1 }): number { return x; }",
13544 "const f = (x?: number) => x;",
13545 "const f = (x: number = effect()) => x;",
13546 "const f = function(x: number = effect()) { return x; };",
13547 "const f = { m(x: number = effect()): number { return x; } };",
13548 "const f = ([x = 1]: number[] = values) => x;",
13549 "let f: (x?: number, ...rest: string[]) => void = value;",
13550 "let f: { method?(x?: number): void } = value;",
13551 "function f(x: number = 1, y?: number): void {}",
13552 ] {
13553 let (_, diags) = parse_str(source);
13554 assert!(diags.is_empty(), "{source}: {diags:?}");
13555 }
13556 }
13557
13558 #[test]
13559 fn parse_invalid_optional_parameters() {
13560 for source in [
13561 "function f(x?: number, y: number): void {}",
13562 "const f = (x?: number, y: number) => y;",
13563 "let f: (x?: number, y: number) => void = value;",
13564 "function f(...x?: number[]): void {}",
13565 "function f(x?: number = 1): void {}",
13566 "const f = (x?: number = 1) => x;",
13567 "function f({x}?: T): void {}",
13568 ] {
13569 let (_, diags) = parse_str(source);
13570 assert!(!diags.is_empty(), "expected diagnostic for {source}");
13571 }
13572 }
13573
13574 #[test]
13575 fn parse_optional_tuple_forms_and_order() {
13576 for source in [
13577 "let x: [number, string?] = value;",
13578 "let x: [x?: number, y?: string] = value;",
13579 "let x: readonly [(number | null)?, string?] = value;",
13580 ] {
13581 let (_, diags) = parse_str(source);
13582 assert!(diags.is_empty(), "{source}: {diags:?}");
13583 }
13584 for source in [
13585 "let x: [number?, string] = value;",
13586 "let x: [x: number?] = value;",
13587 ] {
13588 let (_, diags) = parse_str(source);
13589 assert!(!diags.is_empty(), "expected diagnostic for {source}");
13590 }
13591 }
13592
13593 #[test]
13594 fn parse_array_pattern_defaults_preserve_holes() {
13595 let (ast, diags) = parse_str("const [, a = effect(), b] = values;");
13596 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13597 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
13598 panic!("expected pattern")
13599 };
13600 let crate::Binding::Array {
13601 elems, defaults, ..
13602 } = binding
13603 else {
13604 panic!("expected array")
13605 };
13606 assert_eq!(elems.len(), 3);
13607 assert_eq!(defaults.len(), elems.len());
13608 assert!(elems[0].is_none());
13609 assert!(defaults[0].is_none());
13610 assert!(defaults[1].is_some());
13611 assert!(defaults[2].is_none());
13612 }
13613
13614 #[test]
13615 fn parse_optional_class_method_forms() {
13616 let (ast, diags) =
13617 parse_str("class C { declared?(x?: number): void; implemented?(): void\n{} }");
13618 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13619 let members = class_members(&ast);
13620 assert!(matches!(
13621 &members[0],
13622 crate::ClassMember::Field {
13623 optional: true,
13624 initializer: None,
13625 ty: crate::TypeAnnotation {
13626 kind: crate::TypeAnnotationKind::Function { .. },
13627 ..
13628 },
13629 ..
13630 }
13631 ));
13632 assert!(matches!(
13633 &members[1],
13634 crate::ClassMember::Method { optional: true, .. }
13635 ));
13636 }
13637 #[test]
13638 fn defaults_retain_their_callable_receiver_scope() {
13639 for source in [
13640 "class C { value: number = 1; method(value: number = this.value): number { return value; } }",
13641 "class C { constructor(value: number = this.value) {} value: number = 1; }",
13642 "const f = function(this: { value: number }, value: number = this.value): number { return value; };",
13643 ] {
13644 let (ast, diagnostics) = parse_str(source);
13645 assert!(diagnostics.is_empty(), "{source}: {diagnostics:?}");
13646 assert!(
13647 ast.source_expressions()
13648 .iter()
13649 .any(|expr| matches!(expr.kind, ExprKind::This))
13650 );
13651 assert!(
13652 !ast.source_expressions()
13653 .iter()
13654 .any(|expr| matches!(expr.kind, ExprKind::ThisOutsideReceiver))
13655 );
13656 }
13657 }
13658}