1use crate::compiler_error::{CompileError, CompilerFailure, CompilerStage};
2use crate::{
3 ArrayLiteralElement, ArrowBody, Ast, BinOp, Binding, CatchClause, ClassMember, Diagnostic,
4 EnumInitializer, EnumMember, ExportedDecl, Expr, ExprId, ExprKind, FileId, Ident, ImportKind,
5 ImportSpecifier, ObjectLiteralField, ObjectLiteralMember, ObjectPatternField, ParamDecl,
6 PostfixOp, Severity, Span, Stmt, StmtId, StmtKind, SwitchCase, SwitchDefault, Token, TokenKind,
7 TypeAnnotation, TypeAnnotationField, TypeAnnotationKind, UnOp,
8};
9
10const MAX_ERRORS: usize = 20;
11const MAX_PARSE_DEPTH: usize = 128;
14
15const BUILT_IN_TYPE_NAMES: &[&str] = &[
17 "any",
18 "bigint",
19 "boolean",
20 "never",
21 "number",
22 "object",
23 "string",
24 "symbol",
25 "undefined",
26 "unknown",
27 "void",
28];
29
30#[derive(Clone, Copy, PartialEq)]
36enum TypePos {
37 Anywhere,
38 ArrowReturn,
39}
40
41pub fn parse(source: &str, tokens: Vec<Token>, file: FileId) -> (Ast, Vec<Diagnostic>) {
43 match parse_checked(source, tokens, file) {
44 Ok(result) => result,
45 Err(error) => (Ast::new(), error.into_diagnostics(file)),
46 }
47}
48
49pub fn parse_checked(
50 source: &str,
51 tokens: Vec<Token>,
52 file: FileId,
53) -> Result<(Ast, Vec<Diagnostic>), CompileError> {
54 crate::source::SourceError::check_source_len(source.len())
55 .map_err(|error| error.into_compiler_failure(CompilerStage::Parse))?;
56 validate_token_stream(source, &tokens, file).map_err(|message| CompilerFailure::Internal {
57 stage: CompilerStage::Parse,
58 span: None,
59 message: message.into(),
60 })?;
61 let eof = tokens
62 .last()
63 .cloned()
64 .unwrap_or_else(|| Token::new(TokenKind::Eof, Span::at(file)));
65 let mut p = Parser {
66 source,
67 tokens,
68 split_type_tokens: Vec::new(),
69 file,
70 pos: 0,
71 ast: Ast::new(),
72 diagnostics: Vec::new(),
73 block_depth: 0,
74 class_member_body_depth: 0,
75 function_expression_body_depth: 0,
76 recursion_depth: 0,
77 recursion_limit_span: None,
78 resume_at_next_member: false,
79 last_as_type_end: None,
80 trailing_declarators: Vec::new(),
81 eof,
82 fatal: None,
83 };
84 p.parse_program();
85 if p.fatal.is_none() {
86 p.reject_use_strict_with_non_simple_params();
87 }
88 if let Some(span) = p.recursion_limit_span {
89 p.fatal = Some(CompilerFailure::Limit {
90 stage: CompilerStage::Parse,
91 span: Some(span),
92 message: "parser recursion limit exceeded".into(),
93 help: vec!["simplify nested syntax or split it into separate declarations".into()],
94 });
95 }
96 if let Some(fatal) = p.fatal {
97 return Err(CompileError {
98 diagnostics: p.diagnostics,
99 fatal: Some(fatal),
100 });
101 }
102 p.ast
103 .validate_source(source, file)
104 .map_err(|error| CompileError {
105 diagnostics: p.diagnostics.clone(),
106 fatal: Some(error.into_compiler_failure(CompilerStage::Parse)),
107 })?;
108 crate::tree_height::check_syntax(&p.ast).map_err(|fatal| CompileError {
109 diagnostics: p.diagnostics.clone(),
110 fatal: Some(fatal),
111 })?;
112 Ok((p.ast, p.diagnostics))
113}
114
115fn validate_token_stream(source: &str, tokens: &[Token], file: FileId) -> Result<(), &'static str> {
116 if !matches!(tokens.last().map(|token| &token.kind), Some(TokenKind::Eof)) {
117 return Err("token stream must end with EOF");
118 }
119 let mut previous = 0;
120 for (index, token) in tokens.iter().enumerate() {
121 let span = token.span;
122 if span.file != file
123 || span.start < previous
124 || span.start > span.end
125 || source.get(span.start as usize..span.end as usize).is_none()
126 {
127 return Err("token span is outside its source or out of order");
128 }
129 if matches!(token.kind, TokenKind::Eof) && index != tokens.len() - 1 {
130 return Err("tokens follow EOF");
131 }
132 if let Some(doc) = &token.leading_doc {
133 let span = doc.span;
134 if span.file != file
135 || span.start > span.end
136 || span.end > token.span.start
137 || source.get(span.start as usize..span.end as usize) != Some(doc.text.as_str())
138 || !doc.text.starts_with("/**")
139 || !doc.text.ends_with("*/")
140 {
141 return Err("documentation metadata does not match its source");
142 }
143 }
144 previous = span.start;
145 }
146 Ok(())
147}
148
149pub(crate) struct Parser<'a> {
150 source: &'a str,
151 tokens: Vec<Token>,
152 split_type_tokens: Vec<(usize, Token)>,
153 file: FileId,
154 pos: usize,
155 ast: Ast,
156 diagnostics: Vec<Diagnostic>,
157 block_depth: u32,
159 class_member_body_depth: u32,
161 function_expression_body_depth: u32,
162 recursion_depth: usize,
163 recursion_limit_span: Option<Span>,
164 resume_at_next_member: bool,
168 last_as_type_end: Option<u32>,
172 trailing_declarators: Vec<StmtId>,
174 eof: Token,
175 fatal: Option<CompilerFailure>,
176}
177
178impl<'a> Parser<'a> {
179 fn span(&self, start: u32, end: u32) -> Span {
180 Span {
181 file: self.file,
182 start,
183 end,
184 }
185 }
186
187 fn type_name(&mut self, span: Span) -> Option<Ident> {
188 match span.text(self.source, self.file) {
189 Ok(name) => Some(Ident {
190 name: name.to_string(),
191 span,
192 }),
193 Err(error) => {
194 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
195 None
196 }
197 }
198 }
199
200 fn parse_program(&mut self) {
201 while !self.is_at_eof() {
202 if self.fatal.is_some() || self.error_count() >= MAX_ERRORS {
203 break;
204 }
205 if matches!(self.peek().kind, TokenKind::Semicolon) {
206 self.advance();
207 continue;
208 }
209 let pos_before = self.pos;
210 if let Some(id) = self.parse_statement() {
211 self.ast.top_level.push(id);
212 let trailing = self.take_trailing_declarators();
213 self.ast.top_level.extend(trailing);
214 } else {
215 if self.fatal.is_some() {
216 break;
217 }
218 if self.pos != pos_before && self.peek_starts_top_level_item() {
223 continue;
224 }
225 self.recover();
226 if self.pos != pos_before && matches!(self.peek().kind, TokenKind::RightBrace) {
227 self.advance();
228 continue;
229 }
230 if self.pos == pos_before && !self.is_at_eof() {
232 self.advance();
233 }
234 }
235 }
236 }
237
238 fn parse_statement(&mut self) -> Option<StmtId> {
239 self.trailing_declarators.clear();
240 self.with_recursion_limit(Self::parse_statement_inner)
241 }
242
243 fn parse_statement_inner(&mut self) -> Option<StmtId> {
244 self.reject_async_modifier();
245 match self.peek().kind {
246 TokenKind::Let => self.parse_let_or_const(false),
247 TokenKind::Const => self.parse_let_or_const(true),
248 TokenKind::Function => self.parse_function_decl(),
249 TokenKind::Class => self.parse_class_decl(),
250 TokenKind::Interface => self.parse_interface_decl(),
251 TokenKind::Enum => self.parse_enum_decl(),
252 TokenKind::Identifier if self.at_type_alias_head() => self.parse_type_alias_decl(),
253 TokenKind::Export => self.parse_export(),
254 TokenKind::Import => self.parse_import_decl(),
255 TokenKind::If => self.parse_if(),
256 TokenKind::While => self.parse_while(),
257 TokenKind::Do => self.parse_do_while(),
258 TokenKind::For => self.parse_for(),
259 TokenKind::Switch => self.parse_switch(),
260 TokenKind::Break => self.parse_break(),
261 TokenKind::Continue => self.parse_continue(),
262 TokenKind::Return => self.parse_return(),
263 TokenKind::Throw => self.parse_throw(),
264 TokenKind::Try => self.parse_try(),
265 TokenKind::LeftBrace => self.parse_block(),
266 _ => self.parse_expression_statement(),
267 }
268 }
269
270 fn parse_expression_statement(&mut self) -> Option<StmtId> {
271 let expr_id = self.parse_expression()?;
272 let expr_span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
273 if !self.at_statement_end() {
274 let what = if matches!(
275 parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.kind,
276 ExprKind::Assign { .. }
277 ) {
278 "assignment"
279 } else {
280 "expression"
281 };
282 self.error_at_peek(format!("expected `;` after {what}"));
283 return None;
284 }
285 let end = self.finish_statement();
286 let span = self.span(expr_span.start, end);
287 let kind = self.statement_kind_for(expr_id)?;
288 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
289 }
290
291 fn statement_kind_for(&mut self, expr_id: ExprId) -> Option<StmtKind> {
294 let ExprKind::Assign {
295 target,
296 op,
297 op_span,
298 value,
299 } = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?
300 .kind
301 .clone()
302 else {
303 return Some(StmtKind::Expr(expr_id));
304 };
305 Some(
306 match (
307 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?
308 .kind
309 .clone(),
310 op,
311 ) {
312 (ExprKind::Identifier(target), None) => StmtKind::Assign { target, value },
313 (ExprKind::Identifier(target), Some(op)) => StmtKind::CompoundAssign {
314 target,
315 op,
316 op_span,
317 value,
318 },
319 (ExprKind::FieldAccess { receiver, name }, None) => StmtKind::AssignField {
320 receiver,
321 field_name: name,
322 value,
323 },
324 (ExprKind::FieldAccess { receiver, name }, Some(op)) => {
325 StmtKind::CompoundAssignField {
326 receiver,
327 field_name: name,
328 op,
329 op_span,
330 value,
331 }
332 }
333 (ExprKind::IndexAccess { receiver, index }, None) => StmtKind::AssignIndex {
334 receiver,
335 index,
336 value,
337 },
338 (ExprKind::IndexAccess { receiver, index }, Some(op)) => {
339 StmtKind::CompoundAssignIndex {
340 receiver,
341 index,
342 op,
343 op_span,
344 value,
345 }
346 }
347 _ => return self.invariant_failure("invalid assignment target"),
348 },
349 )
350 }
351
352 fn parse_let_or_const(&mut self, is_const: bool) -> Option<StmtId> {
357 let mut doc = self.take_leading_doc();
358 let kw = self.advance();
359
360 let mut declarators = Vec::new();
361 let mut start = kw.span.start;
362 loop {
363 let Some(kind) = self.parse_declarator(is_const, doc.take()) else {
364 self.discard_detached_patterns(&declarators);
365 return None;
366 };
367 if !matches!(self.peek().kind, TokenKind::Comma) {
368 if !self.at_statement_end() {
369 self.error_at_peek("expected `;` after declaration");
370 self.discard_detached_patterns(&declarators);
371 return None;
372 }
373 let end = self.finish_statement();
374 declarators.push(self.push_declarator(kind, start, end)?);
375 break;
376 }
377 let end = self.prev_token_end();
378 declarators.push(self.push_declarator(kind, start, end)?);
379 self.advance();
380 start = self.peek().span.start;
381 }
382
383 let first = declarators.remove(0);
384 self.trailing_declarators = declarators;
385 Some(first)
386 }
387
388 fn push_declarator(&mut self, kind: StmtKind, start: u32, end: u32) -> Option<StmtId> {
389 let span = self.span(start, end);
390 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
391 }
392
393 fn take_trailing_declarators(&mut self) -> Vec<StmtId> {
397 std::mem::take(&mut self.trailing_declarators)
398 }
399
400 fn parse_declarator(
402 &mut self,
403 is_const: bool,
404 doc: Option<crate::DocComment>,
405 ) -> Option<StmtKind> {
406 let binding = match self.peek().kind {
407 TokenKind::LeftBrace | TokenKind::LeftBracket => Some(self.parse_binding()?),
408 _ => None,
409 };
410
411 let name = if binding.is_none() {
412 let name_tok =
413 self.expect_binding_identifier("expected identifier after `let`/`const`")?;
414 Some(self.ident_from_token(&name_tok))
415 } else {
416 None
417 };
418
419 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
420 self.advance();
421 Some(self.parse_type_annotation()?)
422 } else {
423 None
424 };
425
426 let implicit = !is_const
427 && ty.is_some()
428 && matches!(self.peek().kind, TokenKind::Semicolon | TokenKind::Comma);
429 let value = if let (true, Some(name)) = (implicit, &name) {
430 let value = parse_arena_result(
432 self.ast.try_push_expr(Expr {
433 kind: ExprKind::Identifier(Ident {
434 name: "undefined".to_string(),
435 span: name.span,
436 }),
437 span: name.span,
438 }),
439 &mut self.fatal,
440 )?;
441 self.ast.implicit_initializers.insert(value);
442 self.ast.synthetic_undefined.insert(value);
443 value
444 } else {
445 if !matches!(self.peek().kind, TokenKind::Equals) {
446 let (msg, help) = if is_const {
447 (
448 "`const` declaration requires an initializer",
449 vec!["const x: T = expr;".to_string()],
450 )
451 } else {
452 (
453 "`let` declaration requires an initializer",
454 vec![
455 "let x: T = expr;".to_string(),
456 "or declare one that starts empty: `let x: T | undefined;`".to_string(),
457 ],
458 )
459 };
460 self.error_at_peek_with_help(msg, help);
461 return None;
462 }
463 self.advance();
464 self.parse_expression()?
465 };
466
467 Some(match (binding, name) {
468 (Some(binding), _) => {
469 if is_const {
470 StmtKind::ConstPattern {
471 binding,
472 ty,
473 value,
474 doc,
475 }
476 } else {
477 StmtKind::LetPattern {
478 binding,
479 ty,
480 value,
481 doc,
482 }
483 }
484 }
485 (None, Some(name)) => {
486 if is_const {
487 StmtKind::Const {
488 name,
489 ty,
490 value,
491 doc,
492 }
493 } else {
494 StmtKind::Let {
495 name,
496 ty,
497 value,
498 doc,
499 }
500 }
501 }
502 (None, None) => return self.invariant_failure("either binding or name must be Some"),
503 })
504 }
505
506 fn parse_binding(&mut self) -> Option<Binding> {
508 match self.peek().kind {
509 TokenKind::LeftBrace => self.parse_object_binding(),
510 TokenKind::LeftBracket => self.parse_array_binding(),
511 _ => {
512 self.error_at_peek("expected `{` or `[` to start a destructuring pattern");
513 None
514 }
515 }
516 }
517
518 fn parse_object_binding(&mut self) -> Option<Binding> {
519 let open = self.advance();
520 let mut fields: Vec<ObjectPatternField> = Vec::new();
521 let mut rest: Option<Ident> = None;
522
523 if matches!(self.peek().kind, TokenKind::RightBrace) {
524 self.error_at_peek("empty object destructuring pattern");
525 return None;
526 }
527
528 loop {
529 if matches!(self.peek().kind, TokenKind::DotDotDot) {
530 let dots = self.advance();
531 let name_tok = self.expect_binding_identifier("expected identifier after `...`")?;
532 let name = self.ident_from_token(&name_tok);
533 if !matches!(self.peek().kind, TokenKind::RightBrace) {
534 self.error_at_peek_with_help(
535 "rest element must be the last element in a destructuring pattern",
536 vec!["move `...` after every other binding".to_string()],
537 );
538 return None;
539 }
540 rest = Some(Ident {
541 name: name.name,
542 span: self.span(dots.span.start, name.span.end),
543 });
544 break;
545 }
546
547 let source_tok = self.expect_property_name("expected field name in object pattern")?;
548 let source = self.ident_from_token(&source_tok);
549 let field_span_start = source.span.start;
550
551 let (local, field_end) = if matches!(self.peek().kind, TokenKind::Colon) {
552 self.advance();
553 match self.peek().kind {
554 TokenKind::LeftBrace | TokenKind::LeftBracket => {
555 self.error_at_peek_with_help(
556 "nested destructuring is not supported",
557 vec![
558 "destructure once and access nested fields explicitly: const { a } = obj; const inner = a.b;"
559 .to_string(),
560 ],
561 );
562 return None;
563 }
564 _ => {
565 let local_tok = self.expect_binding_identifier(
566 "expected identifier after `:` in pattern",
567 )?;
568 let local = self.ident_from_token(&local_tok);
569 let end = local.span.end;
570 (local, end)
571 }
572 }
573 } else {
574 if !matches!(source_tok.kind, TokenKind::Identifier) {
575 self.error_at_peek("expected `:` after keyword field name in object pattern");
576 return None;
577 }
578 self.reject_strict_mode_reserved_word(&source_tok);
579 self.reject_eval_or_arguments(source_tok.span);
580 let end = source.span.end;
581 (source.clone(), end)
582 };
583
584 let default = if matches!(self.peek().kind, TokenKind::Equals) {
585 self.advance();
586 Some(self.parse_expression()?)
587 } else {
588 None
589 };
590 let field_end = match default {
591 Some(value) => {
592 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?
593 .span
594 .end
595 }
596 None => field_end,
597 };
598
599 fields.push(ObjectPatternField {
600 source,
601 local,
602 default,
603 span: self.span(field_span_start, field_end),
604 });
605
606 match self.peek().kind {
607 TokenKind::Comma => {
608 self.advance();
609 if matches!(self.peek().kind, TokenKind::RightBrace) {
610 break;
611 }
612 }
613 TokenKind::RightBrace => break,
614 _ => {
615 self.error_at_peek("expected `,` or `}` in object pattern");
616 return None;
617 }
618 }
619 }
620
621 if !matches!(self.peek().kind, TokenKind::RightBrace) {
622 self.error_at_peek("expected `}` to close object pattern");
623 return None;
624 }
625 let close = self.advance();
626
627 Some(Binding::Object {
628 fields,
629 rest,
630 span: self.span(open.span.start, close.span.end),
631 })
632 }
633
634 fn parse_array_binding(&mut self) -> Option<Binding> {
635 let open = self.advance();
636 let mut elems: Vec<Option<Ident>> = Vec::new();
637 let mut defaults = Vec::new();
638 let mut rest: Option<Ident> = None;
639
640 if matches!(self.peek().kind, TokenKind::RightBracket) {
641 self.error_at_peek("empty array destructuring pattern");
642 return None;
643 }
644
645 loop {
646 match self.peek().kind {
647 TokenKind::Comma => {
648 elems.push(None);
649 defaults.push(None);
650 self.advance();
651 if matches!(self.peek().kind, TokenKind::RightBracket) {
652 break;
653 }
654 continue;
655 }
656 TokenKind::RightBracket => break,
657 TokenKind::DotDotDot => {
658 let dots = self.advance();
659 let name_tok =
660 self.expect_binding_identifier("expected identifier after `...`")?;
661 let name = self.ident_from_token(&name_tok);
662 if !matches!(self.peek().kind, TokenKind::RightBracket) {
663 self.error_at_peek_with_help(
664 "rest element must be the last element in a destructuring pattern",
665 vec!["move `...` after every other binding".to_string()],
666 );
667 return None;
668 }
669 rest = Some(Ident {
670 name: name.name,
671 span: self.span(dots.span.start, name.span.end),
672 });
673 break;
674 }
675 TokenKind::LeftBrace | TokenKind::LeftBracket => {
676 self.error_at_peek_with_help(
677 "nested destructuring is not supported",
678 vec![
679 "destructure once and access nested elements explicitly: const [a] = arr; const inner = a[0];"
680 .to_string(),
681 ],
682 );
683 return None;
684 }
685 _ => {
686 let name_tok =
687 self.expect_binding_identifier("expected identifier in array pattern")?;
688 let name = self.ident_from_token(&name_tok);
689
690 let default = if matches!(self.peek().kind, TokenKind::Equals) {
691 self.advance();
692 Some(self.parse_expression()?)
693 } else {
694 None
695 };
696 defaults.push(default);
697 elems.push(Some(name));
698
699 match self.peek().kind {
700 TokenKind::Comma => {
701 self.advance();
702 if matches!(self.peek().kind, TokenKind::RightBracket) {
703 break;
704 }
705 }
706 TokenKind::RightBracket => break,
707 _ => {
708 self.error_at_peek("expected `,` or `]` in array pattern");
709 return None;
710 }
711 }
712 }
713 }
714 }
715
716 if !matches!(self.peek().kind, TokenKind::RightBracket) {
717 self.error_at_peek("expected `]` to close array pattern");
718 return None;
719 }
720 let close = self.advance();
721
722 Some(Binding::Array {
723 elems,
724 defaults,
725 rest,
726 span: self.span(open.span.start, close.span.end),
727 })
728 }
729
730 fn parse_function_decl(&mut self) -> Option<StmtId> {
731 let doc = self.take_leading_doc();
732 let kw = self.advance();
733
734 let name_tok = self.expect_binding_identifier("expected function name")?;
735 let name = self.ident_from_token(&name_tok);
736 let is_main = self.block_depth == 0 && name.name == "main";
737 let decl = self.parse_function_decl_after_name(doc, kw, name);
738 if decl.is_none() && is_main {
739 self.ast.unparsed_main = true;
740 }
741 decl
742 }
743
744 fn parse_function_decl_after_name(
745 &mut self,
746 doc: Option<crate::DocComment>,
747 kw: Token,
748 name: Ident,
749 ) -> Option<StmtId> {
750 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
751 self.parse_generic_param_list()?
752 } else {
753 Vec::new()
754 };
755
756 if !matches!(self.peek().kind, TokenKind::LeftParen) {
757 self.error_at_peek("expected `(` after function name");
758 return None;
759 }
760 self.advance();
761
762 let params = self.parse_param_list(false)?;
763 self.advance(); if !matches!(self.peek().kind, TokenKind::Colon) {
766 self.error_at_peek_with_help(
767 "expected `:` and return type",
768 vec![format!(
769 "write the return type: `function {}(…): T {{ … }}`, with `void` when it returns nothing",
770 name.name
771 )],
772 );
773 if matches!(self.peek().kind, TokenKind::LeftBrace) {
776 let _ = self.parse_outer_this_boundary(Self::parse_block);
777 }
778 return None;
779 }
780 self.advance();
781 let (return_type, type_predicate) =
782 self.parse_predicate_or_return_type(TypePos::Anywhere)?;
783
784 let body = self.parse_outer_this_boundary(Self::parse_block)?;
787 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
788 .span
789 .end;
790
791 parse_arena_result(
792 self.ast.try_push_stmt(Stmt {
793 kind: StmtKind::Function {
794 name,
795 generics,
796 params,
797 return_type,
798 type_predicate,
799 body,
800 doc,
801 },
802 span: self.span(kw.span.start, body_end),
803 }),
804 &mut self.fatal,
805 )
806 }
807
808 fn parse_class_decl(&mut self) -> Option<StmtId> {
809 let doc = self.take_leading_doc();
810 let kw = self.advance();
811 if self.block_depth > 0 {
814 self.error_at_with_help(
815 kw.span,
816 "a `class` must be declared at the top level of the module",
817 vec!["move this `class` outside any block or function body".to_string()],
818 );
819 }
820 let name = self.parse_type_decl_name("a", "class")?;
821
822 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
823 self.parse_generic_param_list()?
824 } else {
825 Vec::new()
826 };
827
828 let extends = if matches!(self.peek().kind, TokenKind::Extends) {
829 self.advance();
830 Some(self.parse_type_annotation()?)
831 } else {
832 None
833 };
834
835 let mut implements: Vec<crate::TypeAnnotation> = Vec::new();
836 if matches!(self.peek().kind, TokenKind::Implements) {
837 self.advance();
838 loop {
839 implements.push(self.parse_type_annotation()?);
840 if matches!(self.peek().kind, TokenKind::Comma) {
841 self.advance();
842 continue;
843 }
844 break;
845 }
846 }
847
848 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
849 self.error_at_peek("expected `{` after class header");
850 return None;
851 }
852 self.advance();
853
854 let mut members: Vec<crate::ClassMember> = Vec::new();
855 let mut member_failed = false;
856 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
860 && !(member_failed && self.peek_starts_top_level_item())
861 {
862 if matches!(self.peek().kind, TokenKind::Semicolon) {
864 self.advance();
865 continue;
866 }
867 match self.parse_class_member() {
868 Some(member) => members.push(member),
869 None if std::mem::take(&mut self.resume_at_next_member) => member_failed = true,
870 None => return None,
871 }
872 }
873
874 if !matches!(self.peek().kind, TokenKind::RightBrace) {
875 self.error_at_peek("expected `}` to close class");
876 return None;
877 }
878 let close = self.advance();
879 if member_failed {
880 return None;
881 }
882
883 parse_arena_result(
884 self.ast.try_push_stmt(Stmt {
885 kind: StmtKind::ClassDecl {
886 name,
887 generics,
888 extends,
889 implements,
890 members,
891 doc,
892 },
893 span: self.span(kw.span.start, close.span.end),
894 }),
895 &mut self.fatal,
896 )
897 }
898
899 fn parse_class_member(&mut self) -> Option<crate::ClassMember> {
900 let doc = self.take_leading_doc();
901 let member_start = self.peek().span.start;
902
903 if self.peek_identifier_text_is("protected") {
905 let tok = self.advance();
906 self.error_at_with_help(
907 tok.span,
908 "`protected` is not supported",
909 vec!["use `private` or `public`".to_string()],
910 );
911 return None;
912 }
913 if self.peek_identifier_text_is("abstract") {
914 let tok = self.advance();
915 self.error_at_with_help(
916 tok.span,
917 "abstract classes are not supported",
918 vec!["provide a concrete implementation instead of `abstract`".to_string()],
919 );
920 return None;
921 }
922
923 let modifiers = self.parse_class_modifiers();
924 self.reject_async_member_modifier();
925
926 for (kw, kind) in [
929 ("get", crate::AccessorKind::Get),
930 ("set", crate::AccessorKind::Set),
931 ] {
932 if self.peek_identifier_text_is(kw)
933 && is_property_name(&self.peek_at(1).kind)
934 && matches!(self.peek_at(2).kind, TokenKind::LeftParen)
935 {
936 if let Some(span) = modifiers.readonly {
937 self.reject_readonly_modifier(span);
938 }
939 if let Some(span) = modifiers.static_span {
940 self.error_at_with_help(
941 span,
942 "static accessors are not supported",
943 vec!["use a static method: `static name(): T { … }`".to_string()],
944 );
945 return None;
946 }
947 return self.parse_accessor(member_start, modifiers, kind, doc);
948 }
949 }
950
951 if self.peek_identifier_text_is("constructor")
953 && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
954 {
955 if let Some(span) = modifiers.static_span {
956 self.error_at_with_help(
957 span,
958 "a constructor cannot be `static`",
959 vec![
960 "remove `static` — the constructor already belongs to the class, not \
961 instances"
962 .to_string(),
963 ],
964 );
965 return None;
966 }
967 if let Some(span) = modifiers.readonly {
968 self.error_at_with_help(
969 span,
970 "a constructor cannot be `readonly`",
971 vec!["remove `readonly`; it applies to fields".to_string()],
972 );
973 return None;
974 }
975 return self.parse_constructor(member_start, modifiers.visibility, doc);
976 }
977
978 let name = self.expect_property_ident("expected class member name")?;
979 let optional_span = if matches!(self.peek().kind, TokenKind::Question) {
980 Some(self.advance().span)
981 } else {
982 None
983 };
984 let optional = optional_span.is_some();
985
986 if matches!(self.peek().kind, TokenKind::LessThan | TokenKind::LeftParen) {
988 if let Some(span) = modifiers.readonly {
989 self.reject_readonly_modifier(span);
990 }
991 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
992 self.parse_generic_param_list()?
993 } else {
994 Vec::new()
995 };
996 if !matches!(self.peek().kind, TokenKind::LeftParen) {
997 self.error_at_peek("expected `(` to start the method parameter list");
998 return None;
999 }
1000 self.advance();
1001 let params = self.parse_class_member_params(false)?;
1002 self.advance(); if !matches!(self.peek().kind, TokenKind::Colon) {
1004 self.error_at_peek_with_help(
1005 "expected `:` and return type",
1006 vec![format!(
1007 "write the return type: `{}(…): T {{ … }}`, with `void` when it returns nothing",
1008 name.name
1009 )],
1010 );
1011 if matches!(self.peek().kind, TokenKind::LeftBrace)
1013 && self.parse_class_member_body().is_some()
1014 {
1015 self.resume_at_next_member = true;
1016 }
1017 return None;
1018 }
1019 self.advance();
1020 let return_type = self.parse_type_annotation()?;
1021 if self.at_inserted_semicolon() && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1022 {
1023 self.advance();
1024 }
1025 if optional
1026 && matches!(
1027 self.peek().kind,
1028 TokenKind::Semicolon | TokenKind::RightBrace
1029 )
1030 {
1031 if !generics.is_empty() {
1032 self.error_at(
1033 name.span,
1034 "generic optional method declarations are not supported",
1035 );
1036 return None;
1037 }
1038 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1039 self.advance().span.end
1040 } else {
1041 return_type.span.end
1042 };
1043 let ty = self.method_declaration_type(
1044 params,
1045 return_type,
1046 self.span(name.span.start, end),
1047 )?;
1048 return Some(crate::ClassMember::Field {
1049 name,
1050 modifiers,
1051 optional: true,
1052 method_signature: true,
1053 ty,
1054 initializer: None,
1055 span: self.span(member_start, end),
1056 doc,
1057 });
1058 }
1059 let body = self.parse_class_member_body()?;
1060 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1061 .span
1062 .end;
1063 return Some(crate::ClassMember::Method {
1064 name,
1065 optional,
1066 modifiers,
1067 generics,
1068 params,
1069 return_type,
1070 body,
1071 span: self.span(member_start, body_end),
1072 doc,
1073 });
1074 }
1075
1076 if let Some(span) = optional_span
1078 && modifiers.static_span.is_some()
1079 {
1080 self.error_at_with_help(
1081 span,
1082 "a static field cannot be optional",
1083 vec!["a static field must be initialized; give it a value with `= …`".to_string()],
1084 );
1085 return None;
1086 }
1087 if !matches!(self.peek().kind, TokenKind::Colon) {
1088 if matches!(self.peek().kind, TokenKind::Equals) {
1089 self.error_at_peek_with_help(
1090 "class fields require a type annotation",
1091 vec!["add `: T` before the initializer, e.g. `name: string = …`".to_string()],
1092 );
1093 } else {
1094 self.error_at_peek("expected `:` and a type for the class field");
1095 }
1096 return None;
1097 }
1098 self.advance();
1099 let ty = self.parse_type_annotation()?;
1100 let initializer = if matches!(self.peek().kind, TokenKind::Equals) {
1101 self.advance();
1102 self.class_member_body_depth += 1;
1105 let init = self.parse_expression();
1106 self.class_member_body_depth -= 1;
1107 Some(init?)
1108 } else {
1109 None
1110 };
1111 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1112 self.error_at_peek("expected `;` after class field");
1113 return None;
1114 }
1115 let semi = self.advance();
1116 Some(crate::ClassMember::Field {
1117 name,
1118 modifiers,
1119 optional,
1120 method_signature: false,
1121 ty,
1122 initializer,
1123 span: self.span(member_start, semi.span.end),
1124 doc,
1125 })
1126 }
1127
1128 fn parse_constructor(
1129 &mut self,
1130 member_start: u32,
1131 visibility: crate::Visibility,
1132 doc: Option<crate::DocComment>,
1133 ) -> Option<crate::ClassMember> {
1134 self.advance(); self.advance(); let params = self.parse_class_member_params(true)?;
1137 self.advance(); if matches!(self.peek().kind, TokenKind::Colon) {
1139 let colon = self.advance();
1140 let _ = self.parse_type_annotation();
1141 self.error_at_with_help(
1142 colon.span,
1143 "a constructor cannot declare a return type",
1144 vec![
1145 "drop the `: T` — a constructor always returns the class instance".to_string(),
1146 ],
1147 );
1148 }
1149 let body = self.parse_class_member_body()?;
1150 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1151 .span
1152 .end;
1153 Some(crate::ClassMember::Constructor {
1154 visibility,
1155 params,
1156 body,
1157 span: self.span(member_start, body_end),
1158 doc,
1159 })
1160 }
1161
1162 fn parse_accessor(
1165 &mut self,
1166 member_start: u32,
1167 modifiers: crate::ClassModifiers,
1168 kind: crate::AccessorKind,
1169 doc: Option<crate::DocComment>,
1170 ) -> Option<crate::ClassMember> {
1171 self.advance(); let name = self.expect_property_ident("expected accessor name")?;
1173 self.advance(); let mut params = self.parse_class_member_params(false)?;
1175 self.advance(); let param = match kind {
1177 crate::AccessorKind::Get => {
1178 if !params.is_empty() {
1179 self.error_at(name.span, "a getter cannot declare parameters");
1180 }
1181 None
1182 }
1183 crate::AccessorKind::Set => {
1184 if params.len() != 1 {
1185 self.error_at(name.span, "a setter must declare exactly one parameter");
1186 return None;
1187 }
1188 let param = params.remove(0);
1189 if param.rest {
1192 self.error_at(
1193 param.name.span,
1194 "a setter's parameter can't be a rest parameter",
1195 );
1196 } else if param.default.is_some() {
1197 self.error_at(name.span, "a setter's parameter can't have a default value");
1198 } else if param.optional {
1199 self.error_at(param.name.span, "a setter's parameter can't be optional");
1200 }
1201 Some(Box::new(param))
1202 }
1203 };
1204 let return_type = if matches!(self.peek().kind, TokenKind::Colon) {
1205 self.advance();
1206 Some(self.parse_type_annotation()?)
1207 } else {
1208 if kind == crate::AccessorKind::Get {
1212 self.error_at_peek_with_help(
1213 "expected `:` and return type",
1214 vec!["get name(): T { … }".to_string()],
1215 );
1216 }
1217 None
1218 };
1219 let body = self.parse_class_member_body()?;
1220 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1221 .span
1222 .end;
1223 Some(crate::ClassMember::Accessor {
1224 name,
1225 modifiers,
1226 kind,
1227 param,
1228 return_type,
1229 body,
1230 span: self.span(member_start, body_end),
1231 doc,
1232 })
1233 }
1234
1235 fn parse_class_modifiers(&mut self) -> crate::ClassModifiers {
1241 let mut visibility = crate::Visibility::Public;
1242 let mut visibility_span: Option<Span> = None;
1243 let mut readonly: Option<Span> = None;
1244 let mut static_span: Option<Span> = None;
1245 loop {
1246 if self.peek_word_is_class_modifier("public")
1247 || self.peek_word_is_class_modifier("private")
1248 {
1249 let (kind, word) = if self.peek_identifier_text_is("private") {
1250 (crate::Visibility::Private, "private")
1251 } else {
1252 (crate::Visibility::Public, "public")
1253 };
1254 let tok = self.advance();
1255 if visibility_span.is_some() {
1256 self.error_at_with_help(
1257 tok.span,
1258 "a class member may have at most one visibility modifier",
1259 vec!["keep a single `public` or `private`".to_string()],
1260 );
1261 continue;
1262 }
1263 let preceding = static_span
1266 .map(|_| "static")
1267 .or(readonly.map(|_| "readonly"));
1268 if let Some(preceding) = preceding {
1269 self.error_at_with_help(
1270 tok.span,
1271 format!("`{word}` must come before `{preceding}`"),
1272 vec![format!("write `{word} {preceding} <name>`")],
1273 );
1274 }
1275 visibility = kind;
1276 visibility_span = Some(tok.span);
1277 continue;
1278 }
1279 if self.peek_word_is_class_modifier("static") {
1280 let tok = self.advance();
1281 if static_span.is_some() {
1282 self.error_at(tok.span, "duplicate `static` modifier");
1283 } else {
1284 static_span = Some(tok.span);
1285 }
1286 continue;
1287 }
1288 if self.peek_word_is_class_modifier("readonly") {
1289 let tok = self.advance();
1290 if readonly.is_some() {
1291 self.error_at(tok.span, "duplicate `readonly` modifier");
1292 } else {
1293 readonly = Some(tok.span);
1294 }
1295 continue;
1296 }
1297 break;
1298 }
1299 crate::ClassModifiers {
1300 visibility,
1301 visibility_span,
1302 readonly,
1303 static_span,
1304 }
1305 }
1306
1307 fn peek_word_is_class_modifier(&self, word: &str) -> bool {
1313 if !self.peek_identifier_text_is(word) {
1314 return false;
1315 }
1316 if word != "static" && self.line_break_after_peek() {
1317 return false;
1318 }
1319 let next = &self.peek_at(1).kind;
1320 is_property_name(next) || matches!(next, TokenKind::StringLiteral(_))
1321 }
1322
1323 fn parse_class_member_params(
1324 &mut self,
1325 allow_param_properties: bool,
1326 ) -> Option<Vec<ParamDecl>> {
1327 let saved = self.class_member_body_depth;
1328 self.class_member_body_depth = 1;
1329 let params = self.parse_param_list(allow_param_properties);
1330 self.class_member_body_depth = saved;
1331 params
1332 }
1333
1334 fn parse_class_member_body(&mut self) -> Option<StmtId> {
1335 self.class_member_body_depth += 1;
1336 let body = self.parse_block();
1337 self.class_member_body_depth -= 1;
1338 body
1339 }
1340
1341 fn parse_interface_decl(&mut self) -> Option<StmtId> {
1342 let doc = self.take_leading_doc();
1343 let kw = self.advance();
1344 let name = self.parse_type_decl_name("an", "interface")?;
1345
1346 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1347 self.parse_generic_param_list()?
1348 } else {
1349 Vec::new()
1350 };
1351
1352 let mut extends = Vec::new();
1353 if matches!(self.peek().kind, TokenKind::Extends) {
1354 self.advance();
1355 loop {
1356 extends.push(self.parse_type_annotation()?);
1357 if !matches!(self.peek().kind, TokenKind::Comma) {
1358 break;
1359 }
1360 self.advance();
1361 }
1362 }
1363 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1364 self.error_at_peek("expected `{` after interface name");
1365 return None;
1366 }
1367 self.advance();
1368
1369 let mut members: Vec<crate::InterfaceMember> = Vec::new();
1370 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
1371 && !self.peek_starts_top_level_item()
1372 {
1373 let member_doc = self.take_leading_doc();
1377 if matches!(self.peek().kind, TokenKind::LeftParen) {
1378 let open = self.advance();
1379 let params = self.parse_param_list(false)?;
1380 self.advance();
1381 if !matches!(self.peek().kind, TokenKind::Colon) {
1382 self.error_at_peek("expected `:` and return type");
1383 return None;
1384 }
1385 self.advance();
1386 let return_type = self.parse_type_annotation()?;
1387 let end = self.finish_interface_member(return_type.span.end)?;
1388 let span = self.span(open.span.start, end);
1389 let sentinel = Ident {
1390 name: "@call".to_string(),
1391 span: open.span,
1392 };
1393 members.push(crate::InterfaceMember::Method {
1394 name: sentinel,
1395 optional: false,
1396 generics: Vec::new(),
1397 params,
1398 return_type,
1399 span,
1400 doc: member_doc,
1401 });
1402 continue;
1403 }
1404 if matches!(self.peek().kind, TokenKind::LessThan) {
1405 self.error_at_peek("generic call signatures are not yet supported");
1406 return None;
1407 }
1408 let member_start = self.peek().span.start;
1409 let readonly = self.eat_readonly_property_modifier();
1410 if self.peek_is_parameterless_index_signature() {
1411 self.skip_parameterless_index_signature(member_start)?;
1412 self.finish_interface_member(self.prev_token_end())?;
1413 continue;
1414 }
1415 if matches!(self.peek().kind, TokenKind::LeftBracket) {
1416 let signature = self.parse_index_signature(readonly)?;
1417 self.finish_interface_member(signature.span.end)?;
1418 members.push(crate::InterfaceMember::IndexSignature(signature));
1419 continue;
1420 }
1421 let member_name = self.expect_property_ident("expected interface member name")?;
1422 let optional = matches!(self.peek().kind, TokenKind::Question);
1423 if optional {
1424 self.advance();
1425 }
1426 if matches!(self.peek().kind, TokenKind::Colon) {
1427 self.advance();
1428 let ty = self.parse_type_annotation()?;
1429 let end = self.finish_interface_member(ty.span.end)?;
1430 let span = self.span(member_name.span.start, end);
1431 members.push(crate::InterfaceMember::Property {
1432 name: member_name,
1433 ty,
1434 optional,
1435 readonly,
1436 span,
1437 doc: member_doc,
1438 });
1439 continue;
1440 }
1441 if readonly {
1442 self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
1443 }
1444 let m_generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1445 self.parse_generic_param_list()?
1446 } else {
1447 Vec::new()
1448 };
1449 if !matches!(self.peek().kind, TokenKind::LeftParen) {
1450 self.error_at_peek(
1451 "expected `(` to start a method signature or `:` to start a property",
1452 );
1453 return None;
1454 }
1455 self.advance();
1456 let params = self.parse_param_list(false)?;
1457 self.advance();
1458 if !matches!(self.peek().kind, TokenKind::Colon) {
1459 self.error_at_peek("expected `:` and return type");
1460 return None;
1461 }
1462 self.advance();
1463 let return_type = self.parse_type_annotation()?;
1464 let end = self.finish_interface_member(return_type.span.end)?;
1465 let span = self.span(member_name.span.start, end);
1466 if optional {
1467 if !m_generics.is_empty() {
1468 self.error_at(
1469 member_name.span,
1470 "generic optional method declarations are not supported",
1471 );
1472 return None;
1473 }
1474 let ty = self.method_declaration_type(params, return_type, span)?;
1475 members.push(crate::InterfaceMember::Property {
1476 name: member_name,
1477 ty,
1478 optional: true,
1479 readonly,
1480 span,
1481 doc: member_doc,
1482 });
1483 } else {
1484 members.push(crate::InterfaceMember::Method {
1485 name: member_name,
1486 optional,
1487 generics: m_generics,
1488 params,
1489 return_type,
1490 span,
1491 doc: member_doc,
1492 });
1493 }
1494 }
1495
1496 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1497 self.error_at_peek("expected `}` to close interface");
1498 return None;
1499 }
1500 let close = self.advance();
1501
1502 parse_arena_result(
1503 self.ast.try_push_stmt(Stmt {
1504 kind: StmtKind::InterfaceDecl {
1505 name,
1506 generics,
1507 extends,
1508 members,
1509 doc,
1510 },
1511 span: self.span(kw.span.start, close.span.end),
1512 }),
1513 &mut self.fatal,
1514 )
1515 }
1516
1517 fn method_declaration_type(
1518 &mut self,
1519 params: Vec<ParamDecl>,
1520 return_type: TypeAnnotation,
1521 span: Span,
1522 ) -> Option<TypeAnnotation> {
1523 let mut fields = Vec::new();
1524 for param in params {
1525 if param.pattern.is_some() || param.default.is_some() {
1526 self.error_at(
1527 param.name.span,
1528 "a method declaration cannot contain a parameter pattern or default value",
1529 );
1530 return None;
1531 }
1532 let Some(ty) = param.ty else {
1533 return self.invariant_failure("method parameter annotation was not retained");
1534 };
1535 fields.push(TypeAnnotationField {
1536 name: param.name,
1537 ty,
1538 optional: param.optional,
1539 readonly: false,
1540 rest: param.rest,
1541 method: false,
1542 });
1543 }
1544 Some(TypeAnnotation {
1545 kind: TypeAnnotationKind::Function {
1546 params: fields,
1547 return_type: Box::new(return_type),
1548 },
1549 span,
1550 })
1551 }
1552
1553 fn finish_interface_member(&mut self, body_end: u32) -> Option<u32> {
1554 self.finish_type_member(body_end, "expected `;`, `,`, or `}` after interface member")
1555 }
1556
1557 fn finish_type_member(&mut self, body_end: u32, message: &str) -> Option<u32> {
1563 match self.peek().kind {
1564 TokenKind::Semicolon | TokenKind::Comma => Some(self.advance().span.end),
1565 TokenKind::RightBrace => Some(body_end),
1566 _ if self.line_break_before_peek() => Some(body_end),
1567 _ => {
1568 self.error_at_peek(message);
1569 None
1570 }
1571 }
1572 }
1573
1574 fn parse_type_alias_decl(&mut self) -> Option<StmtId> {
1575 let doc = self.take_leading_doc();
1576 let kw = self.advance();
1577 let name = self.parse_type_decl_name("a", "type alias")?;
1578
1579 let generics: Vec<Ident> = if matches!(self.peek().kind, TokenKind::LessThan) {
1580 self.parse_generic_param_list()?
1581 } else {
1582 Vec::new()
1583 };
1584
1585 if !matches!(self.peek().kind, TokenKind::Equals) {
1586 self.error_at_peek("expected `=` after type alias name");
1587 return None;
1588 }
1589 self.advance();
1590
1591 let ty = self.parse_type_annotation()?;
1592
1593 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1596 self.advance().span.end
1597 } else if self.line_break_before_peek() {
1598 self.prev_token_end()
1599 } else {
1600 self.error_at_peek("expected `;` after type alias body");
1601 return None;
1602 };
1603
1604 parse_arena_result(
1605 self.ast.try_push_stmt(Stmt {
1606 kind: StmtKind::TypeAliasDecl {
1607 name,
1608 generics,
1609 ty,
1610 doc,
1611 },
1612 span: self.span(kw.span.start, end),
1613 }),
1614 &mut self.fatal,
1615 )
1616 }
1617
1618 fn parse_enum_decl(&mut self) -> Option<StmtId> {
1620 #[derive(Copy, Clone)]
1621 enum Kind {
1622 Number,
1623 String,
1624 }
1625 let doc = self.take_leading_doc();
1626 let kw = self.advance();
1627 let name = self.parse_type_decl_name("an", "enum")?;
1628
1629 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1630 self.error_at_peek("expected `{` after enum name");
1631 return None;
1632 }
1633 self.advance();
1634
1635 let mut members: Vec<EnumMember> = Vec::new();
1636 let mut first_explicit: Option<(Kind, Span)> = None;
1637 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1638 if matches!(self.peek().kind, TokenKind::Semicolon) {
1640 self.advance();
1641 continue;
1642 }
1643 let member_doc = self.take_leading_doc();
1644 let member_name_tok = self.expect_identifier_name("expected enum member name")?;
1645 let member_name = self.ident_from_token(&member_name_tok);
1646
1647 let value = if matches!(self.peek().kind, TokenKind::Equals) {
1648 self.advance();
1649 let init = self.parse_enum_initializer()?;
1650 let (this_kind, this_span) = match &init {
1651 EnumInitializer::Number { span, .. } => (Kind::Number, *span),
1652 EnumInitializer::String { span, .. } => (Kind::String, *span),
1653 };
1654 match first_explicit {
1655 None => first_explicit = Some((this_kind, this_span)),
1656 Some((prev, _)) => {
1657 let mismatched = matches!(
1658 (prev, this_kind),
1659 (Kind::Number, Kind::String) | (Kind::String, Kind::Number)
1660 );
1661 if mismatched {
1662 self.error_at(
1663 this_span,
1664 "mixed numeric and string enum members are not allowed",
1665 );
1666 }
1667 }
1668 }
1669 Some(init)
1670 } else {
1671 None
1672 };
1673
1674 let end_span = value
1675 .as_ref()
1676 .map_or(member_name.span, super::ast::EnumInitializer::span);
1677 let member_span = self.span(member_name.span.start, end_span.end);
1678 members.push(EnumMember {
1679 name: member_name,
1680 value,
1681 span: member_span,
1682 doc: member_doc,
1683 });
1684
1685 match self.peek().kind {
1686 TokenKind::Comma | TokenKind::Semicolon => {
1689 self.advance();
1690 }
1691 TokenKind::RightBrace | TokenKind::Eof => {}
1692 _ => {
1693 self.error_at_peek("expected `,` or `}` after enum member");
1694 return None;
1695 }
1696 }
1697 }
1698
1699 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1700 self.error_at_peek("expected `}` to close enum");
1701 return None;
1702 }
1703 let close = self.advance();
1704
1705 parse_arena_result(
1706 self.ast.try_push_stmt(Stmt {
1707 kind: StmtKind::EnumDecl { name, members, doc },
1708 span: self.span(kw.span.start, close.span.end),
1709 }),
1710 &mut self.fatal,
1711 )
1712 }
1713
1714 fn parse_export(&mut self) -> Option<StmtId> {
1721 let export_tok = self.advance();
1722 let export_span = export_tok.span;
1723
1724 let nested = self.block_depth > 0;
1725 if nested {
1726 self.error_at_with_help(
1727 export_span,
1728 "`export` statements must appear at the top of the file",
1729 vec!["move this `export` to the top level of the module".to_string()],
1730 );
1731 }
1734
1735 if self.peek_identifier_text_is("type")
1738 && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1739 {
1740 self.advance();
1741 }
1742
1743 self.reject_async_modifier();
1744 let declares = matches!(
1745 self.peek().kind,
1746 TokenKind::Function
1747 | TokenKind::Class
1748 | TokenKind::Const
1749 | TokenKind::Let
1750 | TokenKind::Interface
1751 | TokenKind::Enum
1752 ) || self.at_type_alias_head();
1753 if declares {
1754 if export_tok.leading_doc.is_some() && self.peek().leading_doc.is_none() {
1757 if let Some(token) = self.tokens.get_mut(self.pos) {
1758 token.leading_doc = export_tok.leading_doc;
1759 } else {
1760 return self.invariant_failure("export cursor is outside token stream");
1761 }
1762 }
1763 let id = self.parse_statement()?;
1764 if nested {
1765 let detached: Vec<StmtId> = std::iter::once(id)
1766 .chain(self.take_trailing_declarators())
1767 .collect();
1768 self.discard_detached_patterns(&detached);
1769 return None;
1770 }
1771 for stmt in std::iter::once(id).chain(self.trailing_declarators.clone()) {
1773 self.ast
1774 .exported_decls
1775 .push(ExportedDecl { stmt, export_span });
1776 }
1777 return Some(id);
1778 }
1779
1780 match self.peek().kind {
1781 TokenKind::LeftBrace => {
1782 let doc = export_tok
1785 .leading_doc
1786 .as_ref()
1787 .and_then(|raw| self.parse_doc(raw))
1788 .or_else(|| self.take_leading_doc());
1789 self.parse_export_from(export_span, nested, doc)
1790 }
1791 TokenKind::Default => {
1792 self.error_at_peek_with_help(
1793 "`export default` is not supported",
1794 vec![
1795 "default exports are out of scope; drop `default` — a named \
1796 `export` marks the declaration visible to sibling modules"
1797 .to_string(),
1798 ],
1799 );
1800 None
1801 }
1802 _ => {
1803 self.error_at_peek_with_help(
1804 "expected a declaration after `export`",
1805 vec![
1806 "`export` may precede `function`, `class`, `const`, `let`, `type`, \
1807 `interface`, or `enum`, or take the form `export { … }`"
1808 .to_string(),
1809 ],
1810 );
1811 None
1812 }
1813 }
1814 }
1815
1816 fn parse_export_from(
1819 &mut self,
1820 export_span: Span,
1821 nested: bool,
1822 doc: Option<crate::DocComment>,
1823 ) -> Option<StmtId> {
1824 let (specs, open_span) = self.parse_specifier_list(SpecifierList::Export)?;
1825 if specs.is_empty() {
1826 self.error_at_with_help(
1827 open_span,
1828 "empty export specifier list",
1829 vec!["name what you re-export: `export { name } from \"./util\";`".to_string()],
1830 );
1831 return None;
1832 }
1833
1834 let source = if self.peek_identifier_text_is("from") {
1835 self.advance();
1836 let module_tok = self.peek().clone();
1837 let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1838 s.clone()
1839 } else {
1840 self.error_at_peek("expected module specifier string after `from`");
1841 return None;
1842 };
1843 self.advance();
1844 Some((module, module_tok.span))
1845 } else {
1846 None
1847 };
1848
1849 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1850 self.error_at_peek("expected `;` after export statement");
1851 return None;
1852 }
1853 let semi = self.advance();
1854
1855 if nested {
1856 return None;
1857 }
1858
1859 parse_arena_result(
1860 self.ast.try_push_stmt(Stmt {
1861 kind: StmtKind::ExportFrom { specs, source, doc },
1862 span: self.span(export_span.start, semi.span.end),
1863 }),
1864 &mut self.fatal,
1865 )
1866 }
1867
1868 fn parse_import_decl(&mut self) -> Option<StmtId> {
1869 let doc = self.take_leading_doc();
1870 let kw = self.advance();
1871 let kw_span = kw.span;
1872
1873 let nested = self.block_depth > 0;
1874 if nested {
1875 self.error_at_with_help(
1876 kw_span,
1877 "`import` statements must appear at the top of the file",
1878 vec!["move this `import` outside any block / function body".to_string()],
1879 );
1880 }
1882
1883 let kind = self.parse_import_kind()?;
1884
1885 if !self.peek_identifier_text_is("from") {
1886 self.error_at_peek("expected `from` after import specifier list");
1887 return None;
1888 }
1889 self.advance();
1890
1891 let module_tok = self.peek().clone();
1892 let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1893 s.clone()
1894 } else {
1895 self.error_at_peek("expected module specifier string after `from`");
1896 return None;
1897 };
1898 let module_span = module_tok.span;
1899 self.advance();
1900
1901 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1902 self.error_at_peek("expected `;` after import statement");
1903 return None;
1904 }
1905 let semi = self.advance();
1906
1907 if nested {
1908 return None;
1909 }
1910
1911 parse_arena_result(
1912 self.ast.try_push_stmt(Stmt {
1913 kind: StmtKind::Import {
1914 module,
1915 module_span,
1916 kind,
1917 doc,
1918 },
1919 span: self.span(kw_span.start, semi.span.end),
1920 }),
1921 &mut self.fatal,
1922 )
1923 }
1924
1925 fn parse_import_kind(&mut self) -> Option<ImportKind> {
1926 if self.peek_identifier_text_is("type")
1931 && match self.peek_at(1).kind {
1932 TokenKind::LeftBrace | TokenKind::Star => true,
1933 TokenKind::Identifier => {
1934 !(self.peek_at_is_word(1, "from")
1935 && matches!(self.peek_at(2).kind, TokenKind::StringLiteral(_)))
1936 }
1937 _ => false,
1938 }
1939 {
1940 self.advance();
1941 }
1942
1943 match self.peek().kind {
1944 TokenKind::LeftBrace => self.parse_named_imports(),
1945 TokenKind::Identifier => {
1946 let local_tok = self.advance();
1947 let local_name = self.ident_from_token(&local_tok);
1948 if matches!(self.peek().kind, TokenKind::Comma) {
1949 self.error_at_peek_with_help(
1950 "combining default and named imports is not supported",
1951 vec!["use two separate `import` statements".to_string()],
1952 );
1953 return None;
1954 }
1955 Some(ImportKind::Namespace { local_name })
1956 }
1957 TokenKind::Star => {
1960 self.advance();
1961 if !self.peek_identifier_text_is("as") {
1962 self.error_at_peek_with_help(
1963 "expected `as <name>` after `import *`",
1964 vec![
1965 "name the namespace binding: \
1966 `import * as ns from \"pkg\";` (or use the \
1967 short form: `import ns from \"pkg\";`)"
1968 .to_string(),
1969 ],
1970 );
1971 return None;
1972 }
1973 self.advance();
1974 let local_tok = self.expect_identifier("expected namespace name after `as`")?;
1975 let local_name = self.ident_from_token(&local_tok);
1976 Some(ImportKind::Namespace { local_name })
1977 }
1978 TokenKind::StringLiteral(_) => {
1979 self.error_at_peek_with_help(
1980 "side-effect-only imports are not supported",
1981 vec![
1982 "name what you import: `import { name } from \"pkg\";` \
1983 or `import ns from \"pkg\";`"
1984 .to_string(),
1985 ],
1986 );
1987 None
1988 }
1989 _ => {
1990 self.error_at_peek("expected `{` or namespace name after `import`");
1991 None
1992 }
1993 }
1994 }
1995
1996 fn parse_named_imports(&mut self) -> Option<ImportKind> {
1997 let (specs, open_span) = self.parse_specifier_list(SpecifierList::Import)?;
1998 if specs.is_empty() {
1999 self.error_at_with_help(
2000 open_span,
2001 "empty import specifier list",
2002 vec!["name what you import: `import { name } from \"pkg\";`".to_string()],
2003 );
2004 return None;
2005 }
2006 Some(ImportKind::Named(specs))
2007 }
2008
2009 fn parse_specifier_list(
2015 &mut self,
2016 list: SpecifierList,
2017 ) -> Option<(Vec<ImportSpecifier>, Span)> {
2018 let what = list.noun();
2019 let open = self.advance();
2020 let mut specs: Vec<ImportSpecifier> = Vec::new();
2021 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2022 if self.peek_identifier_text_is("type")
2025 && matches!(self.peek_at(1).kind, TokenKind::Identifier)
2026 && !self.peek_at_is_word(1, "as")
2027 {
2028 self.advance();
2029 }
2030 let imported_tok =
2031 self.expect_identifier_name(&format!("expected {what} specifier name"))?;
2032 let imported_name = self.ident_from_token(&imported_tok);
2033
2034 let binds_local = list == SpecifierList::Import;
2035 let local_name = if self.peek_identifier_text_is("as") {
2036 self.advance();
2037 let local_tok = self.expect_identifier_name("expected local name after `as`")?;
2038 if binds_local {
2039 self.reject_strict_mode_reserved_word(&local_tok);
2040 }
2041 self.ident_from_token(&local_tok)
2042 } else {
2043 if binds_local {
2044 self.reject_strict_mode_reserved_word(&imported_tok);
2045 }
2046 imported_name.clone()
2047 };
2048
2049 specs.push(ImportSpecifier {
2050 imported_name,
2051 local_name,
2052 });
2053
2054 match self.peek().kind {
2055 TokenKind::Comma => {
2056 self.advance();
2057 }
2058 TokenKind::RightBrace | TokenKind::Eof => {}
2059 _ => {
2060 self.error_at_peek(format!("expected `,` or `}}` after {what} specifier"));
2061 return None;
2062 }
2063 }
2064 }
2065
2066 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2067 self.error_at_peek(format!("expected `}}` to close {what} specifier list"));
2068 return None;
2069 }
2070 self.advance();
2071
2072 Some((specs, open.span))
2073 }
2074
2075 fn parse_enum_initializer(&mut self) -> Option<EnumInitializer> {
2076 let neg = if matches!(self.peek().kind, TokenKind::Minus) {
2077 Some(self.advance())
2078 } else {
2079 None
2080 };
2081 let tok = self.advance();
2082 match &tok.kind {
2083 TokenKind::NumberLiteral(n) => {
2084 let value = if neg.is_some() { -n } else { *n };
2085 let value = if value == 0.0 { 0.0 } else { value };
2087 let start = neg.as_ref().map_or(tok.span.start, |t| t.span.start);
2088 Some(EnumInitializer::Number {
2089 value,
2090 span: self.span(start, tok.span.end),
2091 })
2092 }
2093 TokenKind::StringLiteral(s) => {
2094 if let Some(neg_tok) = &neg {
2095 self.error_at(
2096 self.span(neg_tok.span.start, tok.span.end),
2097 "cannot negate a string enum initializer",
2098 );
2099 return None;
2100 }
2101 Some(EnumInitializer::String {
2102 value: s.clone(),
2103 span: tok.span,
2104 })
2105 }
2106 _ => {
2107 let span = neg.map_or(tok.span, |t| self.span(t.span.start, tok.span.end));
2108 self.error_at(
2109 span,
2110 "expected a number or string literal for enum initializer",
2111 );
2112 None
2113 }
2114 }
2115 }
2116
2117 fn is_contextual_type_operator(&self, index: usize, operator: &str) -> bool {
2125 let Some(tok) = self.tokens.get(index) else {
2126 return false;
2127 };
2128 if !self.token_is_word(tok, operator) {
2129 return false;
2130 }
2131 matches!(
2132 self.tokens.get(index + 1).map(|t| &t.kind),
2133 Some(
2134 TokenKind::Identifier
2135 | TokenKind::Void
2136 | TokenKind::LeftBrace
2137 | TokenKind::LeftParen
2138 | TokenKind::LeftBracket
2139 | TokenKind::StringLiteral(_)
2140 | TokenKind::NumberLiteral(_)
2141 | TokenKind::BooleanLiteral(_)
2142 | TokenKind::NullLiteral
2143 )
2144 )
2145 }
2146
2147 fn consume_type_close(&mut self) -> Option<Token> {
2150 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2151 return Some(self.advance());
2152 }
2153 let original = self.peek().clone();
2154 if !matches!(original.kind, TokenKind::GreaterEquals) {
2155 return self.invariant_failure("type closing token must start with `>`");
2156 }
2157 let middle = original.span.start + 1;
2158 let close = Token::new(
2159 TokenKind::GreaterThan,
2160 self.span(original.span.start, middle),
2161 );
2162 let remaining = Token::new(TokenKind::Equals, self.span(middle, original.span.end));
2163 self.split_type_tokens.push((self.pos, original));
2164 *self.tokens.get_mut(self.pos)? = remaining;
2165 Some(close)
2166 }
2167
2168 fn restore_type_tokens(&mut self, checkpoint: usize) {
2169 for (index, original) in self.split_type_tokens.drain(checkpoint..).rev() {
2170 self.tokens[index] = original;
2172 }
2173 }
2174
2175 fn parse_type_argument_list(&mut self) -> Option<(Vec<TypeAnnotation>, u32)> {
2176 let lt = self.advance();
2177 if matches!(
2178 self.peek().kind,
2179 TokenKind::GreaterThan | TokenKind::GreaterEquals
2180 ) {
2181 self.error_at(lt.span, "empty generic argument list `<>` is not allowed");
2182 return None;
2183 }
2184 let mut args = Vec::new();
2185 loop {
2186 args.push(self.parse_type_annotation()?);
2187 match self.peek().kind {
2188 TokenKind::Comma => {
2189 self.advance();
2190 if matches!(
2191 self.peek().kind,
2192 TokenKind::GreaterThan | TokenKind::GreaterEquals
2193 ) {
2194 let gt = self.consume_type_close()?;
2195 return Some((args, gt.span.end));
2196 }
2197 }
2198 TokenKind::GreaterThan | TokenKind::GreaterEquals => {
2199 let gt = self.consume_type_close()?;
2200 return Some((args, gt.span.end));
2201 }
2202 _ => {
2203 self.error_at(
2204 lt.span,
2205 "expected `,` or `>` to close generic argument list",
2206 );
2207 return None;
2208 }
2209 }
2210 }
2211 }
2212
2213 fn parse_type_decl_name(&mut self, article: &str, noun: &str) -> Option<Ident> {
2217 let name_tok = self.expect_identifier(&format!("expected {noun} name"))?;
2218 let name = self.ident_from_token(&name_tok);
2219 if BUILT_IN_TYPE_NAMES.contains(&name.name.as_str()) {
2220 self.error_at_with_help(
2221 name.span,
2222 format!(
2223 "`{}` is a built-in type and can't be used as {article} {noun} name",
2224 name.name
2225 ),
2226 vec![format!("rename the {noun}")],
2227 );
2228 }
2229 Some(name)
2230 }
2231
2232 fn parse_generic_param_list(&mut self) -> Option<Vec<Ident>> {
2233 let lt = self.advance();
2234 if matches!(
2235 self.peek().kind,
2236 TokenKind::GreaterThan | TokenKind::GreaterEquals
2237 ) {
2238 self.error_at(lt.span, "empty generic parameter list `<>` is not allowed");
2239 return None;
2240 }
2241 let mut generics = Vec::new();
2242 loop {
2243 let name_tok = self.expect_identifier("expected generic parameter name")?;
2244 let name = self.ident_from_token(&name_tok);
2245 if generics.iter().any(|g: &Ident| g.name == name.name) {
2249 self.error_at_with_help(
2250 name.span,
2251 format!("duplicate type parameter `{}`", name.name),
2252 vec!["give each type parameter its own name".to_string()],
2253 );
2254 } else {
2255 generics.push(name);
2256 }
2257 match self.peek().kind {
2258 TokenKind::Comma => {
2259 self.advance();
2260 if matches!(
2261 self.peek().kind,
2262 TokenKind::GreaterThan | TokenKind::GreaterEquals
2263 ) {
2264 self.consume_type_close()?;
2265 return Some(generics);
2266 }
2267 }
2268 TokenKind::GreaterThan | TokenKind::GreaterEquals => {
2269 self.consume_type_close()?;
2270 return Some(generics);
2271 }
2272 _ => {
2273 self.error_at_peek("expected `,` or `>`");
2274 return None;
2275 }
2276 }
2277 }
2278 }
2279
2280 fn parse_param_list(&mut self, allow_param_properties: bool) -> Option<Vec<ParamDecl>> {
2281 if matches!(self.peek().kind, TokenKind::RightParen) {
2282 return Some(Vec::new());
2283 }
2284 let mut params = Vec::new();
2285 loop {
2286 let param = self.parse_param(allow_param_properties)?;
2287 let is_rest = param.rest;
2288 params.push(param);
2289 match self.peek().kind {
2290 TokenKind::Comma => {
2291 if is_rest {
2292 let comma = self.advance();
2293 self.error_at(comma.span, "rest parameter must be the last parameter");
2294 return None;
2295 }
2296 self.advance();
2297 if matches!(self.peek().kind, TokenKind::RightParen) {
2298 break;
2299 }
2300 }
2301 TokenKind::RightParen => break,
2302 _ => {
2303 self.error_at_peek("expected `,` or `)`");
2304 return None;
2305 }
2306 }
2307 }
2308 self.check_parameter_order(params.iter().map(ParamOrder::of_param));
2309 Some(params)
2310 }
2311
2312 fn parse_param(&mut self, allow_param_properties: bool) -> Option<ParamDecl> {
2313 let modifiers = if self.peek_word_is_class_modifier("public")
2317 || self.peek_word_is_class_modifier("private")
2318 || self.peek_word_is_class_modifier("readonly")
2319 {
2320 let span = self.peek().span;
2321 let parsed = self.parse_class_modifiers();
2322 if allow_param_properties {
2323 Some(parsed)
2324 } else {
2325 self.error_at_with_help(
2326 span,
2327 "parameter properties are only allowed in a constructor",
2328 vec![
2329 "move the `public`/`private`/`readonly` modifier to a constructor parameter"
2330 .to_string(),
2331 ],
2332 );
2333 None
2334 }
2335 } else {
2336 None
2337 };
2338
2339 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
2340 self.advance();
2341 true
2342 } else {
2343 false
2344 };
2345 if modifiers.is_some() && rest {
2346 self.error_at_peek("a parameter property cannot be a rest parameter");
2347 return None;
2348 }
2349
2350 let (name, pattern) = match self.peek().kind {
2353 TokenKind::LeftBrace | TokenKind::LeftBracket => {
2354 if rest {
2355 self.error_at_peek("rest parameter cannot be destructured");
2356 return None;
2357 }
2358 if modifiers.is_some() {
2359 self.error_at_peek("a parameter property cannot be destructured");
2360 return None;
2361 }
2362 let binding = self.parse_binding()?;
2363 let placeholder = Ident {
2364 name: String::new(),
2365 span: binding.span(),
2366 };
2367 (placeholder, Some(binding))
2368 }
2369 _ => {
2370 let name_tok = self.expect_binding_identifier(if rest {
2371 "expected identifier after `...`"
2372 } else {
2373 "expected parameter name"
2374 })?;
2375 let name = self.ident_from_token(&name_tok);
2376 (name, None)
2377 }
2378 };
2379
2380 let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
2381 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2382 self.advance();
2383 Some(self.parse_type_annotation()?)
2384 } else if !rest && matches!(self.peek().kind, TokenKind::Equals) {
2385 None
2386 } else {
2387 if rest {
2388 self.error_at_peek_with_help(
2389 "rest parameter requires a type annotation",
2390 vec!["function name(...args: T[]): R { … }".to_string()],
2391 );
2392 } else {
2393 self.error_at_peek_with_help(
2394 "parameter requires a type annotation",
2395 vec!["add a type (`x: T`) or a default initializer (`x = value`)".to_string()],
2396 );
2397 }
2398 return None;
2399 };
2400 let default = self.parse_parameter_default(rest, optional)?;
2401 Some(ParamDecl {
2402 name,
2403 ty,
2404 default,
2405 optional,
2406 pattern,
2407 rest,
2408 modifiers,
2409 })
2410 }
2411
2412 fn parse_parameter_optional(&mut self, rest: bool, pattern: bool) -> Option<bool> {
2413 if !matches!(self.peek().kind, TokenKind::Question) {
2414 return Some(false);
2415 }
2416 let question = self.advance();
2417 if rest || pattern {
2418 self.error_at(
2419 question.span,
2420 if rest {
2421 "a rest parameter cannot be optional"
2422 } else {
2423 "a destructured parameter cannot be optional; use a default value"
2424 },
2425 );
2426 return None;
2427 }
2428 Some(true)
2429 }
2430
2431 fn parse_parameter_default(&mut self, rest: bool, optional: bool) -> Option<Option<ExprId>> {
2434 if !matches!(self.peek().kind, TokenKind::Equals) {
2435 return Some(None);
2436 }
2437 if rest {
2438 self.error_rest_parameter_default();
2439 return None;
2440 }
2441 if optional {
2442 self.error_at_peek_with_help(
2443 "an optional parameter cannot have a default value",
2444 vec!["drop the `?`: a parameter with a default is already optional".to_string()],
2445 );
2446 }
2447 self.advance();
2448 Some(Some(self.parse_expression()?))
2449 }
2450
2451 fn error_rest_parameter_default(&mut self) {
2453 self.error_at_peek_with_help(
2454 "rest parameter cannot have a default value",
2455 vec!["omit the `= …`; an unspecified rest defaults to `[]`".to_string()],
2456 );
2457 }
2458
2459 fn check_parameter_order<'p>(&mut self, params: impl IntoIterator<Item = ParamOrder<'p>>) {
2463 let mut follows_optional = false;
2464 for param in params {
2465 if param.optional {
2466 follows_optional = true;
2467 continue;
2468 }
2469 if !follows_optional || !param.required {
2470 continue;
2471 }
2472 let label = match param.name {
2473 Some(name) => format!("parameter `{name}`"),
2474 None => "destructured parameter".to_string(),
2475 };
2476 self.error_at_with_help(
2477 param.span,
2478 format!("required {label} cannot follow an optional parameter"),
2479 vec!["make it optional too (`x?: T`), give it a default (`x: T = …`), or move it before the optional parameters".to_string()],
2480 );
2481 return;
2482 }
2483 }
2484
2485 fn eat_asi_semicolon_before(&mut self, next: TokenKind) {
2490 if matches!(self.peek().kind, TokenKind::Semicolon) && self.peek_at(1).kind == next {
2491 self.advance();
2492 }
2493 }
2494
2495 fn parse_block(&mut self) -> Option<StmtId> {
2496 self.eat_asi_semicolon_before(TokenKind::LeftBrace);
2497 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2498 self.error_at_peek("expected `{`");
2499 return None;
2500 }
2501 let open = self.advance();
2502 self.block_depth += 1;
2503
2504 let mut stmts = Vec::new();
2505 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2506 if matches!(self.peek().kind, TokenKind::Semicolon) {
2507 self.advance();
2508 continue;
2509 }
2510 let pos_before = self.pos;
2511 if let Some(id) = self.parse_statement() {
2512 stmts.push(id);
2513 stmts.extend(self.take_trailing_declarators());
2514 } else {
2515 self.recover();
2516 if self.pos == pos_before && !self.is_at_eof() {
2517 self.advance();
2518 }
2519 }
2520 }
2521
2522 self.block_depth -= 1;
2523 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2524 self.error_at_peek("expected `}`");
2525 return None;
2526 }
2527 let close = self.advance();
2528
2529 let span = self.span(open.span.start, close.span.end);
2530 parse_arena_result(
2531 self.ast.try_push_stmt(Stmt {
2532 kind: StmtKind::Block(stmts),
2533 span,
2534 }),
2535 &mut self.fatal,
2536 )
2537 }
2538
2539 fn parse_control_body(&mut self) -> Option<StmtId> {
2544 if matches!(self.peek().kind, TokenKind::LeftBrace) {
2545 return self.parse_block();
2546 }
2547 let keyword = match self.peek().kind {
2552 TokenKind::Let => Some(("a", "let")),
2553 TokenKind::Const => Some(("a", "const")),
2554 TokenKind::Function => Some(("a", "function")),
2555 TokenKind::Interface => Some(("an", "interface")),
2557 TokenKind::Enum => Some(("an", "enum")),
2558 _ if self.peek_identifier_text_is("type") => Some(("a", "type")),
2559 _ => None,
2560 };
2561 let declares = match self.peek_at(1).kind {
2565 TokenKind::Identifier => true,
2566 TokenKind::LeftBracket | TokenKind::LeftBrace => !matches!(keyword, Some((_, "type"))),
2567 _ => false,
2568 };
2569 if let Some((article, keyword)) = keyword
2570 && declares
2571 {
2572 self.error_at_peek_with_help(
2573 format!(
2574 "{article} `{keyword}` declaration can't be the body of a statement without braces"
2575 ),
2576 vec![format!("wrap it in braces: `{{ {keyword} … }}`")],
2577 );
2578 }
2579 self.block_depth += 1;
2580 let stmt = self.parse_statement();
2581 self.block_depth -= 1;
2582 let stmt = stmt?;
2583 let mut stmts = vec![stmt];
2584 stmts.extend(self.take_trailing_declarators());
2585 let first = parse_arena_result(self.ast.try_stmt(stmt), &mut self.fatal)?.span;
2586 let last_id = stmts.last().copied().unwrap_or(stmt);
2587 let last = parse_arena_result(self.ast.try_stmt(last_id), &mut self.fatal)?.span;
2588 let span = self.span(first.start, last.end);
2589 parse_arena_result(
2590 self.ast.try_push_stmt(Stmt {
2591 kind: StmtKind::Block(stmts),
2592 span,
2593 }),
2594 &mut self.fatal,
2595 )
2596 }
2597
2598 fn parse_loop_body(&mut self) -> Option<StmtId> {
2601 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2602 return self.parse_control_body();
2603 }
2604 self.empty_body_at_semicolon()
2605 }
2606
2607 fn parse_branch_body(&mut self, keyword: &str) -> Option<StmtId> {
2611 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2612 return self.parse_control_body();
2613 }
2614 self.error_at_peek_with_help(
2615 format!("`{keyword}` has an empty body"),
2616 vec![format!("remove the `;` after `{keyword}`")],
2617 );
2618 self.empty_body_at_semicolon()
2619 }
2620
2621 fn empty_body_at_semicolon(&mut self) -> Option<StmtId> {
2623 let semi = self.advance();
2624 parse_arena_result(
2625 self.ast.try_push_stmt(Stmt {
2626 kind: StmtKind::Block(vec![]),
2627 span: semi.span,
2628 }),
2629 &mut self.fatal,
2630 )
2631 }
2632
2633 fn parse_if(&mut self) -> Option<StmtId> {
2634 self.with_recursion_limit(Self::parse_if_inner)
2635 }
2636
2637 fn parse_if_inner(&mut self) -> Option<StmtId> {
2638 let kw = self.advance();
2639 let condition = self.parse_paren_condition()?;
2640 let then_block = self.parse_branch_body("if")?;
2641
2642 let else_block = if matches!(self.peek().kind, TokenKind::Else) {
2643 self.advance();
2644 if matches!(self.peek().kind, TokenKind::If) {
2645 Some(self.parse_if()?)
2646 } else {
2647 Some(self.parse_branch_body("else")?)
2648 }
2649 } else {
2650 None
2651 };
2652
2653 let end = match else_block {
2654 Some(id) => {
2655 parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
2656 .span
2657 .end
2658 }
2659 None => {
2660 parse_arena_result(self.ast.try_stmt(then_block), &mut self.fatal)?
2661 .span
2662 .end
2663 }
2664 };
2665
2666 parse_arena_result(
2667 self.ast.try_push_stmt(Stmt {
2668 kind: StmtKind::If {
2669 condition,
2670 then_block,
2671 else_block,
2672 },
2673 span: self.span(kw.span.start, end),
2674 }),
2675 &mut self.fatal,
2676 )
2677 }
2678
2679 fn parse_while(&mut self) -> Option<StmtId> {
2680 let kw = self.advance();
2681 let condition = self.parse_paren_condition()?;
2682 let body = self.parse_loop_body()?;
2683 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2684 .span
2685 .end;
2686
2687 parse_arena_result(
2688 self.ast.try_push_stmt(Stmt {
2689 kind: StmtKind::While { condition, body },
2690 span: self.span(kw.span.start, body_end),
2691 }),
2692 &mut self.fatal,
2693 )
2694 }
2695
2696 fn parse_for(&mut self) -> Option<StmtId> {
2698 let kw = self.advance();
2699 if self.peek_identifier_text_is("await")
2702 && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
2703 {
2704 let tok = self.advance();
2705 self.error_at_with_help(
2706 tok.span,
2707 "`for await` is not supported",
2708 vec![
2709 "calls return their values directly: remove `await` and write \
2710 `for (const e of …)`"
2711 .to_string(),
2712 ],
2713 );
2714 }
2715 if !matches!(self.peek().kind, TokenKind::LeftParen) {
2716 self.error_at_peek("expected `(` after `for`");
2717 return None;
2718 }
2719 self.advance();
2720
2721 if self.scan_for_of_shape() {
2722 self.parse_for_of_tail(kw)
2723 } else {
2724 self.parse_c_for_tail(kw)
2725 }
2726 }
2727
2728 fn scan_for_of_shape(&self) -> bool {
2730 if !matches!(self.peek().kind, TokenKind::Let | TokenKind::Const) {
2731 return false;
2732 }
2733 let (start, mut depth) = match self.peek_at(1).kind {
2735 TokenKind::Identifier => match self.peek_at(2).kind {
2736 TokenKind::Identifier if self.peek_at_is_word(2, "of") => return true,
2737 TokenKind::Colon => (3, 0i32),
2738 _ => return false,
2739 },
2740 TokenKind::LeftBracket | TokenKind::LeftBrace => (2, 1i32),
2741 _ => return false,
2742 };
2743 let mut i = start;
2744 loop {
2745 match self.peek_at(i).kind {
2746 TokenKind::Eof => return false,
2747 TokenKind::LessThan
2748 | TokenKind::LeftParen
2749 | TokenKind::LeftBracket
2750 | TokenKind::LeftBrace => {
2751 depth += 1;
2752 }
2753 TokenKind::GreaterThan
2754 | TokenKind::RightParen
2755 | TokenKind::RightBracket
2756 | TokenKind::RightBrace => {
2757 if depth == 0 {
2758 return false;
2759 }
2760 depth -= 1;
2761 }
2762 TokenKind::Identifier if depth == 0 && self.peek_at_is_word(i, "of") => {
2765 return true;
2766 }
2767 TokenKind::Equals | TokenKind::Semicolon if depth == 0 => return false,
2768 _ => {}
2769 }
2770 i += 1;
2771 }
2772 }
2773
2774 fn parse_c_for_tail(&mut self, kw: Token) -> Option<StmtId> {
2776 let init = match self.peek().kind {
2777 TokenKind::Semicolon => {
2778 self.advance();
2779 Vec::new()
2780 }
2781 TokenKind::Let | TokenKind::Const => {
2782 self.trailing_declarators.clear();
2783 let is_const = matches!(self.peek().kind, TokenKind::Const);
2784 let mut init = vec![self.parse_let_or_const(is_const)?];
2785 init.extend(self.take_trailing_declarators());
2786 init
2787 }
2788 _ => self.parse_for_init_expressions()?,
2789 };
2790 let for_stmt = self.finish_c_for(kw, init.clone());
2791 if for_stmt.is_none() {
2792 self.discard_detached_patterns(&init);
2793 }
2794 for_stmt
2795 }
2796
2797 fn finish_c_for(&mut self, kw: Token, init: Vec<StmtId>) -> Option<StmtId> {
2798 let condition = if matches!(self.peek().kind, TokenKind::Semicolon) {
2799 None
2800 } else {
2801 Some(self.parse_expression()?)
2802 };
2803 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2804 self.error_at_peek("expected `;` after `for` condition");
2805 return None;
2806 }
2807 self.advance();
2808
2809 let update = if matches!(self.peek().kind, TokenKind::RightParen) {
2810 None
2811 } else {
2812 Some(self.parse_for_update_stmt()?)
2813 };
2814 if !matches!(self.peek().kind, TokenKind::RightParen) {
2815 self.error_at_peek("expected `)` after `for` update");
2816 return None;
2817 }
2818 self.advance();
2819
2820 let body = self.parse_loop_body()?;
2821 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2822 .span
2823 .end;
2824 parse_arena_result(
2825 self.ast.try_push_stmt(Stmt {
2826 kind: StmtKind::For {
2827 init,
2828 condition,
2829 update,
2830 body,
2831 },
2832 span: self.span(kw.span.start, body_end),
2833 }),
2834 &mut self.fatal,
2835 )
2836 }
2837
2838 fn discard_detached_patterns(&mut self, stmts: &[StmtId]) {
2843 for &id in stmts {
2844 let Some(stmt) = parse_arena_result(self.ast.try_stmt_mut(id), &mut self.fatal) else {
2845 return;
2846 };
2847 if matches!(
2848 stmt.kind,
2849 StmtKind::LetPattern { .. } | StmtKind::ConstPattern { .. }
2850 ) {
2851 stmt.kind = StmtKind::Block(Vec::new());
2852 }
2853 }
2854 }
2855
2856 fn parse_for_init_expressions(&mut self) -> Option<Vec<StmtId>> {
2859 let mut init = vec![self.parse_for_clause_expr()?];
2860 while matches!(self.peek().kind, TokenKind::Comma) {
2861 self.advance();
2862 init.push(self.parse_for_clause_expr()?);
2863 }
2864 if !self.at_statement_end() {
2865 let last = parse_arena_result(self.ast.try_stmt(*init.last()?), &mut self.fatal)?;
2866 let what = if matches!(last.kind, StmtKind::Expr(_)) {
2867 "expression"
2868 } else {
2869 "assignment"
2870 };
2871 self.error_at_peek(format!("expected `;` after {what}"));
2872 return None;
2873 }
2874 self.finish_statement();
2875 Some(init)
2876 }
2877
2878 fn parse_for_update_stmt(&mut self) -> Option<StmtId> {
2881 let first = self.parse_for_clause_expr()?;
2882 if !matches!(self.peek().kind, TokenKind::Comma) {
2883 return Some(first);
2884 }
2885 let mut stmts = vec![first];
2886 while matches!(self.peek().kind, TokenKind::Comma) {
2887 self.advance();
2888 stmts.push(self.parse_for_clause_expr()?);
2889 }
2890 let start = parse_arena_result(self.ast.try_stmt(first), &mut self.fatal)?
2891 .span
2892 .start;
2893 let span = self.span(start, self.prev_token_end());
2894 parse_arena_result(
2895 self.ast.try_push_stmt(Stmt {
2896 kind: StmtKind::Block(stmts),
2897 span,
2898 }),
2899 &mut self.fatal,
2900 )
2901 }
2902
2903 fn parse_for_clause_expr(&mut self) -> Option<StmtId> {
2904 let expr_id = self.parse_expression()?;
2905 let span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
2906 let kind = self.statement_kind_for(expr_id)?;
2907 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
2908 }
2909
2910 fn parse_for_of_tail(&mut self, kw: Token) -> Option<StmtId> {
2911 let binding_tok = self.advance();
2912 let binding_kind = match binding_tok.kind {
2913 TokenKind::Let => crate::BindingKind::Let,
2914 TokenKind::Const => crate::BindingKind::Const,
2915 _ => return self.invariant_failure("scan_for_of_shape gated this"),
2916 };
2917 let (name, binding) = match self.peek().kind {
2918 TokenKind::LeftBracket => {
2919 let b = self.parse_binding()?;
2920 (None, Some(b))
2921 }
2922 TokenKind::LeftBrace => {
2923 let b = self.parse_binding()?;
2924 self.error_at(
2925 b.span(),
2926 "object destructuring is not supported in `for-of`; use array destructuring or unpack inside the loop body",
2927 );
2928 return None;
2929 }
2930 _ => {
2931 let name_tok = self.expect_binding_identifier(
2932 "expected identifier after `let`/`const` in `for-of`",
2933 )?;
2934 (Some(self.ident_from_token(&name_tok)), None)
2935 }
2936 };
2937 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2938 self.advance();
2939 Some(self.parse_type_annotation()?)
2940 } else {
2941 None
2942 };
2943 if !self.peek_identifier_text_is("of") {
2944 self.error_at_peek("expected `of` in `for-of`");
2945 return None;
2946 }
2947 self.advance();
2948
2949 let iter = self.parse_expression()?;
2950 if !matches!(self.peek().kind, TokenKind::RightParen) {
2951 self.error_at_peek("expected `)` after `for-of` iterable");
2952 return None;
2953 }
2954 self.advance();
2955
2956 let body = self.parse_loop_body()?;
2957 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2958 .span
2959 .end;
2960 let kind = match (binding, name) {
2961 (Some(binding), _) => StmtKind::ForOfPattern {
2962 binding_kind,
2963 binding,
2964 ty,
2965 iter,
2966 body,
2967 },
2968 (None, Some(name)) => StmtKind::ForOf {
2969 binding_kind,
2970 name,
2971 ty,
2972 iter,
2973 body,
2974 },
2975 (None, None) => return self.invariant_failure("either binding or name must be Some"),
2976 };
2977 parse_arena_result(
2978 self.ast.try_push_stmt(Stmt {
2979 kind,
2980 span: self.span(kw.span.start, body_end),
2981 }),
2982 &mut self.fatal,
2983 )
2984 }
2985
2986 fn parse_do_while(&mut self) -> Option<StmtId> {
2987 let kw = self.advance();
2988 let body = self.parse_loop_body()?;
2989 self.eat_asi_semicolon_before(TokenKind::While);
2990 if !matches!(self.peek().kind, TokenKind::While) {
2991 self.error_at_peek("expected `while` after `do` body");
2992 return None;
2993 }
2994 self.advance();
2995 let condition = self.parse_paren_condition()?;
2996 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
3000 self.advance().span.end
3001 } else {
3002 parse_arena_result(self.ast.try_expr(condition), &mut self.fatal)?
3003 .span
3004 .end
3005 };
3006 parse_arena_result(
3007 self.ast.try_push_stmt(Stmt {
3008 kind: StmtKind::DoWhile { body, condition },
3009 span: self.span(kw.span.start, end),
3010 }),
3011 &mut self.fatal,
3012 )
3013 }
3014
3015 fn parse_switch(&mut self) -> Option<StmtId> {
3017 let kw = self.advance();
3018 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3019 self.error_at_peek("expected `(` after `switch`");
3020 return None;
3021 }
3022 self.advance();
3023 let discriminant = self.parse_expression()?;
3024 if !matches!(self.peek().kind, TokenKind::RightParen) {
3025 self.error_at_peek("expected `)` after `switch` discriminant");
3026 return None;
3027 }
3028 self.advance();
3029
3030 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
3031 self.error_at_peek("expected `{` to start `switch` body");
3032 return None;
3033 }
3034 self.advance();
3035 self.block_depth += 1;
3036 let arms = self.parse_switch_arms();
3037 self.block_depth -= 1;
3038 let (cases, default) = arms?;
3039
3040 if !matches!(self.peek().kind, TokenKind::RightBrace) {
3041 self.error_at_peek("expected `}` to close `switch` body");
3042 return None;
3043 }
3044 let close = self.advance();
3045
3046 parse_arena_result(
3047 self.ast.try_push_stmt(Stmt {
3048 kind: StmtKind::Switch {
3049 discriminant,
3050 cases,
3051 default,
3052 },
3053 span: self.span(kw.span.start, close.span.end),
3054 }),
3055 &mut self.fatal,
3056 )
3057 }
3058
3059 fn parse_switch_arms(&mut self) -> Option<(Vec<SwitchCase>, Option<SwitchDefault>)> {
3061 let mut cases: Vec<SwitchCase> = Vec::new();
3062 let mut default: Option<SwitchDefault> = None;
3063 let mut pending_values: Vec<ExprId> = Vec::new();
3064 let mut pending_label_start: Option<u32> = None;
3065 let mut pending_default: Option<Span> = None;
3066
3067 loop {
3068 match self.peek().kind {
3069 TokenKind::Eof => break,
3070 TokenKind::Semicolon => {
3072 self.advance();
3073 }
3074 TokenKind::RightBrace => {
3075 if pending_label_start.is_some() {
3077 self.error_at_peek(
3078 "expected case body before `}` — case labels must be followed by statements",
3079 );
3080 return None;
3081 }
3082 break;
3083 }
3084 TokenKind::Case => {
3085 let case_kw = self.advance();
3086 if pending_label_start.is_none() {
3087 pending_label_start = Some(case_kw.span.start);
3088 }
3089 let value = self.parse_expression()?;
3090 if !matches!(self.peek().kind, TokenKind::Colon) {
3091 self.error_at_peek("expected `:` after `case` label");
3092 return None;
3093 }
3094 self.advance();
3095 pending_values.push(value);
3096 }
3097 TokenKind::Default => {
3098 let kw_tok = self.advance();
3099 if default.is_some() {
3100 self.error_at(kw_tok.span, "duplicate `default` clause in `switch`");
3101 return None;
3102 }
3103 if !matches!(self.peek().kind, TokenKind::Colon) {
3104 self.error_at_peek("expected `:` after `default`");
3105 return None;
3106 }
3107 self.advance();
3108 if pending_label_start.is_none() {
3109 pending_label_start = Some(kw_tok.span.start);
3110 }
3111 pending_default = Some(kw_tok.span);
3112 }
3113 _ => {
3114 if pending_label_start.is_none() {
3115 self.error_at_peek(
3116 "expected `case` or `default` at the start of a `switch` body",
3117 );
3118 return None;
3119 }
3120 let Some(label_start) = pending_label_start else {
3121 return self.invariant_failure("switch arm has no label");
3122 };
3123 let body = self.parse_switch_arm_body(label_start)?;
3124 let arm_span =
3125 parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
3126 if !pending_values.is_empty() {
3127 let first = parse_arena_result(
3128 self.ast.try_expr(pending_values[0]),
3129 &mut self.fatal,
3130 )?
3131 .span
3132 .start;
3133 let case_span = self.span(first, arm_span.end);
3134 cases.push(SwitchCase {
3135 values: std::mem::take(&mut pending_values),
3136 body,
3137 span: case_span,
3138 });
3139 }
3140 if let Some(def_kw_span) = pending_default.take() {
3141 default = Some(SwitchDefault {
3142 body,
3143 span: self.span(def_kw_span.start, arm_span.end),
3144 });
3145 }
3146 pending_label_start = None;
3147 }
3148 }
3149 }
3150 Some((cases, default))
3151 }
3152
3153 fn parse_switch_arm_body(&mut self, body_start: u32) -> Option<StmtId> {
3155 let mut stmts: Vec<StmtId> = Vec::new();
3156 let mut end_offset = body_start;
3157 loop {
3158 if matches!(
3159 self.peek().kind,
3160 TokenKind::Case | TokenKind::Default | TokenKind::RightBrace | TokenKind::Eof
3161 ) {
3162 break;
3163 }
3164 if matches!(self.peek().kind, TokenKind::Semicolon) {
3165 end_offset = self.peek().span.end;
3166 self.advance();
3167 continue;
3168 }
3169 let pos_before = self.pos;
3170 if let Some(id) = self.parse_statement() {
3171 stmts.push(id);
3172 stmts.extend(self.take_trailing_declarators());
3173 let last = stmts.last().copied().unwrap_or(id);
3174 end_offset = parse_arena_result(self.ast.try_stmt(last), &mut self.fatal)?
3175 .span
3176 .end;
3177 } else {
3178 self.recover();
3179 if self.pos == pos_before && !self.is_at_eof() {
3180 self.advance();
3181 }
3182 end_offset = end_offset.max(self.prev_token_end());
3185 }
3186 }
3187 let span = self.span(body_start, end_offset);
3188 parse_arena_result(
3189 self.ast.try_push_stmt(Stmt {
3190 kind: StmtKind::Block(stmts),
3191 span,
3192 }),
3193 &mut self.fatal,
3194 )
3195 }
3196
3197 fn parse_break(&mut self) -> Option<StmtId> {
3198 let kw = self.advance();
3199 if !matches!(self.peek().kind, TokenKind::Semicolon) {
3200 self.error_at_peek("expected `;` after `break`");
3201 return None;
3202 }
3203 let semi = self.advance();
3204 parse_arena_result(
3205 self.ast.try_push_stmt(Stmt {
3206 kind: StmtKind::Break,
3207 span: self.span(kw.span.start, semi.span.end),
3208 }),
3209 &mut self.fatal,
3210 )
3211 }
3212
3213 fn parse_continue(&mut self) -> Option<StmtId> {
3214 let kw = self.advance();
3215 if !matches!(self.peek().kind, TokenKind::Semicolon) {
3216 self.error_at_peek("expected `;` after `continue`");
3217 return None;
3218 }
3219 let semi = self.advance();
3220 parse_arena_result(
3221 self.ast.try_push_stmt(Stmt {
3222 kind: StmtKind::Continue,
3223 span: self.span(kw.span.start, semi.span.end),
3224 }),
3225 &mut self.fatal,
3226 )
3227 }
3228
3229 fn parse_return(&mut self) -> Option<StmtId> {
3230 let kw = self.advance();
3231
3232 let value = if matches!(self.peek().kind, TokenKind::Semicolon) {
3233 None
3234 } else {
3235 Some(self.parse_expression()?)
3236 };
3237
3238 if !self.at_statement_end() {
3239 self.error_at_peek("expected `;` after return");
3240 return None;
3241 }
3242 let end = self.finish_statement();
3243
3244 parse_arena_result(
3245 self.ast.try_push_stmt(Stmt {
3246 kind: StmtKind::Return(value),
3247 span: self.span(kw.span.start, end),
3248 }),
3249 &mut self.fatal,
3250 )
3251 }
3252
3253 fn parse_throw(&mut self) -> Option<StmtId> {
3254 let kw = self.advance();
3255
3256 if matches!(self.peek().kind, TokenKind::Semicolon) {
3257 self.error_at_peek_with_help(
3258 "expected expression after `throw`",
3259 vec!["throw new Error(\"message\");".to_string()],
3260 );
3261 return None;
3262 }
3263
3264 let value = self.parse_expression()?;
3265
3266 if !self.at_statement_end() {
3267 self.error_at_peek("expected `;` after `throw`");
3268 return None;
3269 }
3270 let end = self.finish_statement();
3271
3272 parse_arena_result(
3273 self.ast.try_push_stmt(Stmt {
3274 kind: StmtKind::Throw { value },
3275 span: self.span(kw.span.start, end),
3276 }),
3277 &mut self.fatal,
3278 )
3279 }
3280
3281 fn parse_try(&mut self) -> Option<StmtId> {
3282 let kw = self.advance();
3283
3284 let body = self.parse_block()?;
3285
3286 let mut catches: Vec<CatchClause> = Vec::new();
3287 let mut finally: Option<StmtId> = None;
3288 let mut tail_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3289 .span
3290 .end;
3291
3292 loop {
3293 match self.peek().kind {
3294 TokenKind::Catch => {
3295 if finally.is_some() {
3296 self.error_at_peek("`catch` clause must appear before `finally`");
3297 return None;
3298 }
3299 let clause = self.parse_catch_clause()?;
3300 tail_end = clause.span.end;
3301 catches.push(clause);
3302 }
3303 TokenKind::Finally => {
3304 if finally.is_some() {
3305 self.error_at_peek("duplicate `finally` clause");
3306 return None;
3307 }
3308 self.advance();
3309 let block = self.parse_block()?;
3310 tail_end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
3311 .span
3312 .end;
3313 finally = Some(block);
3314 }
3315 _ => break,
3316 }
3317 }
3318
3319 if catches.is_empty() && finally.is_none() {
3320 self.error_at(
3321 self.span(kw.span.start, tail_end),
3322 "try requires at least one of `catch` or `finally`",
3323 );
3324 return None;
3325 }
3326
3327 parse_arena_result(
3328 self.ast.try_push_stmt(Stmt {
3329 kind: StmtKind::Try {
3330 body,
3331 catches,
3332 finally,
3333 },
3334 span: self.span(kw.span.start, tail_end),
3335 }),
3336 &mut self.fatal,
3337 )
3338 }
3339
3340 fn parse_catch_clause(&mut self) -> Option<CatchClause> {
3341 let kw = self.advance();
3342
3343 let (binding, ty) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
3344 (
3346 Ident {
3347 name: "#catch".into(),
3348 span: kw.span,
3349 },
3350 None,
3351 )
3352 } else {
3353 self.parse_catch_binding()?
3354 };
3355
3356 let body = self.parse_block()?;
3357 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3358 .span
3359 .end;
3360
3361 Some(CatchClause {
3362 binding,
3363 ty,
3364 body,
3365 span: self.span(kw.span.start, body_end),
3366 })
3367 }
3368
3369 fn parse_catch_binding(&mut self) -> Option<(Ident, Option<TypeAnnotation>)> {
3370 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3371 self.error_at_peek("expected `(` after `catch`");
3372 return None;
3373 }
3374 self.advance();
3375 let name_tok = self.expect_binding_identifier("expected catch binding name")?;
3376 let binding = self.ident_from_token(&name_tok);
3377 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3378 self.advance();
3379 if self.reject_any_catch_annotation() {
3380 None
3382 } else {
3383 Some(self.parse_type_annotation()?)
3384 }
3385 } else {
3386 None
3387 };
3388 if !matches!(self.peek().kind, TokenKind::RightParen) {
3389 self.error_at_peek("expected `)` after catch binding");
3390 return None;
3391 }
3392 self.advance();
3393 Some((binding, ty))
3394 }
3395
3396 fn reject_any_catch_annotation(&mut self) -> bool {
3399 let is_bare_any = self.peek_identifier_text_is("any")
3400 && matches!(self.peek_at(1).kind, TokenKind::RightParen);
3401 if !is_bare_any {
3402 return false;
3403 }
3404 let any_tok = self.advance();
3405 self.error_at_with_help(
3407 any_tok.span,
3408 "`any` is not supported",
3409 vec![
3410 "write `catch (e)` to catch every thrown error, or name an error class, \
3411 as in `catch (e: RangeError)`, to catch only that type"
3412 .to_string(),
3413 ],
3414 );
3415 true
3416 }
3417
3418 fn parse_paren_condition(&mut self) -> Option<ExprId> {
3419 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3420 self.error_at_peek("expected `(`");
3421 return None;
3422 }
3423 self.advance();
3424 let expr = self.parse_expression()?;
3425 if !matches!(self.peek().kind, TokenKind::RightParen) {
3426 self.error_at_peek("expected `)`");
3427 return None;
3428 }
3429 self.advance();
3430 Some(expr)
3431 }
3432
3433 fn parse_predicate_or_return_type(
3434 &mut self,
3435 type_pos: TypePos,
3436 ) -> Option<(
3437 Option<TypeAnnotation>,
3438 Option<crate::TypePredicateAnnotation>,
3439 )> {
3440 if matches!(self.peek().kind, TokenKind::Identifier) && self.peek_at_is_word(1, "is") {
3441 let param_tok = self.advance();
3442 let param = self.ident_from_token(¶m_tok);
3443 self.advance();
3444 let asserted = self.parse_type_annotation_in(type_pos)?;
3445 let span = self.span(param.span.start, asserted.span.end);
3446 return Some((
3447 None,
3448 Some(crate::TypePredicateAnnotation {
3449 param,
3450 asserted,
3451 span,
3452 }),
3453 ));
3454 }
3455 Some((Some(self.parse_type_annotation_in(type_pos)?), None))
3456 }
3457
3458 fn parse_type_annotation(&mut self) -> Option<TypeAnnotation> {
3459 self.parse_type_annotation_in(TypePos::Anywhere)
3460 }
3461
3462 fn parse_type_annotation_in(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3463 let leading_pipe = matches!(self.peek().kind, TokenKind::Pipe);
3465 if leading_pipe {
3466 self.advance();
3467 }
3468 let first = self.parse_type_array(type_pos)?;
3469 if !matches!(self.peek().kind, TokenKind::Pipe) {
3470 return Some(first);
3471 }
3472 let start = first.span.start;
3473 let mut end = first.span.end;
3474 let mut members = vec![first];
3475 while matches!(self.peek().kind, TokenKind::Pipe) {
3476 self.advance();
3477 let member = self.parse_type_array(type_pos)?;
3478 end = member.span.end;
3479 members.push(member);
3480 }
3481 Some(TypeAnnotation {
3482 kind: TypeAnnotationKind::Union(members),
3483 span: self.span(start, end),
3484 })
3485 }
3486
3487 fn parse_type_array(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3488 self.with_recursion_limit(|parser| parser.parse_type_array_inner(type_pos))
3489 }
3490
3491 fn parse_type_array_inner(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3492 if self.is_contextual_type_operator(self.pos, "keyof") {
3496 let kw = self.advance();
3497 let operand = self.parse_type_array(type_pos)?;
3498 let span = self.span(kw.span.start, operand.span.end);
3499 return Some(TypeAnnotation {
3500 kind: TypeAnnotationKind::KeyOf(Box::new(operand)),
3501 span,
3502 });
3503 }
3504 if self.is_contextual_type_operator(self.pos, "readonly") {
3505 return self.parse_readonly_type(type_pos);
3506 }
3507
3508 let mut ty = match self.peek().kind {
3509 TokenKind::Typeof => {
3515 let kw = self.advance();
3516 let first = self.expect_identifier("expected a value name after `typeof`")?;
3517 let mut path = vec![self.type_name(first.span)?];
3518 let mut end = first.span.end;
3519 while matches!(self.peek().kind, TokenKind::Dot) {
3520 self.advance();
3521 let seg = self.expect_identifier_name("expected a property name after `.`")?;
3522 end = seg.span.end;
3523 path.push(self.type_name(seg.span)?);
3524 }
3525 TypeAnnotation {
3526 kind: TypeAnnotationKind::TypeOf { path },
3527 span: self.span(kw.span.start, end),
3528 }
3529 }
3530 TokenKind::Identifier | TokenKind::Void => {
3531 let leading_is_identifier = matches!(self.peek().kind, TokenKind::Identifier);
3533 let tok = self.advance();
3534 let first_span = tok.span;
3535 let mut path_end = first_span.end;
3536 let first_name = self.type_name(first_span)?;
3537 let mut path = vec![first_name.clone()];
3538 if leading_is_identifier {
3539 while matches!(self.peek().kind, TokenKind::Dot) {
3540 self.advance();
3541 if !matches!(self.peek().kind, TokenKind::Identifier) {
3542 self.error_at_peek("expected identifier after `.` in type name");
3543 return None;
3544 }
3545 let seg = self.advance();
3546 path_end = seg.span.end;
3547 path.push(self.type_name(seg.span)?);
3548 }
3549 }
3550 let (args, end) = if matches!(self.peek().kind, TokenKind::LessThan)
3552 && !self.line_break_before_peek()
3553 {
3554 self.parse_type_argument_list()?
3555 } else {
3556 (Vec::new(), path_end)
3557 };
3558 let outer_span = self.span(first_span.start, end);
3559 let kind = if path.len() == 1 {
3560 if first_name.name == "any" {
3561 self.error_at_with_help(
3562 first_span,
3563 "`any` is not supported",
3564 vec![
3565 "annotate a concrete type instead — an object shape \
3566 like `{ id: string }`, a named `interface`, or \
3567 `unknown` (a safe dynamic type you narrow before use)"
3568 .to_string(),
3569 ],
3570 );
3571 }
3572 TypeAnnotationKind::Name {
3573 name: first_name,
3574 args,
3575 }
3576 } else {
3577 TypeAnnotationKind::Qualified { path, args }
3578 };
3579 TypeAnnotation {
3580 kind,
3581 span: outer_span,
3582 }
3583 }
3584 TokenKind::NullLiteral => {
3586 let tok = self.advance();
3587 TypeAnnotation {
3588 kind: TypeAnnotationKind::Name {
3589 name: self.type_name(tok.span)?,
3590 args: Vec::new(),
3591 },
3592 span: tok.span,
3593 }
3594 }
3595 TokenKind::StringLiteral(_) => {
3596 let tok = self.advance();
3597 let span = tok.span;
3598 let TokenKind::StringLiteral(s) = tok.kind else {
3599 unreachable!("advance preserves the matched StringLiteral variant");
3601 };
3602 TypeAnnotation {
3603 kind: TypeAnnotationKind::StringLiteral(s),
3604 span,
3605 }
3606 }
3607 TokenKind::NumberLiteral(_) => {
3608 let tok = self.advance();
3609 let span = tok.span;
3610 let TokenKind::NumberLiteral(v) = tok.kind else {
3611 unreachable!("advance preserves the matched NumberLiteral variant");
3613 };
3614 let canonical = if v == 0.0 { 0.0 } else { v };
3616 TypeAnnotation {
3617 kind: TypeAnnotationKind::NumberLiteral(crate::types::LiteralF64(canonical)),
3618 span,
3619 }
3620 }
3621 TokenKind::BigIntLiteral(_) => {
3622 let tok = self.advance();
3623 let TokenKind::BigIntLiteral(digits) = tok.kind else {
3624 unreachable!("advance preserves the matched BigIntLiteral variant");
3626 };
3627 TypeAnnotation {
3628 kind: TypeAnnotationKind::BigIntLiteral(digits),
3629 span: tok.span,
3630 }
3631 }
3632 TokenKind::Minus
3633 if matches!(
3634 self.peek_at(1).kind,
3635 TokenKind::NumberLiteral(_) | TokenKind::BigIntLiteral(_)
3636 ) =>
3637 {
3638 let minus = self.advance().span;
3639 let tok = self.advance();
3640 let span = self.span(minus.start, tok.span.end);
3641 let kind =
3642 match tok.kind {
3643 TokenKind::NumberLiteral(v) => TypeAnnotationKind::NumberLiteral(
3644 crate::types::LiteralF64(if v == 0.0 { 0.0 } else { -v }),
3645 ),
3646 TokenKind::BigIntLiteral(digits) => TypeAnnotationKind::BigIntLiteral(
3647 crate::types::negate_bigint_digits(&digits),
3648 ),
3649 _ => unreachable!("guarded to a numeric literal after `-`"),
3651 };
3652 TypeAnnotation { kind, span }
3653 }
3654 TokenKind::BooleanLiteral(value) => {
3655 let span = self.advance().span;
3656 TypeAnnotation {
3657 kind: TypeAnnotationKind::BooleanLiteral(value),
3658 span,
3659 }
3660 }
3661 TokenKind::LeftBrace => self.parse_object_type_annotation()?,
3662 TokenKind::LeftParen if self.paren_at_opens_function_type(self.pos, type_pos) => {
3663 self.parse_function_type_annotation()?
3664 }
3665 TokenKind::LeftParen => self.parse_grouped_or_function_type()?,
3666 TokenKind::LeftBracket => self.parse_tuple_type_annotation()?,
3668 _ => {
3669 self.error_at_peek("expected type");
3670 return None;
3671 }
3672 };
3673 let mut array_suffixes = 0usize;
3676 while matches!(self.peek().kind, TokenKind::LeftBracket) && !self.line_break_before_peek() {
3679 array_suffixes += 1;
3680 if self.recursion_depth.saturating_add(array_suffixes) >= MAX_PARSE_DEPTH {
3681 self.stop_at_recursion_limit();
3682 return None;
3683 }
3684 let open = self.advance();
3685 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3686 self.error_at(open.span, "expected `]` to close array type");
3687 return None;
3688 }
3689 let close = self.advance();
3690 let span = self.span(ty.span.start, close.span.end);
3691 ty = TypeAnnotation {
3692 kind: TypeAnnotationKind::Array(Box::new(ty)),
3693 span,
3694 };
3695 }
3696 Some(ty)
3697 }
3698
3699 fn parse_readonly_type(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3703 let kw = self.advance();
3704 let opens_group = matches!(self.peek().kind, TokenKind::LeftParen);
3705 let operand = self.parse_type_array(type_pos)?;
3706 let grouped = opens_group
3709 && matches!(
3710 self.pos
3711 .checked_sub(1)
3712 .and_then(|index| self.tokens.get(index))
3713 .map(|token| &token.kind),
3714 Some(TokenKind::RightParen)
3715 );
3716 if grouped
3717 || !matches!(
3718 operand.kind,
3719 TypeAnnotationKind::Array(_) | TypeAnnotationKind::Tuple(_)
3720 )
3721 {
3722 self.error_at_with_help(
3723 kw.span,
3724 "`readonly` only applies to array and tuple types",
3725 vec![
3726 "write `readonly T[]` or `readonly [A, B]`; to protect an object's \
3727 properties, mark each one `readonly` instead"
3728 .to_string(),
3729 ],
3730 );
3731 return Some(operand);
3732 }
3733 let span = self.span(kw.span.start, operand.span.end);
3734 Some(TypeAnnotation {
3735 kind: TypeAnnotationKind::Readonly(Box::new(operand)),
3736 span,
3737 })
3738 }
3739
3740 fn paren_at_opens_function_type(&self, index: usize, type_pos: TypePos) -> bool {
3751 let after = |offset: usize| self.tokens.get(index + offset).map(|t| &t.kind);
3752 match (after(1), after(2)) {
3753 (Some(TokenKind::RightParen | TokenKind::DotDotDot), _) => true,
3755 (Some(TokenKind::Identifier), Some(TokenKind::Colon | TokenKind::Question)) => true,
3757 (Some(TokenKind::Identifier), Some(TokenKind::Comma | TokenKind::RightParen)) => {
3766 type_pos == TypePos::Anywhere
3767 && self.index_after_matching_paren(index).is_some_and(|i| {
3768 matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow))
3769 })
3770 }
3771 _ => false,
3773 }
3774 }
3775
3776 fn parse_grouped_or_function_type(&mut self) -> Option<TypeAnnotation> {
3781 let open = self.pos;
3782 let diagnostics_before = self.diagnostics.len();
3783 if let Some(ty) = self.parse_grouped_type_annotation() {
3784 return Some(ty);
3785 }
3786 let arrow_follows = self
3787 .index_after_matching_paren(open)
3788 .is_some_and(|i| matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)));
3789 if !arrow_follows {
3790 return None;
3791 }
3792 self.pos = open;
3793 self.diagnostics.truncate(diagnostics_before);
3794 self.parse_function_type_annotation()
3795 }
3796
3797 fn parse_grouped_type_annotation(&mut self) -> Option<TypeAnnotation> {
3800 let open = self.advance();
3801 let inner = self.parse_type_annotation()?;
3802 if !matches!(self.peek().kind, TokenKind::RightParen) {
3803 self.error_at_peek("expected `)` to close a parenthesized type");
3804 return None;
3805 }
3806 let close = self.advance();
3807 Some(TypeAnnotation {
3808 span: self.span(open.span.start, close.span.end),
3809 ..inner
3810 })
3811 }
3812
3813 fn parse_function_type_annotation(&mut self) -> Option<TypeAnnotation> {
3815 let open = self.advance();
3816 let params = self.parse_function_type_params()?;
3817 if !matches!(self.peek().kind, TokenKind::Arrow) {
3818 self.error_at_peek("expected `=>` in function-type annotation");
3819 return None;
3820 }
3821 self.advance();
3822 let return_type = self.parse_type_annotation()?;
3823 let end = return_type.span.end;
3824 Some(TypeAnnotation {
3825 kind: TypeAnnotationKind::Function {
3826 params,
3827 return_type: Box::new(return_type),
3828 },
3829 span: self.span(open.span.start, end),
3830 })
3831 }
3832
3833 fn parse_function_type_params(&mut self) -> Option<Vec<TypeAnnotationField>> {
3837 let mut params: Vec<TypeAnnotationField> = Vec::new();
3838 if !matches!(self.peek().kind, TokenKind::RightParen) {
3839 loop {
3840 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
3841 self.advance();
3842 true
3843 } else {
3844 false
3845 };
3846 let name_tok = self.expect_binding_identifier(if rest {
3847 "expected identifier after `...` in function-type annotation"
3848 } else {
3849 "expected parameter name in function-type annotation"
3850 })?;
3851 let name = self.ident_from_token(&name_tok);
3852 let optional = self.parse_parameter_optional(rest, false)?;
3853 if !matches!(self.peek().kind, TokenKind::Colon) {
3854 if rest {
3855 self.error_at_peek_with_help(
3856 "rest parameter requires a type annotation",
3857 vec!["(...args: T[]) => R".to_string()],
3858 );
3859 } else {
3860 self.error_at_peek(
3861 "expected `:` after parameter name (function-type params \
3862 require named annotations in v1)",
3863 );
3864 }
3865 return None;
3866 }
3867 self.advance();
3868 let ty = self.parse_type_annotation()?;
3869 if rest && !is_rest_array_annotation(&ty) {
3870 self.error_at(ty.span, "rest parameter type must be an array");
3871 return None;
3872 }
3873 if matches!(self.peek().kind, TokenKind::Equals) {
3874 if rest {
3875 self.error_rest_parameter_default();
3876 } else {
3877 self.error_at_peek(
3878 "default values in function-type annotations are not yet supported",
3879 );
3880 }
3881 return None;
3882 }
3883 params.push(TypeAnnotationField {
3884 name,
3885 ty,
3886 optional,
3887 readonly: false,
3888 rest,
3889 method: false,
3890 });
3891 match self.peek().kind {
3892 TokenKind::Comma => {
3893 if rest {
3894 let comma = self.advance();
3895 self.error_at(comma.span, "rest parameter must be the last parameter");
3896 return None;
3897 }
3898 self.advance();
3899 if matches!(self.peek().kind, TokenKind::RightParen) {
3900 break;
3901 }
3902 }
3903 TokenKind::RightParen => break,
3904 _ => {
3905 self.error_at_peek("expected `,` or `)`");
3906 return None;
3907 }
3908 }
3909 }
3910 self.check_parameter_order(params.iter().map(ParamOrder::of_field));
3911 }
3912 if !matches!(self.peek().kind, TokenKind::RightParen) {
3913 self.error_at_peek("expected `)`");
3914 return None;
3915 }
3916 self.advance();
3917 Some(params)
3918 }
3919
3920 fn parse_tuple_type_annotation(&mut self) -> Option<TypeAnnotation> {
3921 let open = self.advance();
3922 if matches!(self.peek().kind, TokenKind::RightBracket) {
3923 let close = self.peek().span;
3924 self.error_at(
3925 self.span(open.span.start, close.end),
3926 "tuple types must have at least one element",
3927 );
3928 return None;
3929 }
3930 let mut elements: Vec<TypeAnnotation> = Vec::new();
3931 loop {
3932 if matches!(self.peek().kind, TokenKind::DotDotDot) {
3933 self.error_at_with_help(
3934 self.peek().span,
3935 "rest elements in tuple types are not supported",
3936 vec!["use an array type `T[]` for a list of varying length".to_string()],
3937 );
3938 return None;
3939 }
3940 let label = self.skip_tuple_element_label();
3943 let mut ty = self.parse_type_annotation()?;
3944 let suffix_optional = matches!(self.peek().kind, TokenKind::Question);
3945 if suffix_optional && label != TupleElementLabel::None {
3946 self.error_at_peek_with_help(
3947 "put the optional marker after the tuple element label",
3948 vec!["write `name?: T` instead of `name: T?`".to_string()],
3949 );
3950 return None;
3951 }
3952 let optional = label == TupleElementLabel::Optional || suffix_optional;
3953 if optional {
3954 let end = if suffix_optional {
3955 self.advance().span.end
3956 } else {
3957 ty.span.end
3958 };
3959 let span = self.span(ty.span.start, end);
3960 ty = TypeAnnotation {
3961 kind: TypeAnnotationKind::Optional(Box::new(ty)),
3962 span,
3963 };
3964 } else if elements
3965 .iter()
3966 .any(|element| matches!(element.kind, TypeAnnotationKind::Optional(_)))
3967 {
3968 self.error_at(
3969 ty.span,
3970 "a required tuple element cannot follow an optional element",
3971 );
3972 return None;
3973 }
3974 elements.push(ty);
3975 match self.peek().kind {
3976 TokenKind::Comma => {
3977 self.advance();
3978 if matches!(self.peek().kind, TokenKind::RightBracket) {
3979 break;
3980 }
3981 }
3982 TokenKind::RightBracket => break,
3983 _ => {
3984 self.error_at_peek("expected `,` or `]`");
3985 return None;
3986 }
3987 }
3988 }
3989 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3990 self.error_at_peek("expected `]`");
3991 return None;
3992 }
3993 let close = self.advance();
3994 Some(TypeAnnotation {
3995 kind: TypeAnnotationKind::Tuple(elements),
3996 span: self.span(open.span.start, close.span.end),
3997 })
3998 }
3999
4000 fn skip_tuple_element_label(&mut self) -> TupleElementLabel {
4002 if !is_property_name(&self.peek().kind) {
4003 return TupleElementLabel::None;
4004 }
4005 match self.peek_at(1).kind {
4006 TokenKind::Colon => {
4007 self.advance();
4008 self.advance();
4009 TupleElementLabel::Required
4010 }
4011 TokenKind::Question if matches!(self.peek_at(2).kind, TokenKind::Colon) => {
4012 self.advance();
4013 self.advance();
4014 self.advance();
4015 TupleElementLabel::Optional
4016 }
4017 _ => TupleElementLabel::None,
4018 }
4019 }
4020
4021 fn peek_is_parameterless_index_signature(&self) -> bool {
4024 matches!(self.peek().kind, TokenKind::LeftBracket)
4025 && matches!(self.peek_at(1).kind, TokenKind::RightBracket)
4026 }
4027
4028 fn skip_parameterless_index_signature(&mut self, member_start: u32) -> Option<()> {
4032 self.advance();
4033 let close = self.advance();
4034 self.error_at_with_help(
4035 self.span(member_start, close.span.end),
4036 "an index signature must declare exactly one parameter",
4037 vec!["write `[key: string]: V`".to_string()],
4038 );
4039 if matches!(self.peek().kind, TokenKind::Colon) {
4040 self.advance();
4041 self.parse_type_annotation()?;
4042 }
4043 Some(())
4044 }
4045
4046 fn parse_index_signature(&mut self, readonly: bool) -> Option<crate::IndexSignatureAnnotation> {
4047 let open = self.advance();
4048 let parameter = self.expect_property_ident("expected index parameter name")?;
4049 self.reject_eval_or_arguments(parameter.span);
4050 if !matches!(self.peek().kind, TokenKind::Colon) {
4051 self.error_at_peek("expected `:` after index parameter name");
4052 return None;
4053 }
4054 self.advance();
4055 if !self.peek_identifier_text_is("string") {
4056 self.error_at_peek("index signatures require string keys; use `[key: string]: V`");
4057 return None;
4058 }
4059 self.advance();
4060 if !matches!(self.peek().kind, TokenKind::RightBracket) {
4061 self.error_at_peek("expected `]` after string index key type");
4062 return None;
4063 }
4064 self.advance();
4065 if !matches!(self.peek().kind, TokenKind::Colon) {
4066 self.error_at_peek("expected `:` before index value type");
4067 return None;
4068 }
4069 self.advance();
4070 let value = self.parse_type_annotation()?;
4071 Some(crate::IndexSignatureAnnotation {
4072 span: self.span(open.span.start, value.span.end),
4073 value,
4074 readonly,
4075 })
4076 }
4077
4078 fn parse_object_type_annotation(&mut self) -> Option<TypeAnnotation> {
4079 let open = self.advance();
4080 let mut fields: Vec<TypeAnnotationField> = Vec::new();
4081 let mut index = None;
4082 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
4083 && !self.peek_starts_top_level_item()
4084 {
4085 let member_start = self.peek().span.start;
4086 let readonly = self.eat_readonly_property_modifier();
4087 if self.peek_is_parameterless_index_signature() {
4088 self.skip_parameterless_index_signature(member_start)?;
4089 } else if self.peek_is_construct_signature() {
4090 self.reject_construct_signature()?;
4091 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
4092 let signature = self.parse_index_signature(readonly)?;
4093 if index.is_some() {
4094 self.error_at_with_help(
4095 signature.span,
4096 "duplicate string index signature",
4097 vec![],
4098 );
4099 }
4100 index = Some(Box::new(signature));
4101 } else {
4102 let field = self.parse_object_type_field(readonly, member_start)?;
4103 if let Some(existing) = fields.iter().find(|f| f.name.name == field.name.name) {
4104 self.diagnostics.push(Diagnostic {
4105 severity: Severity::Error,
4106 span: field.name.span,
4107 message: format!("duplicate field `{}` in object type", field.name.name),
4108 help: vec![],
4109 notes: vec![(existing.name.span, "first defined here".into())],
4110 });
4111 }
4112 fields.push(field);
4113 }
4114 self.finish_type_member(self.prev_token_end(), "expected `;`, `,`, or `}`")?;
4115 }
4116 if !matches!(self.peek().kind, TokenKind::RightBrace) {
4117 self.error_at_peek("expected `}`");
4118 return None;
4119 }
4120 let close = self.advance();
4121 Some(TypeAnnotation {
4122 kind: TypeAnnotationKind::Object { index, fields },
4123 span: self.span(open.span.start, close.span.end),
4124 })
4125 }
4126
4127 fn peek_starts_top_level_item(&self) -> bool {
4130 let import_statement = matches!(self.peek().kind, TokenKind::Import)
4131 && matches!(
4132 self.peek_at(1).kind,
4133 TokenKind::LeftBrace
4134 | TokenKind::Star
4135 | TokenKind::Identifier
4136 | TokenKind::StringLiteral(_)
4137 );
4138 import_statement || self.peek_starts_declaration()
4139 }
4140
4141 fn peek_starts_declaration(&self) -> bool {
4148 if matches!(self.peek().kind, TokenKind::Export)
4149 && matches!(
4150 self.peek_at(1).kind,
4151 TokenKind::Default | TokenKind::LeftBrace | TokenKind::Star
4152 )
4153 {
4154 return true;
4155 }
4156 let mut offset = 0;
4157 while self.peek_at_is_declaration_modifier(offset) {
4158 offset += 1;
4159 }
4160 let declaration_keyword = matches!(
4161 self.peek_at(offset).kind,
4162 TokenKind::Function
4163 | TokenKind::Class
4164 | TokenKind::Let
4165 | TokenKind::Const
4166 | TokenKind::Enum
4167 | TokenKind::Interface
4168 ) || self.peek_at_is_word(offset, "type");
4169 declaration_keyword && matches!(self.peek_at(offset + 1).kind, TokenKind::Identifier)
4170 }
4171
4172 fn peek_at_is_declaration_modifier(&self, offset: usize) -> bool {
4174 matches!(self.peek_at(offset).kind, TokenKind::Export)
4175 || ["declare", "async", "abstract"]
4176 .iter()
4177 .any(|word| self.peek_at_is_word(offset, word))
4178 }
4179
4180 fn peek_is_construct_signature(&self) -> bool {
4185 matches!(self.peek().kind, TokenKind::New)
4186 && matches!(
4187 self.peek_at(1).kind,
4188 TokenKind::LeftParen | TokenKind::LessThan
4189 )
4190 }
4191
4192 fn reject_construct_signature(&mut self) -> Option<()> {
4196 let keyword = self.advance();
4197 self.error_at_with_help(
4198 keyword.span,
4199 "construct signatures are not supported in object types",
4200 vec![
4201 "declare an `interface` with the `new (…)` signature, or pass a factory \
4202 function such as `() => T`"
4203 .to_string(),
4204 ],
4205 );
4206 if matches!(self.peek().kind, TokenKind::LessThan) {
4207 self.parse_generic_param_list()?;
4208 }
4209 if !matches!(self.peek().kind, TokenKind::LeftParen) {
4210 self.error_at_peek("expected `(` after construct signature type parameters");
4211 return None;
4212 }
4213 self.parse_object_type_method_signature(keyword.span.start)?;
4214 Some(())
4215 }
4216
4217 fn parse_object_type_field(
4219 &mut self,
4220 readonly: bool,
4221 member_start: u32,
4222 ) -> Option<TypeAnnotationField> {
4223 let name = self.expect_property_ident("expected field name in object type")?;
4224 let optional = matches!(self.peek().kind, TokenKind::Question);
4226 if optional {
4227 self.advance();
4228 }
4229 let method = matches!(self.peek().kind, TokenKind::LeftParen);
4230 let ty = if method {
4231 if readonly {
4232 self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
4233 }
4234 self.parse_object_type_method_signature(name.span.start)?
4235 } else {
4236 if !matches!(self.peek().kind, TokenKind::Colon) {
4237 self.error_at_peek("expected `:` after field name");
4238 return None;
4239 }
4240 self.advance();
4241 self.parse_type_annotation()?
4242 };
4243 Some(TypeAnnotationField {
4244 name,
4245 ty,
4246 optional,
4247 readonly,
4248 rest: false,
4249 method,
4250 })
4251 }
4252
4253 fn parse_object_type_method_signature(&mut self, start: u32) -> Option<TypeAnnotation> {
4258 self.advance();
4259 let params = self.parse_function_type_params()?;
4260 if !matches!(self.peek().kind, TokenKind::Colon) {
4261 self.error_at_peek("expected `:` and return type after method parameters");
4262 return None;
4263 }
4264 self.advance();
4265 let return_type = self.parse_type_annotation()?;
4266 let end = return_type.span.end;
4267 Some(TypeAnnotation {
4268 kind: TypeAnnotationKind::Function {
4269 params,
4270 return_type: Box::new(return_type),
4271 },
4272 span: self.span(start, end),
4273 })
4274 }
4275
4276 fn expect_identifier(&mut self, message: &str) -> Option<Token> {
4279 let tok = self.expect_identifier_name(message)?;
4280 self.reject_strict_mode_reserved_word(&tok);
4281 Some(tok)
4282 }
4283
4284 fn expect_binding_identifier(&mut self, message: &str) -> Option<Token> {
4288 let tok = self.expect_identifier(message)?;
4289 self.reject_eval_or_arguments(tok.span);
4290 Some(tok)
4291 }
4292
4293 fn expect_identifier_name(&mut self, message: &str) -> Option<Token> {
4296 if matches!(self.peek().kind, TokenKind::Identifier) {
4297 return Some(self.advance());
4298 }
4299 if is_reserved_identifier_word(&self.peek().kind) {
4300 let tok = self.advance();
4301 let keyword = &self.source[tok.span.start as usize..tok.span.end as usize];
4302 self.error_at_with_help(
4303 tok.span,
4304 format!("`{keyword}` is a reserved keyword and can't be used as a name"),
4305 vec![format!(
4306 "rename it, for example: `{}`",
4307 reserved_keyword_rename_example(keyword)
4308 )],
4309 );
4310 return None;
4311 }
4312 self.error_at_peek(message.to_string());
4313 None
4314 }
4315
4316 fn reject_strict_mode_reserved_word(&mut self, tok: &Token) {
4319 let word = &self.source[tok.span.start as usize..tok.span.end as usize];
4320 if !STRICT_MODE_RESERVED_WORDS.contains(&word) {
4321 return;
4322 }
4323 self.error_at_with_help(
4324 tok.span,
4325 format!("`{word}` is a reserved word in strict mode and can't be used as a name"),
4326 vec![format!(
4327 "rename it, for example: `{}`",
4328 reserved_keyword_rename_example(word)
4329 )],
4330 );
4331 }
4332
4333 fn reject_eval_or_arguments(&mut self, span: Span) {
4336 let word = &self.source[span.start as usize..span.end as usize];
4337 if !matches!(word, "eval" | "arguments") {
4338 return;
4339 }
4340 self.error_at_with_help(
4341 span,
4342 format!("`{word}` can't name a binding in strict mode"),
4343 vec![format!(
4344 "rename it, for example: `{}`",
4345 reserved_keyword_rename_example(word)
4346 )],
4347 );
4348 }
4349
4350 fn ident_from_token(&self, tok: &Token) -> Ident {
4351 Ident {
4352 name: self.source[tok.span.start as usize..tok.span.end as usize].to_string(),
4353 span: tok.span,
4354 }
4355 }
4356
4357 fn property_ident_from_token(&self, tok: &Token) -> Ident {
4358 if let TokenKind::StringLiteral(s) = &tok.kind {
4359 Ident {
4360 name: s.clone(),
4361 span: tok.span,
4362 }
4363 } else {
4364 self.ident_from_token(tok)
4365 }
4366 }
4367
4368 fn expect_property_name(&mut self, message: &str) -> Option<Token> {
4369 if !is_property_name(&self.peek().kind) {
4370 self.error_at_peek(message.to_string());
4371 return None;
4372 }
4373 Some(self.advance())
4374 }
4375
4376 fn expect_property_ident(&mut self, message: &str) -> Option<Ident> {
4377 if !is_property_name(&self.peek().kind)
4378 && !matches!(self.peek().kind, TokenKind::StringLiteral(_))
4379 {
4380 self.error_at_peek(message.to_string());
4381 return None;
4382 }
4383 let tok = self.advance();
4384 Some(self.property_ident_from_token(&tok))
4385 }
4386
4387 fn readonly_modifier_span(&self, member_start: u32) -> Span {
4390 self.span(member_start, member_start + "readonly".len() as u32)
4391 }
4392
4393 fn reject_readonly_modifier(&mut self, span: Span) {
4396 self.error_at_with_help(
4397 span,
4398 "`readonly` can only modify a property or index signature",
4399 vec!["remove `readonly`".to_string()],
4400 );
4401 }
4402
4403 fn eat_readonly_property_modifier(&mut self) -> bool {
4404 if self.peek_identifier_text_is("readonly")
4405 && !self.line_break_after_peek()
4406 && (self.peek_starts_property_after_readonly()
4407 || matches!(self.peek_at(1).kind, TokenKind::LeftBracket))
4408 {
4409 self.advance();
4410 return true;
4411 }
4412 false
4413 }
4414
4415 fn peek_starts_property_after_readonly(&self) -> bool {
4416 let name = &self.peek_at(1).kind;
4417 let after_name = &self.peek_at(2).kind;
4418 let after_optional = &self.peek_at(3).kind;
4419 let starts_member_type =
4422 |kind: &TokenKind| matches!(kind, TokenKind::Colon | TokenKind::LeftParen);
4423 (is_property_name(name) || matches!(name, TokenKind::StringLiteral(_)))
4424 && (starts_member_type(after_name)
4425 || matches!(after_name, TokenKind::Question) && starts_member_type(after_optional))
4426 }
4427
4428 fn peek_at_is_binary_word(&self, offset: usize) -> bool {
4430 self.peek_at_is_word(offset, "as") || self.peek_at_is_word(offset, "satisfies")
4431 }
4432
4433 fn peek_identifier_text_is(&self, expected: &str) -> bool {
4434 self.token_is_word(self.peek(), expected)
4435 }
4436
4437 fn peek_at_is_word(&self, offset: usize, expected: &str) -> bool {
4438 self.token_is_word(self.peek_at(offset), expected)
4439 }
4440
4441 fn token_is_word(&self, tok: &Token, expected: &str) -> bool {
4442 matches!(tok.kind, TokenKind::Identifier)
4443 && &self.source[tok.span.start as usize..tok.span.end as usize] == expected
4444 }
4445
4446 fn at_delete_operator(&self) -> bool {
4453 if !self.peek_identifier_text_is("delete") {
4454 return false;
4455 }
4456 if !matches!(
4457 self.peek_at(1).kind,
4458 TokenKind::Identifier | TokenKind::This
4459 ) {
4460 return false;
4461 }
4462 let next_is_cast_keyword =
4463 self.peek_at_is_word(1, "as") || self.peek_at_is_word(1, "satisfies");
4464 !next_is_cast_keyword || cannot_start_type(&self.peek_at(2).kind)
4465 }
4466
4467 fn at_type_alias_head(&self) -> bool {
4471 self.peek_identifier_text_is("type")
4472 && matches!(self.peek_at(1).kind, TokenKind::Identifier)
4473 }
4474
4475 fn parse_expression(&mut self) -> Option<ExprId> {
4478 self.with_recursion_limit(Self::parse_expression_inner)
4479 }
4480
4481 fn parse_expression_inner(&mut self) -> Option<ExprId> {
4482 let written = self.parse_conditional()?;
4483 let shift_assignment = self.peek_shift().filter(|shift| shift.assignment);
4484 if !is_assign_lookahead(&self.peek().kind) && shift_assignment.is_none() {
4485 return Some(written);
4486 }
4487 let target_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4488 let target = self.assignment_target(written)?;
4489 let op_tok = self.advance();
4490 let mut op_span = op_tok.span;
4491 let op = if let Some(ShiftToken { op, tokens, .. }) = shift_assignment {
4492 for _ in 1..tokens {
4493 op_span.end = self.advance().span.end;
4494 }
4495 Some(op)
4496 } else {
4497 compound_op_for_token(&op_tok.kind)
4498 };
4499 let value = self.parse_expression()?;
4500 let value_span = parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
4501 parse_arena_result(
4502 self.ast.try_push_expr(Expr {
4503 kind: ExprKind::Assign {
4504 target,
4505 op,
4506 op_span,
4507 value,
4508 },
4509 span: self.span(target_span.start, value_span.end),
4510 }),
4511 &mut self.fatal,
4512 )
4513 }
4514
4515 fn assignment_target(&mut self, written: ExprId) -> Option<ExprId> {
4518 let mut target = written;
4519 while let ExprKind::Paren(inner) =
4520 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind
4521 {
4522 target = inner;
4523 }
4524 if matches!(
4525 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind,
4526 ExprKind::Identifier(_) | ExprKind::FieldAccess { .. } | ExprKind::IndexAccess { .. }
4527 ) {
4528 return Some(target);
4529 }
4530 let error_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4531 self.error_at_with_help(
4532 error_span,
4533 "invalid assignment target",
4534 vec![
4535 "assign to a variable, a field (`o.f = …`), or an element (`a[i] = …`)".to_string(),
4536 ],
4537 );
4538 None
4539 }
4540
4541 fn parse_conditional(&mut self) -> Option<ExprId> {
4542 self.reject_async_modifier();
4543 if self.is_arrow_start() {
4544 return self.parse_arrow();
4545 }
4546 let cond = self.parse_binary(0)?;
4547 if !matches!(self.peek().kind, TokenKind::Question) {
4549 return Some(cond);
4550 }
4551 self.advance();
4552 let then_ = self.parse_expression()?;
4553 if !matches!(self.peek().kind, TokenKind::Colon) {
4554 self.error_at_peek_with_help(
4555 "expected `:` to complete ternary expression",
4556 vec!["const x = cond ? then : else;".to_string()],
4557 );
4558 return None;
4559 }
4560 self.advance();
4561 let else_ = self.parse_expression()?;
4562 let cond_span = parse_arena_result(self.ast.try_expr(cond), &mut self.fatal)?.span;
4563 let else_span = parse_arena_result(self.ast.try_expr(else_), &mut self.fatal)?.span;
4564 parse_arena_result(
4565 self.ast.try_push_expr(Expr {
4566 kind: ExprKind::Ternary { cond, then_, else_ },
4567 span: self.span(cond_span.start, else_span.end),
4568 }),
4569 &mut self.fatal,
4570 )
4571 }
4572
4573 fn is_arrow_start(&self) -> bool {
4574 match self.peek().kind {
4575 TokenKind::Identifier => matches!(self.peek_at(1).kind, TokenKind::Arrow),
4576 TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos),
4577 _ => false,
4578 }
4579 }
4580
4581 fn reject_async_modifier(&mut self) {
4584 if !self.peek_identifier_text_is("async") {
4585 return;
4586 }
4587 let modifies = match self.peek_at(1).kind {
4588 TokenKind::Function => true,
4589 TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos + 1),
4591 ref name
4595 if is_property_name(name)
4596 && !matches!(name, TokenKind::In)
4597 && !self.peek_at_is_binary_word(1) =>
4598 {
4599 matches!(
4600 self.peek_at(2).kind,
4601 TokenKind::Arrow | TokenKind::LeftParen | TokenKind::LessThan
4602 )
4603 }
4604 _ => false,
4605 };
4606 if modifies {
4607 self.report_async_modifier();
4608 }
4609 }
4610
4611 fn reject_async_member_modifier(&mut self) {
4616 if !self.peek_identifier_text_is("async") || self.line_break_after_peek() {
4617 return;
4618 }
4619 let modifies = match self.peek_at(1).kind {
4620 TokenKind::Star
4621 | TokenKind::LeftBracket
4622 | TokenKind::StringLiteral(_)
4623 | TokenKind::NumberLiteral(_) => true,
4624 ref name if is_property_name(name) => {
4625 matches!(
4626 self.peek_at(2).kind,
4627 TokenKind::LeftParen | TokenKind::LessThan
4628 )
4629 }
4630 _ => false,
4631 };
4632 if modifies {
4633 self.report_async_modifier();
4634 }
4635 }
4636
4637 fn report_async_modifier(&mut self) {
4641 let tok = self.advance();
4642 self.error_at_with_help(
4643 tok.span,
4644 "`async` functions are not supported",
4645 vec![
4646 "calls return their value directly: remove `async`, write the return type \
4647 `T` instead of `Promise<T>`, and drop `await`"
4648 .to_string(),
4649 ],
4650 );
4651 }
4652
4653 fn reject_await_operator(&mut self) -> bool {
4659 if !self.peek_identifier_text_is("await") {
4660 return false;
4661 }
4662 let operand_starts = matches!(
4663 self.peek_at(1).kind,
4664 TokenKind::Identifier
4665 | TokenKind::This
4666 | TokenKind::Super
4667 | TokenKind::New
4668 | TokenKind::Function
4669 | TokenKind::StringLiteral(_)
4670 | TokenKind::NumberLiteral(_)
4671 | TokenKind::NullLiteral
4672 | TokenKind::BooleanLiteral(_)
4673 | TokenKind::TemplateHead(_)
4676 | TokenKind::TemplateNoSubstitution(_)
4677 ) && !self.peek_at_is_binary_word(1);
4678 if !operand_starts || self.line_break_after_peek() {
4679 return false;
4680 }
4681 let tok = self.advance();
4682 self.error_at_with_help(
4683 tok.span,
4684 "`await` is not supported",
4685 vec!["calls return their value directly: remove `await`".to_string()],
4686 );
4687 true
4688 }
4689
4690 fn index_after_matching_paren(&self, index: usize) -> Option<usize> {
4693 if !matches!(
4694 self.tokens.get(index).map(|t| &t.kind),
4695 Some(TokenKind::LeftParen)
4696 ) {
4697 return None;
4698 }
4699 self.scan_past_balanced(
4700 index,
4701 |k| matches!(k, TokenKind::LeftParen),
4702 |k| matches!(k, TokenKind::RightParen),
4703 )
4704 }
4705
4706 fn find_arrow_after_matching_paren(&self, open: usize) -> bool {
4707 let Some(i) = self.index_after_matching_paren(open) else {
4708 return false;
4709 };
4710 let after_type = if matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Colon)) {
4711 match self.scan_past_type_annotation(i + 1, TypePos::ArrowReturn) {
4712 Some(p) => p,
4713 None => return false,
4714 }
4715 } else {
4716 i
4717 };
4718 matches!(
4719 self.tokens.get(after_type).map(|t| &t.kind),
4720 Some(TokenKind::Arrow),
4721 )
4722 }
4723
4724 fn scan_past_type_annotation(&self, start: usize, type_pos: TypePos) -> Option<usize> {
4730 self.scan_past_type_annotation_at_depth(start, type_pos, 0)
4731 }
4732
4733 fn scan_past_type_annotation_at_depth(
4734 &self,
4735 start: usize,
4736 type_pos: TypePos,
4737 depth: usize,
4738 ) -> Option<usize> {
4739 if depth >= MAX_PARSE_DEPTH {
4740 return None;
4741 }
4742 if matches!(
4743 self.tokens.get(start).map(|t| &t.kind),
4744 Some(TokenKind::Identifier)
4745 ) && self
4746 .tokens
4747 .get(start + 1)
4748 .is_some_and(|t| self.token_is_word(t, "is"))
4749 {
4750 return self.scan_past_type_annotation_at_depth(start + 2, type_pos, depth + 1);
4751 }
4752 let start = if matches!(
4754 self.tokens.get(start).map(|t| &t.kind),
4755 Some(TokenKind::Pipe)
4756 ) {
4757 start + 1
4758 } else {
4759 start
4760 };
4761 let mut i = self.scan_past_single_type_member(start, type_pos, depth + 1)?;
4762 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Pipe)) {
4763 i = self.scan_past_single_type_member(i + 1, type_pos, depth + 1)?;
4764 }
4765 Some(i)
4766 }
4767
4768 fn scan_past_single_type_member(
4769 &self,
4770 start: usize,
4771 type_pos: TypePos,
4772 depth: usize,
4773 ) -> Option<usize> {
4774 if depth >= MAX_PARSE_DEPTH {
4775 return None;
4776 }
4777 if self.is_contextual_type_operator(start, "keyof")
4778 || self.is_contextual_type_operator(start, "readonly")
4779 {
4780 return self.scan_past_single_type_member(start + 1, type_pos, depth + 1);
4781 }
4782 let mut i = start;
4783 match self.tokens.get(i)?.kind {
4784 TokenKind::Identifier | TokenKind::Void => {
4785 i += 1;
4786 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Dot)) {
4787 if !matches!(
4788 self.tokens.get(i + 1).map(|t| &t.kind),
4789 Some(TokenKind::Identifier)
4790 ) {
4791 return None;
4792 }
4793 i += 2;
4794 }
4795 if matches!(
4796 self.tokens.get(i).map(|t| &t.kind),
4797 Some(TokenKind::LessThan)
4798 ) {
4799 i = self.scan_past_balanced(
4800 i,
4801 |k| matches!(k, TokenKind::LessThan),
4802 |k| matches!(k, TokenKind::GreaterThan),
4803 )?;
4804 }
4805 }
4806 TokenKind::NullLiteral
4807 | TokenKind::StringLiteral(_)
4808 | TokenKind::NumberLiteral(_)
4809 | TokenKind::BooleanLiteral(_) => {
4810 i += 1;
4811 }
4812 TokenKind::LeftBrace => {
4813 i = self.scan_past_balanced(
4814 i,
4815 |k| matches!(k, TokenKind::LeftBrace),
4816 |k| matches!(k, TokenKind::RightBrace),
4817 )?;
4818 }
4819 TokenKind::LeftBracket => {
4820 i = self.scan_past_balanced(
4821 i,
4822 |k| matches!(k, TokenKind::LeftBracket),
4823 |k| matches!(k, TokenKind::RightBracket),
4824 )?;
4825 }
4826 TokenKind::LeftParen => {
4831 let is_function_type = self.paren_at_opens_function_type(i, type_pos);
4832 i = self.scan_past_balanced(
4833 i,
4834 |k| matches!(k, TokenKind::LeftParen),
4835 |k| matches!(k, TokenKind::RightParen),
4836 )?;
4837 if is_function_type {
4838 if !matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)) {
4839 return None;
4840 }
4841 return self.scan_past_type_annotation_at_depth(
4842 i + 1,
4843 TypePos::Anywhere,
4844 depth + 1,
4845 );
4846 }
4847 }
4848 _ => return None,
4849 }
4850 while matches!(
4851 self.tokens.get(i).map(|t| &t.kind),
4852 Some(TokenKind::LeftBracket)
4853 ) {
4854 i += 1;
4855 if !matches!(
4856 self.tokens.get(i).map(|t| &t.kind),
4857 Some(TokenKind::RightBracket)
4858 ) {
4859 return None;
4860 }
4861 i += 1;
4862 }
4863 Some(i)
4864 }
4865
4866 fn scan_past_balanced(
4869 &self,
4870 start: usize,
4871 is_open: fn(&TokenKind) -> bool,
4872 is_close: fn(&TokenKind) -> bool,
4873 ) -> Option<usize> {
4874 let mut depth: i32 = 1;
4875 let mut i = start + 1;
4876 while i < self.tokens.len() && depth > 0 {
4877 let token = self.tokens.get(i)?;
4878 let kind = &token.kind;
4879 if is_open(kind) {
4880 depth += 1;
4881 } else if is_close(kind) {
4882 depth -= 1;
4883 } else if matches!(kind, TokenKind::Eof) {
4884 return None;
4885 }
4886 i += 1;
4887 }
4888 if depth == 0 { Some(i) } else { None }
4889 }
4890
4891 fn parse_arrow(&mut self) -> Option<ExprId> {
4892 let start_span = self.peek().span;
4893
4894 let params = match self.peek().kind {
4895 TokenKind::Identifier => {
4896 let name_tok = self.advance();
4897 self.reject_eval_or_arguments(name_tok.span);
4898 let name = self.ident_from_token(&name_tok);
4899 vec![ParamDecl {
4900 name,
4901 ty: None,
4902 default: None,
4903 optional: false,
4904 pattern: None,
4905 rest: false,
4906 modifiers: None,
4907 }]
4908 }
4909 TokenKind::LeftParen => {
4910 self.advance();
4911 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4912 Vec::new()
4913 } else {
4914 self.parse_arrow_param_list()?
4915 };
4916 if !matches!(self.peek().kind, TokenKind::RightParen) {
4917 self.error_at_peek("expected `)`");
4918 return None;
4919 }
4920 self.advance();
4921 params
4922 }
4923 _ => return self.invariant_failure("is_arrow_start guarded the entry"),
4924 };
4925
4926 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4927 self.advance();
4928 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4929 } else {
4930 (None, None)
4931 };
4932
4933 if !matches!(self.peek().kind, TokenKind::Arrow) {
4934 self.error_at_peek("expected `=>` in arrow function");
4935 return None;
4936 }
4937 if self.line_break_before_peek() {
4939 let arrow_span = self.peek().span;
4940 self.error_at(arrow_span, "a line break is not allowed before `=>`");
4941 }
4942 self.advance();
4943
4944 let (body, end_pos) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
4945 let block = self.parse_block()?;
4946 let span = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?.span;
4947 (ArrowBody::Block(block), span.end)
4948 } else {
4949 let expr = self.parse_expression()?;
4950 let span = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?.span;
4951 (ArrowBody::Expr(expr), span.end)
4952 };
4953
4954 parse_arena_result(
4955 self.ast.try_push_expr(Expr {
4956 kind: ExprKind::Arrow {
4957 params,
4958 return_type,
4959 type_predicate,
4960 body,
4961 },
4962 span: self.span(start_span.start, end_pos),
4963 }),
4964 &mut self.fatal,
4965 )
4966 }
4967
4968 fn parse_function_expression(&mut self) -> Option<ExprId> {
4971 let kw = self.advance();
4972
4973 if matches!(self.peek().kind, TokenKind::LessThan) {
4974 self.error_at_peek("generic function expressions are not supported");
4975 return None;
4976 }
4977
4978 let self_name = if matches!(self.peek().kind, TokenKind::Identifier) {
4979 let name_tok = self.advance();
4980 self.reject_eval_or_arguments(name_tok.span);
4981 Some(self.ident_from_token(&name_tok))
4982 } else {
4983 None
4984 };
4985
4986 if !matches!(self.peek().kind, TokenKind::LeftParen) {
4987 self.error_at_peek("expected `(` after `function`");
4988 return None;
4989 }
4990 self.advance();
4991
4992 let this_type = if matches!(self.peek().kind, TokenKind::This) {
4993 self.advance();
4994 if !matches!(self.peek().kind, TokenKind::Colon) {
4995 self.error_at_peek("expected a type annotation after `this`");
4996 return None;
4997 }
4998 self.advance();
4999 let ty = self.parse_type_annotation()?;
5000 if matches!(self.peek().kind, TokenKind::Comma) {
5001 self.advance();
5002 } else if !matches!(self.peek().kind, TokenKind::RightParen) {
5003 self.error_at_peek("expected `,` or `)` after the `this` parameter");
5004 return None;
5005 }
5006 Some(ty)
5007 } else {
5008 None
5009 };
5010 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
5011 Vec::new()
5012 } else {
5013 let saved_class = self.class_member_body_depth;
5014 let saved_function = self.function_expression_body_depth;
5015 self.class_member_body_depth = 0;
5016 self.function_expression_body_depth = 1;
5017 let params = self.parse_arrow_param_list();
5018 self.class_member_body_depth = saved_class;
5019 self.function_expression_body_depth = saved_function;
5020 params?
5021 };
5022 if !matches!(self.peek().kind, TokenKind::RightParen) {
5023 self.error_at_peek("expected `)`");
5024 return None;
5025 }
5026 self.advance();
5027
5028 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
5029 self.advance();
5030 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
5031 } else {
5032 (None, None)
5033 };
5034
5035 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
5036 self.error_at_peek("expected `{` to open the function body");
5037 return None;
5038 }
5039 let saved = self.class_member_body_depth;
5040 self.class_member_body_depth = 0;
5041 self.function_expression_body_depth += 1;
5042 let block = self.parse_block();
5043 self.function_expression_body_depth -= 1;
5044 self.class_member_body_depth = saved;
5045 let block = block?;
5046 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
5047 .span
5048 .end;
5049
5050 let function = parse_arena_result(
5051 self.ast.try_push_expr(Expr {
5052 kind: ExprKind::Arrow {
5053 params,
5054 return_type,
5055 type_predicate,
5056 body: ArrowBody::Block(block),
5057 },
5058 span: self.span(kw.span.start, end),
5059 }),
5060 &mut self.fatal,
5061 )?;
5062 parse_arena_result(
5063 self.ast.try_push_expr(Expr {
5064 kind: ExprKind::FunctionExpression {
5065 name: self_name,
5066 this_type,
5067 function,
5068 },
5069 span: self.span(kw.span.start, end),
5070 }),
5071 &mut self.fatal,
5072 )
5073 }
5074
5075 fn parse_outer_this_boundary<T>(
5078 &mut self,
5079 parse: impl FnOnce(&mut Self) -> Option<T>,
5080 ) -> Option<T> {
5081 let saved = self.class_member_body_depth;
5082 self.class_member_body_depth = 0;
5083 let saved_function = self.function_expression_body_depth;
5084 self.function_expression_body_depth = 0;
5085 let parsed = parse(self);
5086 self.class_member_body_depth = saved;
5087 self.function_expression_body_depth = saved_function;
5088 parsed
5089 }
5090
5091 fn parse_arrow_param_list(&mut self) -> Option<Vec<ParamDecl>> {
5092 let mut params = Vec::new();
5093 loop {
5094 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
5095 self.advance();
5096 true
5097 } else {
5098 false
5099 };
5100 let (name, pattern) = match self.peek().kind {
5103 TokenKind::LeftBrace | TokenKind::LeftBracket => {
5104 if rest {
5105 self.error_at_peek("rest parameter cannot be destructured");
5106 return None;
5107 }
5108 let binding = self.parse_binding()?;
5109 let placeholder = Ident {
5110 name: String::new(),
5111 span: binding.span(),
5112 };
5113 (placeholder, Some(binding))
5114 }
5115 _ => {
5116 let name_tok = self.expect_binding_identifier(if rest {
5117 "expected identifier after `...`"
5118 } else {
5119 "expected parameter name"
5120 })?;
5121 let name = self.ident_from_token(&name_tok);
5122 (name, None)
5123 }
5124 };
5125 let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
5126 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
5127 self.advance();
5128 Some(self.parse_type_annotation()?)
5129 } else {
5130 None
5133 };
5134 let default = self.parse_parameter_default(rest, optional)?;
5135 params.push(ParamDecl {
5136 name,
5137 ty,
5138 default,
5139 optional,
5140 pattern,
5141 rest,
5142 modifiers: None,
5143 });
5144 match self.peek().kind {
5145 TokenKind::Comma => {
5146 if rest {
5147 let comma = self.advance();
5148 self.error_at(comma.span, "rest parameter must be the last parameter");
5149 return None;
5150 }
5151 self.advance();
5152 if matches!(self.peek().kind, TokenKind::RightParen) {
5153 break;
5154 }
5155 }
5156 TokenKind::RightParen => break,
5157 _ => {
5158 self.error_at_peek("expected `,` or `)`");
5159 return None;
5160 }
5161 }
5162 }
5163 self.check_parameter_order(params.iter().map(ParamOrder::of_param));
5164 Some(params)
5165 }
5166
5167 fn parse_binary(&mut self, min_prec: u8) -> Option<ExprId> {
5168 self.with_recursion_limit(|parser| parser.parse_binary_inner(min_prec))
5169 }
5170
5171 fn peek_shift(&self) -> Option<ShiftToken> {
5174 let first = self.peek();
5175 let second = self.peek_at(1);
5176 if first.span.end != second.span.start {
5177 return None;
5178 }
5179 match (&first.kind, &second.kind) {
5180 (TokenKind::LessThan, TokenKind::LessThan) => {
5181 Some(ShiftToken::new(BinOp::Shl, 2, false))
5182 }
5183 (TokenKind::LessThan, TokenKind::LessEquals) => {
5184 Some(ShiftToken::new(BinOp::Shl, 2, true))
5185 }
5186 (TokenKind::GreaterThan, TokenKind::GreaterEquals) => {
5187 Some(ShiftToken::new(BinOp::Shr, 2, true))
5188 }
5189 (TokenKind::GreaterThan, TokenKind::GreaterThan) => {
5190 let third = self.peek_at(2);
5191 if second.span.end == third.span.start {
5192 match third.kind {
5193 TokenKind::GreaterThan => {
5194 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, false));
5195 }
5196 TokenKind::GreaterEquals => {
5197 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, true));
5198 }
5199 _ => {}
5200 }
5201 }
5202 Some(ShiftToken::new(BinOp::Shr, 2, false))
5203 }
5204 _ => None,
5205 }
5206 }
5207
5208 fn peek_binary(&self) -> Option<BinaryToken> {
5209 if let Some(shift) = self.peek_shift() {
5210 return (!shift.assignment).then_some(BinaryToken {
5211 op: shift.op,
5212 precedence: SHIFT_PRECEDENCE,
5213 tokens: shift.tokens,
5214 });
5215 }
5216 peek_binop(&self.peek().kind).map(|(op, precedence)| BinaryToken {
5217 op,
5218 precedence,
5219 tokens: 1,
5220 })
5221 }
5222
5223 fn parse_binary_inner(&mut self, min_prec: u8) -> Option<ExprId> {
5224 let mut lhs = self.parse_cast()?;
5225 let mut last_op: Option<BinOp> = None;
5229 let mut reported_mixing = false;
5230 while let Some(BinaryToken {
5231 op,
5232 precedence: prec,
5233 tokens: count,
5234 }) = self.peek_binary()
5235 {
5236 if prec < min_prec {
5237 break;
5238 }
5239 if !reported_mixing
5240 && let Some(last) = last_op
5241 && mixed_logical(last, op)
5242 {
5243 reported_mixing = true;
5244 let op_span = self.peek().span;
5245 self.error_at_with_help(
5249 op_span,
5250 "mixing `??` with `||` / `&&` requires parentheses",
5251 vec![mixed_logical_help(last, op)],
5252 );
5253 }
5254 if op == BinOp::Pow
5255 && matches!(
5256 parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.kind,
5257 ExprKind::Unary { .. }
5258 | ExprKind::Typeof { .. }
5259 | ExprKind::Delete { .. }
5260 | ExprKind::Void { .. }
5261 )
5262 {
5263 let error_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5264 self.error_at_with_help(
5265 error_span,
5266 "an unparenthesized unary expression cannot be the left operand of `**`",
5267 vec!["choose the grouping explicitly: `(-x) ** 2` or `-(x ** 2)`".into()],
5268 );
5269 return None;
5270 }
5271 let loose = matches!(self.peek().kind, TokenKind::EqEq | TokenKind::BangEq);
5272 for _ in 0..count {
5273 self.advance();
5274 }
5275 let rhs = self.parse_binary(right_operand_min_prec(op, prec))?;
5276 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5277 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
5278 if loose {
5279 self.reject_loose_nullish_comparison(op, lhs, rhs)?;
5280 }
5281 lhs = parse_arena_result(
5282 self.ast.try_push_expr(Expr {
5283 kind: ExprKind::Binary { op, lhs, rhs },
5284 span: self.span(lhs_span.start, rhs_span.end),
5285 }),
5286 &mut self.fatal,
5287 )?;
5288 last_op = Some(op);
5289 }
5290 Some(lhs)
5291 }
5292
5293 fn parse_cast(&mut self) -> Option<ExprId> {
5295 let mut expr = self.parse_unary()?;
5296 while matches!(self.peek().kind, TokenKind::Instanceof)
5297 || self.peek_identifier_text_is("as")
5298 {
5299 let is_instanceof = matches!(self.peek().kind, TokenKind::Instanceof);
5300 self.advance();
5301 let ty = self.parse_type_annotation()?;
5302 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5303 .span
5304 .start;
5305 let end = ty.span.end;
5306 let kind = if is_instanceof {
5307 ExprKind::InstanceOf { value: expr, ty }
5308 } else {
5309 self.last_as_type_end = Some(end);
5310 ExprKind::As { expr, ty }
5311 };
5312 expr = parse_arena_result(
5313 self.ast.try_push_expr(Expr {
5314 kind,
5315 span: self.span(start, end),
5316 }),
5317 &mut self.fatal,
5318 )?;
5319 }
5320 Some(expr)
5321 }
5322
5323 fn parse_unary(&mut self) -> Option<ExprId> {
5324 self.with_recursion_limit(Self::parse_unary_inner)
5325 }
5326
5327 fn parse_unary_inner(&mut self) -> Option<ExprId> {
5328 if self.reject_await_operator() {
5329 return self.parse_unary();
5330 }
5331 if matches!(self.peek().kind, TokenKind::Typeof) {
5334 return self.parse_prefix_expr(|operand| ExprKind::Typeof { operand });
5335 }
5336 if matches!(self.peek().kind, TokenKind::Void) {
5337 return self.parse_prefix_expr(|operand| ExprKind::Void { operand });
5338 }
5339 if self.at_delete_operator() {
5340 return self.parse_prefix_expr(|operand| ExprKind::Delete { operand });
5341 }
5342 if matches!(self.peek().kind, TokenKind::New) {
5343 let op_tok = self.advance();
5344 let mut callee = self.parse_atom()?;
5345 while matches!(self.peek().kind, TokenKind::Dot) {
5346 self.advance();
5347 if !matches!(self.peek().kind, TokenKind::Identifier) {
5348 self.error_at_peek("expected identifier after `.` in constructor name");
5349 return None;
5350 }
5351 let ident_tok = self.advance();
5352 let name = self.ident_from_token(&ident_tok);
5353 let receiver_span =
5354 parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5355 let combined_span = self.span(receiver_span.start, ident_tok.span.end);
5356 callee = parse_arena_result(
5357 self.ast.try_push_expr(Expr {
5358 kind: ExprKind::FieldAccess {
5359 receiver: callee,
5360 name,
5361 },
5362 span: combined_span,
5363 }),
5364 &mut self.fatal,
5365 )?;
5366 }
5367 let type_args = if matches!(self.peek().kind, TokenKind::LessThan) {
5368 let saved_pos = self.pos;
5369 let saved_splits = self.split_type_tokens.len();
5370 let saved_diag_len = self.diagnostics.len();
5371 if let Some(args) = self.try_parse_type_args_only() {
5372 Some(args)
5373 } else {
5374 self.restore_type_tokens(saved_splits);
5375 self.pos = saved_pos;
5376 self.diagnostics.truncate(saved_diag_len);
5377 None
5378 }
5379 } else {
5380 None
5381 };
5382 if !matches!(self.peek().kind, TokenKind::LeftParen) {
5383 self.error_at_peek("expected `(` after constructor name in `new` expression");
5384 return None;
5385 }
5386 self.advance();
5387 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5388 Vec::new()
5389 } else {
5390 self.parse_call_args()?
5391 };
5392 if !matches!(self.peek().kind, TokenKind::RightParen) {
5393 self.error_at_peek("expected `)`");
5394 return None;
5395 }
5396 let close = self.advance();
5397 let new_expr = parse_arena_result(
5398 self.ast.try_push_expr(Expr {
5399 kind: ExprKind::New {
5400 callee,
5401 type_args,
5402 args,
5403 },
5404 span: self.span(op_tok.span.start, close.span.end),
5405 }),
5406 &mut self.fatal,
5407 )?;
5408 return self.parse_postfix_from(new_expr);
5411 }
5412 let op = match self.peek().kind {
5413 TokenKind::Bang => UnOp::Not,
5414 TokenKind::Tilde => UnOp::BitNot,
5415 TokenKind::Minus => UnOp::Neg,
5416 TokenKind::Plus => UnOp::Pos,
5417 _ => return self.parse_postfix(),
5418 };
5419 self.parse_prefix_expr(|operand| ExprKind::Unary { op, operand })
5420 }
5421
5422 fn parse_prefix_expr(&mut self, kind: impl FnOnce(ExprId) -> ExprKind) -> Option<ExprId> {
5424 let op_tok = self.advance();
5425 if matches!(op_tok.kind, TokenKind::Void) && self.at_inserted_semicolon() {
5428 self.advance();
5429 }
5430 let operand = self.parse_unary()?;
5431 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5432 parse_arena_result(
5433 self.ast.try_push_expr(Expr {
5434 kind: kind(operand),
5435 span: self.span(op_tok.span.start, operand_span.end),
5436 }),
5437 &mut self.fatal,
5438 )
5439 }
5440
5441 fn parse_postfix(&mut self) -> Option<ExprId> {
5442 let expr = self.parse_atom()?;
5443 self.parse_postfix_from(expr)
5444 }
5445
5446 fn parse_postfix_from(&mut self, mut expr: ExprId) -> Option<ExprId> {
5447 let mut chain_parts: Option<(ExprId, Vec<crate::ChainPart>)> = None;
5451 loop {
5452 match self.peek().kind {
5453 TokenKind::LeftParen => {
5454 if let Some((_, parts)) = &mut chain_parts {
5455 let part = self.parse_chain_call_tail(false)?;
5456 parts.push(part);
5457 } else {
5458 expr = self.parse_call_tail(expr, None)?;
5459 }
5460 }
5461 TokenKind::Dot => {
5462 if let Some((_, parts)) = &mut chain_parts {
5463 let part = self.parse_chain_field_tail(false)?;
5464 parts.push(part);
5465 } else {
5466 expr = self.parse_field_access_tail(expr)?;
5467 }
5468 }
5469 TokenKind::LeftBracket => {
5470 if let Some((_, parts)) = &mut chain_parts {
5471 let part = self.parse_chain_index_tail(false)?;
5472 parts.push(part);
5473 } else {
5474 expr = self.parse_index_access_tail(expr)?;
5475 }
5476 }
5477 TokenKind::QuestionDot => {
5478 if chain_parts.is_none() {
5479 chain_parts = Some((expr, Vec::new()));
5480 }
5481 self.advance();
5482 let part = match self.peek().kind {
5483 TokenKind::LeftParen => self.parse_chain_call_tail(true)?,
5484 TokenKind::LeftBracket => self.parse_chain_index_tail(true)?,
5485 _ if is_property_name(&self.peek().kind) => {
5486 self.parse_chain_field_tail(true)?
5487 }
5488 _ => {
5489 self.error_at_peek(
5490 "expected field name, `(` for call, or `[` for index after `?.`",
5491 );
5492 return None;
5493 }
5494 };
5495 if let Some((_, parts)) = &mut chain_parts {
5496 parts.push(part);
5497 }
5498 }
5499 TokenKind::LessThan => {
5503 if chain_parts.is_some() {
5504 break;
5506 }
5507 let saved_pos = self.pos;
5508 let saved_splits = self.split_type_tokens.len();
5509 let saved_diag_len = self.diagnostics.len();
5510 if let Some(call_id) = self.try_parse_generic_call_tail(expr) {
5511 expr = call_id;
5512 } else {
5513 self.restore_type_tokens(saved_splits);
5514 self.pos = saved_pos;
5515 self.diagnostics.truncate(saved_diag_len);
5516 break;
5517 }
5518 }
5519 TokenKind::Bang => {
5520 if let Some((_, parts)) = &mut chain_parts {
5521 let tok = self.advance();
5522 parts.push(crate::ChainPart::NonNull { span: tok.span });
5523 continue;
5524 }
5525 let tok = self.advance();
5526 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5527 .span
5528 .start;
5529 expr = parse_arena_result(
5530 self.ast.try_push_expr(Expr {
5531 kind: ExprKind::PostfixUnary {
5532 op: PostfixOp::NonNullAssert,
5533 operand: expr,
5534 },
5535 span: self.span(start, tok.span.end),
5536 }),
5537 &mut self.fatal,
5538 )?;
5539 }
5540 TokenKind::PlusPlus | TokenKind::MinusMinus => {
5542 if chain_parts.is_some() {
5543 break;
5544 }
5545 let tok = self.advance();
5546 let op = if matches!(tok.kind, TokenKind::PlusPlus) {
5547 PostfixOp::Inc
5548 } else {
5549 PostfixOp::Dec
5550 };
5551 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5552 .span
5553 .start;
5554 expr = parse_arena_result(
5555 self.ast.try_push_expr(Expr {
5556 kind: ExprKind::PostfixUnary { op, operand: expr },
5557 span: self.span(start, tok.span.end),
5558 }),
5559 &mut self.fatal,
5560 )?;
5561 break;
5562 }
5563 _ => break,
5564 }
5565 }
5566 if let Some((base, parts)) = chain_parts {
5567 let base_span = parse_arena_result(self.ast.try_expr(base), &mut self.fatal)?.span;
5568 let end = parts.last().map_or(base_span.end, |p| p.span().end);
5569 Some(parse_arena_result(
5570 self.ast.try_push_expr(Expr {
5571 kind: ExprKind::OptionalChain { base, parts },
5572 span: self.span(base_span.start, end),
5573 }),
5574 &mut self.fatal,
5575 )?)
5576 } else {
5577 Some(expr)
5578 }
5579 }
5580
5581 fn parse_chain_field_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5583 let start_pos = self.peek().span.start;
5584 if !optional {
5585 self.advance();
5586 }
5587 let name_tok = if is_property_name(&self.peek().kind) {
5588 self.advance()
5589 } else {
5590 let what = if optional { "?." } else { "." };
5591 self.error_at_peek(format!("expected field name after `{what}`"));
5592 return None;
5593 };
5594 let name = self.ident_from_token(&name_tok);
5595 let end = name.span.end;
5596 Some(crate::ChainPart::Field {
5597 name,
5598 optional,
5599 span: self.span(start_pos, end),
5600 })
5601 }
5602
5603 fn parse_chain_index_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5604 let open = self.advance();
5605 let idx = self.parse_expression()?;
5606 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5607 self.error_at_peek("expected `]`");
5608 return None;
5609 }
5610 let close = self.advance();
5611 Some(crate::ChainPart::Index {
5612 idx,
5613 optional,
5614 span: self.span(open.span.start, close.span.end),
5615 })
5616 }
5617
5618 fn parse_chain_call_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5619 let open = self.advance();
5620 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5621 Vec::new()
5622 } else {
5623 self.parse_call_args()?
5624 };
5625 if !matches!(self.peek().kind, TokenKind::RightParen) {
5626 self.error_at_peek("expected `)`");
5627 return None;
5628 }
5629 let close = self.advance();
5630 Some(crate::ChainPart::Call {
5631 args,
5632 type_args: None,
5633 optional,
5634 span: self.span(open.span.start, close.span.end),
5635 })
5636 }
5637
5638 fn parse_field_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5639 self.advance();
5640 let name_tok = if is_property_name(&self.peek().kind) {
5642 self.advance()
5643 } else {
5644 self.error_at_peek("expected field name after `.`");
5645 return None;
5646 };
5647 let name = self.ident_from_token(&name_tok);
5648 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5649 parse_arena_result(
5650 self.ast.try_push_expr(Expr {
5651 kind: ExprKind::FieldAccess {
5652 receiver,
5653 name: name.clone(),
5654 },
5655 span: self.span(receiver_span.start, name.span.end),
5656 }),
5657 &mut self.fatal,
5658 )
5659 }
5660
5661 fn parse_index_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5662 self.advance();
5663 let index = self.parse_expression()?;
5664 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5665 self.error_at_peek("expected `]`");
5666 return None;
5667 }
5668 let close = self.advance();
5669 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5670 parse_arena_result(
5671 self.ast.try_push_expr(Expr {
5672 kind: ExprKind::IndexAccess { receiver, index },
5673 span: self.span(receiver_span.start, close.span.end),
5674 }),
5675 &mut self.fatal,
5676 )
5677 }
5678
5679 fn parse_call_tail(
5680 &mut self,
5681 callee: ExprId,
5682 type_args: Option<Vec<TypeAnnotation>>,
5683 ) -> Option<ExprId> {
5684 self.advance();
5685 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5686 Vec::new()
5687 } else {
5688 self.parse_call_args()?
5689 };
5690 if !matches!(self.peek().kind, TokenKind::RightParen) {
5691 self.error_at_peek("expected `)`");
5692 return None;
5693 }
5694 let close = self.advance();
5695
5696 let callee_span = parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5697 parse_arena_result(
5698 self.ast.try_push_expr(Expr {
5699 kind: ExprKind::Call {
5700 callee,
5701 type_args,
5702 args,
5703 },
5704 span: self.span(callee_span.start, close.span.end),
5705 }),
5706 &mut self.fatal,
5707 )
5708 }
5709
5710 fn try_parse_generic_call_tail(&mut self, callee: ExprId) -> Option<ExprId> {
5712 let type_args = self.try_parse_type_args_only()?;
5713 if !matches!(self.peek().kind, TokenKind::LeftParen) {
5714 return None;
5715 }
5716 self.parse_call_tail(callee, Some(type_args))
5717 }
5718
5719 fn try_parse_type_args_only(&mut self) -> Option<Vec<TypeAnnotation>> {
5720 if !matches!(self.peek().kind, TokenKind::LessThan) {
5721 return self.invariant_failure("type argument parser expected `<`");
5722 }
5723 self.advance();
5724
5725 if matches!(
5726 self.peek().kind,
5727 TokenKind::GreaterThan | TokenKind::GreaterEquals
5728 ) {
5729 return None;
5730 }
5731
5732 let mut type_args = Vec::new();
5733 loop {
5734 let ta = self.parse_type_annotation()?;
5735 type_args.push(ta);
5736 match self.peek().kind {
5737 TokenKind::Comma => {
5738 self.advance();
5739 if matches!(
5740 self.peek().kind,
5741 TokenKind::GreaterThan | TokenKind::GreaterEquals
5742 ) {
5743 self.consume_type_close()?;
5744 break;
5745 }
5746 }
5747 TokenKind::GreaterThan | TokenKind::GreaterEquals => {
5748 self.consume_type_close()?;
5749 break;
5750 }
5751 _ => return None,
5752 }
5753 }
5754 Some(type_args)
5755 }
5756
5757 fn parse_call_args(&mut self) -> Option<Vec<ExprId>> {
5758 let mut args = Vec::new();
5759 loop {
5760 let arg = self.parse_expression()?;
5761 args.push(arg);
5762 match self.peek().kind {
5763 TokenKind::Comma => {
5764 self.advance();
5765 if matches!(self.peek().kind, TokenKind::RightParen) {
5766 return Some(args);
5767 }
5768 }
5769 TokenKind::RightParen => return Some(args),
5770 _ => {
5771 self.error_at_peek("expected `,` or `)`");
5772 return None;
5773 }
5774 }
5775 }
5776 }
5777
5778 fn parse_angle_cast(&mut self) -> Option<ExprId> {
5785 let open = self.advance();
5786 let ty = self.parse_type_annotation()?;
5787 if !matches!(self.peek().kind, TokenKind::GreaterThan) {
5788 self.error_at_peek("expected `>` to close a type assertion");
5789 return None;
5790 }
5791 self.advance();
5792 let expr = self.parse_unary()?;
5793 let end = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5794 .span
5795 .end;
5796 parse_arena_result(
5797 self.ast.try_push_expr(Expr {
5798 kind: ExprKind::As { expr, ty },
5799 span: self.span(open.span.start, end),
5800 }),
5801 &mut self.fatal,
5802 )
5803 }
5804
5805 fn parse_atom(&mut self) -> Option<ExprId> {
5806 match self.peek().kind {
5807 TokenKind::LessThan => return self.parse_angle_cast(),
5812 TokenKind::LeftParen => return self.parse_paren(),
5813 TokenKind::LeftBrace => return self.parse_object_literal(),
5816 TokenKind::LeftBracket => return self.parse_array_literal(),
5817 TokenKind::TemplateHead(_) => return self.parse_template_literal(),
5818 TokenKind::This => return self.parse_this_or_super(true),
5819 TokenKind::Super => return self.parse_this_or_super(false),
5820 TokenKind::Function => return self.parse_function_expression(),
5821 _ => {}
5822 }
5823 if !matches!(
5824 self.peek().kind,
5825 TokenKind::NumberLiteral(_)
5826 | TokenKind::BigIntLiteral(_)
5827 | TokenKind::StringLiteral(_)
5828 | TokenKind::TemplateNoSubstitution(_)
5829 | TokenKind::BooleanLiteral(_)
5830 | TokenKind::NullLiteral
5831 | TokenKind::Identifier
5832 | TokenKind::RegexLiteral { .. }
5833 ) {
5834 self.error_at_peek("expected expression");
5835 return None;
5836 }
5837 let tok = self.advance();
5838 let span = tok.span;
5839 let kind = match tok.kind {
5840 TokenKind::NumberLiteral(v) => ExprKind::Number(v),
5841 TokenKind::BigIntLiteral(s) => ExprKind::BigInt(s),
5842 TokenKind::StringLiteral(s) => ExprKind::String(s),
5843 TokenKind::TemplateNoSubstitution(s) => ExprKind::String(s),
5844 TokenKind::BooleanLiteral(b) => ExprKind::Boolean(b),
5845 TokenKind::NullLiteral => ExprKind::Null,
5846 TokenKind::Identifier => ExprKind::Identifier(Ident {
5847 name: self.source[span.start as usize..span.end as usize].to_string(),
5848 span,
5849 }),
5850 TokenKind::RegexLiteral { source, flags } => ExprKind::Regex { source, flags },
5851 _ => unreachable!("advance preserves the accepted expression token"),
5853 };
5854 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5855 }
5856
5857 fn parse_this_or_super(&mut self, is_this: bool) -> Option<ExprId> {
5860 let tok = self.advance();
5861 let span = tok.span;
5862 if !is_this && self.class_member_body_depth == 0 {
5863 self.error_at_with_help(
5864 span,
5865 "`super` is only valid inside a class method or constructor body",
5866 vec![
5867 "call `super(...)` in a subclass constructor or `super.method(...)` \
5868 in a subclass method"
5869 .to_string(),
5870 ],
5871 );
5872 }
5873 let kind = if is_this
5874 && self.class_member_body_depth == 0
5875 && self.function_expression_body_depth == 0
5876 {
5877 ExprKind::ThisOutsideReceiver
5878 } else if is_this {
5879 ExprKind::This
5880 } else {
5881 ExprKind::Super
5882 };
5883 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5884 }
5885
5886 fn parse_template_literal(&mut self) -> Option<ExprId> {
5889 let head_tok = self.advance();
5890 let TokenKind::TemplateHead(head) = head_tok.kind else {
5891 unreachable!("template dispatch requires TemplateHead");
5892 };
5893 let start = head_tok.span.start;
5894 let mut parts: Vec<String> = vec![head];
5895 let mut exprs: Vec<ExprId> = Vec::new();
5896 let mut substitution_spans: Vec<Span> = Vec::new();
5897 let mut substitution_start = head_tok.span.end.saturating_sub(2);
5900 loop {
5901 let expr = self.parse_expression()?;
5902 exprs.push(expr);
5903 let closing = self.peek().span.start;
5904 let substitution_span = self.span(substitution_start, closing.saturating_add(1));
5905 match self.peek().kind.clone() {
5906 TokenKind::TemplateMiddle(s) => {
5907 let tok = self.advance();
5908 substitution_spans.push(substitution_span);
5909 substitution_start = tok.span.end.saturating_sub(2);
5910 parts.push(s);
5911 }
5912 TokenKind::TemplateTail(s) => {
5913 let tok = self.advance();
5914 substitution_spans.push(substitution_span);
5915 parts.push(s);
5916 return parse_arena_result(
5917 self.ast.try_push_expr(Expr {
5918 kind: ExprKind::TemplateLiteral {
5919 parts,
5920 exprs,
5921 substitution_spans,
5922 },
5923 span: self.span(start, tok.span.end),
5924 }),
5925 &mut self.fatal,
5926 );
5927 }
5928 _ => {
5929 self.error_at_peek("expected `}` to close template interpolation");
5930 return None;
5931 }
5932 }
5933 }
5934 }
5935
5936 fn parse_object_literal(&mut self) -> Option<ExprId> {
5937 let open = self.advance();
5938
5939 let mut members: Vec<ObjectLiteralMember> = Vec::new();
5940 if !matches!(self.peek().kind, TokenKind::RightBrace) {
5941 loop {
5942 if matches!(self.peek().kind, TokenKind::DotDotDot) {
5943 let dots = self.advance();
5944 let value = self.parse_expression()?;
5945 let value_span =
5946 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5947 members.push(ObjectLiteralMember::Spread {
5948 value,
5949 span: self.span(dots.span.start, value_span.end),
5950 });
5951 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
5952 self.advance();
5953 let key = self.parse_expression()?;
5954 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5955 self.error_at_peek("expected `]` after computed property key");
5956 return None;
5957 }
5958 self.advance();
5959 if !matches!(self.peek().kind, TokenKind::Colon) {
5960 self.error_at_peek("expected `:` after computed property key; computed methods are not supported");
5961 return None;
5962 }
5963 self.advance();
5964 let value = self.parse_expression()?;
5965 members.push(ObjectLiteralMember::Computed { key, value });
5966 } else {
5967 let field = self.parse_object_literal_field()?;
5968 if let Some(existing) = members.iter().find_map(|m| match m {
5971 ObjectLiteralMember::Field(f) if f.name.name == field.name.name => Some(f),
5972 _ => None,
5973 }) {
5974 self.diagnostics.push(Diagnostic {
5975 severity: Severity::Error,
5976 span: field.name.span,
5977 message: format!(
5978 "duplicate field `{}` in object literal",
5979 field.name.name
5980 ),
5981 help: vec![],
5982 notes: vec![(existing.name.span, "first defined here".into())],
5983 });
5984 }
5986 members.push(ObjectLiteralMember::Field(field));
5987 }
5988 if self.at_inserted_semicolon()
5989 && matches!(self.peek_at(1).kind, TokenKind::RightBrace)
5990 {
5991 self.advance();
5992 }
5993 match self.peek().kind {
5994 TokenKind::Comma => {
5995 self.advance();
5996 if matches!(self.peek().kind, TokenKind::RightBrace) {
5997 break;
5998 }
5999 }
6000 TokenKind::RightBrace => break,
6001 _ => {
6002 self.error_at_peek("expected `,` or `}`");
6003 return None;
6004 }
6005 }
6006 }
6007 }
6008
6009 if !matches!(self.peek().kind, TokenKind::RightBrace) {
6010 self.error_at_peek("expected `}`");
6011 return None;
6012 }
6013 let close = self.advance();
6014
6015 parse_arena_result(
6016 self.ast.try_push_expr(Expr {
6017 kind: ExprKind::ObjectLiteral { members },
6018 span: self.span(open.span.start, close.span.end),
6019 }),
6020 &mut self.fatal,
6021 )
6022 }
6023
6024 fn parse_object_literal_field(&mut self) -> Option<ObjectLiteralField> {
6025 self.reject_async_member_modifier();
6026 let (name, shorthandable) = match self.peek().kind.clone() {
6027 TokenKind::Identifier => {
6028 let tok = self.advance();
6029 (self.ident_from_token(&tok), true)
6030 }
6031 kind if is_property_name(&kind) => {
6032 let tok = self.advance();
6033 (self.ident_from_token(&tok), false)
6034 }
6035 TokenKind::StringLiteral(_) => {
6036 let tok = self.advance();
6037 (self.property_ident_from_token(&tok), false)
6038 }
6039 _ => {
6040 self.error_at_peek("expected field name");
6041 return None;
6042 }
6043 };
6044
6045 if matches!(self.peek().kind, TokenKind::LeftParen) {
6051 let value = self.parse_method_shorthand_body(name.span)?;
6052 return Some(ObjectLiteralField { name, value });
6053 }
6054
6055 if !matches!(self.peek().kind, TokenKind::Colon) {
6056 if shorthandable {
6059 let value = parse_arena_result(
6060 self.ast.try_push_expr(Expr {
6061 kind: ExprKind::Identifier(name.clone()),
6062 span: name.span,
6063 }),
6064 &mut self.fatal,
6065 )?;
6066 return Some(ObjectLiteralField { name, value });
6067 }
6068 self.error_at_peek("expected `:` after field name");
6069 return None;
6070 }
6071 self.advance();
6072
6073 let value = self.parse_expression()?;
6074 Some(ObjectLiteralField { name, value })
6075 }
6076
6077 fn parse_method_shorthand_body(&mut self, name_span: Span) -> Option<ExprId> {
6080 self.advance();
6081
6082 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
6083 Vec::new()
6084 } else {
6085 self.parse_arrow_param_list()?
6086 };
6087 if !matches!(self.peek().kind, TokenKind::RightParen) {
6088 self.error_at_peek("expected `)`");
6089 return None;
6090 }
6091 self.advance();
6092
6093 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
6094 self.advance();
6095 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
6096 } else {
6097 (None, None)
6098 };
6099
6100 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
6101 self.error_at_peek("expected `{` to open the method body");
6102 return None;
6103 }
6104 let block = self.parse_outer_this_boundary(Self::parse_block)?;
6105 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
6106 .span
6107 .end;
6108
6109 parse_arena_result(
6110 self.ast.try_push_expr(Expr {
6111 kind: ExprKind::Arrow {
6112 params,
6113 return_type,
6114 type_predicate,
6115 body: ArrowBody::Block(block),
6116 },
6117 span: self.span(name_span.start, end),
6118 }),
6119 &mut self.fatal,
6120 )
6121 }
6122
6123 fn parse_array_literal(&mut self) -> Option<ExprId> {
6124 let open = self.advance();
6125
6126 let mut elements: Vec<ArrayLiteralElement> = Vec::new();
6127 if !matches!(self.peek().kind, TokenKind::RightBracket) {
6128 loop {
6129 let element = if matches!(self.peek().kind, TokenKind::DotDotDot) {
6130 let dots = self.advance();
6131 let value = self.parse_expression()?;
6132 let value_span =
6133 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
6134 ArrayLiteralElement::Spread {
6135 value,
6136 span: self.span(dots.span.start, value_span.end),
6137 }
6138 } else {
6139 ArrayLiteralElement::Value(self.parse_expression()?)
6140 };
6141 elements.push(element);
6142 match self.peek().kind {
6143 TokenKind::Comma => {
6144 self.advance();
6145 if matches!(self.peek().kind, TokenKind::RightBracket) {
6146 break;
6147 }
6148 }
6149 TokenKind::RightBracket => break,
6150 _ => {
6151 self.error_at_peek("expected `,` or `]`");
6152 return None;
6153 }
6154 }
6155 }
6156 }
6157
6158 if !matches!(self.peek().kind, TokenKind::RightBracket) {
6159 self.error_at_peek("expected `]`");
6160 return None;
6161 }
6162 let close = self.advance();
6163
6164 parse_arena_result(
6165 self.ast.try_push_expr(Expr {
6166 kind: ExprKind::ArrayLiteral { elements },
6167 span: self.span(open.span.start, close.span.end),
6168 }),
6169 &mut self.fatal,
6170 )
6171 }
6172
6173 fn parse_paren(&mut self) -> Option<ExprId> {
6174 let open = self.advance();
6175 let inner = self.parse_expression()?;
6176 if !matches!(self.peek().kind, TokenKind::RightParen) {
6177 self.error_at_peek("expected `)`");
6178 return None;
6179 }
6180 let close = self.advance();
6181 let span = self.span(open.span.start, close.span.end);
6182 parse_arena_result(
6183 self.ast.try_push_expr(Expr {
6184 kind: ExprKind::Paren(inner),
6185 span,
6186 }),
6187 &mut self.fatal,
6188 )
6189 }
6190
6191 fn with_recursion_limit<T>(&mut self, parse: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
6192 if self.fatal.is_some() {
6193 return None;
6194 }
6195 if self.recursion_limit_span.is_some() || self.recursion_depth >= MAX_PARSE_DEPTH {
6196 self.stop_at_recursion_limit();
6197 return None;
6198 }
6199 self.recursion_depth += 1;
6200 let result = parse(self);
6201 self.recursion_depth -= 1;
6202 result
6203 }
6204
6205 fn stop_at_recursion_limit(&mut self) {
6206 self.recursion_limit_span.get_or_insert(self.peek().span);
6207 self.pos = self.tokens.len().saturating_sub(1);
6209 }
6210
6211 fn recover(&mut self) {
6212 while !self.is_at_eof() {
6213 match self.peek().kind {
6214 TokenKind::Semicolon => {
6215 self.advance();
6216 return;
6217 }
6218 TokenKind::RightBrace
6219 | TokenKind::Let
6220 | TokenKind::Const
6221 | TokenKind::Function
6222 | TokenKind::Export
6223 | TokenKind::If
6224 | TokenKind::While
6225 | TokenKind::Return
6226 | TokenKind::Try
6227 | TokenKind::Throw => return,
6228 _ => {
6229 self.advance();
6230 }
6231 }
6232 }
6233 }
6234
6235 fn peek(&self) -> &Token {
6236 self.tokens.get(self.pos).unwrap_or(&self.eof)
6237 }
6238
6239 fn peek_at(&self, offset: usize) -> &Token {
6240 self.pos
6241 .checked_add(offset)
6242 .and_then(|index| self.tokens.get(index))
6243 .unwrap_or(&self.eof)
6244 }
6245
6246 fn at_inserted_semicolon(&self) -> bool {
6248 let token = self.peek();
6249 matches!(token.kind, TokenKind::Semicolon) && token.span.start == token.span.end
6250 }
6251
6252 fn advance(&mut self) -> Token {
6253 let tok = self.peek().clone();
6254 if self.pos < self.tokens.len().saturating_sub(1) {
6255 self.pos += 1;
6256 }
6257 tok
6258 }
6259
6260 fn is_at_eof(&self) -> bool {
6261 self.fatal.is_some() || matches!(self.peek().kind, TokenKind::Eof)
6262 }
6263
6264 fn line_break_before_peek(&self) -> bool {
6266 self.source_has_line_break(self.prev_token_end(), self.peek().span.start)
6267 }
6268
6269 fn line_break_after_peek(&self) -> bool {
6271 self.source_has_line_break(self.peek().span.end, self.peek_at(1).span.start)
6272 }
6273
6274 fn source_has_line_break(&self, start: u32, end: u32) -> bool {
6277 self.source
6278 .get(start as usize..end as usize)
6279 .is_some_and(|gap| gap.contains(['\n', '\r']))
6280 }
6281
6282 fn at_statement_end(&self) -> bool {
6287 matches!(self.peek().kind, TokenKind::Semicolon)
6288 || (self.last_as_type_end == Some(self.prev_token_end())
6289 && self.line_break_before_peek())
6290 }
6291
6292 fn finish_statement(&mut self) -> u32 {
6295 if matches!(self.peek().kind, TokenKind::Semicolon) {
6296 return self.advance().span.end;
6297 }
6298 self.prev_token_end()
6299 }
6300
6301 fn prev_token_end(&self) -> u32 {
6304 self.tokens
6305 .get(self.pos.saturating_sub(1))
6306 .unwrap_or(&self.eof)
6307 .span
6308 .end
6309 }
6310
6311 fn take_leading_doc(&mut self) -> Option<crate::DocComment> {
6312 let parsed = self
6313 .peek()
6314 .leading_doc
6315 .as_ref()
6316 .map(crate::parse_doc_comment)
6317 .transpose();
6318 match parsed {
6319 Ok(doc) => doc,
6320 Err(error) => {
6321 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6322 None
6323 }
6324 }
6325 }
6326
6327 fn parse_doc(&mut self, raw: &crate::RawDoc) -> Option<crate::DocComment> {
6328 match crate::parse_doc_comment(raw) {
6329 Ok(doc) => Some(doc),
6330 Err(error) => {
6331 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6332 None
6333 }
6334 }
6335 }
6336
6337 fn reject_use_strict_with_non_simple_params(&mut self) {
6341 let mut functions: Vec<(Vec<Span>, StmtId)> = Vec::new();
6342 let non_simple = |param: &ParamDecl| {
6343 (param.default.is_some() || param.pattern.is_some() || param.rest).then(|| {
6344 param
6345 .pattern
6346 .as_ref()
6347 .map_or(param.name.span, Binding::span)
6348 })
6349 };
6350 for expr in self.ast.source_expressions() {
6351 if let ExprKind::Arrow {
6352 params,
6353 body: ArrowBody::Block(body),
6354 ..
6355 } = &expr.kind
6356 {
6357 functions.push((params.iter().filter_map(non_simple).collect(), *body));
6358 }
6359 }
6360 for stmt in self.ast.source_statements() {
6361 match &stmt.kind {
6362 StmtKind::Function { params, body, .. } => {
6363 functions.push((params.iter().filter_map(non_simple).collect(), *body));
6364 }
6365 StmtKind::ClassDecl { members, .. } => {
6366 for member in members {
6367 match member {
6368 ClassMember::Method { params, body, .. }
6369 | ClassMember::Constructor { params, body, .. } => {
6370 functions
6371 .push((params.iter().filter_map(non_simple).collect(), *body));
6372 }
6373 ClassMember::Accessor { param, body, .. } => {
6374 let params = param.as_deref().and_then(non_simple);
6375 functions.push((params.into_iter().collect(), *body));
6376 }
6377 _ => {}
6378 }
6379 }
6380 }
6381 _ => {}
6382 }
6383 }
6384 for (params, body) in functions {
6385 if params.is_empty() {
6386 continue;
6387 }
6388 let Some(directive) = self.use_strict_directive(body) else {
6389 continue;
6390 };
6391 for span in params {
6392 self.error_at(
6393 span,
6394 "this parameter is not allowed with a `\"use strict\"` directive",
6395 );
6396 }
6397 self.error_at_with_help(
6398 directive,
6399 "a `\"use strict\"` directive can't be used with a parameter list that has defaults, patterns, or a rest parameter",
6400 vec!["remove the directive; Submilli code is always strict".into()],
6401 );
6402 }
6403 }
6404
6405 fn use_strict_directive(&self, body: StmtId) -> Option<Span> {
6409 let StmtKind::Block(stmts) = &self.ast.try_stmt(body).ok()?.kind else {
6410 return None;
6411 };
6412 for stmt in stmts {
6413 let stmt = self.ast.try_stmt(*stmt).ok()?;
6414 let StmtKind::Expr(expr) = stmt.kind else {
6415 return None;
6416 };
6417 let expr = self.ast.try_expr(expr).ok()?;
6418 let ExprKind::String(value) = &expr.kind else {
6419 return None;
6420 };
6421 if self.source.as_bytes().get(expr.span.start as usize) == Some(&b'`') {
6422 return None;
6423 }
6424 if value == "use strict" {
6425 return Some(stmt.span);
6426 }
6427 }
6428 None
6429 }
6430
6431 fn error_at(&mut self, span: Span, message: impl Into<String>) {
6432 self.diagnostics.push(Diagnostic {
6433 severity: Severity::Error,
6434 span,
6435 message: message.into(),
6436 help: vec![],
6437 notes: vec![],
6438 });
6439 }
6440
6441 fn reject_loose_nullish_comparison(
6444 &mut self,
6445 op: BinOp,
6446 lhs: ExprId,
6447 rhs: ExprId,
6448 ) -> Option<()> {
6449 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
6450 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
6451 let (nullish, operand, nullish_first) =
6452 match (self.nullish_operand(lhs)?, self.nullish_operand(rhs)?) {
6453 (Some(left), Some(right)) => {
6454 self.reject_nullish_against_nullish(op, lhs_span, rhs_span, [left, right]);
6455 return Some(());
6456 }
6457 (None, Some(nullish)) => (nullish, lhs, false),
6458 (Some(nullish), None) => (nullish, rhs, true),
6459 (None, None) => return Some(()),
6460 };
6461 let comparison = LooseComparison {
6462 nullish,
6463 nullish_first,
6464 negated: op == BinOp::NotEq,
6465 };
6466 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
6467 let x = self.single_line_source(operand_span).unwrap_or("x");
6468 let strict = self.strict_suggestion(&comparison, x);
6469 let either = self.either_suggestion(&comparison, operand, x)?;
6470 let literal = comparison.nullish.literal.text();
6471 let written = self
6473 .single_line_source(self.span(lhs_span.start, rhs_span.end))
6474 .map_or_else(
6475 || {
6476 let written_op = if comparison.negated { "!=" } else { "==" };
6477 comparison.ordered(x, written_op, literal)
6478 },
6479 str::to_string,
6480 );
6481 self.error_at_with_help(
6482 self.span(lhs_span.start, rhs_span.end),
6483 format!(
6484 "`{written}` does not also match `{}`: `==` compares like `===`",
6485 comparison.nullish.literal.other().text()
6486 ),
6487 vec![
6488 either,
6489 format!("to test for `{literal}` alone, write `{strict}`"),
6490 ],
6491 );
6492 Some(())
6493 }
6494
6495 fn reject_nullish_against_nullish(
6499 &mut self,
6500 op: BinOp,
6501 lhs: Span,
6502 rhs: Span,
6503 sides: [NullishOperand; 2],
6504 ) {
6505 let span = self.span(lhs.start, rhs.end);
6506 let subject = self.single_line_source(span).map_or_else(
6509 || "this comparison".to_string(),
6510 |written| format!("`{written}`"),
6511 );
6512 let value = if op == BinOp::NotEq { "false" } else { "true" };
6513 let effects: Vec<String> = sides
6514 .iter()
6515 .filter_map(|side| side.effect)
6516 .map(|effect| {
6517 self.single_line_source(effect.operand).map_or_else(
6518 || "the `void` operand's expression".to_string(),
6519 |statement| format!("`{statement};`"),
6520 )
6521 })
6522 .collect();
6523 let help = if effects.is_empty() {
6524 format!("in TypeScript this is always `{value}`; write `{value}`")
6525 } else {
6526 format!(
6527 "in TypeScript this is always `{value}`; run {} {}, then use `{value}`",
6528 effects.join(" and "),
6529 if effects.len() > 1 {
6530 "each as its own statement"
6531 } else {
6532 "as its own statement"
6533 }
6534 )
6535 };
6536 self.error_at_with_help(
6537 span,
6538 format!("{subject} compares two nullish values: `==` compares like `===`"),
6539 vec![help],
6540 );
6541 }
6542
6543 fn strict_suggestion(&self, comparison: &LooseComparison, x: &str) -> String {
6546 let eq = comparison.strict_op();
6547 match comparison.nullish.effect {
6548 Some(effect) => {
6549 let void = self.single_line_source(effect.void).unwrap_or("void expr");
6550 comparison.ordered(x, eq, void)
6551 }
6552 None => format!("{x} {eq} {}", comparison.nullish.literal.text()),
6553 }
6554 }
6555
6556 fn either_suggestion(
6560 &mut self,
6561 comparison: &LooseComparison,
6562 operand: ExprId,
6563 x: &str,
6564 ) -> Option<String> {
6565 let mut steps = Vec::new();
6566 let tested = if self.repeats_without_effect(operand)? {
6567 x
6568 } else {
6569 steps.push(format!("bind `{x}` to a `const` (`const value = {x};`)"));
6570 "value"
6571 };
6572 if let Some(effect) = comparison.nullish.effect {
6573 let statement = self.single_line_source(effect.operand).unwrap_or("expr");
6574 let step = format!("run `{statement};` as its own statement");
6575 if comparison.nullish_first {
6576 steps.insert(0, step);
6577 } else {
6578 steps.push(step);
6579 }
6580 }
6581 let eq = comparison.strict_op();
6582 let joiner = if comparison.negated { "&&" } else { "||" };
6583 let both = format!("{tested} {eq} null {joiner} {tested} {eq} undefined");
6584 Some(if steps.is_empty() {
6585 format!("to test for either, write `{both}`")
6586 } else {
6587 format!(
6588 "to test for either, first {}, then write `{both}`",
6589 steps.join(", then ")
6590 )
6591 })
6592 }
6593
6594 fn repeats_without_effect(&mut self, id: ExprId) -> Option<bool> {
6597 let unwrapped = self.unparenthesized(id)?;
6598 let expr = parse_arena_result(self.ast.try_expr(unwrapped), &mut self.fatal)?;
6599 if is_literal(&expr.kind) {
6600 return Some(true);
6601 }
6602 if self
6603 .nullish_operand(unwrapped)?
6604 .is_some_and(|nullish| nullish.effect.is_none())
6605 {
6606 return Some(true);
6607 }
6608 self.is_reference_path(id)
6609 }
6610
6611 fn nullish_operand(&mut self, id: ExprId) -> Option<Option<NullishOperand>> {
6613 let id = self.unparenthesized(id)?;
6614 let expr = parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?;
6615 let span = expr.span;
6616 Some(match &expr.kind {
6617 ExprKind::Null => Some(NullishOperand {
6618 literal: Nullish::Null,
6619 effect: None,
6620 }),
6621 ExprKind::Identifier(ident) if ident.name == "undefined" => Some(NullishOperand {
6622 literal: Nullish::Undefined,
6623 effect: None,
6624 }),
6625 ExprKind::Void { operand } => {
6626 let operand_id = self.unparenthesized(*operand)?;
6627 let operand =
6628 parse_arena_result(self.ast.try_expr(operand_id), &mut self.fatal)?.clone();
6629 let effect_free =
6631 is_literal(&operand.kind) || self.is_reference_path(operand_id)?;
6632 let effect = (!effect_free).then_some(VoidEffect {
6633 void: span,
6634 operand: operand.span,
6635 });
6636 Some(NullishOperand {
6637 literal: Nullish::Undefined,
6638 effect,
6639 })
6640 }
6641 _ => None,
6642 })
6643 }
6644
6645 fn is_reference_path(&mut self, id: ExprId) -> Option<bool> {
6648 let mut id = id;
6649 loop {
6650 match &parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6651 ExprKind::Identifier(_) | ExprKind::This => return Some(true),
6652 ExprKind::FieldAccess { receiver, .. } => id = *receiver,
6653 ExprKind::Paren(inner) => id = *inner,
6654 _ => return Some(false),
6655 }
6656 }
6657 }
6658
6659 fn unparenthesized(&mut self, id: ExprId) -> Option<ExprId> {
6660 let mut id = id;
6661 loop {
6662 match parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6663 ExprKind::Paren(inner) => id = inner,
6664 _ => return Some(id),
6665 }
6666 }
6667 }
6668
6669 fn single_line_source(&self, span: Span) -> Option<&'a str> {
6670 self.source
6671 .get(span.start as usize..span.end as usize)
6672 .filter(|text| !text.contains('\n'))
6673 }
6674
6675 fn error_at_with_help(&mut self, span: Span, message: impl Into<String>, help: Vec<String>) {
6676 self.diagnostics.push(Diagnostic {
6677 severity: Severity::Error,
6678 span,
6679 message: message.into(),
6680 help,
6681 notes: vec![],
6682 });
6683 }
6684
6685 fn invariant_failure<T>(&mut self, message: &str) -> Option<T> {
6686 self.fatal.get_or_insert_with(|| CompilerFailure::Internal {
6687 stage: CompilerStage::Parse,
6688 span: None,
6689 message: message.into(),
6690 });
6691 self.pos = self.tokens.len();
6692 None
6693 }
6694
6695 fn error_at_peek(&mut self, message: impl Into<String>) {
6696 let span = self.peek().span;
6697 self.error_at(span, message);
6698 }
6699
6700 fn error_at_peek_with_help(&mut self, message: impl Into<String>, help: Vec<String>) {
6701 let span = self.peek().span;
6702 self.error_at_with_help(span, message, help);
6703 }
6704
6705 fn error_count(&self) -> usize {
6706 self.diagnostics
6707 .iter()
6708 .filter(|d| d.severity == Severity::Error)
6709 .count()
6710 }
6711}
6712
6713pub(crate) fn is_property_name(kind: &TokenKind) -> bool {
6714 matches!(
6715 kind,
6716 TokenKind::Identifier
6717 | TokenKind::BooleanLiteral(_)
6718 | TokenKind::NullLiteral
6719 | TokenKind::Let
6720 | TokenKind::Const
6721 | TokenKind::Function
6722 | TokenKind::If
6723 | TokenKind::Else
6724 | TokenKind::While
6725 | TokenKind::Do
6726 | TokenKind::For
6727 | TokenKind::Break
6728 | TokenKind::Continue
6729 | TokenKind::Return
6730 | TokenKind::Switch
6731 | TokenKind::Case
6732 | TokenKind::Default
6733 | TokenKind::Void
6734 | TokenKind::Interface
6735 | TokenKind::Enum
6736 | TokenKind::In
6737 | TokenKind::Typeof
6738 | TokenKind::Import
6739 | TokenKind::Export
6740 | TokenKind::New
6741 | TokenKind::Try
6742 | TokenKind::Catch
6743 | TokenKind::Finally
6744 | TokenKind::Throw
6745 | TokenKind::Class
6746 | TokenKind::Extends
6747 | TokenKind::Implements
6748 | TokenKind::Super
6749 | TokenKind::This
6750 )
6751}
6752
6753fn is_reserved_identifier_word(kind: &TokenKind) -> bool {
6754 matches!(
6755 kind,
6756 TokenKind::BooleanLiteral(_)
6757 | TokenKind::NullLiteral
6758 | TokenKind::Let
6759 | TokenKind::Const
6760 | TokenKind::Function
6761 | TokenKind::If
6762 | TokenKind::Else
6763 | TokenKind::While
6764 | TokenKind::Do
6765 | TokenKind::For
6766 | TokenKind::Break
6767 | TokenKind::Continue
6768 | TokenKind::Return
6769 | TokenKind::Switch
6770 | TokenKind::Case
6771 | TokenKind::Default
6772 | TokenKind::Export
6773 | TokenKind::Void
6774 | TokenKind::Interface
6775 | TokenKind::Enum
6776 | TokenKind::In
6777 | TokenKind::Typeof
6778 | TokenKind::Import
6779 | TokenKind::New
6780 | TokenKind::Try
6781 | TokenKind::Catch
6782 | TokenKind::Finally
6783 | TokenKind::Throw
6784 | TokenKind::Class
6785 | TokenKind::Extends
6786 | TokenKind::Implements
6787 | TokenKind::Super
6788 | TokenKind::This
6789 )
6790}
6791
6792#[derive(Clone, Copy, PartialEq, Eq)]
6796enum SpecifierList {
6797 Import,
6798 Export,
6799}
6800
6801impl SpecifierList {
6802 fn noun(self) -> &'static str {
6803 match self {
6804 SpecifierList::Import => "import",
6805 SpecifierList::Export => "export",
6806 }
6807 }
6808}
6809
6810const STRICT_MODE_RESERVED_WORDS: [&str; 6] = [
6814 "package",
6815 "private",
6816 "protected",
6817 "public",
6818 "static",
6819 "yield",
6820];
6821
6822fn reserved_keyword_rename_example(keyword: &str) -> &'static str {
6823 match keyword {
6824 "default" => "defaultValue",
6825 _ => "valueName",
6826 }
6827}
6828
6829fn mixed_logical(prev: BinOp, next: BinOp) -> bool {
6830 let prev_logical = matches!(prev, BinOp::Or | BinOp::And);
6831 let next_logical = matches!(next, BinOp::Or | BinOp::And);
6832 let prev_nullish = matches!(prev, BinOp::NullishCoalesce);
6833 let next_nullish = matches!(next, BinOp::NullishCoalesce);
6834 (prev_logical && next_nullish) || (prev_nullish && next_logical)
6835}
6836
6837fn mixed_logical_help(prev: BinOp, next: BinOp) -> String {
6840 let symbol = |op: BinOp| if op == BinOp::And { "&&" } else { "||" };
6841 let (first, second) = if prev == BinOp::NullishCoalesce {
6842 ("??", symbol(next))
6843 } else {
6844 (symbol(prev), "??")
6845 };
6846 format!(
6847 "add parentheses to choose the grouping: `(a {first} b) {second} c` or `a {first} (b {second} c)`"
6848 )
6849}
6850
6851const LOGICAL_AND_PREC: u8 = 2;
6852
6853#[derive(Clone, Copy)]
6855struct NullishOperand {
6856 literal: Nullish,
6857 effect: Option<VoidEffect>,
6859}
6860
6861#[derive(Clone, Copy, PartialEq, Eq)]
6862enum Nullish {
6863 Null,
6864 Undefined,
6865}
6866
6867impl Nullish {
6868 fn text(self) -> &'static str {
6869 match self {
6870 Nullish::Null => "null",
6871 Nullish::Undefined => "undefined",
6872 }
6873 }
6874
6875 fn other(self) -> Self {
6877 match self {
6878 Nullish::Null => Nullish::Undefined,
6879 Nullish::Undefined => Nullish::Null,
6880 }
6881 }
6882}
6883
6884struct LooseComparison {
6886 nullish: NullishOperand,
6887 nullish_first: bool,
6889 negated: bool,
6891}
6892
6893impl LooseComparison {
6894 fn strict_op(&self) -> &'static str {
6895 if self.negated { "!==" } else { "===" }
6896 }
6897
6898 fn ordered(&self, x: &str, op: &str, nullish: &str) -> String {
6900 if self.nullish_first {
6901 format!("{nullish} {op} {x}")
6902 } else {
6903 format!("{x} {op} {nullish}")
6904 }
6905 }
6906}
6907
6908#[derive(Clone, Copy)]
6909struct VoidEffect {
6910 void: Span,
6912 operand: Span,
6914}
6915
6916fn is_literal(kind: &ExprKind) -> bool {
6917 matches!(
6918 kind,
6919 ExprKind::Number(_)
6920 | ExprKind::BigInt(_)
6921 | ExprKind::String(_)
6922 | ExprKind::Boolean(_)
6923 | ExprKind::Null
6924 )
6925}
6926
6927struct ParamOrder<'p> {
6929 optional: bool,
6930 required: bool,
6932 name: Option<&'p str>,
6934 span: Span,
6935}
6936
6937impl<'p> ParamOrder<'p> {
6938 fn of_param(param: &'p ParamDecl) -> Self {
6939 let (name, span) = match ¶m.pattern {
6940 Some(binding) => (None, binding.span()),
6941 None => (Some(param.name.name.as_str()), param.name.span),
6942 };
6943 Self {
6944 optional: param.optional,
6945 required: !param.is_omittable() && !param.rest,
6946 name,
6947 span,
6948 }
6949 }
6950
6951 fn of_field(field: &'p TypeAnnotationField) -> Self {
6952 Self {
6953 optional: field.optional,
6954 required: !field.optional && !field.rest,
6955 name: Some(field.name.name.as_str()),
6956 span: field.name.span,
6957 }
6958 }
6959}
6960
6961struct BinaryToken {
6963 op: BinOp,
6964 precedence: u8,
6965 tokens: usize,
6966}
6967
6968struct ShiftToken {
6970 op: BinOp,
6971 tokens: usize,
6972 assignment: bool,
6974}
6975
6976impl ShiftToken {
6977 fn new(op: BinOp, tokens: usize, assignment: bool) -> Self {
6978 Self {
6979 op,
6980 tokens,
6981 assignment,
6982 }
6983 }
6984}
6985
6986const SHIFT_PRECEDENCE: u8 = 8;
6988
6989#[derive(Clone, Copy, PartialEq)]
6990enum TupleElementLabel {
6991 None,
6992 Required,
6994 Optional,
6996}
6997
6998fn peek_binop(kind: &TokenKind) -> Option<(BinOp, u8)> {
6999 Some(match kind {
7000 TokenKind::QuestionQuestion => (BinOp::NullishCoalesce, 0),
7001 TokenKind::PipePipe => (BinOp::Or, 1),
7002 TokenKind::AmpAmp => (BinOp::And, LOGICAL_AND_PREC),
7003 TokenKind::Pipe => (BinOp::BitOr, 3),
7004 TokenKind::Caret => (BinOp::BitXor, 4),
7005 TokenKind::Amp => (BinOp::BitAnd, 5),
7006 TokenKind::EqEqEq | TokenKind::EqEq => (BinOp::Eq, 6),
7007 TokenKind::BangEqEq | TokenKind::BangEq => (BinOp::NotEq, 6),
7008 TokenKind::LessThan => (BinOp::Lt, 7),
7009 TokenKind::GreaterThan => (BinOp::Gt, 7),
7010 TokenKind::LessEquals => (BinOp::Le, 7),
7011 TokenKind::GreaterEquals => (BinOp::Ge, 7),
7012 TokenKind::In => (BinOp::In, 7),
7013 TokenKind::Plus => (BinOp::Add, 9),
7016 TokenKind::Minus => (BinOp::Sub, 9),
7017 TokenKind::Star => (BinOp::Mul, 10),
7018 TokenKind::Slash => (BinOp::Div, 10),
7019 TokenKind::Percent => (BinOp::Rem, 10),
7020 TokenKind::StarStar => (BinOp::Pow, 11),
7022 _ => return None,
7023 })
7024}
7025
7026fn cannot_start_type(kind: &TokenKind) -> bool {
7030 matches!(
7031 kind,
7032 TokenKind::Dot
7033 | TokenKind::QuestionDot
7034 | TokenKind::LeftBracket
7035 | TokenKind::Bang
7036 | TokenKind::Semicolon
7037 | TokenKind::RightParen
7038 | TokenKind::RightBrace
7039 | TokenKind::Comma
7040 | TokenKind::Eof
7041 )
7042}
7043
7044fn right_operand_min_prec(op: BinOp, prec: u8) -> u8 {
7048 match op {
7049 BinOp::Pow => prec,
7050 BinOp::NullishCoalesce => LOGICAL_AND_PREC + 1,
7051 _ => prec + 1,
7052 }
7053}
7054
7055fn compound_op_for_token(kind: &TokenKind) -> Option<BinOp> {
7056 Some(match kind {
7057 TokenKind::PlusEquals => BinOp::Add,
7058 TokenKind::MinusEquals => BinOp::Sub,
7059 TokenKind::StarEquals => BinOp::Mul,
7060 TokenKind::SlashEquals => BinOp::Div,
7061 TokenKind::PercentEquals => BinOp::Rem,
7062 TokenKind::StarStarEquals => BinOp::Pow,
7063 TokenKind::AmpEquals => BinOp::BitAnd,
7064 TokenKind::PipeEquals => BinOp::BitOr,
7065 TokenKind::CaretEquals => BinOp::BitXor,
7066 _ => return None,
7067 })
7068}
7069
7070fn is_assign_lookahead(kind: &TokenKind) -> bool {
7071 matches!(kind, TokenKind::Equals) || compound_op_for_token(kind).is_some()
7072}
7073
7074fn parse_arena_result<T>(
7077 result: Result<T, crate::arena::ArenaError>,
7078 fatal: &mut Option<CompilerFailure>,
7079) -> Option<T> {
7080 match result {
7081 Ok(value) => Some(value),
7082 Err(error) => {
7083 fatal.get_or_insert_with(|| error.into_compiler_failure(CompilerStage::Parse));
7084 None
7085 }
7086 }
7087}
7088
7089fn is_rest_array_annotation(ty: &crate::TypeAnnotation) -> bool {
7091 use crate::ast::TypeAnnotationKind;
7092 match &ty.kind {
7093 TypeAnnotationKind::Array(_) => true,
7094 TypeAnnotationKind::Readonly(inner) => matches!(inner.kind, TypeAnnotationKind::Array(_)),
7095 _ => false,
7096 }
7097}
7098
7099#[cfg(test)]
7100mod tests {
7101 use super::{MAX_ERRORS, MAX_PARSE_DEPTH, Parser, parse};
7102 use crate::source::Sources;
7103 use crate::{Asi, Ast, ExprKind, FileId, ImportKind, StmtKind, Token, TokenKind, diagnostics};
7104
7105 const F: FileId = FileId(0);
7106
7107 #[test]
7108 fn malformed_direct_token_streams_return_typed_failures() {
7109 use crate::compiler_error::CompilerFailure;
7110 for tokens in [
7111 Vec::new(),
7112 vec![Token::new(
7113 TokenKind::Identifier,
7114 crate::Span::new(F, 0, 2).unwrap(),
7115 )],
7116 vec![
7117 Token::new(
7118 TokenKind::Identifier,
7119 crate::Span {
7120 file: F,
7121 start: 1,
7122 end: 2,
7123 },
7124 ),
7125 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
7126 ],
7127 vec![
7128 Token::new(TokenKind::Eof, crate::Span::at(F)),
7129 Token::new(TokenKind::Eof, crate::Span::at(F)),
7130 ],
7131 vec![
7132 Token::new(
7133 TokenKind::Identifier,
7134 crate::Span {
7135 file: F,
7136 start: 2,
7137 end: 0,
7138 },
7139 ),
7140 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
7141 ],
7142 vec![Token::new(TokenKind::Eof, crate::Span::at(FileId(9)))],
7143 ] {
7144 let error = super::parse_checked("é", tokens, F).expect_err("invalid token metadata");
7145 assert!(matches!(
7146 error.fatal,
7147 Some(CompilerFailure::Internal { .. })
7148 ));
7149 }
7150 }
7151
7152 #[test]
7153 fn malformed_documentation_metadata_returns_a_fatal_error() {
7154 let source = "/** hello */ function main(): void {}";
7155 let valid = tokens_of(source);
7156 for doc in [
7157 crate::RawDoc {
7158 text: "/** hi */".into(),
7159 span: crate::Span {
7160 file: F,
7161 start: u32::MAX,
7162 end: u32::MAX,
7163 },
7164 },
7165 crate::RawDoc {
7166 text: "/** different */".into(),
7167 span: crate::Span::new(F, 0, 12).unwrap(),
7168 },
7169 crate::RawDoc {
7170 text: "/** hello */".into(),
7171 span: crate::Span::new(FileId(9), 0, 12).unwrap(),
7172 },
7173 ] {
7174 let mut tokens = valid.clone();
7175 tokens[0].leading_doc = Some(doc);
7176 let error = super::parse_checked(source, tokens, F).expect_err("invalid doc metadata");
7177 assert!(error.to_string().contains("documentation metadata"));
7178 }
7179 assert!(super::parse_checked(source, valid, F).is_ok());
7180 }
7181
7182 #[test]
7183 fn invalid_parser_dispatch_survives_diagnostic_rollback() {
7184 let mut parser = parser_from_source("let x = 1;");
7185 assert!(parser.try_parse_type_args_only().is_none());
7186 parser.diagnostics.clear();
7187 assert!(parser.fatal.is_some());
7188 assert!(parser.parse_expression().is_none());
7189 }
7190
7191 #[test]
7192 fn invalid_arena_read_stops_parser_and_survives_diagnostic_rollback() {
7193 use crate::{
7194 ExprId,
7195 compiler_error::{CompilerFailure, CompilerStage},
7196 };
7197 let mut parser = parser_from_source("let x = 1;");
7198 assert!(parser.statement_kind_for(ExprId(u32::MAX)).is_none());
7199 assert!(matches!(
7200 parser.fatal,
7201 Some(CompilerFailure::Internal {
7202 stage: CompilerStage::Parse,
7203 span: None,
7204 ..
7205 })
7206 ));
7207 parser.diagnostics.clear();
7208 let position = parser.pos;
7209 assert!(parser.parse_statement().is_none());
7210 parser.recover();
7211 assert_eq!(parser.pos, position);
7212 assert!(parser.ast.expr_ids().unwrap().next().is_none());
7213 }
7214
7215 fn tokens_of(source: &str) -> Vec<Token> {
7216 let mut asi = Asi::new(source, crate::FileId(0));
7217 let mut tokens = Vec::new();
7218 loop {
7219 let tok = asi.next_token();
7220 let is_eof = matches!(tok.kind, TokenKind::Eof);
7221 tokens.push(tok);
7222 if is_eof {
7223 break;
7224 }
7225 }
7226 let diags = asi.into_diagnostics();
7227 assert!(diags.is_empty(), "unexpected lexer diagnostics: {diags:?}");
7228 tokens
7229 }
7230
7231 fn parse_str(source: &str) -> (Ast, Vec<crate::Diagnostic>) {
7232 parse(source, tokens_of(source), crate::FileId(0))
7233 }
7234
7235 fn parser_from_source(source: &str) -> Parser<'_> {
7236 Parser {
7237 source,
7238 tokens: tokens_of(source),
7239 split_type_tokens: Vec::new(),
7240 file: F,
7241 pos: 0,
7242 ast: Ast::new(),
7243 diagnostics: Vec::new(),
7244 block_depth: 0,
7245 class_member_body_depth: 0,
7246 function_expression_body_depth: 0,
7247 recursion_depth: 0,
7248 recursion_limit_span: None,
7249 resume_at_next_member: false,
7250 last_as_type_end: None,
7251 trailing_declarators: Vec::new(),
7252 eof: Token::new(
7253 TokenKind::Eof,
7254 crate::Span::new(F, source.len() as u32, source.len() as u32).unwrap(),
7255 ),
7256 fatal: None,
7257 }
7258 }
7259
7260 #[test]
7261 fn recursion_budget_accepts_boundary_and_restores_depth() {
7262 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH - 1));
7263 let mut parser = parser_from_source(&source);
7264 assert!(parser.parse_unary().is_some());
7265 assert_eq!(parser.recursion_depth, 0);
7266 assert!(parser.recursion_limit_span.is_none());
7267
7268 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH));
7269 let mut parser = parser_from_source(&source);
7270 assert!(parser.parse_unary().is_none());
7271 assert_eq!(parser.recursion_depth, 0);
7272 assert!(parser.recursion_limit_span.is_some());
7273 }
7274
7275 #[test]
7276 fn parse_empty_input() {
7277 let (ast, diags) = parse_str("");
7278 assert!(diags.is_empty());
7279 assert!(ast.top_level.is_empty());
7280 }
7281
7282 #[test]
7283 fn any_type_is_rejected() {
7284 for src in [
7287 "function main(): void { const x: any = 1; }",
7288 "function main(): void { const x = 1 as any; }",
7289 "function main(): void { const x: any[] = []; }",
7290 ] {
7291 let (_ast, diags) = parse_str(src);
7292 assert!(
7293 diags
7294 .iter()
7295 .any(|d| d.message.contains("`any` is not supported")),
7296 "expected an `any` rejection for {src:?}, got: {diags:?}",
7297 );
7298 assert!(
7299 diags
7300 .iter()
7301 .any(|d| d.help.iter().any(|h| h.contains("`unknown`"))),
7302 "expected the fix-shape help for {src:?}, got: {diags:?}",
7303 );
7304 }
7305 }
7306
7307 #[test]
7308 fn any_catch_binding_help_suggests_catch_forms() {
7309 let (_ast, diags) = parse_str("function main(): void { try { } catch (e: any) { } }");
7312 let any_diags: Vec<_> = diags
7313 .iter()
7314 .filter(|d| d.message.contains("`any` is not supported"))
7315 .collect();
7316 assert_eq!(any_diags.len(), 1, "got: {diags:?}");
7317 assert!(
7318 any_diags[0].help.iter().any(|h| h.contains("`catch (e)`")),
7319 "got: {diags:?}"
7320 );
7321 assert!(
7322 !any_diags[0].help.iter().any(|h| h.contains("`unknown`")),
7323 "got: {diags:?}"
7324 );
7325 }
7326
7327 #[test]
7328 fn arrow_return_type_scanner_matches_type_grammar() {
7329 for ty in [
7334 "number",
7335 "void",
7336 "null",
7337 "ns.Foo",
7338 "Map<string, number>",
7339 "ns.Foo<string>[]",
7340 "number[]",
7341 "[number, number]",
7342 "[number, [string, boolean]]",
7343 "[number, string][]",
7344 "{ a: number }",
7345 "{ a: number }[]",
7346 "\"on\" | \"off\"",
7347 "1 | 2",
7348 "Point | null",
7349 "| number | string",
7350 "(n: number) => number",
7351 "(n: number) => [number, number]",
7352 "readonly number[]",
7353 "readonly number[][]",
7354 "readonly [number, string]",
7355 "readonly number[] | null",
7356 "[first: number, second: string]",
7357 "[new: number, string]",
7358 "keyof { a: number }",
7359 ] {
7360 let annotation = format!("function f(): void {{ const a: {ty} = x; }}");
7361 let (_ast, diags) = parse_str(&annotation);
7362 assert!(
7363 diags.is_empty(),
7364 "annotation grammar rejected {ty:?}: {diags:?}",
7365 );
7366
7367 let arrow = format!("function f(): void {{ const g = (): {ty} => x; }}");
7368 let (_ast, diags) = parse_str(&arrow);
7369 assert!(
7370 diags.is_empty(),
7371 "arrow lookahead rejected return type {ty:?}: {diags:?}",
7372 );
7373 }
7374
7375 let (_ast, diags) =
7377 parse_str("function f(): void { const g = (v: unknown): v is string => x; }");
7378 assert!(
7379 diags.is_empty(),
7380 "arrow lookahead rejected predicate return: {diags:?}",
7381 );
7382 }
7383
7384 #[test]
7385 fn bare_semicolons_are_silent_empty_statements() {
7386 let (ast, diags) = parse_str(";");
7387 assert!(diags.is_empty());
7388 assert!(ast.top_level.is_empty());
7389 }
7390
7391 #[test]
7392 fn parse_single_non_expression_token_errors() {
7393 let (ast, diags) = parse_str("}");
7394 assert_eq!(diags.len(), 1);
7395 assert_eq!(diags[0].message, "expected expression");
7396 assert!(ast.top_level.is_empty());
7397 }
7398
7399 #[test]
7400 fn recovery_across_many_bad_starts() {
7401 let (ast, diags) = parse_str("} } } x;");
7402 assert_eq!(diags.len(), 3);
7403 for d in &diags {
7404 assert_eq!(d.message, "expected expression");
7405 }
7406 assert_eq!(ast.top_level.len(), 1);
7407 }
7408
7409 #[test]
7410 fn recovery_consumes_semicolon() {
7411 let mut p = parser_from_source("foo ;");
7412 p.error_at_peek("test");
7413 p.recover();
7414 assert!(matches!(
7415 p.peek().kind,
7416 TokenKind::Semicolon | TokenKind::Eof
7417 ));
7418 assert!(matches!(p.peek().kind, TokenKind::Eof));
7419 }
7420
7421 #[test]
7422 fn recovery_stops_at_right_brace() {
7423 let mut p = parser_from_source("foo }");
7424 p.error_at_peek("test");
7425 p.recover();
7426 assert!(matches!(p.peek().kind, TokenKind::RightBrace));
7427 }
7428
7429 #[test]
7430 fn recovery_stops_at_let_keyword() {
7431 let mut p = parser_from_source("foo let x");
7432 p.error_at_peek("test");
7433 p.recover();
7434 assert!(matches!(p.peek().kind, TokenKind::Let));
7435 }
7436
7437 #[test]
7438 fn recovery_stops_at_function_keyword() {
7439 let mut p = parser_from_source("foo function bar");
7440 p.error_at_peek("test");
7441 p.recover();
7442 assert!(matches!(p.peek().kind, TokenKind::Function));
7443 }
7444
7445 #[test]
7446 fn error_cap_at_20() {
7447 let source = "} ".repeat(25);
7448 let (_, diags) = parse_str(&source);
7449 assert_eq!(diags.len(), MAX_ERRORS);
7450 }
7451
7452 #[test]
7453 fn driver_makes_progress_past_unmatched_brace() {
7454 let (ast, diags) = parse_str("} x;");
7455 assert_eq!(diags.len(), 1);
7456 assert_eq!(diags[0].message, "expected expression");
7457 assert_eq!(ast.top_level.len(), 1);
7458 }
7459
7460 #[test]
7461 fn snapshot_multi_error_render() {
7462 let source = "foo bar;\nbaz qux;\nhello world;";
7463 let (_, diags) = parse_str(source);
7464 let (sources, _) = Sources::single("script.subm", source).unwrap();
7465 let rendered: String = diags
7466 .iter()
7467 .map(|d| diagnostics::render(d, &sources))
7468 .collect::<Vec<_>>()
7469 .join("\n");
7470 insta::assert_snapshot!(rendered);
7471 }
7472
7473 fn single_stmt(ast: &Ast) -> &crate::Stmt {
7474 assert_eq!(ast.top_level.len(), 1);
7475 ast.try_stmt(ast.top_level[0]).unwrap()
7476 }
7477
7478 fn expr_of_single_stmt(ast: &Ast) -> &crate::Expr {
7479 let stmt = single_stmt(ast);
7480 match stmt.kind {
7481 crate::StmtKind::Expr(id) => ast.try_expr(id).unwrap(),
7482 _ => panic!("expected expression statement, got {:?}", stmt.kind),
7483 }
7484 }
7485
7486 #[test]
7487 fn parse_number_literal() {
7488 let (ast, diags) = parse_str("42;");
7489 assert!(diags.is_empty());
7490 let expr = expr_of_single_stmt(&ast);
7491 assert_eq!(expr.kind, crate::ExprKind::Number(42.0));
7492 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
7493 }
7494
7495 #[test]
7496 fn parse_string_literal() {
7497 let (ast, diags) = parse_str(r#""s";"#);
7498 assert!(diags.is_empty());
7499 let expr = expr_of_single_stmt(&ast);
7500 assert_eq!(expr.kind, crate::ExprKind::String("s".to_string()));
7501 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7502 }
7503
7504 #[test]
7505 fn parse_boolean_true() {
7506 let (ast, diags) = parse_str("true;");
7507 assert!(diags.is_empty());
7508 let expr = expr_of_single_stmt(&ast);
7509 assert_eq!(expr.kind, crate::ExprKind::Boolean(true));
7510 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7511 }
7512
7513 #[test]
7514 fn parse_boolean_false() {
7515 let (ast, diags) = parse_str("false;");
7516 assert!(diags.is_empty());
7517 let expr = expr_of_single_stmt(&ast);
7518 assert_eq!(expr.kind, crate::ExprKind::Boolean(false));
7519 assert_eq!(expr.span, crate::Span::new(F, 0, 5).unwrap());
7520 }
7521
7522 #[test]
7523 fn parse_null_literal() {
7524 let (ast, diags) = parse_str("null;");
7525 assert!(diags.is_empty());
7526 let expr = expr_of_single_stmt(&ast);
7527 assert_eq!(expr.kind, crate::ExprKind::Null);
7528 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7529 }
7530
7531 #[test]
7532 fn parse_identifier() {
7533 let (ast, diags) = parse_str("x;");
7534 assert!(diags.is_empty());
7535 let expr = expr_of_single_stmt(&ast);
7536 assert!(matches!(expr.kind, crate::ExprKind::Identifier(_)));
7537 assert_eq!(expr.span, crate::Span::new(F, 0, 1).unwrap());
7538 }
7539
7540 #[test]
7541 fn reserved_keywords_remain_invalid_declaration_names() {
7542 let (_ast, diags) = parse_str("function default(): void {}");
7543 assert_eq!(diags.len(), 1, "{diags:#?}");
7544 assert_eq!(
7545 diags[0].message,
7546 "`default` is a reserved keyword and can't be used as a name"
7547 );
7548 assert!(
7549 diags[0]
7550 .help
7551 .iter()
7552 .any(|h| h == "rename it, for example: `defaultValue`"),
7553 "expected rename help, got: {diags:?}"
7554 );
7555 }
7556
7557 #[test]
7558 fn eval_and_arguments_cannot_name_bindings() {
7559 let bindings = [
7560 ("let arguments = 1;", "arguments"),
7561 ("const [eval] = [1];", "eval"),
7562 ("const { a: eval } = { a: 1 };", "eval"),
7563 ("const { eval } = { eval: 1 };", "eval"),
7564 ("const [...arguments] = [1];", "arguments"),
7565 ("function eval(): void {}", "eval"),
7566 ("function f(arguments: number): void {}", "arguments"),
7567 ("const f = arguments => arguments;", "arguments"),
7568 ("const f = (...eval: number[]) => 1;", "eval"),
7569 ("const f = function eval(): void {};", "eval"),
7570 ("for (const eval of [1]) {}", "eval"),
7571 ("try {} catch (arguments) {}", "arguments"),
7572 ("type F = (eval: number) => void;", "eval"),
7573 ("type G = (...arguments: number[]) => void;", "arguments"),
7574 ("type H = { [eval: string]: number };", "eval"),
7575 ];
7576 for (source, word) in bindings {
7577 let (_ast, diags) = parse_str(source);
7578 assert!(
7579 diags
7580 .iter()
7581 .any(|d| d.message == format!("`{word}` can't name a binding in strict mode")),
7582 "{source}: {diags:#?}"
7583 );
7584 }
7585 }
7586
7587 #[test]
7588 fn eval_and_arguments_still_name_types_and_properties() {
7589 for source in [
7590 "class eval {}",
7591 "interface arguments { eval: number }",
7592 "type eval = number;",
7593 "enum arguments { eval }",
7594 "const o = { eval: 1, arguments: 2 };",
7595 "import { eval } from \"./m\";",
7596 ] {
7597 let (_ast, diags) = parse_str(source);
7598 assert!(diags.is_empty(), "{source}: {diags:#?}");
7599 }
7600 }
7601
7602 #[test]
7603 fn reserved_keywords_remain_invalid_parameter_names() {
7604 let (_ast, diags) = parse_str("function f(default: string): void {}");
7605 assert_eq!(diags.len(), 1, "{diags:#?}");
7606 assert_eq!(
7607 diags[0].message,
7608 "`default` is a reserved keyword and can't be used as a name"
7609 );
7610 assert!(
7611 diags[0]
7612 .help
7613 .iter()
7614 .any(|h| h == "rename it, for example: `defaultValue`"),
7615 "expected rename help, got: {diags:?}"
7616 );
7617 }
7618
7619 #[test]
7620 fn contextual_keywords_are_valid_names() {
7621 for src in [
7622 "const type = \"x\";",
7623 "const from = 1;",
7624 "let of = 2;",
7625 "const as = 3;",
7626 "const is = 4;",
7627 "function f(from: string): void {}",
7628 ] {
7629 let (_ast, diags) = parse_str(src);
7630 assert!(
7631 diags.is_empty(),
7632 "unexpected diagnostics for {src:?}: {diags:#?}"
7633 );
7634 }
7635 }
7636
7637 #[test]
7638 fn type_alias_dispatch_table() {
7639 let (ast, diags) = parse_str("type X = number;");
7640 assert!(diags.is_empty(), "{diags:#?}");
7641 assert!(matches!(
7642 ast.try_stmt(ast.top_level[0]).unwrap().kind,
7643 crate::StmtKind::TypeAliasDecl { .. }
7644 ));
7645
7646 for src in ["type = 5;", "type;", "type(1);", "type < 3;", "type.foo;"] {
7648 let (ast, diags) = parse_str(src);
7649 assert!(
7650 diags.is_empty(),
7651 "unexpected diagnostics for {src:?}: {diags:#?}"
7652 );
7653 assert!(
7654 !matches!(
7655 ast.try_stmt(ast.top_level[0]).unwrap().kind,
7656 crate::StmtKind::TypeAliasDecl { .. }
7657 ),
7658 "{src:?} must not parse as a type alias"
7659 );
7660 }
7661
7662 let (_ast, diags) = parse_str("type X;");
7663 assert!(
7664 diags
7665 .iter()
7666 .any(|d| d.message == "expected `=` after type alias name"),
7667 "bare `type X` should commit to the alias diagnostic, got: {diags:#?}"
7668 );
7669 }
7670
7671 #[test]
7672 fn export_type_alias_dispatch() {
7673 let (ast, diags) = parse_str("export type X = number;");
7674 assert!(diags.is_empty(), "{diags:#?}");
7675 assert_eq!(ast.exported_decls.len(), 1);
7676 assert!(matches!(
7677 ast.try_stmt(ast.exported_decls[0].stmt).unwrap().kind,
7678 crate::StmtKind::TypeAliasDecl { .. }
7679 ));
7680
7681 let (_ast, diags) = parse_str("export type = 5;");
7682 assert!(
7683 diags
7684 .iter()
7685 .any(|d| d.message == "expected a declaration after `export`"),
7686 "`export type = 5` must not become an exported assignment, got: {diags:#?}"
7687 );
7688 }
7689
7690 #[test]
7691 fn parse_paren_simple() {
7692 let (ast, diags) = parse_str("(x);");
7693 assert!(diags.is_empty());
7694 let expr = expr_of_single_stmt(&ast);
7695 let crate::ExprKind::Paren(inner_id) = expr.kind else {
7696 panic!("expected Paren, got {:?}", expr.kind);
7697 };
7698 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7699 let inner = ast.try_expr(inner_id).unwrap();
7700 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7701 assert_eq!(inner.span, crate::Span::new(F, 1, 2).unwrap());
7702 }
7703
7704 #[test]
7705 fn parse_paren_nested() {
7706 let (ast, diags) = parse_str("((x));");
7707 assert!(diags.is_empty());
7708 let outer = expr_of_single_stmt(&ast);
7709 let crate::ExprKind::Paren(mid_id) = outer.kind else {
7710 panic!("expected outer Paren");
7711 };
7712 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7713 let mid = ast.try_expr(mid_id).unwrap();
7714 let crate::ExprKind::Paren(inner_id) = mid.kind else {
7715 panic!("expected inner Paren");
7716 };
7717 assert_eq!(mid.span, crate::Span::new(F, 1, 4).unwrap());
7718 let inner = ast.try_expr(inner_id).unwrap();
7719 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7720 assert_eq!(inner.span, crate::Span::new(F, 2, 3).unwrap());
7721 }
7722
7723 #[test]
7724 fn parse_sequence_of_atoms() {
7725 let (ast, diags) = parse_str(r#"42; "hi"; true; null; x; (x);"#);
7726 assert!(diags.is_empty());
7727 assert_eq!(ast.top_level.len(), 6);
7728 insta::assert_debug_snapshot!(ast);
7729 }
7730
7731 #[test]
7732 fn missing_semicolon_after_expression() {
7733 let (ast, diags) = parse_str("42 43");
7734 assert_eq!(diags.len(), 1);
7735 assert_eq!(diags[0].message, "expected `;` after expression");
7736 assert!(ast.top_level.is_empty());
7737 }
7738
7739 #[test]
7740 fn empty_paren_diagnoses() {
7741 let (ast, diags) = parse_str("();");
7742 assert_eq!(diags.len(), 1);
7743 assert_eq!(diags[0].message, "expected expression");
7744 assert!(ast.top_level.is_empty());
7745 }
7746
7747 #[test]
7748 fn unterminated_paren_diagnoses() {
7749 let (ast, diags) = parse_str("(x;");
7750 assert_eq!(diags.len(), 1);
7751 assert_eq!(diags[0].message, "expected `)`");
7752 assert!(ast.top_level.is_empty());
7753 }
7754
7755 fn binary(e: &crate::Expr) -> (crate::BinOp, crate::ExprId, crate::ExprId) {
7756 match e.kind {
7757 crate::ExprKind::Binary { op, lhs, rhs } => (op, lhs, rhs),
7758 _ => panic!("expected Binary, got {:?}", e.kind),
7759 }
7760 }
7761
7762 #[test]
7763 fn parse_binary_add() {
7764 let (ast, diags) = parse_str("a + b;");
7765 assert!(diags.is_empty());
7766 let outer = expr_of_single_stmt(&ast);
7767 let (op, lhs, rhs) = binary(outer);
7768 assert_eq!(op, crate::BinOp::Add);
7769 assert!(matches!(
7770 ast.try_expr(lhs).unwrap().kind,
7771 crate::ExprKind::Identifier(_)
7772 ));
7773 assert!(matches!(
7774 ast.try_expr(rhs).unwrap().kind,
7775 crate::ExprKind::Identifier(_)
7776 ));
7777 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7778 }
7779
7780 #[test]
7781 fn bitwise_precedence_and_shift_adjacency() {
7782 let (ast, diags) = parse_str("a | b ^ c & d == e >> f + g;");
7783 assert!(diags.is_empty(), "{diags:?}");
7784 let (op, _, mut rhs) = binary(expr_of_single_stmt(&ast));
7785 assert_eq!(op, crate::BinOp::BitOr);
7786 for expected in [
7787 crate::BinOp::BitXor,
7788 crate::BinOp::BitAnd,
7789 crate::BinOp::Eq,
7790 crate::BinOp::Shr,
7791 crate::BinOp::Add,
7792 ] {
7793 let (op, _, next) = binary(ast.try_expr(rhs).unwrap());
7794 assert_eq!(op, expected);
7795 rhs = next;
7796 }
7797 for source in ["a > > b;", "a >/*gap*/> b;", "a < < b;"] {
7798 let (_, diags) = parse_str(source);
7799 assert!(!diags.is_empty(), "{source}");
7800 }
7801 }
7802
7803 #[test]
7804 fn bitwise_shift_assignment_span() {
7805 let (ast, diags) = parse_str("a >>>= b;");
7806 assert!(diags.is_empty(), "{diags:?}");
7807 let crate::StmtKind::CompoundAssign { op, op_span, .. } = single_stmt(&ast).kind else {
7808 panic!("expected assignment");
7809 };
7810 assert_eq!(op, crate::BinOp::UnsignedShr);
7811 assert_eq!(op_span, crate::Span::new(F, 2, 6).unwrap());
7812 }
7813
7814 #[test]
7815 fn precedence_mul_binds_tighter_than_add() {
7816 let (ast, diags) = parse_str("a + b * c;");
7817 assert!(diags.is_empty());
7818 let outer = expr_of_single_stmt(&ast);
7819 let (op, lhs, rhs) = binary(outer);
7820 assert_eq!(op, crate::BinOp::Add);
7821 assert!(matches!(
7822 ast.try_expr(lhs).unwrap().kind,
7823 crate::ExprKind::Identifier(_)
7824 ));
7825 let (rop, rlhs, rrhs) = binary(ast.try_expr(rhs).unwrap());
7826 assert_eq!(rop, crate::BinOp::Mul);
7827 assert!(matches!(
7828 ast.try_expr(rlhs).unwrap().kind,
7829 crate::ExprKind::Identifier(_)
7830 ));
7831 assert!(matches!(
7832 ast.try_expr(rrhs).unwrap().kind,
7833 crate::ExprKind::Identifier(_)
7834 ));
7835 }
7836
7837 #[test]
7838 fn precedence_mul_left_of_add() {
7839 let (ast, diags) = parse_str("a * b + c;");
7840 assert!(diags.is_empty());
7841 let outer = expr_of_single_stmt(&ast);
7842 let (op, lhs, rhs) = binary(outer);
7843 assert_eq!(op, crate::BinOp::Add);
7844 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7845 assert_eq!(lop, crate::BinOp::Mul);
7846 assert!(matches!(
7847 ast.try_expr(rhs).unwrap().kind,
7848 crate::ExprKind::Identifier(_)
7849 ));
7850 }
7851
7852 #[test]
7853 fn left_associative_same_precedence() {
7854 let (ast, diags) = parse_str("a - b - c;");
7855 assert!(diags.is_empty());
7856 let outer = expr_of_single_stmt(&ast);
7857 let (op, lhs, rhs) = binary(outer);
7858 assert_eq!(op, crate::BinOp::Sub);
7859 let (lop, llhs, lrhs) = binary(ast.try_expr(lhs).unwrap());
7860 assert_eq!(lop, crate::BinOp::Sub);
7861 assert!(matches!(
7862 ast.try_expr(llhs).unwrap().kind,
7863 crate::ExprKind::Identifier(_)
7864 ));
7865 assert!(matches!(
7866 ast.try_expr(lrhs).unwrap().kind,
7867 crate::ExprKind::Identifier(_)
7868 ));
7869 assert!(matches!(
7870 ast.try_expr(rhs).unwrap().kind,
7871 crate::ExprKind::Identifier(_)
7872 ));
7873 }
7874
7875 #[test]
7876 fn equality_inside_logical() {
7877 let (ast, diags) = parse_str("a === b && c === d;");
7878 assert!(diags.is_empty());
7879 let outer = expr_of_single_stmt(&ast);
7880 let (op, lhs, rhs) = binary(outer);
7881 assert_eq!(op, crate::BinOp::And);
7882 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7883 assert_eq!(lop, crate::BinOp::Eq);
7884 let (rop, ..) = binary(ast.try_expr(rhs).unwrap());
7885 assert_eq!(rop, crate::BinOp::Eq);
7886 }
7887
7888 #[test]
7889 fn double_equal_is_same_variant_as_triple() {
7890 let (ast, _) = parse_str("a == b;");
7891 let outer = expr_of_single_stmt(&ast);
7892 let (op, ..) = binary(outer);
7893 assert_eq!(op, crate::BinOp::Eq);
7894 }
7895
7896 #[test]
7897 fn bang_eq_is_not_eq() {
7898 let (ast, _) = parse_str("a != b;");
7899 let outer = expr_of_single_stmt(&ast);
7900 let (op, ..) = binary(outer);
7901 assert_eq!(op, crate::BinOp::NotEq);
7902 }
7903
7904 #[test]
7905 fn comparison_has_higher_prec_than_logical() {
7906 let (ast, _) = parse_str("a < b && c;");
7907 let outer = expr_of_single_stmt(&ast);
7908 let (op, lhs, _) = binary(outer);
7909 assert_eq!(op, crate::BinOp::And);
7910 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7911 assert_eq!(lop, crate::BinOp::Lt);
7912 }
7913
7914 #[test]
7915 fn full_precedence_chain_snapshot() {
7916 let (ast, diags) = parse_str("a || b && c === d < e + f * g;");
7917 assert!(diags.is_empty());
7918 insta::assert_debug_snapshot!(ast);
7919 }
7920
7921 #[test]
7922 fn paren_overrides_precedence() {
7923 let (ast, _) = parse_str("(a + b) * c;");
7924 let outer = expr_of_single_stmt(&ast);
7925 let (op, lhs, rhs) = binary(outer);
7926 assert_eq!(op, crate::BinOp::Mul);
7927 let crate::ExprKind::Paren(inner_id) = ast.try_expr(lhs).unwrap().kind else {
7928 panic!("expected Paren on lhs");
7929 };
7930 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7931 assert_eq!(inner_op, crate::BinOp::Add);
7932 assert!(matches!(
7933 ast.try_expr(rhs).unwrap().kind,
7934 crate::ExprKind::Identifier(_)
7935 ));
7936 }
7937
7938 #[test]
7939 fn binary_missing_rhs_diagnoses() {
7940 let (ast, diags) = parse_str("a +;");
7941 assert_eq!(diags.len(), 1);
7942 assert_eq!(diags[0].message, "expected expression");
7943 assert!(ast.top_level.is_empty());
7944 }
7945
7946 fn unary(e: &crate::Expr) -> (crate::UnOp, crate::ExprId) {
7947 match e.kind {
7948 crate::ExprKind::Unary { op, operand } => (op, operand),
7949 _ => panic!("expected Unary, got {:?}", e.kind),
7950 }
7951 }
7952
7953 #[test]
7954 fn parse_unary_not() {
7955 let (ast, _) = parse_str("!x;");
7956 let outer = expr_of_single_stmt(&ast);
7957 let (op, operand) = unary(outer);
7958 assert_eq!(op, crate::UnOp::Not);
7959 assert!(matches!(
7960 ast.try_expr(operand).unwrap().kind,
7961 crate::ExprKind::Identifier(_)
7962 ));
7963 assert_eq!(outer.span, crate::Span::new(F, 0, 2).unwrap());
7964 }
7965
7966 #[test]
7967 fn parse_unary_neg() {
7968 let (ast, _) = parse_str("-x;");
7969 let (op, _) = unary(expr_of_single_stmt(&ast));
7970 assert_eq!(op, crate::UnOp::Neg);
7971 }
7972
7973 #[test]
7974 fn parse_unary_pos() {
7975 let (ast, _) = parse_str("+x;");
7976 let (op, _) = unary(expr_of_single_stmt(&ast));
7977 assert_eq!(op, crate::UnOp::Pos);
7978 }
7979
7980 #[test]
7981 fn repeated_unary_not() {
7982 let (ast, _) = parse_str("!!x;");
7983 let outer = expr_of_single_stmt(&ast);
7984 let (op, operand) = unary(outer);
7985 assert_eq!(op, crate::UnOp::Not);
7986 let (inner_op, inner_operand) = unary(ast.try_expr(operand).unwrap());
7987 assert_eq!(inner_op, crate::UnOp::Not);
7988 assert!(matches!(
7989 ast.try_expr(inner_operand).unwrap().kind,
7990 crate::ExprKind::Identifier(_)
7991 ));
7992 }
7993
7994 #[test]
7995 fn unary_binds_tighter_than_binary() {
7996 let (ast, _) = parse_str("-x + 1;");
7997 let outer = expr_of_single_stmt(&ast);
7998 let (op, lhs, rhs) = binary(outer);
7999 assert_eq!(op, crate::BinOp::Add);
8000 let (unop, _) = unary(ast.try_expr(lhs).unwrap());
8001 assert_eq!(unop, crate::UnOp::Neg);
8002 assert_eq!(
8003 ast.try_expr(rhs).unwrap().kind,
8004 crate::ExprKind::Number(1.0)
8005 );
8006 }
8007
8008 #[test]
8009 fn unary_over_paren() {
8010 let (ast, _) = parse_str("-(x + 1);");
8011 let outer = expr_of_single_stmt(&ast);
8012 let (op, operand) = unary(outer);
8013 assert_eq!(op, crate::UnOp::Neg);
8014 let crate::ExprKind::Paren(inner_id) = ast.try_expr(operand).unwrap().kind else {
8015 panic!("expected Paren");
8016 };
8017 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
8018 assert_eq!(inner_op, crate::BinOp::Add);
8019 }
8020
8021 #[test]
8022 fn parse_is_as_expression_rejected() {
8023 let (_, diags) = parse_str("x is number;");
8024 assert!(!diags.is_empty(), "expected parse error for `x is T`");
8025 }
8026
8027 fn as_cast(e: &crate::Expr) -> (crate::ExprId, &crate::TypeAnnotation) {
8028 match &e.kind {
8029 crate::ExprKind::As { expr, ty } => (*expr, ty),
8030 other => panic!("expected ExprKind::As, got {other:?}"),
8031 }
8032 }
8033
8034 #[test]
8035 fn parse_instanceof_class() {
8036 let (ast, diags) = parse_str("x instanceof Foo;");
8037 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8038 let outer = expr_of_single_stmt(&ast);
8039 let crate::ExprKind::InstanceOf { value, ty } = &outer.kind else {
8040 panic!("expected ExprKind::InstanceOf, got {:?}", outer.kind);
8041 };
8042 assert!(matches!(
8043 ast.try_expr(*value).unwrap().kind,
8044 crate::ExprKind::Identifier(_)
8045 ));
8046 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8047 }
8048
8049 #[test]
8050 fn parse_instanceof_binds_tighter_than_logical_and() {
8051 let (ast, diags) = parse_str("a instanceof B && c;");
8053 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8054 let outer = expr_of_single_stmt(&ast);
8055 let (op, lhs, rhs) = binary(outer);
8056 assert_eq!(op, crate::BinOp::And);
8057 assert!(matches!(
8058 ast.try_expr(lhs).unwrap().kind,
8059 crate::ExprKind::InstanceOf { .. }
8060 ));
8061 assert!(matches!(
8062 ast.try_expr(rhs).unwrap().kind,
8063 crate::ExprKind::Identifier(_)
8064 ));
8065 }
8066
8067 #[test]
8070 fn parse_angle_cast_lowers_to_the_as_node() {
8071 let (ast, diags) = parse_str("<number>x;");
8072 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8073 let outer = expr_of_single_stmt(&ast);
8074 let (inner, ty) = as_cast(outer);
8075 assert!(matches!(
8076 ast.try_expr(inner).unwrap().kind,
8077 crate::ExprKind::Identifier(_)
8078 ));
8079 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8080 }
8081
8082 #[test]
8086 fn a_less_than_comparison_is_not_read_as_a_cast() {
8087 let (ast, diags) = parse_str("a < b;");
8088 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8089 let e = expr_of_single_stmt(&ast);
8090 assert!(matches!(e.kind, crate::ExprKind::Binary { .. }));
8091 }
8092
8093 #[test]
8094 fn parse_typeof_in_type_position() {
8095 let (_ast, diags) = parse_str("let a: typeof b = 1;");
8096 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8097 }
8098
8099 #[test]
8100 fn parse_typeof_with_a_dotted_path() {
8101 let (_ast, diags) = parse_str("let a: typeof b.c.d = 1;");
8102 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8103 }
8104
8105 #[test]
8106 fn parse_as_primitive() {
8107 let (ast, diags) = parse_str("x as number;");
8108 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8109 let outer = expr_of_single_stmt(&ast);
8110 let (inner, ty) = as_cast(outer);
8111 assert!(matches!(
8112 ast.try_expr(inner).unwrap().kind,
8113 crate::ExprKind::Identifier(_)
8114 ));
8115 match &ty.kind {
8116 crate::TypeAnnotationKind::Name { args, .. } => {
8117 assert!(args.is_empty());
8118 }
8119 other => panic!("expected Name target, got {other:?}"),
8120 }
8121 assert_eq!(outer.span, crate::Span::new(F, 0, 11).unwrap());
8122 }
8123
8124 #[test]
8125 fn parse_as_object_literal_type() {
8126 let (ast, diags) = parse_str("x as { a: number };");
8127 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8128 let outer = expr_of_single_stmt(&ast);
8129 let (_, ty) = as_cast(outer);
8130 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Object { .. }));
8131 }
8132
8133 #[test]
8134 fn parse_as_array_short() {
8135 let (ast, diags) = parse_str("x as string[];");
8136 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8137 let outer = expr_of_single_stmt(&ast);
8138 let (_, ty) = as_cast(outer);
8139 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
8140 }
8141
8142 #[test]
8143 fn parse_as_array_generic() {
8144 let (ast, diags) = parse_str("x as T[];");
8145 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8146 let outer = expr_of_single_stmt(&ast);
8147 let (_, ty) = as_cast(outer);
8148 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
8149 }
8150
8151 #[test]
8152 fn parse_as_generic_instantiation() {
8153 let (ast, diags) = parse_str("x as Foo<Bar>;");
8154 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8155 let outer = expr_of_single_stmt(&ast);
8156 let (_, ty) = as_cast(outer);
8157 match &ty.kind {
8158 crate::TypeAnnotationKind::Name { args, .. } => {
8159 assert_eq!(args.len(), 1, "Foo<Bar> has one type arg");
8160 }
8161 other => panic!("expected Name with type args, got {other:?}"),
8162 }
8163 }
8164
8165 #[test]
8166 fn parse_as_precedence_higher_than_additive() {
8167 let (ast, diags) = parse_str("x as number + 1;");
8168 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8169 let outer = expr_of_single_stmt(&ast);
8170 let (op, lhs, rhs) = binary(outer);
8171 assert_eq!(op, crate::BinOp::Add);
8172 assert!(matches!(
8173 ast.try_expr(lhs).unwrap().kind,
8174 crate::ExprKind::As { .. }
8175 ));
8176 assert!(matches!(
8177 ast.try_expr(rhs).unwrap().kind,
8178 crate::ExprKind::Number(_)
8179 ));
8180 }
8181
8182 #[test]
8183 fn parse_as_precedence_lower_than_unary() {
8184 let (ast, diags) = parse_str("!x as boolean;");
8185 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8186 let outer = expr_of_single_stmt(&ast);
8187 let (inner, _) = as_cast(outer);
8188 assert!(matches!(
8189 ast.try_expr(inner).unwrap().kind,
8190 crate::ExprKind::Unary {
8191 op: crate::UnOp::Not,
8192 ..
8193 }
8194 ));
8195 }
8196
8197 #[test]
8198 fn parse_as_left_associative_chain() {
8199 let (ast, diags) = parse_str("x as A as B;");
8200 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8201 let outer = expr_of_single_stmt(&ast);
8202 let (inner, _outer_ty) = as_cast(outer);
8203 let (innermost, _inner_ty) = as_cast(ast.try_expr(inner).unwrap());
8204 assert!(matches!(
8205 ast.try_expr(innermost).unwrap().kind,
8206 crate::ExprKind::Identifier(_)
8207 ));
8208 }
8209
8210 #[test]
8211 fn parse_as_inside_call_arg() {
8212 let (ast, diags) = parse_str("f(x as T);");
8213 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8214 let outer = expr_of_single_stmt(&ast);
8215 match &outer.kind {
8216 crate::ExprKind::Call { args, .. } => {
8217 assert_eq!(args.len(), 1);
8218 assert!(matches!(
8219 ast.try_expr(args[0]).unwrap().kind,
8220 crate::ExprKind::As { .. }
8221 ));
8222 }
8223 other => panic!("expected Call, got {other:?}"),
8224 }
8225 }
8226
8227 #[test]
8228 fn parse_as_after_field_access() {
8229 let (ast, diags) = parse_str("obj.field as T;");
8230 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8231 let outer = expr_of_single_stmt(&ast);
8232 let (inner, _) = as_cast(outer);
8233 assert!(matches!(
8234 ast.try_expr(inner).unwrap().kind,
8235 crate::ExprKind::FieldAccess { .. }
8236 ));
8237 }
8238
8239 #[test]
8240 fn parse_type_guard_return_annotation_on_function() {
8241 let src = "function isCircle(s: Shape): s is Circle { return true; }";
8242 let (ast, diags) = parse_str(src);
8243 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8244 let stmt = single_stmt(&ast);
8245 match stmt.kind {
8246 crate::StmtKind::Function {
8247 ref return_type,
8248 ref type_predicate,
8249 ..
8250 } => {
8251 assert!(
8252 return_type.is_none(),
8253 "predicate form leaves return_type empty",
8254 );
8255 let tp = type_predicate.as_ref().expect("predicate parsed");
8256 assert_eq!(tp.param.name, "s");
8257 assert!(matches!(
8258 tp.asserted.kind,
8259 crate::TypeAnnotationKind::Name { .. }
8260 ));
8261 assert_eq!(tp.span, crate::Span::new(F, 29, 40).unwrap());
8262 }
8263 _ => panic!("expected Function"),
8264 }
8265 }
8266
8267 #[test]
8268 fn parse_type_guard_return_annotation_on_arrow() {
8269 let src = "const f = (s: Shape): s is Circle => true;";
8270 let (ast, diags) = parse_str(src);
8271 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8272 let value = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
8273 crate::StmtKind::Const { value, .. } => *value,
8274 _ => panic!("expected const"),
8275 };
8276 match &ast.try_expr(value).unwrap().kind {
8277 crate::ExprKind::Arrow {
8278 return_type,
8279 type_predicate,
8280 ..
8281 } => {
8282 assert!(return_type.is_none());
8283 let tp = type_predicate.as_ref().expect("predicate parsed");
8284 assert_eq!(tp.param.name, "s");
8285 }
8286 other => panic!("expected Arrow, got {other:?}"),
8287 }
8288 }
8289
8290 #[test]
8291 fn parse_plain_return_type_unaffected_by_predicate_parser() {
8292 let src = "function f(s: Shape): boolean { return true; }";
8293 let (ast, diags) = parse_str(src);
8294 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8295 let stmt = single_stmt(&ast);
8296 match stmt.kind {
8297 crate::StmtKind::Function {
8298 ref return_type,
8299 ref type_predicate,
8300 ..
8301 } => {
8302 assert!(return_type.is_some(), "plain form has return_type");
8303 assert!(type_predicate.is_none(), "plain form has no type_predicate",);
8304 }
8305 _ => panic!("expected Function"),
8306 }
8307 }
8308
8309 #[test]
8310 fn parse_type_guard_rejects_non_return_position() {
8311 let (_, diags) = parse_str("let x: y is Foo = null;");
8312 assert!(
8313 !diags.is_empty(),
8314 "expected parse error for `is` outside return-type position",
8315 );
8316 }
8317
8318 #[test]
8319 fn parse_typeof_prefix() {
8320 let (ast, diags) = parse_str("typeof x;");
8321 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8322 let outer = expr_of_single_stmt(&ast);
8323 let crate::ExprKind::Typeof { operand } = outer.kind else {
8324 panic!("expected Typeof, got {:?}", outer.kind);
8325 };
8326 assert!(matches!(
8327 ast.try_expr(operand).unwrap().kind,
8328 crate::ExprKind::Identifier(_)
8329 ));
8330 assert_eq!(outer.span, crate::Span::new(F, 0, 8).unwrap());
8331 }
8332
8333 #[test]
8334 fn parse_typeof_in_equality() {
8335 let (ast, diags) = parse_str(r#"typeof x === "number";"#);
8336 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8337 let outer = expr_of_single_stmt(&ast);
8338 let (op, lhs, rhs) = binary(outer);
8339 assert_eq!(op, crate::BinOp::Eq);
8340 assert!(matches!(
8341 ast.try_expr(lhs).unwrap().kind,
8342 crate::ExprKind::Typeof { .. }
8343 ));
8344 assert!(matches!(
8345 ast.try_expr(rhs).unwrap().kind,
8346 crate::ExprKind::String(ref s) if s == "number"
8347 ));
8348 }
8349
8350 #[test]
8351 fn parse_let_without_type() {
8352 let (ast, diags) = parse_str("let x = 1;");
8353 assert!(diags.is_empty());
8354 let stmt = single_stmt(&ast);
8355 assert_eq!(stmt.span, crate::Span::new(F, 0, 10).unwrap());
8356 match stmt.kind {
8357 crate::StmtKind::Let {
8358 ref name,
8359 ref ty,
8360 value,
8361 ..
8362 } => {
8363 assert_eq!(name.name, "x");
8364 assert_eq!(name.span, crate::Span::new(F, 4, 5).unwrap());
8365 assert!(ty.is_none());
8366 assert_eq!(
8367 ast.try_expr(value).unwrap().kind,
8368 crate::ExprKind::Number(1.0)
8369 );
8370 }
8371 _ => panic!("expected Let"),
8372 }
8373 }
8374
8375 #[test]
8376 fn parse_const_with_type_and_string() {
8377 let (ast, diags) = parse_str(r#"const y: string = "hi";"#);
8378 assert!(diags.is_empty());
8379 let stmt = single_stmt(&ast);
8380 match stmt.kind {
8381 crate::StmtKind::Const {
8382 ref name,
8383 ref ty,
8384 value,
8385 ..
8386 } => {
8387 assert_eq!(name.name, "y");
8388 assert_eq!(name.span, crate::Span::new(F, 6, 7).unwrap());
8389 let ty = ty.as_ref().expect("expected type annotation");
8390 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8391 assert_eq!(ty.span, crate::Span::new(F, 9, 15).unwrap());
8392 assert_eq!(
8393 ast.try_expr(value).unwrap().kind,
8394 crate::ExprKind::String("hi".to_string())
8395 );
8396 }
8397 _ => panic!("expected Const"),
8398 }
8399 }
8400
8401 #[test]
8402 fn parse_let_with_type_and_binary_initializer() {
8403 let (ast, diags) = parse_str("let z: number = 1 + 2;");
8404 assert!(diags.is_empty());
8405 let stmt = single_stmt(&ast);
8406 match stmt.kind {
8407 crate::StmtKind::Let { ref ty, value, .. } => {
8408 assert!(ty.is_some());
8409 let (op, ..) = binary(ast.try_expr(value).unwrap());
8410 assert_eq!(op, crate::BinOp::Add);
8411 }
8412 _ => panic!("expected Let"),
8413 }
8414 }
8415
8416 #[test]
8417 fn void_type_annotation_is_accepted_syntactically() {
8418 let (ast, diags) = parse_str("let v: void = null;");
8419 assert!(diags.is_empty());
8420 let stmt = single_stmt(&ast);
8421 match stmt.kind {
8422 crate::StmtKind::Let { ref ty, .. } => {
8423 let ty = ty.as_ref().unwrap();
8424 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8425 assert_eq!(ty.span, crate::Span::new(F, 7, 11).unwrap());
8426 }
8427 _ => panic!("expected Let"),
8428 }
8429 }
8430
8431 #[test]
8432 fn const_missing_initializer_diagnoses() {
8433 let (ast, diags) = parse_str("const x;");
8434 assert_eq!(diags.len(), 1);
8435 assert_eq!(
8436 diags[0].message,
8437 "`const` declaration requires an initializer"
8438 );
8439 assert!(ast.top_level.is_empty());
8440 }
8441
8442 #[test]
8443 fn parse_const_object_shorthand_pattern() {
8444 let (ast, diags) = parse_str("const { a, b } = obj;");
8445 assert!(diags.is_empty(), "unexpected: {diags:?}");
8446 let stmt = single_stmt(&ast);
8447 match stmt.kind {
8448 crate::StmtKind::ConstPattern {
8449 binding:
8450 crate::Binding::Object {
8451 ref fields,
8452 ref rest,
8453 ..
8454 },
8455 ..
8456 } => {
8457 assert_eq!(fields.len(), 2);
8458 assert_eq!(fields[0].source.name, "a");
8459 assert_eq!(fields[0].local.name, "a");
8460 assert_eq!(fields[1].source.name, "b");
8461 assert_eq!(fields[1].local.name, "b");
8462 assert!(rest.is_none());
8463 }
8464 _ => panic!("expected ConstPattern Object, got {:?}", stmt.kind),
8465 }
8466 }
8467
8468 #[test]
8469 fn parse_const_object_pattern_keyword_source_key() {
8470 let (ast, diags) = parse_str("const { type: statusType, default: fallback } = obj;");
8471 assert!(diags.is_empty(), "unexpected: {diags:?}");
8472 let stmt = single_stmt(&ast);
8473 let crate::StmtKind::ConstPattern {
8474 binding: crate::Binding::Object { ref fields, .. },
8475 ..
8476 } = stmt.kind
8477 else {
8478 panic!("expected object const pattern");
8479 };
8480
8481 let names: Vec<(&str, &str)> = fields
8482 .iter()
8483 .map(|f| (f.source.name.as_str(), f.local.name.as_str()))
8484 .collect();
8485 assert_eq!(names, vec![("type", "statusType"), ("default", "fallback")]);
8486 }
8487
8488 #[test]
8489 fn parse_const_object_pattern_keyword_shorthand_rejected() {
8490 let (_ast, diags) = parse_str("const { default } = obj;");
8491 assert!(
8492 diags
8493 .iter()
8494 .any(|d| d.message == "expected `:` after keyword field name in object pattern"),
8495 "expected keyword-shorthand diagnostic, got: {diags:?}"
8496 );
8497 }
8498
8499 #[test]
8500 fn parse_const_object_renamed_pattern() {
8501 let (ast, diags) = parse_str("const { a: x, b: y } = obj;");
8502 assert!(diags.is_empty(), "unexpected: {diags:?}");
8503 match single_stmt(&ast).kind {
8504 crate::StmtKind::ConstPattern {
8505 binding: crate::Binding::Object { ref fields, .. },
8506 ..
8507 } => {
8508 assert_eq!(fields[0].source.name, "a");
8509 assert_eq!(fields[0].local.name, "x");
8510 assert_eq!(fields[1].source.name, "b");
8511 assert_eq!(fields[1].local.name, "y");
8512 }
8513 ref other => panic!("expected ConstPattern Object, got {other:?}"),
8514 }
8515 }
8516
8517 #[test]
8518 fn parse_const_object_with_rest() {
8519 let (ast, diags) = parse_str("const { a, ...rest } = obj;");
8520 assert!(diags.is_empty(), "unexpected: {diags:?}");
8521 match single_stmt(&ast).kind {
8522 crate::StmtKind::ConstPattern {
8523 binding:
8524 crate::Binding::Object {
8525 ref fields,
8526 ref rest,
8527 ..
8528 },
8529 ..
8530 } => {
8531 assert_eq!(fields.len(), 1);
8532 assert_eq!(fields[0].source.name, "a");
8533 let r = rest.as_ref().expect("expected rest");
8534 assert_eq!(r.name, "rest");
8535 }
8536 ref other => panic!("expected ConstPattern Object, got {other:?}"),
8537 }
8538 }
8539
8540 #[test]
8541 fn parse_const_array_pattern() {
8542 let (ast, diags) = parse_str("const [x, y] = arr;");
8543 assert!(diags.is_empty(), "unexpected: {diags:?}");
8544 match single_stmt(&ast).kind {
8545 crate::StmtKind::ConstPattern {
8546 binding:
8547 crate::Binding::Array {
8548 ref elems,
8549 ref rest,
8550 ..
8551 },
8552 ..
8553 } => {
8554 assert_eq!(elems.len(), 2);
8555 assert_eq!(elems[0].as_ref().unwrap().name, "x");
8556 assert_eq!(elems[1].as_ref().unwrap().name, "y");
8557 assert!(rest.is_none());
8558 }
8559 ref other => panic!("expected ConstPattern Array, got {other:?}"),
8560 }
8561 }
8562
8563 #[test]
8564 fn parse_const_array_with_hole_and_rest() {
8565 let (ast, diags) = parse_str("const [, x, ...rest] = arr;");
8566 assert!(diags.is_empty(), "unexpected: {diags:?}");
8567 match single_stmt(&ast).kind {
8568 crate::StmtKind::ConstPattern {
8569 binding:
8570 crate::Binding::Array {
8571 ref elems,
8572 ref rest,
8573 ..
8574 },
8575 ..
8576 } => {
8577 assert_eq!(elems.len(), 2);
8578 assert!(elems[0].is_none(), "first slot should be a hole");
8579 assert_eq!(elems[1].as_ref().unwrap().name, "x");
8580 assert_eq!(rest.as_ref().unwrap().name, "rest");
8581 }
8582 ref other => panic!("expected ConstPattern Array, got {other:?}"),
8583 }
8584 }
8585
8586 #[test]
8587 fn parse_let_object_pattern() {
8588 let (ast, diags) = parse_str("let { a, b } = obj;");
8589 assert!(diags.is_empty(), "unexpected: {diags:?}");
8590 assert!(matches!(
8591 single_stmt(&ast).kind,
8592 crate::StmtKind::LetPattern {
8593 binding: crate::Binding::Object { .. },
8594 ..
8595 }
8596 ));
8597 }
8598
8599 #[test]
8600 fn parse_function_param_object_pattern() {
8601 let src = "function f({ a, b }: T): void { return; }";
8602 let (ast, diags) = parse_str(src);
8603 assert!(diags.is_empty(), "unexpected: {diags:?}");
8604 match single_stmt(&ast).kind {
8605 crate::StmtKind::Function { ref params, .. } => {
8606 assert_eq!(params.len(), 1);
8607 let p = ¶ms[0];
8608 assert!(p.pattern.is_some(), "expected pattern param");
8609 assert_eq!(p.name.name, "", "pattern placeholder uses empty name");
8610 match p.pattern.as_ref().unwrap() {
8611 crate::Binding::Object { fields, .. } => {
8612 assert_eq!(fields.len(), 2);
8613 assert_eq!(fields[0].source.name, "a");
8614 }
8615 other => panic!("expected Object binding, got {other:?}"),
8616 }
8617 }
8618 ref other => panic!("expected Function, got {other:?}"),
8619 }
8620 }
8621
8622 #[test]
8623 fn parse_arrow_param_array_pattern() {
8624 let src = "const g = ([x, y]: number[]): number => x + y;";
8625 let (_ast, diags) = parse_str(src);
8626 assert!(diags.is_empty(), "unexpected: {diags:?}");
8627 }
8628
8629 #[test]
8630 fn parse_destructure_nested_rejected() {
8631 let (_ast, diags) = parse_str("const { a: { b } } = obj;");
8632 assert!(
8633 diags
8634 .iter()
8635 .any(|d| d.message.contains("nested destructuring")),
8636 "expected nested-destructuring diagnostic, got: {diags:?}",
8637 );
8638 }
8639
8640 #[test]
8641 fn parse_destructure_array_nested_rejected() {
8642 let (_ast, diags) = parse_str("const [[a]] = arr;");
8643 assert!(
8644 diags
8645 .iter()
8646 .any(|d| d.message.contains("nested destructuring")),
8647 "expected nested-destructuring diagnostic, got: {diags:?}",
8648 );
8649 }
8650
8651 #[test]
8652 fn parse_destructure_pattern_defaults() {
8653 let (ast, diags) = parse_str("const { a = 1, b: c = 2 } = obj;");
8654 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8655 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
8656 panic!("expected pattern")
8657 };
8658 let crate::Binding::Object { fields, .. } = binding else {
8659 panic!("expected object")
8660 };
8661 assert_eq!(fields.len(), 2);
8662 assert!(fields.iter().all(|field| field.default.is_some()));
8663 assert_eq!(fields[1].local.name, "c");
8664 }
8665
8666 #[test]
8667 fn parse_destructure_param_default() {
8668 let (ast, diags) = parse_str("function f({ a = 1 }: T = obj): void { return; }");
8669 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8670 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
8671 panic!("expected function")
8672 };
8673 assert!(params[0].default.is_some());
8674 assert!(params[0].pattern.is_some());
8675 }
8676
8677 #[test]
8678 fn parse_destructure_rest_not_last_rejected() {
8679 let (_ast, diags) = parse_str("const { ...rest, a } = obj;");
8680 assert!(
8681 diags
8682 .iter()
8683 .any(|d| d.message.contains("rest element must be the last element")),
8684 "expected rest-not-last diagnostic, got: {diags:?}",
8685 );
8686 }
8687
8688 #[test]
8689 fn parse_destructure_empty_object_rejected() {
8690 let (_ast, diags) = parse_str("const { } = obj;");
8691 assert!(
8692 diags
8693 .iter()
8694 .any(|d| d.message.contains("empty object destructuring pattern")),
8695 "expected empty-pattern diagnostic, got: {diags:?}",
8696 );
8697 }
8698
8699 #[test]
8700 fn parse_ternary_simple() {
8701 let (ast, diags) = parse_str("const x: number = a ? 1 : 2;");
8702 assert!(diags.is_empty(), "unexpected: {diags:?}");
8703 let stmt = single_stmt(&ast);
8704 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8705 panic!("expected Const, got {:?}", stmt.kind);
8706 };
8707 assert!(matches!(
8708 ast.try_expr(value).unwrap().kind,
8709 crate::ExprKind::Ternary { .. }
8710 ));
8711 }
8712
8713 #[test]
8714 fn parse_ternary_nested_right_assoc() {
8715 let (ast, diags) = parse_str("const x: number = a ? 1 : b ? 2 : 3;");
8716 assert!(diags.is_empty(), "unexpected: {diags:?}");
8717 let stmt = single_stmt(&ast);
8718 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8719 panic!("expected Const");
8720 };
8721 let crate::ExprKind::Ternary { else_, .. } = ast.try_expr(value).unwrap().kind else {
8722 panic!("expected outer Ternary");
8723 };
8724 assert!(matches!(
8725 ast.try_expr(else_).unwrap().kind,
8726 crate::ExprKind::Ternary { .. }
8727 ));
8728 }
8729
8730 #[test]
8731 fn parse_ternary_missing_colon_diagnoses() {
8732 let (_, diags) = parse_str("const x: number = a ? 1;");
8733 assert!(
8734 diags
8735 .iter()
8736 .any(|d| d.message.contains("expected `:` to complete ternary")),
8737 "expected colon diagnostic, got: {diags:?}",
8738 );
8739 }
8740
8741 #[test]
8742 fn parse_nullish_coalesce_binop() {
8743 let (ast, diags) = parse_str("const x: number = a ?? 1;");
8744 assert!(diags.is_empty(), "unexpected: {diags:?}");
8745 let stmt = single_stmt(&ast);
8746 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8747 panic!("expected Const");
8748 };
8749 match ast.try_expr(value).unwrap().kind {
8750 crate::ExprKind::Binary { op, .. } => {
8751 assert_eq!(op, crate::BinOp::NullishCoalesce);
8752 }
8753 ref other => panic!("expected Binary NullishCoalesce, got {other:?}"),
8754 }
8755 }
8756
8757 #[test]
8758 fn parse_nullish_left_associative() {
8759 let (ast, diags) = parse_str("const x: number = a ?? b ?? c;");
8760 assert!(diags.is_empty(), "unexpected: {diags:?}");
8761 let stmt = single_stmt(&ast);
8762 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8763 panic!("expected Const");
8764 };
8765 let crate::ExprKind::Binary { lhs, .. } = ast.try_expr(value).unwrap().kind else {
8766 panic!("expected outer Binary");
8767 };
8768 match ast.try_expr(lhs).unwrap().kind {
8769 crate::ExprKind::Binary { op, .. } => {
8770 assert_eq!(op, crate::BinOp::NullishCoalesce);
8771 }
8772 ref other => panic!("expected inner Binary, got {other:?}"),
8773 }
8774 }
8775
8776 #[test]
8777 fn parse_pow_is_right_associative() {
8778 let (ast, diags) = parse_str("const x: number = 2 ** 3 ** 2;");
8779 assert!(diags.is_empty(), "unexpected: {diags:?}");
8780 let stmt = single_stmt(&ast);
8781 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8782 panic!("expected Const");
8783 };
8784 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8785 panic!("expected outer Binary");
8786 };
8787 assert_eq!(op, crate::BinOp::Pow);
8788 match ast.try_expr(rhs).unwrap().kind {
8789 crate::ExprKind::Binary { op: inner_op, .. } => {
8790 assert_eq!(inner_op, crate::BinOp::Pow);
8791 }
8792 ref other => panic!("expected inner Pow Binary on RHS, got {other:?}"),
8793 }
8794 }
8795
8796 #[test]
8797 fn parse_pow_higher_than_multiply() {
8798 let (ast, diags) = parse_str("const x: number = 2 * 3 ** 2;");
8799 assert!(diags.is_empty(), "unexpected: {diags:?}");
8800 let stmt = single_stmt(&ast);
8801 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8802 panic!("expected Const");
8803 };
8804 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8805 panic!("expected outer Binary");
8806 };
8807 assert_eq!(op, crate::BinOp::Mul);
8808 match ast.try_expr(rhs).unwrap().kind {
8809 crate::ExprKind::Binary { op: inner_op, .. } => {
8810 assert_eq!(inner_op, crate::BinOp::Pow);
8811 }
8812 ref other => panic!("expected Pow on the RHS of *, got {other:?}"),
8813 }
8814 }
8815
8816 #[test]
8817 fn parse_compound_assign_ident() {
8818 let (ast, diags) = parse_str("function main(): void { let x: number = 0; x += 5; }");
8819 assert!(diags.is_empty(), "unexpected: {diags:?}");
8820 let func = ast.top_level.iter().find_map(|sid| {
8821 if let crate::StmtKind::Function { body, .. } = &ast.try_stmt(*sid).unwrap().kind {
8822 Some(*body)
8823 } else {
8824 None
8825 }
8826 });
8827 let crate::StmtKind::Block(stmts) = &ast.try_stmt(func.expect("main")).unwrap().kind else {
8828 panic!("expected function body to be a block");
8829 };
8830 let target = stmts
8831 .iter()
8832 .find_map(|sid| match &ast.try_stmt(*sid).unwrap().kind {
8833 crate::StmtKind::CompoundAssign { target, op, .. } => {
8834 Some((target.name.clone(), *op))
8835 }
8836 _ => None,
8837 });
8838 let (name, op) = target.expect("expected a CompoundAssign in the body");
8839 assert_eq!(name, "x");
8840 assert_eq!(op, crate::BinOp::Add);
8841 }
8842
8843 #[test]
8844 fn parse_nullish_mixed_with_or_rejected() {
8845 let (_, diags) = parse_str("const x: number = a || b ?? c;");
8846 assert!(
8847 diags
8848 .iter()
8849 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8850 "expected mixing diagnostic, got: {diags:?}",
8851 );
8852 }
8853
8854 #[test]
8855 fn parse_nullish_mixed_with_and_rejected() {
8856 let (_, diags) = parse_str("const x: number = a && b ?? c;");
8857 assert!(
8858 diags
8859 .iter()
8860 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8861 "expected mixing diagnostic, got: {diags:?}",
8862 );
8863 }
8864
8865 #[test]
8866 fn parse_nullish_followed_by_logical_rejected() {
8867 for (source, help) in [
8868 (
8869 "const x: number = a ?? b || c;",
8870 "`(a ?? b) || c` or `a ?? (b || c)`",
8871 ),
8872 (
8873 "const x: number = a ?? b && c;",
8874 "`(a ?? b) && c` or `a ?? (b && c)`",
8875 ),
8876 (
8877 "const x: number = a ?? b ?? c || d;",
8878 "`(a ?? b) || c` or `a ?? (b || c)`",
8879 ),
8880 (
8882 "const x: number = a || b ?? c || d;",
8883 "`(a || b) ?? c` or `a || (b ?? c)`",
8884 ),
8885 ] {
8886 let (_, diags) = parse_str(source);
8887 assert_eq!(diags.len(), 1, "diags for {source:?}: {diags:?}");
8888 assert!(
8889 diags[0].message.contains("mixing `??` with `||` / `&&`"),
8890 "message for {source:?}: {diags:?}",
8891 );
8892 assert!(
8893 diags[0].help.iter().any(|h| h.contains(help)),
8894 "help for {source:?}: {diags:?}",
8895 );
8896 }
8897 }
8898
8899 #[test]
8900 fn parse_nullish_operand_keeps_tighter_operators() {
8901 let (ast, diags) = parse_str("const x: boolean = a ?? b == c;");
8903 assert!(diags.is_empty(), "unexpected: {diags:?}");
8904 let stmt = single_stmt(&ast);
8905 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8906 panic!("expected Const");
8907 };
8908 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8909 panic!("expected outer Binary");
8910 };
8911 assert_eq!(op, crate::BinOp::NullishCoalesce);
8912 assert!(matches!(
8913 ast.try_expr(rhs).unwrap().kind,
8914 crate::ExprKind::Binary {
8915 op: crate::BinOp::Eq,
8916 ..
8917 }
8918 ));
8919 }
8920
8921 #[test]
8922 fn parse_nullish_with_parens_ok() {
8923 let (_, diags) = parse_str("const x: number = (a || b) ?? c;");
8924 assert!(diags.is_empty(), "unexpected: {diags:?}");
8925 }
8926
8927 #[test]
8928 fn parse_optional_field_chain() {
8929 let (ast, diags) = parse_str("const x: number = a?.b;");
8930 assert!(diags.is_empty(), "unexpected: {diags:?}");
8931 let stmt = single_stmt(&ast);
8932 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8933 panic!("expected Const");
8934 };
8935 match ast.try_expr(value).unwrap().kind {
8936 crate::ExprKind::OptionalChain { ref parts, .. } => {
8937 assert_eq!(parts.len(), 1);
8938 match &parts[0] {
8939 crate::ChainPart::Field { name, optional, .. } => {
8940 assert_eq!(name.name, "b");
8941 assert!(*optional);
8942 }
8943 other => panic!("expected Field, got {other:?}"),
8944 }
8945 }
8946 ref other => panic!("expected OptionalChain, got {other:?}"),
8947 }
8948 }
8949
8950 #[test]
8951 fn parse_optional_chain_continues_after_first_dot() {
8952 let (ast, diags) = parse_str("const x: number = a?.b.c;");
8953 assert!(diags.is_empty(), "unexpected: {diags:?}");
8954 let stmt = single_stmt(&ast);
8955 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8956 panic!("expected Const");
8957 };
8958 match ast.try_expr(value).unwrap().kind {
8959 crate::ExprKind::OptionalChain { ref parts, .. } => {
8960 assert_eq!(parts.len(), 2);
8961 if let crate::ChainPart::Field { optional, .. } = &parts[0] {
8962 assert!(*optional);
8963 }
8964 if let crate::ChainPart::Field { optional, .. } = &parts[1] {
8965 assert!(!*optional);
8966 }
8967 }
8968 ref other => panic!("expected OptionalChain, got {other:?}"),
8969 }
8970 }
8971
8972 #[test]
8973 fn parse_optional_chain_multiple_short_circuits() {
8974 let (ast, diags) = parse_str("const x: number = a?.b?.c;");
8975 assert!(diags.is_empty(), "unexpected: {diags:?}");
8976 let stmt = single_stmt(&ast);
8977 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8978 panic!("expected Const");
8979 };
8980 let crate::ExprKind::OptionalChain { parts, .. } = &ast.try_expr(value).unwrap().kind
8981 else {
8982 panic!("expected OptionalChain");
8983 };
8984 assert_eq!(parts.len(), 2);
8985 for p in parts {
8986 if let crate::ChainPart::Field { optional, .. } = p {
8987 assert!(*optional, "both parts should be optional");
8988 }
8989 }
8990 }
8991
8992 #[test]
8993 fn parse_optional_call_and_index() {
8994 let (_, diags) = parse_str("const x: number = f?.();");
8995 assert!(diags.is_empty(), "unexpected: {diags:?}");
8996 let (_, diags) = parse_str("const y: number = arr?.[0];");
8997 assert!(diags.is_empty(), "unexpected: {diags:?}");
8998 }
8999
9000 #[test]
9001 fn parse_optional_chain_terminates_at_questionmark_for_ternary() {
9002 let (ast, diags) = parse_str("const x: number = a?.b ? c : d;");
9003 assert!(diags.is_empty(), "unexpected: {diags:?}");
9004 let stmt = single_stmt(&ast);
9005 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9006 panic!("expected Const");
9007 };
9008 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
9009 panic!(
9010 "expected top-level Ternary, got {:?}",
9011 ast.try_expr(value).unwrap().kind
9012 );
9013 };
9014 assert!(matches!(
9015 ast.try_expr(cond).unwrap().kind,
9016 crate::ExprKind::OptionalChain { .. }
9017 ));
9018 }
9019
9020 #[test]
9021 fn parse_nullish_below_ternary() {
9022 let (ast, diags) = parse_str("const x: number = a ?? b ? c : d;");
9023 assert!(diags.is_empty(), "unexpected: {diags:?}");
9024 let stmt = single_stmt(&ast);
9025 let crate::StmtKind::Const { value, .. } = stmt.kind else {
9026 panic!("expected Const");
9027 };
9028 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
9029 panic!("expected top-level Ternary");
9030 };
9031 match ast.try_expr(cond).unwrap().kind {
9032 crate::ExprKind::Binary { op, .. } => {
9033 assert_eq!(op, crate::BinOp::NullishCoalesce);
9034 }
9035 ref other => panic!("expected Binary ??, got {other:?}"),
9036 }
9037 }
9038
9039 #[test]
9040 fn let_missing_initializer_diagnoses() {
9041 let (ast, diags) = parse_str("let x;");
9042 assert_eq!(diags.len(), 1);
9043 assert_eq!(
9044 diags[0].message,
9045 "`let` declaration requires an initializer"
9046 );
9047 assert!(ast.top_level.is_empty());
9048 }
9049
9050 #[test]
9051 fn missing_identifier_after_let_diagnoses() {
9052 let (ast, diags) = parse_str("let = 1;");
9053 assert_eq!(diags.len(), 1);
9054 assert_eq!(diags[0].message, "expected identifier after `let`/`const`");
9055 assert!(ast.top_level.is_empty());
9056 }
9057
9058 #[test]
9059 fn missing_type_after_colon_diagnoses() {
9060 let (ast, diags) = parse_str("let x: = 1;");
9061 assert_eq!(diags.len(), 1);
9062 assert_eq!(diags[0].message, "expected type");
9063 assert!(ast.top_level.is_empty());
9064 }
9065
9066 #[test]
9067 fn missing_semicolon_after_declaration_diagnoses() {
9068 let (ast, diags) = parse_str("let x = 1 y;");
9069 assert_eq!(diags.len(), 1);
9070 assert_eq!(diags[0].message, "expected `;` after declaration");
9071 assert!(ast.top_level.is_empty());
9072 }
9073
9074 #[test]
9075 fn expression_statements_still_parse() {
9076 let (ast, diags) = parse_str("42;");
9077 assert!(diags.is_empty());
9078 assert_eq!(ast.top_level.len(), 1);
9079 }
9080
9081 #[test]
9082 fn snapshot_mixed_declarations() {
9083 let (ast, diags) =
9084 parse_str(r#"let x = 1; const y: string = "hi"; let z: number = 1 + 2;"#);
9085 assert!(diags.is_empty());
9086 assert_eq!(ast.top_level.len(), 3);
9087 insta::assert_debug_snapshot!(ast);
9088 }
9089
9090 #[test]
9091 fn parse_function_typical() {
9092 let (ast, diags) = parse_str("function f(a: number, b: string): boolean { }");
9093 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9094 let stmt = single_stmt(&ast);
9095 match stmt.kind {
9096 crate::StmtKind::Function {
9097 ref name,
9098 ref generics,
9099 ref params,
9100 ref return_type,
9101 body,
9102 ..
9103 } => {
9104 assert_eq!(name.name, "f");
9105 assert_eq!(name.span, crate::Span::new(F, 9, 10).unwrap());
9106 assert!(generics.is_empty());
9107 assert_eq!(params.len(), 2);
9108 assert_eq!(params[0].name.name, "a");
9109 assert_eq!(params[0].name.span, crate::Span::new(F, 11, 12).unwrap());
9110 let p0_ty = params[0]
9111 .ty
9112 .as_ref()
9113 .expect("function-decl param requires annotation");
9114 assert!(matches!(p0_ty.kind, crate::TypeAnnotationKind::Name { .. }));
9115 assert_eq!(p0_ty.span, crate::Span::new(F, 14, 20).unwrap()); assert_eq!(params[1].name.name, "b");
9117 assert_eq!(params[1].name.span, crate::Span::new(F, 22, 23).unwrap());
9118 let p1_ty = params[1]
9119 .ty
9120 .as_ref()
9121 .expect("function-decl param requires annotation");
9122 assert_eq!(p1_ty.span, crate::Span::new(F, 25, 31).unwrap()); let return_type = return_type.as_ref().expect("plain return type");
9124 assert!(matches!(
9125 return_type.kind,
9126 crate::TypeAnnotationKind::Name { .. }
9127 ));
9128 assert_eq!(return_type.span, crate::Span::new(F, 34, 41).unwrap()); let block = ast.try_stmt(body).unwrap();
9130 assert!(matches!(block.kind, crate::StmtKind::Block(ref v) if v.is_empty()));
9131 }
9132 _ => panic!("expected Function"),
9133 }
9134 }
9135
9136 #[test]
9137 fn parse_function_with_doc_comment() {
9138 let (ast, diags) = parse_str(
9139 "/**\n * Sum two numbers.\n * @param a First.\n * @param b Second.\n */\nfunction add(a: number, b: number): number { return a + b; }",
9140 );
9141 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9142 let stmt = single_stmt(&ast);
9143 match &stmt.kind {
9144 crate::StmtKind::Function { doc, .. } => {
9145 let d = doc.as_ref().expect("doc attached");
9146 assert_eq!(d.summary, "Sum two numbers.");
9147 assert_eq!(d.params.len(), 2);
9148 assert_eq!(d.params[0].name, "a");
9149 assert_eq!(d.params[1].name, "b");
9150 }
9151 _ => panic!("expected Function"),
9152 }
9153 }
9154
9155 #[test]
9156 fn parse_interface_member_docs() {
9157 let (ast, diags) = parse_str(
9158 "interface Foo {\n /** First method. */ first(): void;\n /** A field. */ field: number;\n}",
9159 );
9160 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9161 let stmt = single_stmt(&ast);
9162 match &stmt.kind {
9163 crate::StmtKind::InterfaceDecl { members, .. } => {
9164 assert_eq!(members.len(), 2);
9165 match &members[0] {
9166 crate::InterfaceMember::Method { doc, .. } => {
9167 assert_eq!(doc.as_ref().unwrap().summary, "First method.");
9168 }
9169 _ => panic!("expected Method first"),
9170 }
9171 match &members[1] {
9172 crate::InterfaceMember::Property { doc, .. } => {
9173 assert_eq!(doc.as_ref().unwrap().summary, "A field.");
9174 }
9175 _ => panic!("expected Property second"),
9176 }
9177 }
9178 _ => panic!("expected InterfaceDecl"),
9179 }
9180 }
9181
9182 #[test]
9183 fn parse_numeric_enum_all_implicit() {
9184 let (ast, diags) = parse_str("enum Direction { Up, Down, Left, Right }");
9185 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9186 let stmt = single_stmt(&ast);
9187 match &stmt.kind {
9188 crate::StmtKind::EnumDecl { name, members, doc } => {
9189 assert_eq!(name.name, "Direction");
9190 assert!(doc.is_none());
9191 assert_eq!(members.len(), 4);
9192 for (m, expected) in members.iter().zip(["Up", "Down", "Left", "Right"]) {
9193 assert_eq!(m.name.name, expected);
9194 assert!(m.value.is_none(), "expected no initializer on {expected}");
9195 }
9196 }
9197 _ => panic!("expected EnumDecl"),
9198 }
9199 }
9200
9201 #[test]
9202 fn parse_string_enum_all_explicit() {
9203 let (ast, diags) =
9204 parse_str("enum Status { Active = \"active\", Inactive = \"inactive\" }");
9205 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9206 let stmt = single_stmt(&ast);
9207 match &stmt.kind {
9208 crate::StmtKind::EnumDecl { name, members, .. } => {
9209 assert_eq!(name.name, "Status");
9210 assert_eq!(members.len(), 2);
9211 match &members[0].value {
9212 Some(crate::EnumInitializer::String { value, .. }) => {
9213 assert_eq!(value, "active");
9214 }
9215 other => panic!("expected String init, got {other:?}"),
9216 }
9217 match &members[1].value {
9218 Some(crate::EnumInitializer::String { value, .. }) => {
9219 assert_eq!(value, "inactive");
9220 }
9221 other => panic!("expected String init, got {other:?}"),
9222 }
9223 }
9224 _ => panic!("expected EnumDecl"),
9225 }
9226 }
9227
9228 #[test]
9229 fn parse_numeric_enum_with_negative_init() {
9230 let (ast, diags) = parse_str("enum D { Up = 1, Down = -1 }");
9231 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9232 let stmt = single_stmt(&ast);
9233 match &stmt.kind {
9234 crate::StmtKind::EnumDecl { members, .. } => {
9235 assert_eq!(members.len(), 2);
9236 match &members[0].value {
9237 Some(crate::EnumInitializer::Number { value, .. }) => {
9238 assert_eq!(*value, 1.0);
9239 }
9240 other => panic!("expected Number init, got {other:?}"),
9241 }
9242 match &members[1].value {
9243 Some(crate::EnumInitializer::Number { value, .. }) => {
9244 assert_eq!(*value, -1.0);
9245 }
9246 other => panic!("expected Number init, got {other:?}"),
9247 }
9248 }
9249 _ => panic!("expected EnumDecl"),
9250 }
9251 }
9252
9253 #[test]
9254 fn parse_enum_mixed_kinds_is_parse_error() {
9255 let (_ast, diags) = parse_str("enum Mix { A = 1, B = \"two\" }");
9256 assert!(
9257 diags
9258 .iter()
9259 .any(|d| d.message.contains("mixed numeric and string")),
9260 "expected mixed-kind diagnostic, got: {diags:?}"
9261 );
9262 }
9263
9264 #[test]
9265 fn parse_enum_member_docs() {
9266 let (ast, diags) = parse_str(
9267 "enum D {\n /** The first one. */ A = 1,\n /** The second one. */ B = 2,\n}",
9268 );
9269 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9270 let stmt = single_stmt(&ast);
9271 match &stmt.kind {
9272 crate::StmtKind::EnumDecl { members, .. } => {
9273 assert_eq!(members.len(), 2);
9274 assert_eq!(
9275 members[0].doc.as_ref().expect("first doc").summary,
9276 "The first one."
9277 );
9278 assert_eq!(
9279 members[1].doc.as_ref().expect("second doc").summary,
9280 "The second one."
9281 );
9282 }
9283 _ => panic!("expected EnumDecl"),
9284 }
9285 }
9286
9287 #[test]
9288 fn parse_enum_trailing_comma_ok() {
9289 let (ast, diags) = parse_str("enum D { A, B, }");
9290 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9291 let stmt = single_stmt(&ast);
9292 match &stmt.kind {
9293 crate::StmtKind::EnumDecl { members, .. } => assert_eq!(members.len(), 2),
9294 _ => panic!("expected EnumDecl"),
9295 }
9296 }
9297
9298 #[test]
9299 fn parse_function_empty_params() {
9300 let (ast, diags) = parse_str("function noop(): void { }");
9301 assert!(diags.is_empty());
9302 let stmt = single_stmt(&ast);
9303 match stmt.kind {
9304 crate::StmtKind::Function { ref params, .. } => assert!(params.is_empty()),
9305 _ => panic!("expected Function"),
9306 }
9307 }
9308
9309 #[test]
9310 fn parse_generic_function_single_param() {
9311 let (ast, diags) = parse_str("function identity<T>(x: T): T { return x; }");
9312 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9313 let stmt = single_stmt(&ast);
9314 match stmt.kind {
9315 crate::StmtKind::Function {
9316 ref name,
9317 ref generics,
9318 ref params,
9319 ..
9320 } => {
9321 assert_eq!(name.name, "identity");
9322 assert_eq!(generics.len(), 1);
9323 assert_eq!(generics[0].name, "T");
9324 assert_eq!(params.len(), 1);
9325 assert_eq!(params[0].name.name, "x");
9326 }
9327 _ => panic!("expected Function"),
9328 }
9329 }
9330
9331 #[test]
9332 fn parse_generic_function_multiple_params() {
9333 let (ast, diags) = parse_str("function pair<T, U>(a: T, b: U): T { return a; }");
9334 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9335 let stmt = single_stmt(&ast);
9336 match stmt.kind {
9337 crate::StmtKind::Function { ref generics, .. } => {
9338 assert_eq!(generics.len(), 2);
9339 assert_eq!(generics[0].name, "T");
9340 assert_eq!(generics[1].name, "U");
9341 }
9342 _ => panic!("expected Function"),
9343 }
9344 }
9345
9346 #[test]
9347 fn parse_non_generic_function_has_empty_generics() {
9348 let (ast, diags) = parse_str("function noop(): void { }");
9349 assert!(diags.is_empty());
9350 let stmt = single_stmt(&ast);
9351 match stmt.kind {
9352 crate::StmtKind::Function { ref generics, .. } => assert!(generics.is_empty()),
9353 _ => panic!("expected Function"),
9354 }
9355 }
9356
9357 #[test]
9358 fn parse_empty_generic_list_diagnoses() {
9359 let (_ast, diags) = parse_str("function f<>(): void { }");
9360 assert!(
9361 diags
9362 .iter()
9363 .any(|d| d.message.contains("empty generic parameter list")),
9364 "expected empty-generic-list diagnostic, got: {diags:?}",
9365 );
9366 }
9367
9368 #[test]
9369 fn parse_trailing_comma_in_generic_list_ok() {
9370 let (ast, diags) = parse_str("function f<T,>(): void { }");
9371 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9372 match single_stmt(&ast).kind {
9373 crate::StmtKind::Function { ref generics, .. } => assert_eq!(generics.len(), 1),
9374 _ => panic!("expected Function"),
9375 }
9376 }
9377
9378 #[test]
9379 fn parse_generic_call_single_type_arg() {
9380 let (ast, diags) = parse_str("identity<number>(42);");
9381 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9382 let stmt = single_stmt(&ast);
9383 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9384 panic!("expected Expr stmt");
9385 };
9386 match &ast.try_expr(call_id).unwrap().kind {
9387 crate::ExprKind::Call {
9388 type_args, args, ..
9389 } => {
9390 let ta = type_args.as_ref().expect("type_args set for generic call");
9391 assert_eq!(ta.len(), 1);
9392 assert_eq!(args.len(), 1);
9393 }
9394 other => panic!("expected Call, got {other:?}"),
9395 }
9396 }
9397
9398 #[test]
9399 fn parse_generic_call_trailing_comma_ok() {
9400 let (ast, diags) = parse_str("identity<number,>(42);");
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().expect("type_args set").len(), 1);
9411 assert_eq!(args.len(), 1);
9412 }
9413 other => panic!("expected Call, got {other:?}"),
9414 }
9415 }
9416
9417 #[test]
9418 fn parse_generic_call_multi_type_args() {
9419 let (ast, diags) = parse_str("pair<number, string>(1, \"x\");");
9420 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9421 let stmt = single_stmt(&ast);
9422 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9423 panic!("expected Expr stmt");
9424 };
9425 match &ast.try_expr(call_id).unwrap().kind {
9426 crate::ExprKind::Call { type_args, .. } => {
9427 let ta = type_args.as_ref().expect("type_args set for generic call");
9428 assert_eq!(ta.len(), 2);
9429 }
9430 other => panic!("expected Call, got {other:?}"),
9431 }
9432 }
9433
9434 #[test]
9435 fn parse_comparison_chain_unaffected_by_generic_lookahead() {
9436 let (ast, diags) = parse_str("let z = a < b > c;");
9437 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9438 assert!(
9439 !ast_contains_call_with_type_args(&ast),
9440 "should not have parsed any generic call",
9441 );
9442 }
9443
9444 #[test]
9445 fn parse_lt_with_paren_rhs_is_comparison_not_generic() {
9446 let (ast, diags) = parse_str("let z = a < (b);");
9447 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9448 assert!(!ast_contains_call_with_type_args(&ast));
9449 }
9450
9451 #[test]
9452 fn parse_generic_call_no_args() {
9453 let (ast, diags) = parse_str("none<number>();");
9454 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9455 let stmt = single_stmt(&ast);
9456 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9457 panic!("expected Expr stmt");
9458 };
9459 match &ast.try_expr(call_id).unwrap().kind {
9460 crate::ExprKind::Call {
9461 type_args, args, ..
9462 } => {
9463 assert_eq!(type_args.as_ref().unwrap().len(), 1);
9464 assert!(args.is_empty());
9465 }
9466 other => panic!("expected Call, got {other:?}"),
9467 }
9468 }
9469
9470 fn ast_contains_call_with_type_args(ast: &crate::Ast) -> bool {
9471 for id in 0..ast.top_level.len() {
9472 let stmt = ast.try_stmt(ast.top_level[id]).unwrap();
9473 if walk_stmt_for_typed_call(ast, stmt) {
9474 return true;
9475 }
9476 }
9477 false
9478 }
9479
9480 fn walk_stmt_for_typed_call(ast: &crate::Ast, stmt: &crate::Stmt) -> bool {
9481 match &stmt.kind {
9482 crate::StmtKind::Let { value, .. } | crate::StmtKind::Const { value, .. } => {
9483 walk_expr_for_typed_call(ast, *value)
9484 }
9485 crate::StmtKind::Expr(e) => walk_expr_for_typed_call(ast, *e),
9486 crate::StmtKind::Block(stmts) => stmts
9487 .iter()
9488 .any(|sid| walk_stmt_for_typed_call(ast, ast.try_stmt(*sid).unwrap())),
9489 _ => false,
9490 }
9491 }
9492
9493 fn walk_expr_for_typed_call(ast: &crate::Ast, id: crate::ExprId) -> bool {
9494 match &ast.try_expr(id).unwrap().kind {
9495 crate::ExprKind::Call {
9496 type_args: Some(_), ..
9497 } => true,
9498 crate::ExprKind::Call { callee, args, .. } => {
9499 walk_expr_for_typed_call(ast, *callee)
9500 || args.iter().any(|a| walk_expr_for_typed_call(ast, *a))
9501 }
9502 crate::ExprKind::Binary { lhs, rhs, .. } => {
9503 walk_expr_for_typed_call(ast, *lhs) || walk_expr_for_typed_call(ast, *rhs)
9504 }
9505 crate::ExprKind::Unary { operand, .. } => walk_expr_for_typed_call(ast, *operand),
9506 crate::ExprKind::Paren(e) => walk_expr_for_typed_call(ast, *e),
9507 crate::ExprKind::FieldAccess { receiver, .. } => {
9508 walk_expr_for_typed_call(ast, *receiver)
9509 }
9510 crate::ExprKind::IndexAccess { receiver, index } => {
9511 walk_expr_for_typed_call(ast, *receiver) || walk_expr_for_typed_call(ast, *index)
9512 }
9513 _ => false,
9514 }
9515 }
9516
9517 #[test]
9518 fn parse_function_body_with_statements() {
9519 let (ast, diags) = parse_str("function g(x: number): number { let y = x; y; }");
9520 assert!(diags.is_empty());
9521 let stmt = single_stmt(&ast);
9522 match stmt.kind {
9523 crate::StmtKind::Function { body, .. } => {
9524 let block = ast.try_stmt(body).unwrap();
9525 match block.kind {
9526 crate::StmtKind::Block(ref v) => assert_eq!(v.len(), 2),
9527 _ => panic!("expected Block"),
9528 }
9529 }
9530 _ => panic!("expected Function"),
9531 }
9532 }
9533
9534 #[test]
9535 fn function_missing_return_type_diagnoses() {
9536 let (ast, diags) = parse_str("function f() { }");
9537 assert!(!diags.is_empty());
9538 assert_eq!(diags[0].message, "expected `:` and return type");
9539 assert!(ast.top_level.is_empty());
9540 }
9541
9542 #[test]
9543 fn method_missing_return_type_still_reads_the_body() {
9544 let (_ast, diags) = parse_str(
9545 "class Box {\n async run() {\n const x = await f();\n return x;\n }\n ok(): void {}\n}",
9546 );
9547 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9548 assert_eq!(
9549 messages,
9550 [
9551 "`async` functions are not supported",
9552 "expected `:` and return type",
9553 "`await` is not supported"
9554 ],
9555 "{diags:?}"
9556 );
9557 assert_eq!(
9558 diags[1].help,
9559 ["write the return type: `run(…): T { … }`, with `void` when it returns nothing"]
9560 );
9561 }
9562
9563 #[test]
9564 fn an_unclosed_method_body_leaves_the_next_declaration_alone() {
9565 let (ast, diags) =
9566 parse_str("class Box {\n a() { return 1;\n}\nfunction main(): number { return 0; }");
9567 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9568 assert_eq!(
9569 messages,
9570 [
9571 "expected `:` and return type",
9572 "expected `}` to close class"
9573 ],
9574 "{diags:?}"
9575 );
9576 assert_eq!(ast.top_level.len(), 1, "`main` is still declared");
9577 }
9578
9579 #[test]
9580 fn every_declaration_after_an_unclosed_method_body_is_kept() {
9581 for next in [
9582 "class B { ok(): number { return 2; } }",
9583 "interface I { a: number; }",
9584 "enum E { A }",
9585 "type X = number;",
9586 "import * as fs from \"submilli:fs\";",
9587 "async function g(): number { return 1; }",
9588 ] {
9589 let source = format!(
9590 "class A {{\n bad() {{\n return 1;\n}}\n{next}\nfunction main(): void {{}}"
9591 );
9592 let (ast, diags) = parse_str(&source);
9593 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9594 let expected_async = next.starts_with("async");
9595 let mut expected = vec![
9596 "expected `:` and return type",
9597 "expected `}` to close class",
9598 ];
9599 if expected_async {
9600 expected.push("`async` functions are not supported");
9601 }
9602 assert_eq!(messages, expected, "{next}: {diags:?}");
9603 assert_eq!(
9604 ast.top_level.len(),
9605 2,
9606 "{next} and `main` are kept: {diags:?}"
9607 );
9608 }
9609 }
9610
9611 #[test]
9612 fn a_failed_statement_leaves_the_next_declaration_to_be_parsed() {
9613 for (source, kept) in [
9614 ("let x = 1 +\nimport { exists } from \"submilli:fs\";", 1),
9615 ("const y =\nclass C {}\nfunction main(): void {}", 2),
9616 (
9617 "interface I {\n a: number;\nimport * as fs from \"submilli:fs\";",
9618 1,
9619 ),
9620 ] {
9621 let (ast, diags) = parse_str(source);
9622 assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9623 assert_eq!(ast.top_level.len(), kept, "{source}: {diags:?}");
9624 }
9625 }
9626
9627 #[test]
9628 fn a_dynamic_import_after_a_failed_statement_reports_once() {
9629 for source in [
9630 "const m = import(\"submilli:fs\");\nclass C {}",
9631 "const d = import.meta.dirname;\nclass C {}",
9632 ] {
9633 let (_ast, diags) = parse_str(source);
9634 assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9635 }
9636 }
9637
9638 #[test]
9639 fn members_named_like_declarations_after_a_bad_method_stay_members() {
9640 let (_ast, diags) = parse_str(
9641 "class A {\n bad() {}\n type: number = 1;\n function(): void {}\n declare(): void {}\n async(): void {}\n}",
9642 );
9643 assert_eq!(diags.len(), 1, "{diags:?}");
9644 }
9645
9646 #[test]
9647 fn every_member_after_a_bad_method_is_still_read() {
9648 let (_ast, diags) = parse_str(
9649 "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}",
9650 );
9651 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9652 assert_eq!(messages, ["expected `:` and return type"; 3], "{diags:?}");
9653 }
9654
9655 #[test]
9656 fn a_member_failing_after_a_type_literal_reports_once() {
9657 let (_ast, diags) = parse_str("class A {\n x: { a: number } 5\n}");
9658 assert_eq!(diags.len(), 1, "{diags:?}");
9659 }
9660
9661 #[test]
9662 fn multiple_declarators_become_one_statement_each() {
9663 let (ast, diags) = parse_str("let a = 0, b: string = \"s\", [c] = [1];");
9664 assert!(diags.is_empty(), "{diags:?}");
9665 let kinds: Vec<&crate::StmtKind> = ast
9666 .top_level
9667 .iter()
9668 .map(|id| &ast.try_stmt(*id).unwrap().kind)
9669 .collect();
9670 assert!(
9671 matches!(
9672 kinds.as_slice(),
9673 [
9674 crate::StmtKind::Let { name: a, ty: None, .. },
9675 crate::StmtKind::Let { name: b, ty: Some(_), .. },
9676 crate::StmtKind::LetPattern { .. },
9677 ] if a.name == "a" && b.name == "b"
9678 ),
9679 "{kinds:?}"
9680 );
9681 }
9682
9683 #[test]
9684 fn declarators_keep_their_own_spans() {
9685 let source = "const a = 1, b = 2;";
9686 let (ast, diags) = parse_str(source);
9687 assert!(diags.is_empty(), "{diags:?}");
9688 let spans: Vec<&str> = ast
9689 .top_level
9690 .iter()
9691 .map(|id| {
9692 let span = ast.try_stmt(*id).unwrap().span;
9693 &source[span.start as usize..span.end as usize]
9694 })
9695 .collect();
9696 assert_eq!(spans, ["const a = 1", "b = 2;"]);
9697 }
9698
9699 #[test]
9700 fn a_declarator_missing_its_initializer_is_reported_at_it() {
9701 let (_ast, diags) = parse_str("const a = 1, b;");
9702 assert_eq!(diags.len(), 1, "{diags:?}");
9703 assert_eq!(
9704 diags[0].message,
9705 "`const` declaration requires an initializer"
9706 );
9707 assert_eq!(diags[0].span.start, 14, "{diags:?}");
9708 let (_ast, diags) = parse_str("let a = 1, ;");
9709 assert_eq!(
9710 diags[0].message, "expected identifier after `let`/`const`",
9711 "{diags:?}"
9712 );
9713 }
9714
9715 #[test]
9716 fn a_typed_declarator_without_a_value_may_precede_another() {
9717 let (ast, diags) = parse_str("let a: number | undefined, b = 1;");
9718 assert!(diags.is_empty(), "{diags:?}");
9719 assert_eq!(ast.top_level.len(), 2);
9720 }
9721
9722 #[test]
9723 fn declarators_stay_in_their_block() {
9724 let (ast, diags) = parse_str("function f(): void { let a = 1, b = 2; }\nlet c = 3;");
9725 assert!(diags.is_empty(), "{diags:?}");
9726 assert_eq!(
9727 ast.top_level.len(),
9728 2,
9729 "the block's declarators don't leak out"
9730 );
9731 }
9732
9733 #[test]
9734 fn every_exported_declarator_is_exported() {
9735 let (ast, diags) = parse_str("export const a = 1, b = 2;");
9736 assert!(diags.is_empty(), "{diags:?}");
9737 assert_eq!(ast.top_level.len(), 2);
9738 let exported: Vec<crate::StmtId> =
9739 ast.exported_decls.iter().map(|decl| decl.stmt).collect();
9740 assert_eq!(exported, ast.top_level);
9741 }
9742
9743 #[test]
9744 fn a_for_head_holds_every_declarator() {
9745 let (ast, diags) = parse_str("for (let i = 0, j = 10; i < j; i++, j--) {}");
9746 assert!(diags.is_empty(), "{diags:?}");
9747 let crate::StmtKind::For { init, update, .. } = &single_stmt(&ast).kind else {
9748 panic!("expected For");
9749 };
9750 assert_eq!(init.len(), 2);
9751 let (ast2, diags) = parse_str("for (i = 0, j = 10; i < j; i++) {}");
9752 assert!(diags.is_empty(), "{diags:?}");
9753 let crate::StmtKind::For { init: assigns, .. } = &single_stmt(&ast2).kind else {
9754 panic!("expected For");
9755 };
9756 assert_eq!(assigns.len(), 2);
9757 let update = ast.try_stmt(update.unwrap()).unwrap();
9758 assert!(
9759 matches!(&update.kind, crate::StmtKind::Block(stmts) if stmts.len() == 2),
9760 "{update:?}"
9761 );
9762 }
9763
9764 #[test]
9765 fn missing_return_type_still_reads_the_body() {
9766 let (_ast, diags) = parse_str(
9767 "async function main() {\n const x = await f();\n if (x) { return 1; }\n return 2;\n}\nfunction g(): void {}",
9768 );
9769 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9770 assert_eq!(
9771 messages,
9772 [
9773 "`async` functions are not supported",
9774 "expected `:` and return type",
9775 "`await` is not supported"
9776 ],
9777 "{diags:?}"
9778 );
9779 assert_eq!(
9780 diags[1].help,
9781 [
9782 "write the return type: `function main(…): T { … }`, with `void` when it returns nothing"
9783 ]
9784 );
9785 }
9786
9787 #[test]
9788 fn function_missing_param_type_diagnoses() {
9789 let (ast, diags) = parse_str("function f(a): void { }");
9790 assert!(!diags.is_empty());
9791 assert_eq!(diags[0].message, "parameter requires a type annotation");
9792 assert!(ast.top_level.is_empty());
9793 }
9794
9795 #[test]
9796 fn function_missing_name_diagnoses() {
9797 let (ast, diags) = parse_str("function (x: number): void { }");
9798 assert!(!diags.is_empty());
9799 assert_eq!(diags[0].message, "expected function name");
9800 assert!(ast.top_level.is_empty());
9801 }
9802
9803 #[test]
9804 fn function_trailing_param_comma_ok() {
9805 let (ast, diags) = parse_str("function f(a: number,): void { }");
9806 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9807 match single_stmt(&ast).kind {
9808 crate::StmtKind::Function { ref params, .. } => assert_eq!(params.len(), 1),
9809 _ => panic!("expected Function"),
9810 }
9811 }
9812
9813 #[test]
9814 fn function_missing_open_brace_diagnoses() {
9815 let (ast, diags) = parse_str("function f(): void");
9816 assert_eq!(diags.len(), 1);
9817 assert_eq!(diags[0].message, "expected `{`");
9818 assert!(ast.top_level.is_empty());
9819 }
9820
9821 #[test]
9822 fn snapshot_function_declaration() {
9823 let (ast, diags) =
9824 parse_str("function add(a: number, b: number): number { let sum = a + b; sum; }");
9825 assert!(diags.is_empty());
9826 insta::assert_debug_snapshot!(ast);
9827 }
9828
9829 #[test]
9830 fn rest_param_basic_parses() {
9831 let (_, diags) = parse_str("function sum(...nums: number[]): number { return 0; }");
9832 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9833 }
9834
9835 #[test]
9836 fn rest_param_with_fixed_prefix_parses() {
9837 let (_, diags) =
9838 parse_str("function tag(label: string, ...vals: number[]): string { return label; }");
9839 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9840 }
9841
9842 #[test]
9843 fn rest_param_in_arrow_parses() {
9844 let (_, diags) = parse_str("const f = (...n: number[]) => n.length;");
9845 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9846 }
9847
9848 #[test]
9849 fn rest_param_in_function_type_annotation_parses() {
9850 let (_, diags) =
9851 parse_str("let f: (a: number, ...rest: string[]) => void = (a, ...r) => {};");
9852 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9853 }
9854
9855 #[test]
9856 fn rest_param_not_last_rejected() {
9857 let (_, diags) = parse_str("function f(...xs: number[], y: number): void {}");
9858 assert!(
9859 diags.iter().any(|d| d
9860 .message
9861 .contains("rest parameter must be the last parameter")),
9862 "expected 'must be last' diagnostic, got {diags:?}",
9863 );
9864 }
9865
9866 #[test]
9867 fn rest_param_with_default_rejected() {
9868 let (_, diags) = parse_str("function f(...xs: number[] = []): void {}");
9869 assert!(
9870 diags.iter().any(|d| d
9871 .message
9872 .contains("rest parameter cannot have a default value")),
9873 "expected default-rejection diagnostic, got {diags:?}",
9874 );
9875 }
9876
9877 #[test]
9878 fn rest_param_without_annotation_rejected() {
9879 let (_, diags) = parse_str("function f(...xs): void {}");
9880 assert!(
9881 diags.iter().any(|d| d
9882 .message
9883 .contains("rest parameter requires a type annotation")),
9884 "expected missing-annotation diagnostic, got {diags:?}",
9885 );
9886 }
9887
9888 #[test]
9889 fn rest_param_destructured_rejected() {
9890 let (_, diags) = parse_str("function f(...{a, b}): void {}");
9891 assert!(
9892 diags
9893 .iter()
9894 .any(|d| d.message.contains("rest parameter cannot be destructured")),
9895 "expected destructured-rejection diagnostic, got {diags:?}",
9896 );
9897 }
9898
9899 #[test]
9900 fn rest_param_in_function_type_with_non_array_rejected() {
9901 let (_, diags) = parse_str("let f: (...xs: number) => void = () => {};");
9902 assert!(
9903 diags
9904 .iter()
9905 .any(|d| d.message.contains("rest parameter type must be an array")),
9906 "expected non-array-type diagnostic, got {diags:?}",
9907 );
9908 }
9909
9910 fn call(e: &crate::Expr) -> (crate::ExprId, &[crate::ExprId]) {
9911 match e.kind {
9912 crate::ExprKind::Call {
9913 callee, ref args, ..
9914 } => (callee, args.as_slice()),
9915 _ => panic!("expected Call, got {:?}", e.kind),
9916 }
9917 }
9918
9919 #[test]
9920 fn parse_call_no_args() {
9921 let (ast, diags) = parse_str("f();");
9922 assert!(diags.is_empty());
9923 let outer = expr_of_single_stmt(&ast);
9924 let (callee, args) = call(outer);
9925 assert!(matches!(
9926 ast.try_expr(callee).unwrap().kind,
9927 crate::ExprKind::Identifier(_)
9928 ));
9929 assert!(args.is_empty());
9930 assert_eq!(outer.span, crate::Span::new(F, 0, 3).unwrap());
9931 }
9932
9933 #[test]
9934 fn parse_call_one_arg() {
9935 let (ast, diags) = parse_str("f(1);");
9936 assert!(diags.is_empty());
9937 let outer = expr_of_single_stmt(&ast);
9938 let (callee, args) = call(outer);
9939 assert!(matches!(
9940 ast.try_expr(callee).unwrap().kind,
9941 crate::ExprKind::Identifier(_)
9942 ));
9943 assert_eq!(args.len(), 1);
9944 assert_eq!(
9945 ast.try_expr(args[0]).unwrap().kind,
9946 crate::ExprKind::Number(1.0)
9947 );
9948 }
9949
9950 #[test]
9951 fn parse_call_three_args() {
9952 let (ast, diags) = parse_str("f(1, 2, 3);");
9953 assert!(diags.is_empty());
9954 let outer = expr_of_single_stmt(&ast);
9955 let (_callee, args) = call(outer);
9956 assert_eq!(args.len(), 3);
9957 assert_eq!(
9958 ast.try_expr(args[0]).unwrap().kind,
9959 crate::ExprKind::Number(1.0)
9960 );
9961 assert_eq!(
9962 ast.try_expr(args[1]).unwrap().kind,
9963 crate::ExprKind::Number(2.0)
9964 );
9965 assert_eq!(
9966 ast.try_expr(args[2]).unwrap().kind,
9967 crate::ExprKind::Number(3.0)
9968 );
9969 }
9970
9971 #[test]
9972 fn parse_call_nested() {
9973 let (ast, diags) = parse_str("f(g(x));");
9974 assert!(diags.is_empty());
9975 let outer = expr_of_single_stmt(&ast);
9976 let (_callee, args) = call(outer);
9977 assert_eq!(args.len(), 1);
9978 let (_inner_callee, inner_args) = call(ast.try_expr(args[0]).unwrap());
9979 assert_eq!(inner_args.len(), 1);
9980 assert!(matches!(
9981 ast.try_expr(inner_args[0]).unwrap().kind,
9982 crate::ExprKind::Identifier(_)
9983 ));
9984 }
9985
9986 #[test]
9987 fn parse_curried_call() {
9988 let (ast, diags) = parse_str("f()(x);");
9989 assert!(diags.is_empty());
9990 let outer = expr_of_single_stmt(&ast);
9991 let (callee, args) = call(outer);
9992 assert_eq!(args.len(), 1);
9993 let (_inner_callee, inner_args) = call(ast.try_expr(callee).unwrap());
9994 assert!(inner_args.is_empty());
9995 }
9996
9997 #[test]
9998 fn unary_binds_looser_than_call() {
9999 let (ast, diags) = parse_str("-f();");
10000 assert!(diags.is_empty());
10001 let outer = expr_of_single_stmt(&ast);
10002 let (op, operand) = unary(outer);
10003 assert_eq!(op, crate::UnOp::Neg);
10004 let (_callee, args) = call(ast.try_expr(operand).unwrap());
10005 assert!(args.is_empty());
10006 }
10007
10008 #[test]
10009 fn call_inside_binary() {
10010 let (ast, diags) = parse_str("f() + 1;");
10011 assert!(diags.is_empty());
10012 let outer = expr_of_single_stmt(&ast);
10013 let (op, lhs, rhs) = binary(outer);
10014 assert_eq!(op, crate::BinOp::Add);
10015 let (_callee, args) = call(ast.try_expr(lhs).unwrap());
10016 assert!(args.is_empty());
10017 assert_eq!(
10018 ast.try_expr(rhs).unwrap().kind,
10019 crate::ExprKind::Number(1.0)
10020 );
10021 }
10022
10023 #[test]
10024 fn call_with_complex_args() {
10025 let (ast, diags) = parse_str("f(1 + 2, g(x));");
10026 assert!(diags.is_empty());
10027 let outer = expr_of_single_stmt(&ast);
10028 let (_callee, args) = call(outer);
10029 assert_eq!(args.len(), 2);
10030 let (op, ..) = binary(ast.try_expr(args[0]).unwrap());
10031 assert_eq!(op, crate::BinOp::Add);
10032 let (_, inner_args) = call(ast.try_expr(args[1]).unwrap());
10033 assert_eq!(inner_args.len(), 1);
10034 }
10035
10036 #[test]
10037 fn call_trailing_comma_ok() {
10038 let (ast, diags) = parse_str("f(1, 2,);");
10039 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10040 let (_callee, args) = call(expr_of_single_stmt(&ast));
10041 assert_eq!(args.len(), 2);
10042 }
10043
10044 #[test]
10045 fn unclosed_call_diagnoses() {
10046 let (ast, diags) = parse_str("f(1, 2");
10047 assert!(!diags.is_empty());
10048 assert!(diags[0].message == "expected `,` or `)`" || diags[0].message == "expected `)`");
10049 assert!(ast.top_level.is_empty());
10050 }
10051
10052 #[test]
10053 fn call_expression_statement() {
10054 let (ast, diags) = parse_str(r#"greet("world");"#);
10055 assert!(diags.is_empty());
10056 let outer = expr_of_single_stmt(&ast);
10057 let (callee, args) = call(outer);
10058 assert!(matches!(
10059 ast.try_expr(callee).unwrap().kind,
10060 crate::ExprKind::Identifier(_)
10061 ));
10062 assert_eq!(args.len(), 1);
10063 assert_eq!(
10064 ast.try_expr(args[0]).unwrap().kind,
10065 crate::ExprKind::String("world".to_string())
10066 );
10067 }
10068
10069 #[test]
10070 fn snapshot_multi_call_program() {
10071 let (ast, diags) = parse_str("f(); g(1); h(f(2), 3);");
10072 assert!(diags.is_empty());
10073 assert_eq!(ast.top_level.len(), 3);
10074 insta::assert_debug_snapshot!(ast);
10075 }
10076
10077 #[test]
10078 fn parse_if_simple() {
10079 let (ast, diags) = parse_str("if (a) { }");
10080 assert!(diags.is_empty());
10081 let stmt = single_stmt(&ast);
10082 match stmt.kind {
10083 crate::StmtKind::If {
10084 condition,
10085 then_block,
10086 else_block,
10087 } => {
10088 assert!(matches!(
10089 ast.try_expr(condition).unwrap().kind,
10090 crate::ExprKind::Identifier(_)
10091 ));
10092 assert!(matches!(
10093 ast.try_stmt(then_block).unwrap().kind,
10094 crate::StmtKind::Block(ref v) if v.is_empty()
10095 ));
10096 assert!(else_block.is_none());
10097 }
10098 _ => panic!("expected If"),
10099 }
10100 }
10101
10102 #[test]
10103 fn parse_if_else() {
10104 let (ast, diags) = parse_str("if (a) { } else { }");
10105 assert!(diags.is_empty());
10106 let stmt = single_stmt(&ast);
10107 match stmt.kind {
10108 crate::StmtKind::If { else_block, .. } => {
10109 let else_id = else_block.expect("expected else block");
10110 assert!(matches!(
10111 ast.try_stmt(else_id).unwrap().kind,
10112 crate::StmtKind::Block(ref v) if v.is_empty()
10113 ));
10114 }
10115 _ => panic!("expected If"),
10116 }
10117 }
10118
10119 #[test]
10120 fn parse_else_if_chain() {
10121 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10122 assert!(diags.is_empty());
10123 let stmt = single_stmt(&ast);
10124 let crate::StmtKind::If {
10125 else_block: Some(inner_id),
10126 ..
10127 } = stmt.kind
10128 else {
10129 panic!("expected outer If with else_block");
10130 };
10131 let inner = ast.try_stmt(inner_id).unwrap();
10132 let crate::StmtKind::If {
10133 else_block: Some(tail_id),
10134 ..
10135 } = inner.kind
10136 else {
10137 panic!("expected inner If for `else if`");
10138 };
10139 assert!(matches!(
10140 ast.try_stmt(tail_id).unwrap().kind,
10141 crate::StmtKind::Block(_)
10142 ));
10143 }
10144
10145 #[test]
10146 fn parse_if_with_body_statements() {
10147 let (ast, diags) = parse_str("if (a) { let x = 1; }");
10148 assert!(diags.is_empty());
10149 let stmt = single_stmt(&ast);
10150 match stmt.kind {
10151 crate::StmtKind::If { then_block, .. } => {
10152 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind
10153 else {
10154 panic!("expected Block")
10155 };
10156 assert_eq!(body.len(), 1);
10157 }
10158 _ => panic!("expected If"),
10159 }
10160 }
10161
10162 #[test]
10163 fn parse_if_braceless_body() {
10164 let (ast, diags) = parse_str("if (a) return b;");
10165 assert!(diags.is_empty(), "{diags:?}");
10166 let stmt = single_stmt(&ast);
10167 let crate::StmtKind::If {
10168 then_block,
10169 else_block,
10170 ..
10171 } = stmt.kind
10172 else {
10173 panic!("expected If");
10174 };
10175 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10176 panic!("braceless body should wrap in a Block");
10177 };
10178 assert_eq!(body.len(), 1);
10179 assert!(matches!(
10180 ast.try_stmt(body[0]).unwrap().kind,
10181 crate::StmtKind::Return(_)
10182 ));
10183 assert!(else_block.is_none());
10184 }
10185
10186 #[test]
10187 fn parse_braceless_dangling_else_binds_nearest_if() {
10188 let (ast, diags) = parse_str("if (a) if (b) x(); else y();");
10190 assert!(diags.is_empty(), "{diags:?}");
10191 let crate::StmtKind::If {
10192 then_block,
10193 else_block: outer_else,
10194 ..
10195 } = single_stmt(&ast).kind
10196 else {
10197 panic!("expected outer If");
10198 };
10199 assert!(outer_else.is_none(), "else must bind to the inner if");
10200 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10201 panic!("expected wrapped Block");
10202 };
10203 let crate::StmtKind::If {
10204 else_block: Some(_),
10205 ..
10206 } = ast.try_stmt(body[0]).unwrap().kind
10207 else {
10208 panic!("inner If should own the else");
10209 };
10210 }
10211
10212 #[test]
10213 fn parse_while_braceless_body() {
10214 let (ast, diags) = parse_str("while (a) x();");
10215 assert!(diags.is_empty(), "{diags:?}");
10216 let crate::StmtKind::While { body, .. } = single_stmt(&ast).kind else {
10217 panic!("expected While");
10218 };
10219 assert!(matches!(
10220 ast.try_stmt(body).unwrap().kind,
10221 crate::StmtKind::Block(ref v) if v.len() == 1
10222 ));
10223 }
10224
10225 #[test]
10226 fn parse_loop_with_empty_body() {
10227 for src in [
10228 "while (a());",
10229 "for (const x of xs);",
10230 "for (let i = 0; i < 3; i++);",
10231 "do ; while (a());",
10232 ] {
10233 let (ast, diags) = parse_str(src);
10234 assert!(diags.is_empty(), "{src}: {diags:?}");
10235 let body = match single_stmt(&ast).kind {
10236 crate::StmtKind::While { body, .. }
10237 | crate::StmtKind::DoWhile { body, .. }
10238 | crate::StmtKind::For { body, .. }
10239 | crate::StmtKind::ForOf { body, .. } => body,
10240 ref other => panic!("{src}: expected a loop, got {other:?}"),
10241 };
10242 assert!(
10243 matches!(ast.try_stmt(body).unwrap().kind, crate::StmtKind::Block(ref v) if v.is_empty()),
10244 "{src}"
10245 );
10246 }
10247 }
10248
10249 #[test]
10250 fn parse_if_with_empty_body_is_an_error() {
10251 let (_ast, diags) = parse_str("if (a);");
10252 assert_eq!(diags[0].message, "`if` has an empty body");
10253 }
10254
10255 #[test]
10256 fn parse_empty_branch_body_reports_once() {
10257 let (_ast, diags) = parse_str("if (a) ; else b();");
10258 assert_eq!(diags.len(), 1, "{diags:?}");
10259 assert_eq!(diags[0].message, "`if` has an empty body");
10260 let (_ast, diags) = parse_str("if (a) b(); else ;");
10261 assert_eq!(diags.len(), 1, "{diags:?}");
10262 assert_eq!(diags[0].message, "`else` has an empty body");
10263 let (_ast, diags) = parse_str("if (a) ; else if (b) ; else ;");
10265 let messages: Vec<_> = diags.iter().map(|d| d.message.as_str()).collect();
10266 assert_eq!(
10267 messages,
10268 [
10269 "`if` has an empty body",
10270 "`if` has an empty body",
10271 "`else` has an empty body"
10272 ]
10273 );
10274 }
10275
10276 #[test]
10277 fn parse_for_of_braceless_body() {
10278 let (ast, diags) = parse_str("for (const x of xs) f(x);");
10279 assert!(diags.is_empty(), "{diags:?}");
10280 let crate::StmtKind::ForOf { body, .. } = single_stmt(&ast).kind else {
10281 panic!("expected ForOf");
10282 };
10283 assert!(matches!(
10284 ast.try_stmt(body).unwrap().kind,
10285 crate::StmtKind::Block(ref v) if v.len() == 1
10286 ));
10287 }
10288
10289 #[test]
10290 fn parse_nested_if() {
10291 let (ast, diags) = parse_str("if (a) { if (b) { } }");
10292 assert!(diags.is_empty());
10293 let outer = single_stmt(&ast);
10294 let crate::StmtKind::If { then_block, .. } = outer.kind else {
10295 panic!("expected outer If");
10296 };
10297 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10298 panic!("expected Block")
10299 };
10300 assert_eq!(body.len(), 1);
10301 assert!(matches!(
10302 ast.try_stmt(body[0]).unwrap().kind,
10303 crate::StmtKind::If { .. }
10304 ));
10305 }
10306
10307 #[test]
10308 fn if_missing_open_paren_diagnoses() {
10309 let (ast, diags) = parse_str("if a { }");
10310 assert!(!diags.is_empty());
10311 assert_eq!(diags[0].message, "expected `(`");
10312 assert!(ast.top_level.is_empty());
10313 }
10314
10315 #[test]
10316 fn if_missing_close_paren_diagnoses() {
10317 let (ast, diags) = parse_str("if (a { }");
10318 assert!(!diags.is_empty());
10319 assert_eq!(diags[0].message, "expected `)`");
10320 assert!(ast.top_level.is_empty());
10321 }
10322
10323 #[test]
10324 fn if_braceless_body_parses() {
10325 let (ast, diags) = parse_str("if (a) x;");
10326 assert!(diags.is_empty(), "{diags:?}");
10327 let crate::StmtKind::If { then_block, .. } = single_stmt(&ast).kind else {
10328 panic!("expected If");
10329 };
10330 assert!(matches!(
10331 ast.try_stmt(then_block).unwrap().kind,
10332 crate::StmtKind::Block(ref v) if v.len() == 1
10333 ));
10334 }
10335
10336 #[test]
10337 fn snapshot_else_if_chain() {
10338 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10339 assert!(diags.is_empty());
10340 insta::assert_debug_snapshot!(ast);
10341 }
10342
10343 #[test]
10344 fn parse_while_simple() {
10345 let (ast, diags) = parse_str("while (a) { }");
10346 assert!(diags.is_empty());
10347 let stmt = single_stmt(&ast);
10348 match stmt.kind {
10349 crate::StmtKind::While { condition, body } => {
10350 assert!(matches!(
10351 ast.try_expr(condition).unwrap().kind,
10352 crate::ExprKind::Identifier(_)
10353 ));
10354 assert!(matches!(
10355 ast.try_stmt(body).unwrap().kind,
10356 crate::StmtKind::Block(ref v) if v.is_empty()
10357 ));
10358 }
10359 _ => panic!("expected While"),
10360 }
10361 }
10362
10363 #[test]
10364 fn parse_while_with_body() {
10365 let (ast, diags) = parse_str("while (a) { x; y; }");
10366 assert!(diags.is_empty());
10367 let stmt = single_stmt(&ast);
10368 let crate::StmtKind::While { body, .. } = stmt.kind else {
10369 panic!("expected While");
10370 };
10371 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10372 panic!("expected Block")
10373 };
10374 assert_eq!(stmts.len(), 2);
10375 }
10376
10377 #[test]
10378 fn while_missing_paren_diagnoses() {
10379 let (ast, diags) = parse_str("while a { }");
10380 assert!(!diags.is_empty());
10381 assert_eq!(diags[0].message, "expected `(`");
10382 assert!(ast.top_level.is_empty());
10383 }
10384
10385 #[test]
10386 fn parse_for_all_slots() {
10387 let (ast, diags) = parse_str("for (let i = 0; i < 10; i = i + 1) { x; }");
10388 assert!(diags.is_empty(), "{diags:?}");
10389 let stmt = single_stmt(&ast);
10390 let crate::StmtKind::For {
10391 init,
10392 condition,
10393 update,
10394 body,
10395 } = &stmt.kind
10396 else {
10397 panic!("expected For");
10398 };
10399 assert!(!init.is_empty(), "init missing");
10400 assert!(condition.is_some(), "condition missing");
10401 assert!(update.is_some(), "update missing");
10402 assert!(matches!(
10403 ast.try_stmt(*body).unwrap().kind,
10404 crate::StmtKind::Block(_)
10405 ));
10406 }
10407
10408 #[test]
10409 fn parse_for_empty_slots() {
10410 let (ast, diags) = parse_str("for (;;) { break; }");
10411 assert!(diags.is_empty(), "{diags:?}");
10412 let stmt = single_stmt(&ast);
10413 let crate::StmtKind::For {
10414 init,
10415 condition,
10416 update,
10417 ..
10418 } = &stmt.kind
10419 else {
10420 panic!("expected For");
10421 };
10422 assert!(init.is_empty() && condition.is_none() && update.is_none());
10423 }
10424
10425 #[test]
10426 fn parse_for_of_const_binding() {
10427 let (ast, diags) = parse_str("for (const x of arr) { y; }");
10428 assert!(diags.is_empty(), "{diags:?}");
10429 let stmt = single_stmt(&ast);
10430 let crate::StmtKind::ForOf {
10431 binding_kind,
10432 ref name,
10433 ref ty,
10434 ..
10435 } = stmt.kind
10436 else {
10437 panic!("expected ForOf");
10438 };
10439 assert!(matches!(binding_kind, crate::BindingKind::Const));
10440 assert_eq!(name.name, "x");
10441 assert!(ty.is_none());
10442 }
10443
10444 #[test]
10445 fn parse_for_of_let_with_annotation() {
10446 let (ast, diags) = parse_str("for (let x: number of arr) { y; }");
10447 assert!(diags.is_empty(), "{diags:?}");
10448 let stmt = single_stmt(&ast);
10449 let crate::StmtKind::ForOf {
10450 binding_kind,
10451 ref ty,
10452 ..
10453 } = stmt.kind
10454 else {
10455 panic!("expected ForOf");
10456 };
10457 assert!(matches!(binding_kind, crate::BindingKind::Let));
10458 assert!(ty.is_some());
10459 }
10460
10461 #[test]
10462 fn parse_do_while_simple() {
10463 let (ast, diags) = parse_str("do { x; } while (a);");
10464 assert!(diags.is_empty(), "{diags:?}");
10465 let stmt = single_stmt(&ast);
10466 assert!(matches!(stmt.kind, crate::StmtKind::DoWhile { .. }));
10467 }
10468
10469 #[test]
10470 fn parse_break_and_continue() {
10471 let (ast, diags) = parse_str("while (a) { break; continue; }");
10472 assert!(diags.is_empty(), "{diags:?}");
10473 let stmt = single_stmt(&ast);
10474 let crate::StmtKind::While { body, .. } = stmt.kind else {
10475 panic!("expected While");
10476 };
10477 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10478 panic!("expected Block");
10479 };
10480 assert!(matches!(
10481 ast.try_stmt(stmts[0]).unwrap().kind,
10482 crate::StmtKind::Break
10483 ));
10484 assert!(matches!(
10485 ast.try_stmt(stmts[1]).unwrap().kind,
10486 crate::StmtKind::Continue
10487 ));
10488 }
10489
10490 #[test]
10491 fn parse_bare_return_in_function() {
10492 let (ast, diags) = parse_str("function f(): void { return; }");
10493 assert!(diags.is_empty());
10494 let f = single_stmt(&ast);
10495 let crate::StmtKind::Function { body, .. } = f.kind else {
10496 panic!("expected Function");
10497 };
10498 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10499 panic!("expected Block")
10500 };
10501 assert_eq!(stmts.len(), 1);
10502 match ast.try_stmt(stmts[0]).unwrap().kind {
10503 crate::StmtKind::Return(None) => {}
10504 _ => panic!("expected Return(None)"),
10505 }
10506 }
10507
10508 #[test]
10509 fn parse_return_with_value() {
10510 let (ast, diags) = parse_str("function f(): number { return 1; }");
10511 assert!(diags.is_empty());
10512 let f = single_stmt(&ast);
10513 let crate::StmtKind::Function { body, .. } = f.kind else {
10514 panic!("expected Function");
10515 };
10516 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10517 panic!("expected Block")
10518 };
10519 match ast.try_stmt(stmts[0]).unwrap().kind {
10520 crate::StmtKind::Return(Some(value)) => {
10521 assert_eq!(
10522 ast.try_expr(value).unwrap().kind,
10523 crate::ExprKind::Number(1.0)
10524 );
10525 }
10526 _ => panic!("expected Return(Some)"),
10527 }
10528 }
10529
10530 #[test]
10531 fn parse_return_with_expression() {
10532 let (ast, diags) = parse_str("function f(): number { return 1 + 2; }");
10533 assert!(diags.is_empty());
10534 let f = single_stmt(&ast);
10535 let crate::StmtKind::Function { body, .. } = f.kind else {
10536 panic!("expected Function");
10537 };
10538 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10539 panic!("expected Block")
10540 };
10541 match ast.try_stmt(stmts[0]).unwrap().kind {
10542 crate::StmtKind::Return(Some(value)) => {
10543 let (op, ..) = binary(ast.try_expr(value).unwrap());
10544 assert_eq!(op, crate::BinOp::Add);
10545 }
10546 _ => panic!("expected Return(Some(Binary))"),
10547 }
10548 }
10549
10550 #[test]
10551 fn return_missing_semicolon_diagnoses() {
10552 let (_, diags) = parse_str("function f(): number { return x x; }");
10553 assert!(!diags.is_empty());
10554 assert_eq!(diags[0].message, "expected `;` after return");
10555 }
10556
10557 #[test]
10558 fn parse_bare_block() {
10559 let (ast, diags) = parse_str("{ x; y; }");
10560 assert!(diags.is_empty());
10561 let stmt = single_stmt(&ast);
10562 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10563 panic!("expected Block");
10564 };
10565 assert_eq!(stmts.len(), 2);
10566 }
10567
10568 #[test]
10569 fn parse_empty_bare_block() {
10570 let (ast, diags) = parse_str("{ }");
10571 assert!(diags.is_empty());
10572 let stmt = single_stmt(&ast);
10573 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10574 panic!("expected Block");
10575 };
10576 assert!(stmts.is_empty());
10577 }
10578
10579 #[test]
10580 fn parse_nested_bare_blocks() {
10581 let (ast, diags) = parse_str("{ { } }");
10582 assert!(diags.is_empty());
10583 let outer = single_stmt(&ast);
10584 let crate::StmtKind::Block(ref stmts) = outer.kind else {
10585 panic!("expected outer Block");
10586 };
10587 assert_eq!(stmts.len(), 1);
10588 assert!(matches!(
10589 ast.try_stmt(stmts[0]).unwrap().kind,
10590 crate::StmtKind::Block(ref v) if v.is_empty()
10591 ));
10592 }
10593
10594 #[test]
10595 fn snapshot_function_with_if_and_return() {
10596 let source = "function isPositive(n: number): boolean {\n if (n > 0) {\n return true;\n } else {\n return false;\n }\n}";
10597 let (ast, diags) = parse_str(source);
10598 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10599 insta::assert_debug_snapshot!(ast);
10600 }
10601
10602 fn object_literal(e: &crate::Expr) -> &[crate::ObjectLiteralMember] {
10603 match e.kind {
10604 crate::ExprKind::ObjectLiteral { ref members } => members.as_slice(),
10605 _ => panic!("expected ObjectLiteral, got {:?}", e.kind),
10606 }
10607 }
10608
10609 fn object_literal_field(m: &crate::ObjectLiteralMember) -> &crate::ObjectLiteralField {
10610 match m {
10611 crate::ObjectLiteralMember::Field(f) => f,
10612 crate::ObjectLiteralMember::Spread { .. }
10613 | crate::ObjectLiteralMember::Computed { .. } => {
10614 panic!("expected Field member, got Spread")
10615 }
10616 }
10617 }
10618
10619 fn array_literal(e: &crate::Expr) -> &[crate::ArrayLiteralElement] {
10620 match e.kind {
10621 crate::ExprKind::ArrayLiteral { ref elements } => elements.as_slice(),
10622 _ => panic!("expected ArrayLiteral, got {:?}", e.kind),
10623 }
10624 }
10625
10626 fn array_literal_value(el: &crate::ArrayLiteralElement) -> crate::ExprId {
10627 match el {
10628 crate::ArrayLiteralElement::Value(id) => *id,
10629 crate::ArrayLiteralElement::Spread { .. } => {
10630 panic!("expected Value element, got Spread")
10631 }
10632 }
10633 }
10634
10635 #[test]
10636 fn parse_empty_object_literal() {
10637 let (ast, diags) = parse_str("let x = {};");
10638 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10639 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10640 panic!("expected Let");
10641 };
10642 assert!(object_literal(ast.try_expr(value).unwrap()).is_empty());
10643 }
10644
10645 #[test]
10646 fn parse_object_literal_simple() {
10647 let (ast, diags) = parse_str("let p = { x: 1, y: 2 };");
10648 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10649 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10650 panic!("expected Let");
10651 };
10652 let members = object_literal(ast.try_expr(value).unwrap());
10653 assert_eq!(members.len(), 2);
10654 let f0 = object_literal_field(&members[0]);
10655 let f1 = object_literal_field(&members[1]);
10656 assert_eq!(f0.name.name, "x");
10657 assert_eq!(f1.name.name, "y");
10658 assert_eq!(
10659 ast.try_expr(f0.value).unwrap().kind,
10660 crate::ExprKind::Number(1.0)
10661 );
10662 assert_eq!(
10663 ast.try_expr(f1.value).unwrap().kind,
10664 crate::ExprKind::Number(2.0)
10665 );
10666 }
10667
10668 #[test]
10669 fn parse_object_literal_keyword_keys() {
10670 let (ast, diags) = parse_str("let p = { type: 1, default: 2, null: 3, true: 4 };");
10671 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10672 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10673 panic!("expected Let");
10674 };
10675 let names: Vec<&str> = object_literal(ast.try_expr(value).unwrap())
10676 .iter()
10677 .map(object_literal_field)
10678 .map(|f| f.name.name.as_str())
10679 .collect();
10680 assert_eq!(names, vec!["type", "default", "null", "true"]);
10681 }
10682
10683 #[test]
10684 fn parse_object_literal_string_key() {
10685 let (ast, diags) = parse_str(r#"let p = { "hello": 1 };"#);
10686 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10687 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10688 panic!("expected Let");
10689 };
10690 let members = object_literal(ast.try_expr(value).unwrap());
10691 assert_eq!(members.len(), 1);
10692 let f0 = object_literal_field(&members[0]);
10693 assert_eq!(f0.name.name, "hello");
10694 }
10695
10696 #[test]
10697 fn parse_object_literal_string_key_decodes_escape() {
10698 let (ast, diags) = parse_str(r#"let p = { "content\u002dtype": 1 };"#);
10699 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10700 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10701 panic!("expected Let");
10702 };
10703 let members = object_literal(ast.try_expr(value).unwrap());
10704 assert_eq!(members.len(), 1);
10705 let f0 = object_literal_field(&members[0]);
10706 assert_eq!(f0.name.name, "content-type");
10707 }
10708
10709 #[test]
10710 fn parse_object_literal_shorthand() {
10711 let (ast, diags) = parse_str("let p = { x, y: 2 };");
10712 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10713 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10714 panic!("expected Let");
10715 };
10716 let members = object_literal(ast.try_expr(value).unwrap());
10717 assert_eq!(members.len(), 2);
10718 let f0 = object_literal_field(&members[0]);
10719 assert_eq!(f0.name.name, "x");
10720 assert_eq!(
10722 ast.try_expr(f0.value).unwrap().kind,
10723 crate::ExprKind::Identifier(crate::Ident {
10724 name: "x".into(),
10725 span: f0.name.span,
10726 })
10727 );
10728 let f1 = object_literal_field(&members[1]);
10729 assert_eq!(f1.name.name, "y");
10730 assert_eq!(
10731 ast.try_expr(f1.value).unwrap().kind,
10732 crate::ExprKind::Number(2.0)
10733 );
10734 }
10735
10736 #[test]
10737 fn parse_object_literal_string_key_shorthand_rejected() {
10738 let (_ast, diags) = parse_str(r#"let p = { "x" };"#);
10739 assert!(
10740 diags
10741 .iter()
10742 .any(|d| d.message.contains("expected `:` after field name")),
10743 "string-key shorthand should be rejected: {diags:?}"
10744 );
10745 }
10746
10747 #[test]
10748 fn parse_object_literal_trailing_comma() {
10749 let (ast, diags) = parse_str("let p = { x: 1, y: 2, };");
10750 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10751 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10752 panic!("expected Let");
10753 };
10754 assert_eq!(object_literal(ast.try_expr(value).unwrap()).len(), 2);
10755 }
10756
10757 #[test]
10758 fn parse_object_literal_duplicate_key_diagnoses() {
10759 let (_, diags) = parse_str("let p = { x: 1, x: 2 };");
10760 assert_eq!(diags.len(), 1);
10761 assert_eq!(diags[0].message, "duplicate field `x` in object literal");
10762 }
10763
10764 #[test]
10765 fn parse_empty_array_literal() {
10766 let (ast, diags) = parse_str("let xs = [];");
10767 assert!(diags.is_empty());
10768 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10769 panic!("expected Let");
10770 };
10771 assert!(array_literal(ast.try_expr(value).unwrap()).is_empty());
10772 }
10773
10774 #[test]
10775 fn parse_array_literal_simple() {
10776 let (ast, diags) = parse_str("let xs = [1, 2, 3];");
10777 assert!(diags.is_empty());
10778 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10779 panic!("expected Let");
10780 };
10781 let elements = array_literal(ast.try_expr(value).unwrap());
10782 assert_eq!(elements.len(), 3);
10783 assert_eq!(
10784 ast.try_expr(array_literal_value(&elements[0]))
10785 .unwrap()
10786 .kind,
10787 crate::ExprKind::Number(1.0)
10788 );
10789 assert_eq!(
10790 ast.try_expr(array_literal_value(&elements[2]))
10791 .unwrap()
10792 .kind,
10793 crate::ExprKind::Number(3.0)
10794 );
10795 }
10796
10797 #[test]
10798 fn parse_array_literal_trailing_comma() {
10799 let (ast, diags) = parse_str("let xs = [1, 2,];");
10800 assert!(diags.is_empty());
10801 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10802 panic!("expected Let");
10803 };
10804 assert_eq!(array_literal(ast.try_expr(value).unwrap()).len(), 2);
10805 }
10806
10807 #[test]
10808 fn parse_nested_array_in_object() {
10809 let (ast, diags) = parse_str("let p = { items: [1, 2] };");
10810 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10811 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10812 panic!("expected Let");
10813 };
10814 let members = object_literal(ast.try_expr(value).unwrap());
10815 let f0 = object_literal_field(&members[0]);
10816 assert_eq!(f0.name.name, "items");
10817 let inner = array_literal(ast.try_expr(f0.value).unwrap());
10818 assert_eq!(inner.len(), 2);
10819 }
10820
10821 #[test]
10822 fn parse_nested_object_in_array() {
10823 let (ast, diags) = parse_str("let xs = [{ x: 1 }, { x: 2 }];");
10824 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10825 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10826 panic!("expected Let");
10827 };
10828 let elements = array_literal(ast.try_expr(value).unwrap());
10829 assert_eq!(elements.len(), 2);
10830 assert_eq!(
10831 object_literal(ast.try_expr(array_literal_value(&elements[0])).unwrap()).len(),
10832 1
10833 );
10834 assert_eq!(
10835 object_literal(ast.try_expr(array_literal_value(&elements[1])).unwrap()).len(),
10836 1
10837 );
10838 }
10839
10840 #[test]
10841 fn brace_at_statement_start_is_block_not_object() {
10842 let (_, diags) = parse_str("{ x: 1 }");
10843 assert!(
10844 !diags.is_empty(),
10845 "expected a diagnostic for `x:` inside block"
10846 );
10847 }
10848
10849 fn field_access(e: &crate::Expr) -> (crate::ExprId, &str) {
10850 match e.kind {
10851 crate::ExprKind::FieldAccess { receiver, ref name } => (receiver, name.name.as_str()),
10852 _ => panic!("expected FieldAccess, got {:?}", e.kind),
10853 }
10854 }
10855
10856 fn index_access(e: &crate::Expr) -> (crate::ExprId, crate::ExprId) {
10857 match e.kind {
10858 crate::ExprKind::IndexAccess { receiver, index } => (receiver, index),
10859 _ => panic!("expected IndexAccess, got {:?}", e.kind),
10860 }
10861 }
10862
10863 #[test]
10864 fn parse_field_access_simple() {
10865 let (ast, diags) = parse_str("a.b;");
10866 assert!(diags.is_empty());
10867 let outer = expr_of_single_stmt(&ast);
10868 let (receiver, name) = field_access(outer);
10869 assert!(matches!(
10870 ast.try_expr(receiver).unwrap().kind,
10871 crate::ExprKind::Identifier(_)
10872 ));
10873 assert_eq!(name, "b");
10874 }
10875
10876 #[test]
10877 fn parse_field_access_keyword_name() {
10878 let (ast, diags) = parse_str("status.type.default.null;");
10879 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10880 let outer = expr_of_single_stmt(&ast);
10881 let (receiver, name) = field_access(outer);
10882 assert_eq!(name, "null");
10883 let (receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10884 assert_eq!(name, "default");
10885 let (_receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10886 assert_eq!(name, "type");
10887 }
10888
10889 #[test]
10890 fn parse_field_access_chain_left_assoc() {
10891 let (ast, diags) = parse_str("a.b.c;");
10892 assert!(diags.is_empty());
10893 let outer = expr_of_single_stmt(&ast);
10894 let (mid_id, c) = field_access(outer);
10895 assert_eq!(c, "c");
10896 let (a_id, b) = field_access(ast.try_expr(mid_id).unwrap());
10897 assert_eq!(b, "b");
10898 assert!(matches!(
10899 ast.try_expr(a_id).unwrap().kind,
10900 crate::ExprKind::Identifier(_)
10901 ));
10902 }
10903
10904 #[test]
10905 fn parse_index_access_simple() {
10906 let (ast, diags) = parse_str("a[0];");
10907 assert!(diags.is_empty());
10908 let outer = expr_of_single_stmt(&ast);
10909 let (receiver, idx) = index_access(outer);
10910 assert!(matches!(
10911 ast.try_expr(receiver).unwrap().kind,
10912 crate::ExprKind::Identifier(_)
10913 ));
10914 assert_eq!(
10915 ast.try_expr(idx).unwrap().kind,
10916 crate::ExprKind::Number(0.0)
10917 );
10918 }
10919
10920 #[test]
10921 fn parse_index_access_chain() {
10922 let (ast, diags) = parse_str("a[0][1];");
10923 assert!(diags.is_empty());
10924 let outer = expr_of_single_stmt(&ast);
10925 let (mid_id, one) = index_access(outer);
10926 assert_eq!(
10927 ast.try_expr(one).unwrap().kind,
10928 crate::ExprKind::Number(1.0)
10929 );
10930 let (a_id, zero) = index_access(ast.try_expr(mid_id).unwrap());
10931 assert_eq!(
10932 ast.try_expr(zero).unwrap().kind,
10933 crate::ExprKind::Number(0.0)
10934 );
10935 assert!(matches!(
10936 ast.try_expr(a_id).unwrap().kind,
10937 crate::ExprKind::Identifier(_)
10938 ));
10939 }
10940
10941 #[test]
10942 fn parse_mixed_access_and_call_chain() {
10943 let (ast, diags) = parse_str("a.b[c].d();");
10944 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10945 let outer = expr_of_single_stmt(&ast);
10946 let (callee, args) = call(outer);
10947 assert!(args.is_empty());
10948 let (idx_id, d_name) = field_access(ast.try_expr(callee).unwrap());
10949 assert_eq!(d_name, "d");
10950 let (b_id, c_idx) = index_access(ast.try_expr(idx_id).unwrap());
10951 assert!(matches!(
10952 ast.try_expr(c_idx).unwrap().kind,
10953 crate::ExprKind::Identifier(_)
10954 ));
10955 let (a_id, b_name) = field_access(ast.try_expr(b_id).unwrap());
10956 assert_eq!(b_name, "b");
10957 assert!(matches!(
10958 ast.try_expr(a_id).unwrap().kind,
10959 crate::ExprKind::Identifier(_)
10960 ));
10961 }
10962
10963 #[test]
10964 fn parse_call_then_field() {
10965 let (ast, diags) = parse_str("f().x;");
10966 assert!(diags.is_empty());
10967 let outer = expr_of_single_stmt(&ast);
10968 let (call_id, x_name) = field_access(outer);
10969 assert_eq!(x_name, "x");
10970 let (_callee, args) = call(ast.try_expr(call_id).unwrap());
10971 assert!(args.is_empty());
10972 }
10973
10974 #[test]
10975 fn parse_index_with_complex_expression() {
10976 let (ast, diags) = parse_str("a[i + 1];");
10977 assert!(diags.is_empty());
10978 let outer = expr_of_single_stmt(&ast);
10979 let (_, idx) = index_access(outer);
10980 let (op, ..) = binary(ast.try_expr(idx).unwrap());
10981 assert_eq!(op, crate::BinOp::Add);
10982 }
10983
10984 #[test]
10985 fn field_access_missing_name_diagnoses() {
10986 let (_, diags) = parse_str("a.;");
10987 assert!(!diags.is_empty());
10988 assert_eq!(diags[0].message, "expected field name after `.`");
10989 }
10990
10991 #[test]
10992 fn parse_postfix_increment() {
10993 let (ast, diags) = parse_str("x++;");
10994 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10995 let expr = expr_of_single_stmt(&ast);
10996 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10997 panic!("expected PostfixUnary, got {:?}", expr.kind);
10998 };
10999 assert_eq!(op, crate::PostfixOp::Inc);
11000 assert!(matches!(
11001 ast.try_expr(operand).unwrap().kind,
11002 crate::ExprKind::Identifier(_)
11003 ));
11004 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
11005 }
11006
11007 #[test]
11008 fn parse_postfix_decrement() {
11009 let (ast, diags) = parse_str("x--;");
11010 assert!(diags.is_empty());
11011 let expr = expr_of_single_stmt(&ast);
11012 let crate::ExprKind::PostfixUnary { op, .. } = expr.kind else {
11013 panic!("expected PostfixUnary");
11014 };
11015 assert_eq!(op, crate::PostfixOp::Dec);
11016 }
11017
11018 #[test]
11019 fn parse_postfix_non_null_assertion() {
11020 let (ast, diags) = parse_str("x!;");
11021 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11022 let expr = expr_of_single_stmt(&ast);
11023 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
11024 panic!("expected PostfixUnary, got {:?}", expr.kind);
11025 };
11026 assert_eq!(op, crate::PostfixOp::NonNullAssert);
11027 assert!(matches!(
11028 ast.try_expr(operand).unwrap().kind,
11029 crate::ExprKind::Identifier(_)
11030 ));
11031 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
11032 }
11033
11034 #[test]
11035 fn parse_non_null_assertion_continues_chain() {
11036 let (ast, diags) = parse_str("x!.y;");
11037 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11038 let expr = expr_of_single_stmt(&ast);
11039 let crate::ExprKind::FieldAccess { receiver, name } = &expr.kind else {
11040 panic!("expected FieldAccess, got {:?}", expr.kind);
11041 };
11042 assert_eq!(name.name, "y");
11043 assert!(matches!(
11044 ast.try_expr(*receiver).unwrap().kind,
11045 crate::ExprKind::PostfixUnary {
11046 op: crate::PostfixOp::NonNullAssert,
11047 ..
11048 }
11049 ));
11050 }
11051
11052 #[test]
11053 fn parse_postfix_on_field_access() {
11054 let (ast, diags) = parse_str("o.f++;");
11055 assert!(diags.is_empty());
11056 let expr = expr_of_single_stmt(&ast);
11057 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
11058 panic!("expected PostfixUnary");
11059 };
11060 assert!(matches!(
11061 ast.try_expr(operand).unwrap().kind,
11062 crate::ExprKind::FieldAccess { .. }
11063 ));
11064 }
11065
11066 #[test]
11067 fn parse_postfix_on_index_access() {
11068 let (ast, diags) = parse_str("xs[0]++;");
11069 assert!(diags.is_empty());
11070 let expr = expr_of_single_stmt(&ast);
11071 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
11072 panic!("expected PostfixUnary");
11073 };
11074 assert!(matches!(
11075 ast.try_expr(operand).unwrap().kind,
11076 crate::ExprKind::IndexAccess { .. }
11077 ));
11078 }
11079
11080 #[test]
11081 fn parse_postfix_terminates_chain() {
11082 let (_, diags) = parse_str("x++.y;");
11083 assert!(!diags.is_empty());
11084 assert_eq!(diags[0].message, "expected `;` after expression");
11085 }
11086
11087 #[test]
11088 fn parse_postfix_in_binary_continues() {
11089 let (ast, diags) = parse_str("let y = x++ + 1;");
11090 assert!(diags.is_empty(), "unexpected: {diags:?}");
11091 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11092 panic!("expected Let");
11093 };
11094 let (op, lhs, _) = binary(ast.try_expr(value).unwrap());
11095 assert_eq!(op, crate::BinOp::Add);
11096 assert!(matches!(
11097 ast.try_expr(lhs).unwrap().kind,
11098 crate::ExprKind::PostfixUnary { .. }
11099 ));
11100 }
11101
11102 #[test]
11103 fn parse_index_assignment() {
11104 let (ast, diags) = parse_str("xs[0] = 1;");
11105 assert!(diags.is_empty(), "unexpected: {diags:?}");
11106 let stmt = single_stmt(&ast);
11107 let crate::StmtKind::AssignIndex {
11108 receiver,
11109 index,
11110 value,
11111 } = stmt.kind
11112 else {
11113 panic!("expected AssignIndex, got {:?}", stmt.kind);
11114 };
11115 assert!(matches!(
11116 ast.try_expr(receiver).unwrap().kind,
11117 crate::ExprKind::Identifier(_)
11118 ));
11119 assert_eq!(
11120 ast.try_expr(index).unwrap().kind,
11121 crate::ExprKind::Number(0.0)
11122 );
11123 assert_eq!(
11124 ast.try_expr(value).unwrap().kind,
11125 crate::ExprKind::Number(1.0)
11126 );
11127 }
11128
11129 #[test]
11130 fn parse_for_update_postfix() {
11131 let (ast, diags) = parse_str("function main(): void { for (let i = 0; i < 3; i++) {} }");
11132 assert!(diags.is_empty(), "unexpected: {diags:?}");
11133 assert_eq!(ast.top_level.len(), 1);
11134 }
11135
11136 #[test]
11137 fn parse_for_update_index_assignment() {
11138 let (ast, diags) =
11139 parse_str("function main(): void { for (let i = 0; i < 3; xs[i] = i) {} }");
11140 assert!(diags.is_empty(), "unexpected: {diags:?}");
11141 assert_eq!(ast.top_level.len(), 1);
11142 }
11143
11144 #[test]
11145 fn index_access_missing_close_bracket_diagnoses() {
11146 let (_, diags) = parse_str("a[1;");
11147 assert!(!diags.is_empty());
11148 assert_eq!(diags[0].message, "expected `]`");
11149 }
11150
11151 #[test]
11152 fn field_access_inside_object_literal_value_does_not_steal_dot() {
11153 let (ast, diags) = parse_str("let x = { a: 1 }.foo;");
11154 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11155 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
11156 panic!("expected Let");
11157 };
11158 let (recv_id, name) = field_access(ast.try_expr(value).unwrap());
11159 assert_eq!(name, "foo");
11160 assert_eq!(object_literal(ast.try_expr(recv_id).unwrap()).len(), 1);
11161 }
11162
11163 fn type_of_let(stmt: &crate::Stmt) -> &crate::TypeAnnotation {
11164 match stmt.kind {
11165 crate::StmtKind::Let { ref ty, .. } => ty.as_ref().expect("expected type annotation"),
11166 _ => panic!("expected Let"),
11167 }
11168 }
11169
11170 #[test]
11171 fn parse_array_type_annotation() {
11172 let (ast, diags) = parse_str("let xs: number[] = null;");
11173 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11174 let ty = type_of_let(single_stmt(&ast));
11175 match ty.kind {
11176 crate::TypeAnnotationKind::Array(ref inner) => {
11177 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Name { .. }));
11178 }
11179 _ => panic!("expected Array, got {:?}", ty.kind),
11180 }
11181 }
11182
11183 #[test]
11184 fn parse_nested_array_type_annotation() {
11185 let (ast, diags) = parse_str("let xs: number[][] = null;");
11186 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11187 let ty = type_of_let(single_stmt(&ast));
11188 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11189 panic!("expected outer Array");
11190 };
11191 let crate::TypeAnnotationKind::Array(ref inner2) = inner.kind else {
11192 panic!("expected inner Array");
11193 };
11194 assert!(matches!(
11195 inner2.kind,
11196 crate::TypeAnnotationKind::Name { .. }
11197 ));
11198 }
11199
11200 #[test]
11201 fn parse_object_type_annotation_simple() {
11202 let (ast, diags) = parse_str("let p: { x: number; y: number } = null;");
11203 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11204 let ty = type_of_let(single_stmt(&ast));
11205 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11206 panic!("expected Object, got {:?}", ty.kind);
11207 };
11208 assert_eq!(fields.len(), 2);
11209 assert_eq!(fields[0].name.name, "x");
11210 assert_eq!(fields[1].name.name, "y");
11211 }
11212
11213 #[test]
11214 fn readonly_is_rejected_on_methods_accessors_and_method_signatures() {
11215 let source = "class K {\n\
11216 readonly m(): number { return 1; }\n\
11217 public readonly n<T>(): number { return 1; }\n\
11218 readonly get g(): number { return 1; }\n\
11219 readonly p: number = 1;\n\
11220 }\n\
11221 type T = { readonly r(a: number): boolean; readonly f: () => void };\n\
11222 interface I { readonly s(): void; readonly q: number; }\n";
11223 let (_, diags) = parse_str(source);
11224 let lines: Vec<usize> = diags
11225 .iter()
11226 .map(|d| {
11227 assert_eq!(
11228 d.message,
11229 "`readonly` can only modify a property or index signature"
11230 );
11231 source[..d.span.start as usize].matches('\n').count() + 1
11232 })
11233 .collect();
11234 assert_eq!(lines, vec![2, 3, 4, 7, 8]);
11235 }
11236
11237 #[test]
11238 fn parse_object_type_method_members() {
11239 let (ast, diags) =
11242 parse_str("let p: { m(): number; opt?(): string; r(a: number): boolean } = null;");
11243 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11244 let ty = type_of_let(single_stmt(&ast));
11245 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11246 panic!("expected Object, got {:?}", ty.kind);
11247 };
11248 assert_eq!(fields.len(), 3);
11249 for field in fields {
11250 assert!(
11251 matches!(field.ty.kind, crate::TypeAnnotationKind::Function { .. }),
11252 "member `{}` should be function-typed, got {:?}",
11253 field.name.name,
11254 field.ty.kind,
11255 );
11256 }
11257 assert!(fields[1].optional, "`opt?()` is an optional member");
11258 let crate::TypeAnnotationKind::Function { ref params, .. } = fields[2].ty.kind else {
11259 unreachable!("checked above");
11260 };
11261 assert_eq!(params.len(), 1);
11262 assert_eq!(params[0].name.name, "a");
11263 }
11264
11265 #[test]
11266 fn parse_object_type_annotation_with_commas() {
11267 let (ast, diags) = parse_str("let p: { x: number, y: number } = null;");
11268 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11269 let ty = type_of_let(single_stmt(&ast));
11270 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11271 panic!("expected Object");
11272 };
11273 assert_eq!(fields.len(), 2);
11274 }
11275
11276 #[test]
11277 fn parse_object_type_annotation_empty() {
11278 let (ast, diags) = parse_str("let p: {} = null;");
11279 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11280 let ty = type_of_let(single_stmt(&ast));
11281 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11282 panic!("expected Object");
11283 };
11284 assert!(fields.is_empty());
11285 }
11286
11287 #[test]
11288 fn parse_object_type_with_optional_field() {
11289 let (ast, diags) = parse_str("let p: { id: number; nick?: string } = null;");
11290 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11291 let ty = type_of_let(single_stmt(&ast));
11292 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11293 panic!("expected Object");
11294 };
11295 assert_eq!(fields.len(), 2);
11296 assert_eq!(fields[0].name.name, "id");
11297 assert!(!fields[0].optional);
11298 assert_eq!(fields[1].name.name, "nick");
11299 assert!(fields[1].optional);
11300 }
11301
11302 #[test]
11303 fn parse_object_type_with_readonly_fields() {
11304 let (ast, diags) = parse_str(
11305 "let issue: { readonly id: string; readonly title?: string; body: string } = null;",
11306 );
11307 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11308 let ty = type_of_let(single_stmt(&ast));
11309 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11310 panic!("expected Object");
11311 };
11312 let fields: Vec<(&str, bool, bool)> = fields
11313 .iter()
11314 .map(|field| (field.name.name.as_str(), field.optional, field.readonly))
11315 .collect();
11316 assert_eq!(
11317 fields,
11318 vec![
11319 ("id", false, true),
11320 ("title", true, true),
11321 ("body", false, false),
11322 ]
11323 );
11324 }
11325
11326 #[test]
11327 fn parse_object_type_mixed_optional_and_required() {
11328 let (ast, diags) = parse_str("let p: { a?: number; b: string; c?: boolean } = null;");
11329 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11330 let ty = type_of_let(single_stmt(&ast));
11331 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11332 panic!("expected Object");
11333 };
11334 let opt: Vec<(&str, bool)> = fields
11335 .iter()
11336 .map(|f| (f.name.name.as_str(), f.optional))
11337 .collect();
11338 assert_eq!(opt, vec![("a", true), ("b", false), ("c", true)]);
11339 }
11340
11341 #[test]
11342 fn parse_object_type_keyword_fields() {
11343 let (ast, diags) =
11344 parse_str("let p: { type: string; default?: number; null: boolean } = null;");
11345 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11346 let ty = type_of_let(single_stmt(&ast));
11347 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11348 panic!("expected Object");
11349 };
11350 let opt: Vec<(&str, bool)> = fields
11351 .iter()
11352 .map(|f| (f.name.name.as_str(), f.optional))
11353 .collect();
11354 assert_eq!(
11355 opt,
11356 vec![("type", false), ("default", true), ("null", false)]
11357 );
11358 }
11359
11360 #[test]
11361 fn parse_object_type_quoted_fields() {
11362 let (ast, diags) =
11363 parse_str(r#"let p: { "content-type": string; "x\u002drequest-id"?: string } = null;"#);
11364 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11365 let ty = type_of_let(single_stmt(&ast));
11366 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11367 panic!("expected Object");
11368 };
11369 let opt: Vec<(&str, bool)> = fields
11370 .iter()
11371 .map(|f| (f.name.name.as_str(), f.optional))
11372 .collect();
11373 assert_eq!(opt, vec![("content-type", false), ("x-request-id", true)]);
11374 }
11375
11376 #[test]
11377 fn parse_interface_with_optional_property() {
11378 let (ast, diags) = parse_str("interface User { id: number; deletedAt?: string; }");
11379 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11380 let stmt = single_stmt(&ast);
11381 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11382 panic!("expected interface decl");
11383 };
11384 let opt: Vec<(&str, bool)> = members
11385 .iter()
11386 .map(|m| match m {
11387 crate::InterfaceMember::Property { name, optional, .. } => {
11388 (name.name.as_str(), *optional)
11389 }
11390 _ => panic!("expected property"),
11391 })
11392 .collect();
11393 assert_eq!(opt, vec![("id", false), ("deletedAt", true)]);
11394 }
11395
11396 #[test]
11397 fn parse_interface_with_readonly_properties() {
11398 let (ast, diags) = parse_str(
11399 "interface Issue { readonly id: string; readonly title?: string; body: string; }",
11400 );
11401 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11402 let stmt = single_stmt(&ast);
11403 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11404 panic!("expected interface decl");
11405 };
11406 let opt: Vec<(&str, bool, bool)> = members
11407 .iter()
11408 .map(|member| match member {
11409 crate::InterfaceMember::Property {
11410 name,
11411 optional,
11412 readonly,
11413 ..
11414 } => (name.name.as_str(), *optional, *readonly),
11415 _ => panic!("expected property"),
11416 })
11417 .collect();
11418 assert_eq!(
11419 opt,
11420 vec![
11421 ("id", false, true),
11422 ("title", true, true),
11423 ("body", false, false),
11424 ]
11425 );
11426 }
11427
11428 #[test]
11429 fn parse_readonly_property_name_without_modifier() {
11430 let (ast, diags) = parse_str(
11431 "interface Meta { readonly: boolean; }\nlet meta: { readonly: boolean } = null;",
11432 );
11433 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11434 let crate::StmtKind::InterfaceDecl { ref members, .. } =
11435 ast.try_stmt(ast.top_level[0]).unwrap().kind
11436 else {
11437 panic!("expected interface decl");
11438 };
11439 let crate::InterfaceMember::Property {
11440 ref name, readonly, ..
11441 } = members[0]
11442 else {
11443 panic!("expected property");
11444 };
11445 assert_eq!(name.name, "readonly");
11446 assert!(
11447 !readonly,
11448 "`readonly` here is the property name, not a modifier"
11449 );
11450
11451 let ty = type_of_let(ast.try_stmt(ast.top_level[1]).unwrap());
11452 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11453 panic!("expected Object");
11454 };
11455 assert_eq!(fields[0].name.name, "readonly");
11456 assert!(!fields[0].readonly);
11457 }
11458
11459 #[test]
11460 fn parse_interface_keyword_members() {
11461 let (ast, diags) =
11462 parse_str("interface IssueStatus { type: string; default(): string; null: boolean; }");
11463 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11464 let stmt = single_stmt(&ast);
11465 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11466 panic!("expected interface decl");
11467 };
11468 let names: Vec<&str> = members
11469 .iter()
11470 .map(|m| match m {
11471 crate::InterfaceMember::Method { name, .. }
11472 | crate::InterfaceMember::Property { name, .. } => name.name.as_str(),
11473 crate::InterfaceMember::IndexSignature(_) => panic!("unexpected index signature"),
11474 })
11475 .collect();
11476 assert_eq!(names, vec!["type", "default", "null"]);
11477 }
11478
11479 #[test]
11480 fn parse_interface_quoted_property_members() {
11481 let (ast, diags) = parse_str(
11482 r#"interface Headers { "content-type": string; "x\u002drequest-id"?: string; }"#,
11483 );
11484 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11485 let stmt = single_stmt(&ast);
11486 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11487 panic!("expected interface decl");
11488 };
11489 let names: Vec<(&str, bool)> = members
11490 .iter()
11491 .map(|m| match m {
11492 crate::InterfaceMember::Property { name, optional, .. } => {
11493 (name.name.as_str(), *optional)
11494 }
11495 _ => panic!("expected property"),
11496 })
11497 .collect();
11498 assert_eq!(names, vec![("content-type", false), ("x-request-id", true)]);
11499 }
11500
11501 #[test]
11502 fn parse_param_optional_marker() {
11503 let (ast, diags) = parse_str("function f(x?: number): void {}");
11504 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11505 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
11506 panic!("expected function")
11507 };
11508 assert!(params[0].optional);
11509 assert!(params[0].default.is_none());
11510 }
11511
11512 #[test]
11513 fn parse_interface_optional_method() {
11514 let (ast, diags) = parse_str("interface F { foo?(x?: number): void; }");
11515 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11516 let crate::StmtKind::InterfaceDecl { ref members, .. } = single_stmt(&ast).kind else {
11517 panic!("expected interface")
11518 };
11519 let crate::InterfaceMember::Property { optional, ty, .. } = &members[0] else {
11520 panic!("expected callable property")
11521 };
11522 assert!(*optional);
11523 let crate::TypeAnnotationKind::Function { params, .. } = &ty.kind else {
11524 panic!("expected function type")
11525 };
11526 assert!(params[0].optional);
11527 }
11528
11529 #[test]
11530 fn parse_interface_with_call_signature() {
11531 let (ast, diags) = parse_str("interface F { (s: string): number; }");
11532 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11533 let stmt = single_stmt(&ast);
11534 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11535 panic!("expected interface decl");
11536 };
11537 assert_eq!(members.len(), 1);
11538 match &members[0] {
11539 crate::InterfaceMember::Method { name, params, .. } => {
11540 assert_eq!(name.name, "@call");
11541 assert_eq!(params.len(), 1);
11542 assert_eq!(params[0].name.name, "s");
11543 }
11544 other => panic!("expected method (call signature), got {other:?}"),
11545 }
11546 }
11547
11548 #[test]
11549 fn parse_interface_mixed_call_signature_and_method() {
11550 let (ast, diags) = parse_str("interface F { greet(): string; (x: number): number; }");
11551 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11552 let stmt = single_stmt(&ast);
11553 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11554 panic!("expected interface decl");
11555 };
11556 let names: Vec<&str> = members
11557 .iter()
11558 .map(|m| match m {
11559 crate::InterfaceMember::Method { name, .. } => name.name.as_str(),
11560 _ => "<property>",
11561 })
11562 .collect();
11563 assert_eq!(names, vec!["greet", "@call"]);
11564 }
11565
11566 #[test]
11567 fn parse_interface_rejects_generic_call_signature() {
11568 let (_ast, diags) = parse_str("interface F { <T>(x: T): T; }");
11569 assert!(
11570 diags
11571 .iter()
11572 .any(|d| d.message.contains("generic call signatures")),
11573 "expected generic-call-signature diagnostic, got {diags:?}"
11574 );
11575 }
11576
11577 #[test]
11578 fn parse_object_type_with_array_field() {
11579 let (ast, diags) = parse_str("let p: { xs: number[] } = null;");
11580 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11581 let ty = type_of_let(single_stmt(&ast));
11582 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11583 panic!("expected Object");
11584 };
11585 assert_eq!(fields[0].name.name, "xs");
11586 assert!(matches!(
11587 fields[0].ty.kind,
11588 crate::TypeAnnotationKind::Array(_)
11589 ));
11590 }
11591
11592 #[test]
11593 fn parse_array_of_objects() {
11594 let (ast, diags) = parse_str("let xs: { x: number }[] = null;");
11595 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11596 let ty = type_of_let(single_stmt(&ast));
11597 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11598 panic!("expected Array");
11599 };
11600 assert!(matches!(
11601 inner.kind,
11602 crate::TypeAnnotationKind::Object { .. }
11603 ));
11604 }
11605
11606 #[test]
11607 fn parse_tuple_type_annotation() {
11608 let (ast, diags) = parse_str("let p: [string, number] = null;");
11609 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11610 let ty = type_of_let(single_stmt(&ast));
11611 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11612 panic!("expected Tuple, got {:?}", ty.kind);
11613 };
11614 assert_eq!(elems.len(), 2);
11615 assert!(matches!(
11616 elems[0].kind,
11617 crate::TypeAnnotationKind::Name { .. }
11618 ));
11619 assert!(matches!(
11620 elems[1].kind,
11621 crate::TypeAnnotationKind::Name { .. }
11622 ));
11623 }
11624
11625 #[test]
11626 fn parse_singleton_tuple_type() {
11627 let (ast, diags) = parse_str("let p: [number] = null;");
11628 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11629 let ty = type_of_let(single_stmt(&ast));
11630 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11631 panic!("expected Tuple");
11632 };
11633 assert_eq!(elems.len(), 1);
11634 }
11635
11636 #[test]
11637 fn parse_nested_tuple_type() {
11638 let (ast, diags) = parse_str("let p: [[number, string], boolean] = null;");
11639 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11640 let ty = type_of_let(single_stmt(&ast));
11641 let crate::TypeAnnotationKind::Tuple(ref outer) = ty.kind else {
11642 panic!("expected outer Tuple");
11643 };
11644 assert_eq!(outer.len(), 2);
11645 let crate::TypeAnnotationKind::Tuple(ref inner) = outer[0].kind else {
11646 panic!("expected inner Tuple");
11647 };
11648 assert_eq!(inner.len(), 2);
11649 }
11650
11651 #[test]
11652 fn parse_array_of_tuple_type() {
11653 let (ast, diags) = parse_str("let xs: [string, number][] = null;");
11654 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11655 let ty = type_of_let(single_stmt(&ast));
11656 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11657 panic!("expected outer Array, got {:?}", ty.kind);
11658 };
11659 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Tuple(_)));
11660 }
11661
11662 #[test]
11663 fn parse_trailing_comma_tuple_type() {
11664 let (ast, diags) = parse_str("let p: [number, string,] = null;");
11665 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11666 let ty = type_of_let(single_stmt(&ast));
11667 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11668 panic!("expected Tuple");
11669 };
11670 assert_eq!(elems.len(), 2);
11671 }
11672
11673 #[test]
11674 fn parse_empty_tuple_type_rejected() {
11675 let (_ast, diags) = parse_str("let p: [] = null;");
11676 assert!(
11677 diags.iter().any(|d| d
11678 .message
11679 .contains("tuple types must have at least one element")),
11680 "expected empty-tuple diagnostic, got: {diags:?}"
11681 );
11682 }
11683
11684 #[test]
11685 fn parse_labeled_tuple_drops_labels() {
11686 let (ast, diags) = parse_str("let p: [first: number, string] = null;");
11687 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11688 let ty = type_of_let(single_stmt(&ast));
11689 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11690 panic!("expected Tuple, got {:?}", ty.kind);
11691 };
11692 assert_eq!(elems.len(), 2);
11693 assert!(
11694 elems
11695 .iter()
11696 .all(|e| matches!(e.kind, crate::TypeAnnotationKind::Name { .. }))
11697 );
11698 }
11699
11700 #[test]
11701 fn parse_labeled_optional_tuple_element() {
11702 let (ast, diags) = parse_str("let p: [first: number, second?: string] = null;");
11703 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11704 let ty = type_of_let(single_stmt(&ast));
11705 let crate::TypeAnnotationKind::Tuple(elements) = &ty.kind else {
11706 panic!("expected tuple")
11707 };
11708 assert!(matches!(
11709 elements[1].kind,
11710 crate::TypeAnnotationKind::Optional(_)
11711 ));
11712 }
11713
11714 #[test]
11715 fn parse_readonly_binds_to_the_postfix_type() {
11716 let (ast, diags) = parse_str("let p: readonly number[][] | null = null;");
11717 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11718 let ty = type_of_let(single_stmt(&ast));
11719 let crate::TypeAnnotationKind::Union(ref members) = ty.kind else {
11720 panic!("expected Union, got {:?}", ty.kind);
11721 };
11722 let crate::TypeAnnotationKind::Readonly(ref operand) = members[0].kind else {
11723 panic!("expected Readonly, got {:?}", members[0].kind);
11724 };
11725 let crate::TypeAnnotationKind::Array(ref inner) = operand.kind else {
11726 panic!("expected Array, got {:?}", operand.kind);
11727 };
11728 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Array(_)));
11729 }
11730
11731 #[test]
11732 fn parse_readonly_as_a_type_name_is_not_the_operator() {
11733 let (ast, diags) = parse_str("let p: readonly = null;");
11734 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11735 let ty = type_of_let(single_stmt(&ast));
11736 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11737 }
11738
11739 #[test]
11740 fn parse_generic_one_arg() {
11741 let (ast, diags) = parse_str("let x: Foo<number> = null;");
11742 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11743 let ty = type_of_let(single_stmt(&ast));
11744 let crate::TypeAnnotationKind::Name { ref name, ref args } = ty.kind else {
11745 panic!("expected Name, got {:?}", ty.kind);
11746 };
11747 assert_eq!(name.span, crate::Span::new(F, 7, 10).unwrap());
11748 assert_eq!(ty.span, crate::Span::new(F, 7, 18).unwrap());
11749 assert_eq!(args.len(), 1);
11750 assert!(matches!(
11751 args[0].kind,
11752 crate::TypeAnnotationKind::Name { .. }
11753 ));
11754 }
11755
11756 #[test]
11757 fn parse_generic_two_args() {
11758 let (ast, diags) = parse_str("let m: Map<string, number> = null;");
11759 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11760 let ty = type_of_let(single_stmt(&ast));
11761 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11762 panic!("expected Name");
11763 };
11764 assert_eq!(args.len(), 2);
11765 }
11766
11767 #[test]
11768 fn parse_generic_nested() {
11769 let (ast, diags) = parse_str("let b: Box<Box<T>> = null;");
11770 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11771 let ty = type_of_let(single_stmt(&ast));
11772 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11773 panic!("expected outer Name");
11774 };
11775 assert_eq!(args.len(), 1);
11776 let crate::TypeAnnotationKind::Name {
11777 args: ref inner, ..
11778 } = args[0].kind
11779 else {
11780 panic!("expected inner Name");
11781 };
11782 assert_eq!(inner.len(), 1);
11783 }
11784
11785 #[test]
11786 fn parse_generic_array_postfix() {
11787 let (ast, diags) = parse_str("let xs: Foo<number>[] = null;");
11788 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11789 let ty = type_of_let(single_stmt(&ast));
11790 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11791 panic!("expected Array");
11792 };
11793 assert!(
11794 matches!(inner.kind, crate::TypeAnnotationKind::Name { ref args, .. } if args.len() == 1)
11795 );
11796 }
11797
11798 #[test]
11799 fn parse_generic_empty_diagnoses() {
11800 let (_ast, diags) = parse_str("let x: Foo<> = null;");
11801 assert!(
11802 diags
11803 .iter()
11804 .any(|d| d.message.contains("empty generic argument list")),
11805 "expected empty-generic-args diagnostic, got: {diags:?}"
11806 );
11807 }
11808
11809 #[test]
11810 fn parse_generic_trailing_comma_ok() {
11811 let (_ast, diags) = parse_str("let x: Foo<number,> = null;");
11812 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11813 }
11814
11815 #[test]
11816 fn parse_generic_missing_close_diagnoses() {
11817 let (_ast, diags) = parse_str("let x: Foo<number = null;");
11818 assert!(
11819 diags
11820 .iter()
11821 .any(|d| d.message.contains("expected `,` or `>`")),
11822 "expected unterminated-generic diagnostic, got: {diags:?}"
11823 );
11824 }
11825
11826 #[test]
11827 fn parse_qualified_two_segments() {
11828 let (ast, diags) = parse_str("let x: Foo.Bar = null;");
11829 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11830 let ty = type_of_let(single_stmt(&ast));
11831 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11832 panic!("expected Qualified, got {:?}", ty.kind);
11833 };
11834 assert_eq!(path.len(), 2);
11835 assert_eq!(path[0].span, crate::Span::new(F, 7, 10).unwrap());
11837 assert_eq!(path[1].span, crate::Span::new(F, 11, 14).unwrap());
11838 assert!(args.is_empty());
11839 assert_eq!(ty.span, crate::Span::new(F, 7, 14).unwrap());
11840 }
11841
11842 #[test]
11843 fn parse_qualified_three_segments() {
11844 let (ast, diags) = parse_str("let x: Foo.Bar.Baz = null;");
11845 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11846 let ty = type_of_let(single_stmt(&ast));
11847 let crate::TypeAnnotationKind::Qualified { ref path, .. } = ty.kind else {
11848 panic!("expected Qualified, got {:?}", ty.kind);
11849 };
11850 assert_eq!(path.len(), 3);
11851 }
11852
11853 #[test]
11854 fn parse_qualified_with_generic_args() {
11855 let (ast, diags) = parse_str("let x: Foo.Box<number> = null;");
11856 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11857 let ty = type_of_let(single_stmt(&ast));
11858 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11859 panic!("expected Qualified, got {:?}", ty.kind);
11860 };
11861 assert_eq!(path.len(), 2);
11862 assert_eq!(args.len(), 1);
11863 assert!(matches!(
11864 args[0].kind,
11865 crate::TypeAnnotationKind::Name { .. }
11866 ));
11867 }
11868
11869 #[test]
11870 fn parse_qualified_with_array_postfix() {
11871 let (ast, diags) = parse_str("let xs: Foo.Bar[] = null;");
11872 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11873 let ty = type_of_let(single_stmt(&ast));
11874 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11875 panic!("expected Array, got {:?}", ty.kind);
11876 };
11877 assert!(matches!(
11878 inner.kind,
11879 crate::TypeAnnotationKind::Qualified { .. }
11880 ));
11881 }
11882
11883 #[test]
11884 fn parse_qualified_trailing_dot_diagnoses() {
11885 let (_ast, diags) = parse_str("let x: Foo. = null;");
11886 assert!(
11887 diags.iter().any(|d| d
11888 .message
11889 .contains("expected identifier after `.` in type name")),
11890 "expected dotted-name diagnostic, got: {diags:?}"
11891 );
11892 }
11893
11894 #[test]
11895 fn parse_qualified_void_root_rejected_dot() {
11896 let (ast, diags) = parse_str("function f(): void.Bar { }");
11897 assert!(!diags.is_empty(), "expected a diagnostic for `void.Bar`");
11898 let _ = ast;
11899 }
11900
11901 fn union_members(ty: &crate::TypeAnnotation) -> &[crate::TypeAnnotation] {
11902 match &ty.kind {
11903 crate::TypeAnnotationKind::Union(members) => members,
11904 other => panic!("expected Union, got {other:?}"),
11905 }
11906 }
11907
11908 #[test]
11909 fn parse_union_two_members() {
11910 let (ast, diags) = parse_str("let x: number | string = null;");
11911 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11912 let ty = type_of_let(single_stmt(&ast));
11913 let members = union_members(ty);
11914 assert_eq!(members.len(), 2);
11915 assert!(matches!(
11916 members[0].kind,
11917 crate::TypeAnnotationKind::Name { .. }
11918 ));
11919 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
11921 members[1].kind,
11922 crate::TypeAnnotationKind::Name { .. }
11923 ));
11924 assert_eq!(members[1].span, crate::Span::new(F, 16, 22).unwrap()); assert_eq!(ty.span, crate::Span::new(F, 7, 22).unwrap());
11926 }
11927
11928 #[test]
11929 fn parse_union_three_members() {
11930 let (ast, diags) = parse_str("let x: A | B | C = null;");
11931 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11932 let ty = type_of_let(single_stmt(&ast));
11933 let members = union_members(ty);
11934 assert_eq!(members.len(), 3);
11935 for m in members {
11936 assert!(matches!(m.kind, crate::TypeAnnotationKind::Name { .. }));
11937 }
11938 }
11939
11940 #[test]
11941 fn parse_union_lower_than_array() {
11942 let (ast, diags) = parse_str("let x: number[] | string = null;");
11943 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11944 let ty = type_of_let(single_stmt(&ast));
11945 let members = union_members(ty);
11946 assert_eq!(members.len(), 2);
11947 assert!(
11948 matches!(members[0].kind, crate::TypeAnnotationKind::Array(_)),
11949 "expected first member to be Array, got {:?}",
11950 members[0].kind
11951 );
11952 assert!(matches!(
11953 members[1].kind,
11954 crate::TypeAnnotationKind::Name { .. }
11955 ));
11956 }
11957
11958 #[test]
11959 fn parse_union_with_null_member() {
11960 let (ast, diags) = parse_str("let x: number | null = null;");
11961 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11962 let ty = type_of_let(single_stmt(&ast));
11963 let members = union_members(ty);
11964 assert_eq!(members.len(), 2);
11965 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
11967 members[1].kind,
11968 crate::TypeAnnotationKind::Name { .. }
11969 ));
11970 assert_eq!(members[1].span, crate::Span::new(F, 16, 20).unwrap()); }
11972
11973 #[test]
11974 fn parse_union_in_function_return_type() {
11975 let (ast, diags) = parse_str("function f(): number | string { }");
11976 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11977 let stmt = single_stmt(&ast);
11978 match stmt.kind {
11979 crate::StmtKind::Function {
11980 ref return_type, ..
11981 } => {
11982 let return_type = return_type.as_ref().expect("plain return type");
11983 let members = union_members(return_type);
11984 assert_eq!(members.len(), 2);
11985 }
11986 _ => panic!("expected Function"),
11987 }
11988 }
11989
11990 #[test]
11991 fn parse_union_in_function_param_type() {
11992 let (ast, diags) = parse_str("function f(x: number | string): void { }");
11993 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11994 let stmt = single_stmt(&ast);
11995 match stmt.kind {
11996 crate::StmtKind::Function { ref params, .. } => {
11997 let p_ty = params[0].ty.as_ref().expect("param type");
11998 let members = union_members(p_ty);
11999 assert_eq!(members.len(), 2);
12000 }
12001 _ => panic!("expected Function"),
12002 }
12003 }
12004
12005 #[test]
12006 fn parse_union_in_object_field_type() {
12007 let (ast, diags) = parse_str("let p: { x: number | string } = null;");
12008 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12009 let ty = type_of_let(single_stmt(&ast));
12010 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
12011 panic!("expected Object, got {:?}", ty.kind);
12012 };
12013 assert_eq!(fields.len(), 1);
12014 let members = union_members(&fields[0].ty);
12015 assert_eq!(members.len(), 2);
12016 }
12017
12018 #[test]
12019 fn parse_union_leading_pipe_is_forgiven() {
12020 let (ast, diags) = parse_str("let x: | number | string = null;");
12021 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12022 let ty = type_of_let(single_stmt(&ast));
12023 let members = union_members(ty);
12024 assert_eq!(members.len(), 2);
12025 }
12026
12027 #[test]
12028 fn parse_union_leading_pipe_single_member_unwraps() {
12029 let (ast, diags) = parse_str("let x: | number = null;");
12030 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12031 let ty = type_of_let(single_stmt(&ast));
12032 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
12033 }
12034
12035 #[test]
12036 fn parse_union_trailing_pipe_diagnoses() {
12037 let (_, diags) = parse_str("let x: number | = null;");
12038 assert!(!diags.is_empty());
12039 assert_eq!(diags[0].message, "expected type");
12040 }
12041
12042 #[test]
12043 fn snapshot_union_type_mix() {
12044 let (ast, diags) = parse_str("let xs: number[] | string | null = null;");
12045 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12046 insta::assert_debug_snapshot!(ast);
12047 }
12048
12049 #[test]
12050 fn parse_object_type_duplicate_field_diagnoses() {
12051 let (_, diags) = parse_str("let p: { readonly x: number; x: string } = null;");
12052 assert!(!diags.is_empty());
12053 assert!(
12054 diags
12055 .iter()
12056 .any(|d| d.message == "duplicate field `x` in object type"),
12057 "expected duplicate-field diagnostic, got: {diags:?}"
12058 );
12059 }
12060
12061 #[test]
12062 fn snapshot_object_and_array_literals() {
12063 let (ast, diags) = parse_str(r#"let mix = [{ a: 1, b: "hi" }, {}];"#);
12064 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12065 insta::assert_debug_snapshot!(ast);
12066 }
12067
12068 #[test]
12069 fn snapshot_function_with_while() {
12070 let source = "function loop(n: number): void {\n while (n > 0) {\n log(n);\n }\n}";
12071 let (ast, diags) = parse_str(source);
12072 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12073 insta::assert_debug_snapshot!(ast);
12074 }
12075
12076 fn parse_arrow_const(
12077 source: &str,
12078 ) -> (
12079 Ast,
12080 Vec<crate::ParamDecl>,
12081 Option<crate::TypeAnnotation>,
12082 crate::ArrowBody,
12083 ) {
12084 let (ast, diags) = parse_str(source);
12085 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12086 assert_eq!(ast.top_level.len(), 1);
12087 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12088 let value = match &stmt.kind {
12089 StmtKind::Const { value, .. } => *value,
12090 _ => panic!("expected const declaration"),
12091 };
12092 let value = match ast.try_expr(value).unwrap().kind {
12093 ExprKind::FunctionExpression { function, .. } => function,
12094 _ => value,
12095 };
12096 let expr = ast.try_expr(value).unwrap().clone();
12097 let (params, return_type, body) = match expr.kind {
12098 ExprKind::Arrow {
12099 params,
12100 return_type,
12101 body,
12102 ..
12103 } => (params, return_type, body),
12104 other => panic!("expected Arrow, got {other:?}"),
12105 };
12106 (ast, params, return_type, body)
12107 }
12108
12109 #[test]
12110 fn parses_anonymous_function_expression_as_arrow() {
12111 let (ast, params, return_type, body) =
12112 parse_arrow_const("const f = function (x: number): number { return x + 1; };");
12113 assert_eq!(params.len(), 1);
12114 assert_eq!(params[0].name.name, "x");
12115 assert!(return_type.is_some());
12116 assert!(matches!(body, crate::ArrowBody::Block(_)));
12117 let _ = ast;
12118 }
12119
12120 #[test]
12121 fn parses_function_expression_with_no_params() {
12122 let (_ast, params, _return_type, body) =
12123 parse_arrow_const("const f = function (): void {};");
12124 assert!(params.is_empty());
12125 assert!(matches!(body, crate::ArrowBody::Block(_)));
12126 }
12127
12128 #[test]
12130 fn parses_named_function_expression_signature() {
12131 let (_ast, params, _return_type, _body) =
12132 parse_arrow_const("const f = function named(x: number): number { return x + 1; };");
12133 assert_eq!(params.len(), 1);
12134 }
12135
12136 #[test]
12138 fn accepts_a_named_function_expression_that_calls_itself() {
12139 let (_ast, diags) =
12140 parse_str("const f = function bar(x: number): number { return bar(x); };");
12141 assert!(diags.is_empty(), "{diags:?}");
12142 }
12143
12144 #[test]
12146 fn accepts_a_named_function_expression_using_the_name_as_a_property() {
12147 let (_ast, diags) = parse_str("const f = function bar(o: O): number { return o.bar; };");
12148 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12149 }
12150
12151 #[test]
12153 fn accepts_a_named_function_expression_using_the_name_as_an_object_key() {
12154 let (_ast, diags) = parse_str(
12155 "const f = function bar(x: number): number { const o = { bar: 1 }; return o.bar + x; };",
12156 );
12157 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12158 }
12159
12160 #[test]
12162 fn accepts_a_named_function_expression_with_its_name_in_a_string() {
12163 let (_ast, diags) =
12164 parse_str("const f = function bar(x: number): string { return \"bar\"; };");
12165 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12166 }
12167
12168 #[test]
12170 fn accepts_a_self_call_inside_a_template_substitution() {
12171 let (_ast, diags) =
12172 parse_str("const f = function bar(x: number): string { return `${bar(0)}`; };");
12173 assert!(diags.is_empty(), "{diags:?}");
12174 }
12175
12176 #[test]
12177 fn rejects_a_generic_function_expression() {
12178 let (_ast, diags) = parse_str("const f = function <T>(x: T): T { return x; };");
12179 assert!(
12180 diags
12181 .iter()
12182 .any(|d| d.message.contains("generic function expressions")),
12183 "generic function expression should be rejected: {diags:?}"
12184 );
12185 }
12186
12187 #[test]
12188 fn accepts_this_inside_a_function_expression_in_a_class_method() {
12189 let (_ast, diags) = parse_str(
12190 "class C { x: number = 1; m(): number { \
12191 const f = function (this: { x: number }): number { return this.x; }; return f(); } }",
12192 );
12193 assert!(diags.is_empty(), "{diags:?}");
12194 }
12195
12196 #[test]
12197 fn retains_this_boundary_inside_method_shorthand() {
12198 let (ast, diags) = parse_str(
12199 "class C { x: number = 1; m(): number { \
12200 const o = { x: 2, g(): number { return this.x; } }; return o.g(); } }",
12201 );
12202 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12203 assert!((0..ast.exprs_len()).any(|index| matches!(
12204 ast.try_expr(crate::ExprId(index as u32)).unwrap().kind,
12205 ExprKind::ThisOutsideReceiver
12206 )));
12207 }
12208
12209 #[test]
12210 fn this_still_parses_in_an_ordinary_class_method() {
12211 let (_ast, diags) = parse_str("class C { x: number = 1; m(): number { return this.x; } }");
12212 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12213 }
12214
12215 #[test]
12217 fn function_keyword_still_parses_as_an_object_key() {
12218 let (_ast, diags) = parse_str("let o = { function: 1 };");
12219 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12220 }
12221
12222 fn object_literal_member_values(source: &str) -> (Ast, Vec<crate::ExprId>) {
12223 let (ast, diags) = parse_str(source);
12224 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12225 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12226 let value = match &stmt.kind {
12227 StmtKind::Const { value, .. } => *value,
12228 other => panic!("expected a const declaration, got {other:?}"),
12229 };
12230 let members = match &ast.try_expr(value).unwrap().kind {
12231 ExprKind::ObjectLiteral { members } => members
12232 .iter()
12233 .map(crate::ObjectLiteralMember::value)
12234 .collect::<Vec<_>>(),
12235 other => panic!("expected ObjectLiteral, got {other:?}"),
12236 };
12237 (ast, members)
12238 }
12239
12240 #[test]
12241 fn parses_object_literal_method_shorthand_as_arrow() {
12242 let (ast, members) =
12243 object_literal_member_values("const o = { m(x: number): number { return x; } };");
12244 assert_eq!(members.len(), 1);
12245 match &ast.try_expr(members[0]).unwrap().kind {
12246 ExprKind::Arrow { params, body, .. } => {
12247 assert_eq!(params.len(), 1);
12248 assert_eq!(params[0].name.name, "x");
12249 assert!(matches!(body, crate::ArrowBody::Block(_)));
12250 }
12251 other => panic!("expected Arrow, got {other:?}"),
12252 }
12253 }
12254
12255 #[test]
12257 fn parses_method_shorthand_with_a_keyword_name() {
12258 let (ast, members) =
12259 object_literal_member_values("const o = { if(x: number): number { return x; } };");
12260 assert!(matches!(
12261 ast.try_expr(members[0]).unwrap().kind,
12262 ExprKind::Arrow { .. }
12263 ));
12264 }
12265
12266 #[test]
12267 fn parses_method_shorthand_alongside_plain_and_shorthand_properties() {
12268 let (ast, members) =
12269 object_literal_member_values("const o = { a: 1, m(): void {}, b: 2 };");
12270 assert_eq!(members.len(), 3);
12271 assert!(matches!(
12272 ast.try_expr(members[1]).unwrap().kind,
12273 ExprKind::Arrow { .. }
12274 ));
12275 }
12276
12277 #[test]
12278 fn parses_bare_ident_arrow_expression_body() {
12279 let (_ast, params, return_type, body) = parse_arrow_const("const f = x => x + 1;");
12280 assert_eq!(params.len(), 1);
12281 assert_eq!(params[0].name.name, "x");
12282 assert!(params[0].ty.is_none());
12283 assert!(return_type.is_none());
12284 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12285 }
12286
12287 #[test]
12288 fn parses_zero_param_arrow() {
12289 let (_ast, params, return_type, body) = parse_arrow_const("const f = () => 0;");
12290 assert!(params.is_empty());
12291 assert!(return_type.is_none());
12292 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12293 }
12294
12295 #[test]
12296 fn parses_paren_unannotated_arrow() {
12297 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x) => x;");
12298 assert_eq!(params.len(), 1);
12299 assert_eq!(params[0].name.name, "x");
12300 assert!(params[0].ty.is_none());
12301 }
12302
12303 #[test]
12304 fn parses_typed_param_arrow() {
12305 let (_ast, params, return_type, body) =
12306 parse_arrow_const("const f = (x: number) => x * 2;");
12307 assert_eq!(params.len(), 1);
12308 assert!(params[0].ty.is_some());
12309 assert!(return_type.is_none());
12310 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12311 }
12312
12313 #[test]
12314 fn parses_typed_param_and_return_arrow() {
12315 let (_ast, params, return_type, body) =
12316 parse_arrow_const("const f = (x: number): number => x * 2;");
12317 assert_eq!(params.len(), 1);
12318 assert!(params[0].ty.is_some());
12319 assert!(return_type.is_some());
12320 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12321 }
12322
12323 #[test]
12324 fn parses_multi_param_unannotated_arrow() {
12325 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x, y) => x + y;");
12326 assert_eq!(params.len(), 2);
12327 assert_eq!(params[0].name.name, "x");
12328 assert_eq!(params[1].name.name, "y");
12329 assert!(params[0].ty.is_none());
12330 assert!(params[1].ty.is_none());
12331 }
12332
12333 #[test]
12334 fn parses_multi_param_typed_arrow() {
12335 let (_ast, params, _return_type, _body) =
12336 parse_arrow_const("const f = (x: number, y: number) => x + y;");
12337 assert_eq!(params.len(), 2);
12338 assert!(params[0].ty.is_some());
12339 assert!(params[1].ty.is_some());
12340 }
12341
12342 #[test]
12343 fn parses_block_body_arrow() {
12344 let (_ast, params, _return_type, body) =
12345 parse_arrow_const("const f = (x: number) => { return x; };");
12346 assert_eq!(params.len(), 1);
12347 assert!(matches!(body, crate::ArrowBody::Block(_)));
12348 }
12349
12350 #[test]
12351 fn rejects_typed_bare_ident_arrow() {
12352 let (_ast, diags) = parse_str("const f = x: number => x;");
12353 assert!(
12354 !diags.is_empty(),
12355 "expected diagnostics for `x: number => …`",
12356 );
12357 }
12358
12359 #[test]
12360 fn arrow_inside_call_argument_parses() {
12361 let (ast, diags) = parse_str("f((x: number) => x + 1, 2);");
12362 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12363 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12364 let call = match &stmt.kind {
12365 StmtKind::Expr(eid) => ast.try_expr(*eid).unwrap(),
12366 _ => panic!("expected expression statement"),
12367 };
12368 let args = match &call.kind {
12369 ExprKind::Call { args, .. } => args.clone(),
12370 other => panic!("expected Call, got {other:?}"),
12371 };
12372 assert_eq!(args.len(), 2);
12373 assert!(matches!(
12374 ast.try_expr(args[0]).unwrap().kind,
12375 ExprKind::Arrow { .. }
12376 ));
12377 }
12378
12379 #[test]
12380 fn function_decl_param_still_requires_annotation() {
12381 let (_ast, diags) = parse_str("function f(x): number { return x; }");
12382 assert!(
12383 diags.iter().any(|d| d.message.contains("type annotation")),
12384 "expected `parameter requires a type annotation` diagnostic, got {diags:?}",
12385 );
12386 }
12387
12388 #[test]
12389 fn paren_expression_still_parses_when_no_arrow() {
12390 let (ast, diags) = parse_str("const a = (1 + 2);");
12391 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12392 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12393 let value = match &stmt.kind {
12394 StmtKind::Const { value, .. } => *value,
12395 _ => panic!("expected const declaration"),
12396 };
12397 assert!(matches!(
12398 ast.try_expr(value).unwrap().kind,
12399 ExprKind::Paren(_)
12400 ));
12401 }
12402
12403 #[test]
12404 fn parse_named_import_single() {
12405 let (ast, diags) = parse_str("import { v4 } from \"submilli:uuid\";");
12406 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12407 assert_eq!(ast.top_level.len(), 1);
12408 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12409 let (module, kind) = match &stmt.kind {
12410 StmtKind::Import { module, kind, .. } => (module, kind),
12411 other => panic!("expected Import, got {other:?}"),
12412 };
12413 assert_eq!(module, "submilli:uuid");
12414 let specs = match kind {
12415 ImportKind::Named(specs) => specs,
12416 other => panic!("expected Named, got {other:?}"),
12417 };
12418 assert_eq!(specs.len(), 1);
12419 assert_eq!(specs[0].imported_name.name, "v4");
12420 assert_eq!(specs[0].local_name.name, "v4");
12421 }
12422
12423 #[test]
12424 fn parse_named_import_multi_and_alias() {
12425 let (ast, diags) =
12426 parse_str("import { v4, v7 as makeId, validate } from \"submilli:uuid\";");
12427 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12428 let specs = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12429 StmtKind::Import {
12430 kind: ImportKind::Named(s),
12431 ..
12432 } => s.clone(),
12433 other => panic!("expected named import, got {other:?}"),
12434 };
12435 assert_eq!(specs.len(), 3);
12436 assert_eq!(specs[0].imported_name.name, "v4");
12437 assert_eq!(specs[0].local_name.name, "v4");
12438 assert_eq!(specs[1].imported_name.name, "v7");
12439 assert_eq!(specs[1].local_name.name, "makeId");
12440 assert_eq!(specs[2].imported_name.name, "validate");
12441 assert_eq!(specs[2].local_name.name, "validate");
12442 }
12443
12444 #[test]
12445 fn parse_namespace_import() {
12446 let (ast, diags) = parse_str("import uuid from \"submilli:uuid\";");
12447 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12448 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12449 StmtKind::Import { kind, .. } => kind.clone(),
12450 other => panic!("expected import, got {other:?}"),
12451 };
12452 let local = match kind {
12453 ImportKind::Namespace { local_name } => local_name,
12454 other => panic!("expected Namespace, got {other:?}"),
12455 };
12456 assert_eq!(local.name, "uuid");
12457 }
12458
12459 #[test]
12460 fn parse_wildcard_namespace_import_synonym() {
12461 let (ast, diags) = parse_str("import * as uuid from \"submilli:uuid\";");
12462 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12463 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12464 StmtKind::Import { kind, .. } => kind.clone(),
12465 other => panic!("expected import, got {other:?}"),
12466 };
12467 let local = match kind {
12468 ImportKind::Namespace { local_name } => local_name,
12469 other => panic!("expected Namespace, got {other:?}"),
12470 };
12471 assert_eq!(local.name, "uuid");
12472 }
12473
12474 #[test]
12475 fn parse_wildcard_namespace_import_without_as_rejected() {
12476 let (_ast, diags) = parse_str("import * from \"submilli:uuid\";");
12477 assert!(
12478 diags
12479 .iter()
12480 .any(|d| d.message.contains("expected `as <name>`")),
12481 "expected missing-as diagnostic, got: {diags:?}",
12482 );
12483 }
12484
12485 #[test]
12486 fn parse_combined_default_and_named_rejected() {
12487 let (_ast, diags) = parse_str("import uuid, { v4 } from \"submilli:uuid\";");
12488 assert!(
12489 diags
12490 .iter()
12491 .any(|d| d.message.contains("combining default and named")),
12492 "expected combined-import diagnostic, got: {diags:?}",
12493 );
12494 }
12495
12496 #[test]
12497 fn parse_side_effect_import_rejected() {
12498 let (_ast, diags) = parse_str("import \"submilli:uuid\";");
12499 assert!(
12500 diags
12501 .iter()
12502 .any(|d| d.message.contains("side-effect-only imports")),
12503 "expected side-effect-import diagnostic, got: {diags:?}",
12504 );
12505 }
12506
12507 #[test]
12508 fn parse_empty_specifier_list_rejected() {
12509 let (_ast, diags) = parse_str("import {} from \"submilli:uuid\";");
12510 assert!(
12511 diags
12512 .iter()
12513 .any(|d| d.message.contains("empty import specifier list")),
12514 "expected empty-specifier diagnostic, got: {diags:?}",
12515 );
12516 }
12517
12518 #[test]
12519 fn parse_import_inside_function_body_rejected() {
12520 let (_ast, diags) =
12521 parse_str("function main(): void { import { v4 } from \"submilli:uuid\"; }");
12522 assert!(
12523 diags
12524 .iter()
12525 .any(|d| d.message.contains("must appear at the top of the file")),
12526 "expected top-level diagnostic, got: {diags:?}",
12527 );
12528 }
12529
12530 #[test]
12531 fn parse_missing_from_rejected() {
12532 let (_ast, diags) = parse_str("import { v4 } \"submilli:uuid\";");
12533 assert!(
12534 diags.iter().any(|d| d.message.contains("expected `from`")),
12535 "expected missing-from diagnostic, got: {diags:?}",
12536 );
12537 }
12538
12539 #[test]
12540 fn parse_missing_module_specifier_rejected() {
12541 let (_ast, diags) = parse_str("import { v4 } from;");
12542 assert!(
12543 diags
12544 .iter()
12545 .any(|d| d.message.contains("expected module specifier")),
12546 "expected missing-module diagnostic, got: {diags:?}",
12547 );
12548 }
12549
12550 #[test]
12551 fn parse_export_function_marks_exported() {
12552 let (ast, diags) = parse_str("export function main(): void {}");
12555 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12556 let stmt = single_stmt(&ast);
12557 assert!(
12558 matches!(stmt.kind, crate::StmtKind::Function { .. }),
12559 "expected Function decl, got {:?}",
12560 stmt.kind,
12561 );
12562 assert_eq!(ast.exported_decls.len(), 1, "one exported decl");
12563 assert_eq!(ast.exported_decls[0].stmt, ast.top_level[0]);
12564 }
12565
12566 #[test]
12567 fn parse_export_const_type_interface_enum_are_marked() {
12568 for src in [
12569 "export const X: number = 1;",
12570 "export type Id = string;",
12571 "export interface P { x: number; }",
12572 "export enum E { A, B }",
12573 ] {
12574 let (ast, diags) = parse_str(src);
12575 assert!(diags.is_empty(), "unexpected diags for {src:?}: {diags:?}");
12576 assert_eq!(ast.top_level.len(), 1, "one decl for {src:?}");
12577 assert_eq!(ast.exported_decls.len(), 1, "one exported decl for {src:?}");
12578 }
12579 }
12580
12581 #[test]
12582 fn parse_export_default_is_rejected_with_focused_message() {
12583 let (_ast, diags) = parse_str("export default function main(): void {}");
12584 assert!(
12585 diags
12586 .iter()
12587 .any(|d| d.message.contains("`export default` is not supported")),
12588 "expected focused export-default diagnostic, got: {diags:?}",
12589 );
12590 assert!(
12591 !diags.iter().any(|d| d.message.contains("expected `;`")),
12592 "the spurious missing-semicolon error must be gone: {diags:?}",
12593 );
12594 }
12595
12596 #[test]
12597 fn parse_export_list_parses_as_export_from() {
12598 let (ast, diags) = parse_str("function main(): void {}\nexport { main };");
12601 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12602 assert_eq!(ast.top_level.len(), 2, "function + export-from");
12603 let export = ast.try_stmt(ast.top_level[1]).unwrap();
12604 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12605 panic!("expected ExportFrom, got {:?}", export.kind);
12606 };
12607 assert!(source.is_none(), "bare `export {{ x }}` has no source");
12608 assert_eq!(specs.len(), 1);
12609 assert_eq!(specs[0].imported_name.name, "main");
12610 assert_eq!(specs[0].local_name.name, "main");
12611 }
12612
12613 #[test]
12614 fn parse_export_from_with_source_and_alias_parses() {
12615 let (ast, diags) = parse_str("export { foo, bar as baz } from \"./util\";");
12616 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12617 let export = ast.try_stmt(ast.top_level[0]).unwrap();
12618 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12619 panic!("expected ExportFrom, got {:?}", export.kind);
12620 };
12621 let Some((module, _)) = source else {
12622 panic!("expected a `from` source");
12623 };
12624 assert_eq!(module, "./util");
12625 assert_eq!(specs.len(), 2);
12626 assert_eq!(specs[1].imported_name.name, "bar");
12627 assert_eq!(specs[1].local_name.name, "baz");
12628 }
12629
12630 #[test]
12631 fn parse_export_inside_function_body_rejected() {
12632 let (_ast, diags) = parse_str("function main(): void { export const X: number = 1; }");
12633 assert!(
12634 diags
12635 .iter()
12636 .any(|d| d.message.contains("must appear at the top of the file")),
12637 "expected top-level diagnostic, got: {diags:?}",
12638 );
12639 }
12640
12641 #[test]
12642 fn parse_throw_with_expression() {
12643 let (ast, diags) = parse_str(r#"throw new Error("oops");"#);
12644 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12645 let stmt = single_stmt(&ast);
12646 let crate::StmtKind::Throw { value } = stmt.kind else {
12647 panic!("expected Throw, got {:?}", stmt.kind);
12648 };
12649 assert!(matches!(
12650 ast.try_expr(value).unwrap().kind,
12651 crate::ExprKind::New { .. }
12652 ));
12653 }
12654
12655 #[test]
12656 fn parse_new_with_dotted_callee() {
12657 let (ast, diags) = parse_str("new Temporal.Duration({});");
12658 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12659 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12660 panic!("expected expression statement");
12661 };
12662 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12663 panic!("expected New");
12664 };
12665 let crate::ExprKind::FieldAccess { name, .. } = &ast.try_expr(*callee).unwrap().kind else {
12666 panic!("expected FieldAccess callee");
12667 };
12668 assert_eq!(name.name, "Duration");
12669 }
12670
12671 #[test]
12672 fn parse_new_with_three_level_dotted_callee() {
12673 let (ast, diags) = parse_str("new Foo.Bar.Baz();");
12674 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12675 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12676 panic!("expected expression statement");
12677 };
12678 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12679 panic!("expected New");
12680 };
12681 let crate::ExprKind::FieldAccess { receiver, name } = &ast.try_expr(*callee).unwrap().kind
12682 else {
12683 panic!("expected outer FieldAccess");
12684 };
12685 assert_eq!(name.name, "Baz");
12686 let crate::ExprKind::FieldAccess { name: mid_name, .. } =
12687 &ast.try_expr(*receiver).unwrap().kind
12688 else {
12689 panic!("expected inner FieldAccess");
12690 };
12691 assert_eq!(mid_name.name, "Bar");
12692 }
12693
12694 #[test]
12695 fn parse_new_with_trailing_dot_diagnoses() {
12696 let (_ast, diags) = parse_str("new Foo.;");
12697 assert!(
12698 diags.iter().any(|d| d
12699 .message
12700 .contains("expected identifier after `.` in constructor name")),
12701 "expected trailing-dot diagnostic, got: {diags:?}",
12702 );
12703 }
12704
12705 #[test]
12706 fn throw_without_expression_diagnoses() {
12707 let (_ast, diags) = parse_str("throw;");
12708 assert!(
12709 diags
12710 .iter()
12711 .any(|d| d.message == "expected expression after `throw`"),
12712 "expected bare-throw diagnostic, got: {diags:?}",
12713 );
12714 }
12715
12716 #[test]
12717 fn parse_try_catch() {
12718 let (ast, diags) = parse_str("try { } catch (e: Error) { }");
12719 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12720 let stmt = single_stmt(&ast);
12721 let crate::StmtKind::Try {
12722 body,
12723 ref catches,
12724 ref finally,
12725 } = stmt.kind
12726 else {
12727 panic!("expected Try, got {:?}", stmt.kind);
12728 };
12729 assert!(matches!(
12730 ast.try_stmt(body).unwrap().kind,
12731 crate::StmtKind::Block(ref v) if v.is_empty()
12732 ));
12733 let [clause] = catches.as_slice() else {
12734 panic!("expected one catch clause, got {catches:?}");
12735 };
12736 assert_eq!(clause.binding.name, "e");
12737 assert!(clause.ty.is_some());
12738 assert!(finally.is_none());
12739 }
12740
12741 #[test]
12742 fn parse_try_finally() {
12743 let (ast, diags) = parse_str("try { } finally { }");
12744 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12745 let stmt = single_stmt(&ast);
12746 let crate::StmtKind::Try {
12747 ref catches,
12748 ref finally,
12749 ..
12750 } = stmt.kind
12751 else {
12752 panic!("expected Try");
12753 };
12754 assert!(catches.is_empty());
12755 let finally_id = finally.expect("finally block");
12756 assert!(matches!(
12757 ast.try_stmt(finally_id).unwrap().kind,
12758 crate::StmtKind::Block(_)
12759 ));
12760 }
12761
12762 #[test]
12763 fn parse_try_catch_finally() {
12764 let (ast, diags) = parse_str("try { } catch (e: Error) { } finally { }");
12765 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12766 let stmt = single_stmt(&ast);
12767 let crate::StmtKind::Try {
12768 ref catches,
12769 ref finally,
12770 ..
12771 } = stmt.kind
12772 else {
12773 panic!("expected Try");
12774 };
12775 assert_eq!(catches.len(), 1);
12776 assert!(finally.is_some());
12777 }
12778
12779 #[test]
12780 fn bare_try_diagnoses() {
12781 let (_ast, diags) = parse_str("try { }");
12782 assert!(
12783 diags
12784 .iter()
12785 .any(|d| d.message == "try requires at least one of `catch` or `finally`"),
12786 "expected bare-try diagnostic, got: {diags:?}",
12787 );
12788 }
12789
12790 #[test]
12791 fn parse_untyped_catch() {
12792 let (ast, diags) = parse_str("try { } catch (e) { }");
12793 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12794 let stmt = single_stmt(&ast);
12795 let crate::StmtKind::Try {
12796 ref catches,
12797 ref finally,
12798 ..
12799 } = stmt.kind
12800 else {
12801 panic!("expected Try");
12802 };
12803 let [clause] = catches.as_slice() else {
12804 panic!("expected one catch clause, got {catches:?}");
12805 };
12806 assert_eq!(clause.binding.name, "e");
12807 assert!(clause.ty.is_none());
12808 assert!(finally.is_none());
12809 }
12810
12811 #[test]
12812 fn parse_multiple_catch_clauses() {
12813 let (ast, diags) = parse_str("try { } catch (e: HttpError) { } catch (f) { }");
12814 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12815 let stmt = single_stmt(&ast);
12816 let crate::StmtKind::Try {
12817 ref catches,
12818 ref finally,
12819 ..
12820 } = stmt.kind
12821 else {
12822 panic!("expected Try");
12823 };
12824 let [first, second] = catches.as_slice() else {
12825 panic!("expected two catch clauses, got {catches:?}");
12826 };
12827 assert_eq!(first.binding.name, "e");
12828 assert!(first.ty.is_some());
12829 assert_eq!(second.binding.name, "f");
12830 assert!(second.ty.is_none());
12831 assert!(finally.is_none());
12832 }
12833
12834 #[test]
12835 fn catch_after_finally_diagnoses() {
12836 let (_ast, diags) = parse_str("try { } finally { } catch (e: Error) { }");
12837 assert!(
12838 !diags.is_empty(),
12839 "expected diagnostic for catch-after-finally"
12840 );
12841 }
12842
12843 #[test]
12844 fn catch_missing_open_paren_diagnoses() {
12845 let (_ast, diags) = parse_str("try { } catch e: Error) { }");
12846 assert!(
12847 diags
12848 .iter()
12849 .any(|d| d.message == "expected `(` after `catch`"),
12850 "expected open-paren diagnostic, got: {diags:?}",
12851 );
12852 }
12853
12854 fn class_members(ast: &Ast) -> &[crate::ClassMember] {
12855 match &single_stmt(ast).kind {
12856 StmtKind::ClassDecl { members, .. } => members,
12857 other => panic!("expected ClassDecl, got {other:?}"),
12858 }
12859 }
12860
12861 #[test]
12862 fn parse_animal_class() {
12863 let src = r#"
12864class Animal {
12865 name: string;
12866 private sound: string;
12867 readonly species: string;
12868
12869 constructor(name: string, sound: string, species: string) {
12870 this.name = name;
12871 this.sound = sound;
12872 this.species = species;
12873 }
12874
12875 speak(): string {
12876 return this.name + " says " + this.sound;
12877 }
12878}
12879"#;
12880 let (ast, diags) = parse_str(src);
12881 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12882 let StmtKind::ClassDecl {
12883 name,
12884 generics,
12885 extends,
12886 implements,
12887 members,
12888 ..
12889 } = &single_stmt(&ast).kind
12890 else {
12891 panic!("expected ClassDecl");
12892 };
12893 assert_eq!(name.name, "Animal");
12894 assert!(generics.is_empty());
12895 assert!(extends.is_none());
12896 assert!(implements.is_empty());
12897 assert_eq!(members.len(), 5);
12898
12899 let crate::ClassMember::Field {
12900 name, modifiers, ..
12901 } = &members[0]
12902 else {
12903 panic!("member 0 should be a field");
12904 };
12905 assert_eq!(name.name, "name");
12906 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12907 assert!(modifiers.readonly.is_none());
12908
12909 let crate::ClassMember::Field { modifiers, .. } = &members[1] else {
12910 panic!("member 1 should be a field");
12911 };
12912 assert_eq!(modifiers.visibility, crate::Visibility::Private);
12913
12914 let crate::ClassMember::Field { modifiers, .. } = &members[2] else {
12915 panic!("member 2 should be a field");
12916 };
12917 assert!(modifiers.readonly.is_some());
12918 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12919
12920 assert!(matches!(
12921 &members[3],
12922 crate::ClassMember::Constructor { .. }
12923 ));
12924
12925 let crate::ClassMember::Method { name, .. } = &members[4] else {
12926 panic!("member 4 should be a method");
12927 };
12928 assert_eq!(name.name, "speak");
12929 }
12930
12931 #[test]
12932 fn parse_dog_class_extends_super_and_void_method() {
12933 let src = r#"
12934class Dog extends Animal {
12935 private tricks: string[];
12936
12937 constructor(name: string) {
12938 super(name, "woof", "canis familiaris");
12939 this.tricks = [];
12940 }
12941
12942 learn(trick: string): void {
12943 this.tricks.push(trick);
12944 }
12945}
12946"#;
12947 let (ast, diags) = parse_str(src);
12948 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12949 let StmtKind::ClassDecl {
12950 extends, members, ..
12951 } = &single_stmt(&ast).kind
12952 else {
12953 panic!("expected ClassDecl");
12954 };
12955 assert!(extends.is_some(), "Dog should extend Animal");
12956 assert_eq!(members.len(), 3);
12957 assert!(matches!(&members[0], crate::ClassMember::Field { .. }));
12958 assert!(matches!(
12959 &members[1],
12960 crate::ClassMember::Constructor { .. }
12961 ));
12962 assert!(matches!(&members[2], crate::ClassMember::Method { .. }));
12963 }
12964
12965 #[test]
12966 fn parse_class_implements_list() {
12967 let (ast, diags) = parse_str("class C implements I, J { run(): void {} }");
12968 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12969 let StmtKind::ClassDecl { implements, .. } = &single_stmt(&ast).kind else {
12970 panic!("expected ClassDecl");
12971 };
12972 assert_eq!(implements.len(), 2);
12973 }
12974
12975 #[test]
12976 fn parse_class_extends_and_implements() {
12977 let (ast, diags) = parse_str("class C extends B implements I { run(): void {} }");
12978 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12979 let StmtKind::ClassDecl {
12980 extends,
12981 implements,
12982 ..
12983 } = &single_stmt(&ast).kind
12984 else {
12985 panic!("expected ClassDecl");
12986 };
12987 assert!(extends.is_some());
12988 assert_eq!(implements.len(), 1);
12989 }
12990
12991 #[test]
12992 fn parse_class_generics_optional_field_and_initializer() {
12993 let (ast, diags) = parse_str("class Box<T> { value?: T; items: T[] = []; }");
12994 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12995 let StmtKind::ClassDecl {
12996 generics, members, ..
12997 } = &single_stmt(&ast).kind
12998 else {
12999 panic!("expected ClassDecl");
13000 };
13001 assert_eq!(generics.len(), 1);
13002 assert_eq!(generics[0].name, "T");
13003
13004 let crate::ClassMember::Field {
13005 optional,
13006 initializer,
13007 ..
13008 } = &members[0]
13009 else {
13010 panic!("member 0 should be a field");
13011 };
13012 assert!(*optional);
13013 assert!(initializer.is_none());
13014
13015 let crate::ClassMember::Field { initializer, .. } = &members[1] else {
13016 panic!("member 1 should be a field");
13017 };
13018 assert!(initializer.is_some());
13019 }
13020
13021 #[test]
13022 fn export_class_is_marked() {
13023 let (ast, diags) = parse_str("export class Foo {}");
13024 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13025 assert_eq!(ast.top_level.len(), 1);
13026 assert_eq!(ast.exported_decls.len(), 1);
13027 assert!(matches!(
13028 &single_stmt(&ast).kind,
13029 StmtKind::ClassDecl { .. }
13030 ));
13031 }
13032
13033 #[test]
13034 fn parameterless_index_signature_is_reported_from_its_member_start() {
13035 for (source, expected) in [
13036 ("interface P { readonly []: number }", "readonly []"),
13037 ("let p: { readonly []: number } = {};", "readonly []"),
13038 ("let q: { []: number } = {};", "[]"),
13039 ] {
13040 let (_ast, diags) = parse_str(source);
13041 let diag = diags
13042 .iter()
13043 .find(|d| d.message.contains("must declare exactly one parameter"))
13044 .unwrap_or_else(|| panic!("no diagnostic for {source}"));
13045 let reported = &source[diag.span.start as usize..diag.span.end as usize];
13046 assert_eq!(reported, expected, "{source}");
13047 }
13048 }
13049
13050 #[test]
13051 fn unterminated_object_type_expects_closing_brace() {
13052 let (_ast, diags) = parse_str("let p: { a: number\n");
13053 assert!(
13054 diags.iter().any(|d| d.message == "expected `}`"),
13055 "{diags:?}"
13056 );
13057 }
13058
13059 #[test]
13060 fn conflicting_visibility_modifiers_diagnose() {
13061 let (_ast, diags) = parse_str("class C { public private x: number; }");
13062 assert!(
13063 diags
13064 .iter()
13065 .any(|d| d.message.contains("at most one visibility modifier")),
13066 "got: {diags:?}",
13067 );
13068 }
13069
13070 #[test]
13071 fn duplicate_readonly_modifier_diagnoses() {
13072 let (_ast, diags) = parse_str("class C { readonly readonly x: number; }");
13073 assert!(
13074 diags
13075 .iter()
13076 .any(|d| d.message.contains("duplicate `readonly`")),
13077 "got: {diags:?}",
13078 );
13079 }
13080
13081 #[test]
13082 fn field_named_like_a_modifier_is_allowed() {
13083 let (ast, diags) = parse_str("class C { private: number; }");
13084 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13085 let crate::ClassMember::Field {
13086 name, modifiers, ..
13087 } = &class_members(&ast)[0]
13088 else {
13089 panic!("expected a field");
13090 };
13091 assert_eq!(name.name, "private");
13092 assert_eq!(modifiers.visibility, crate::Visibility::Public);
13093 }
13094
13095 #[test]
13096 fn method_named_get_is_allowed() {
13097 let (ast, diags) = parse_str("class C { get(): number { return 1; } }");
13098 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13099 let crate::ClassMember::Method { name, .. } = &class_members(&ast)[0] else {
13100 panic!("expected a method");
13101 };
13102 assert_eq!(name.name, "get");
13103 }
13104
13105 #[test]
13106 fn static_method_parses() {
13107 let (ast, diags) = parse_str("class C { static f(): number { return 1; } }");
13108 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13109 let crate::ClassMember::Method {
13110 name, modifiers, ..
13111 } = &class_members(&ast)[0]
13112 else {
13113 panic!("expected a method");
13114 };
13115 assert_eq!(name.name, "f");
13116 assert!(modifiers.static_span.is_some());
13117 }
13118
13119 #[test]
13120 fn static_readonly_field_parses() {
13121 let (ast, diags) = parse_str("class C { static readonly x: number = 1; }");
13122 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13123 let crate::ClassMember::Field {
13124 name,
13125 modifiers,
13126 initializer,
13127 ..
13128 } = &class_members(&ast)[0]
13129 else {
13130 panic!("expected a field");
13131 };
13132 assert_eq!(name.name, "x");
13133 assert!(modifiers.static_span.is_some());
13134 assert!(modifiers.readonly.is_some());
13135 assert!(initializer.is_some());
13136 }
13137
13138 #[test]
13139 fn private_static_method_parses() {
13140 let (ast, diags) = parse_str("class C { private static f(): void {} }");
13141 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13142 let crate::ClassMember::Method { modifiers, .. } = &class_members(&ast)[0] else {
13143 panic!("expected a method");
13144 };
13145 assert_eq!(modifiers.visibility, crate::Visibility::Private);
13146 assert!(modifiers.static_span.is_some());
13147 }
13148
13149 #[test]
13150 fn method_named_static_is_allowed() {
13151 let (ast, diags) = parse_str("class C { static(): number { return 1; } }");
13152 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13153 let crate::ClassMember::Method {
13154 name, modifiers, ..
13155 } = &class_members(&ast)[0]
13156 else {
13157 panic!("expected a method");
13158 };
13159 assert_eq!(name.name, "static");
13160 assert!(modifiers.static_span.is_none());
13161 }
13162
13163 #[test]
13164 fn visibility_after_static_diagnoses() {
13165 let (_ast, diags) = parse_str("class C { static private f(): void {} }");
13166 assert!(
13167 diags
13168 .iter()
13169 .any(|d| d.message.contains("`private` must come before `static`")),
13170 "got: {diags:?}",
13171 );
13172 }
13173
13174 #[test]
13175 fn duplicate_static_modifier_diagnoses() {
13176 let (_ast, diags) = parse_str("class C { static static f(): void {} }");
13177 assert!(
13178 diags
13179 .iter()
13180 .any(|d| d.message.contains("duplicate `static` modifier")),
13181 "got: {diags:?}",
13182 );
13183 }
13184
13185 #[test]
13186 fn static_accessor_is_rejected() {
13187 let (_ast, diags) = parse_str("class C { static get x(): number { return 1; } }");
13188 assert!(
13189 diags
13190 .iter()
13191 .any(|d| d.message.contains("static accessors are not supported")),
13192 "got: {diags:?}",
13193 );
13194 }
13195
13196 #[test]
13197 fn static_constructor_is_rejected() {
13198 let (_ast, diags) = parse_str("class C { static constructor() {} }");
13199 assert!(
13200 diags
13201 .iter()
13202 .any(|d| d.message.contains("a constructor cannot be `static`")),
13203 "got: {diags:?}",
13204 );
13205 }
13206
13207 #[test]
13208 fn optional_static_field_is_rejected() {
13209 let (_ast, diags) = parse_str("class C { static x?: number; }");
13210 assert!(
13211 diags
13212 .iter()
13213 .any(|d| d.message.contains("a static field cannot be optional")),
13214 "got: {diags:?}",
13215 );
13216 }
13217
13218 #[test]
13219 fn async_function_names_the_synchronous_rewrite() {
13220 let (_ast, diags) = parse_str(
13221 "async function main(): Promise<string> {\n const x = await f();\n return x;\n}",
13222 );
13223 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13224 assert_eq!(
13225 messages,
13226 [
13227 "`async` functions are not supported",
13228 "`await` is not supported"
13229 ],
13230 "{diags:?}"
13231 );
13232 assert_eq!(
13233 diags[0].help,
13234 [
13235 "calls return their value directly: remove `async`, write the return type `T` \
13236 instead of `Promise<T>`, and drop `await`"
13237 ]
13238 );
13239 assert_eq!(
13240 diags[1].help,
13241 ["calls return their value directly: remove `await`"]
13242 );
13243 }
13244
13245 #[test]
13246 fn async_is_rejected_on_every_function_form() {
13247 for source in [
13248 "export async function f(): void {}",
13249 "const f = async (): void => {};",
13250 "const f = async x => x;",
13251 "const f = async function (): void {};",
13252 "class C { async run(): void {} }",
13253 "class C { static async run(): void {} }",
13254 ] {
13255 let (_ast, diags) = parse_str(source);
13256 assert_eq!(
13257 diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13258 ["`async` functions are not supported"],
13259 "{source}: {diags:?}"
13260 );
13261 }
13262 }
13263
13264 #[test]
13265 fn await_before_a_template_is_reported() {
13266 for source in ["const a = await `hi`;", "const a = await `a${1}b`;"] {
13267 let (_ast, diags) = parse_str(source);
13268 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13269 assert_eq!(
13270 messages,
13271 ["`await` is not supported"],
13272 "{source}: {diags:?}"
13273 );
13274 }
13275 }
13276
13277 #[test]
13278 fn async_and_await_stay_ordinary_names() {
13279 for source in [
13280 "const async = 1; const y = async + 1;",
13281 "function async(n: number): number { return n; } async(1);",
13282 "const await = 2; const z = await * 2;",
13283 "const await = (n: number): number => n; const a = await(1);",
13284 "const await = [1, 2]; const b = await[0];",
13285 "const async = { k: 1 }; const c = \"k\" in async; const d = async in ({ o: 1 });",
13286 "const async: unknown = 1; const n = async as (number);",
13287 "const await: unknown = 1; const m = await as number;",
13288 "const await = 1; const t = `${await}`;",
13289 "class C { async(): void {} async: number = 1; }",
13290 ] {
13291 let (_ast, diags) = parse_str(source);
13292 assert!(diags.is_empty(), "{source}: {diags:?}");
13293 }
13294 }
13295
13296 #[test]
13297 fn an_async_object_literal_method_names_the_rewrite_once() {
13298 assert_eq!(
13299 crate::typechecker::rules::test_util::compile_messages(
13300 "function main(): string {\n const o = { async run(): string { return \"a\"; } };\n return o.run();\n}\n"
13301 ),
13302 ["`async` functions are not supported"]
13303 );
13304 let (_ast, diags) = parse_str("const o = { async run(): string { return \"a\"; } };");
13305 assert_eq!(
13306 diags[0].help,
13307 [
13308 "calls return their value directly: remove `async`, write the return type `T` \
13309 instead of `Promise<T>`, and drop `await`"
13310 ]
13311 );
13312 }
13313
13314 #[test]
13315 fn await_in_an_async_object_literal_method_is_reported_too() {
13316 let (_ast, diags) =
13317 parse_str("const o = { async run(): string { const x = await f(); return x; } };");
13318 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13319 assert_eq!(
13320 messages,
13321 [
13322 "`async` functions are not supported",
13323 "`await` is not supported"
13324 ],
13325 "{diags:?}"
13326 );
13327 }
13328
13329 #[test]
13330 fn every_async_member_form_reports_async() {
13331 for source in [
13332 "const o = { async *run(): void {} };",
13333 "class C { async *run(): void {} }",
13334 "const o = { async [k](): void {} };",
13335 "const o = { async in(): void {} };",
13336 "class C { async as(): void {} }",
13337 ] {
13338 let (_ast, diags) = parse_str(source);
13339 assert_eq!(
13340 diags[0].message, "`async` functions are not supported",
13341 "{source}: {diags:?}"
13342 );
13343 }
13344 }
13345
13346 #[test]
13347 fn async_string_key_methods_report_only_async() {
13348 for source in [
13349 "const o = { async \"s\"(): string { return \"a\"; } };",
13350 "class C { async \"s\"(): string { return \"a\"; } }",
13351 ] {
13352 let (_ast, diags) = parse_str(source);
13353 assert_eq!(
13354 diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13355 ["`async` functions are not supported"],
13356 "{source}: {diags:?}"
13357 );
13358 }
13359 }
13360
13361 #[test]
13362 fn async_before_a_line_break_is_a_member_of_its_own() {
13363 for source in [
13364 "class C {\n async\n run(): void {}\n}",
13365 "const o = { async\n run(): void {} };",
13366 ] {
13367 let (_ast, diags) = parse_str(source);
13368 assert!(
13369 diags
13370 .iter()
13371 .all(|d| d.message != "`async` functions are not supported"),
13372 "{source}: {diags:?}"
13373 );
13374 }
13375 }
13376
13377 #[test]
13378 fn async_stays_an_object_literal_key() {
13379 let messages = crate::typechecker::rules::test_util::compile_messages(
13380 "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",
13381 );
13382 assert!(messages.is_empty(), "{messages:?}");
13383 }
13384
13385 #[test]
13386 fn for_await_names_the_rewrite_once() {
13387 assert_eq!(
13388 crate::typechecker::rules::test_util::compile_messages(
13389 "import * as fs from \"submilli:fs\";\nfunction main(): string {\n for await (const e of fs.list(\".\", { recursive: false })) { }\n return \"x\";\n}\n"
13390 ),
13391 ["`for await` is not supported"]
13392 );
13393 let (ast, diags) = parse_str("for await (const e of [1]) { }");
13394 assert_eq!(
13395 diags[0].help,
13396 ["calls return their values directly: remove `await` and write `for (const e of …)`"]
13397 );
13398 assert_eq!(ast.top_level.len(), 1, "the loop is still read");
13399 }
13400
13401 #[test]
13402 fn a_broken_switch_leaves_later_declarations_at_the_top_level() {
13403 assert_eq!(
13404 crate::typechecker::rules::test_util::compile_messages(
13405 "function g(): void { switch (1) { case 1 } }\nclass C {}\nfunction main() { return \"x\"; }\n"
13406 ),
13407 [
13408 "expected `:` after `case` label",
13409 "expected expression",
13410 "expected `:` and return type"
13411 ]
13412 );
13413 }
13414
13415 #[test]
13416 fn every_switch_arm_error_restores_the_nesting() {
13417 for switch in [
13418 "switch (1) { case 1 }",
13419 "switch (1) { case : break; }",
13420 "switch (1) { default: break; default: break; }",
13421 "switch (1) { default }",
13422 "switch (1) { x; }",
13423 "switch (1) { case 1: }",
13424 ] {
13425 let source = format!("function g(): void {{ {switch} }}\nclass C {{}}\n");
13426 let (_ast, diags) = parse_str(&source);
13427 assert!(
13428 diags.iter().all(|d| !d.message.contains("top level")),
13429 "{switch}: {diags:?}"
13430 );
13431 }
13432 }
13433
13434 #[test]
13435 fn protected_member_is_rejected() {
13436 let (_ast, diags) = parse_str("class C { protected x: number; }");
13437 assert!(
13438 diags
13439 .iter()
13440 .any(|d| d.message.contains("`protected` is not supported")),
13441 "got: {diags:?}",
13442 );
13443 }
13444
13445 #[test]
13446 fn abstract_member_is_rejected() {
13447 let (_ast, diags) = parse_str("class C { abstract run(): void; }");
13448 assert!(
13449 diags
13450 .iter()
13451 .any(|d| d.message.contains("abstract classes are not supported")),
13452 "got: {diags:?}",
13453 );
13454 }
13455
13456 #[test]
13457 fn getter_parses_as_accessor() {
13458 let (ast, diags) = parse_str("class C { get value(): number { return 1; } }");
13459 assert!(diags.is_empty(), "got: {diags:?}");
13460 let crate::ClassMember::Accessor { kind, name, .. } = &class_members(&ast)[0] else {
13461 panic!("expected an accessor member");
13462 };
13463 assert_eq!(*kind, crate::AccessorKind::Get);
13464 assert_eq!(name.name, "value");
13465 }
13466
13467 #[test]
13468 fn getter_without_return_type_is_rejected() {
13469 let (ast, diags) = parse_str("class C { get value() { return 1; } }");
13470 assert_eq!(diags.len(), 1, "got: {diags:?}");
13471 assert_eq!(diags[0].message, "expected `:` and return type");
13472 assert_eq!(diags[0].help, vec!["get name(): T { … }".to_string()]);
13473 assert!(
13474 matches!(
13475 &class_members(&ast)[0],
13476 crate::ClassMember::Accessor {
13477 return_type: None,
13478 ..
13479 }
13480 ),
13481 "the getter is still recovered as a member"
13482 );
13483 }
13484
13485 #[test]
13486 fn setter_parses_as_accessor() {
13487 let (ast, diags) = parse_str("class C { set value(v: number) {} }");
13488 assert!(diags.is_empty(), "got: {diags:?}");
13489 let crate::ClassMember::Accessor { kind, param, .. } = &class_members(&ast)[0] else {
13490 panic!("expected an accessor member");
13491 };
13492 assert_eq!(*kind, crate::AccessorKind::Set);
13493 assert!(param.is_some(), "setter should have a parameter");
13494 }
13495
13496 #[test]
13497 fn param_property_modifier_outside_constructor_is_rejected() {
13498 let (_ast, diags) = parse_str("function f(public x: number): void {}");
13499 assert!(
13500 diags.iter().any(|d| d
13501 .message
13502 .contains("parameter properties are only allowed in a constructor")),
13503 "got: {diags:?}",
13504 );
13505 }
13506
13507 #[test]
13508 fn constructor_return_type_is_rejected() {
13509 let (_ast, diags) = parse_str("class C { constructor(): void {} }");
13510 assert!(
13511 diags.iter().any(|d| d
13512 .message
13513 .contains("constructor cannot declare a return type")),
13514 "got: {diags:?}",
13515 );
13516 }
13517
13518 #[test]
13519 fn field_initializer_without_type_is_rejected() {
13520 let (_ast, diags) = parse_str("class C { x = 1; }");
13521 assert!(
13522 diags
13523 .iter()
13524 .any(|d| d.message.contains("class fields require a type annotation")),
13525 "got: {diags:?}",
13526 );
13527 }
13528
13529 #[test]
13530 fn this_outside_method_is_left_to_typechecker() {
13531 let (_ast, diags) = parse_str("function f(): string { return this.name; }");
13532 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13533 }
13534
13535 #[test]
13536 fn super_outside_method_is_rejected() {
13537 let (_ast, diags) = parse_str("function f(): void { super(1); }");
13538 assert!(
13539 diags.iter().any(|d| d
13540 .message
13541 .contains("`super` is only valid inside a class method or constructor body")),
13542 "got: {diags:?}",
13543 );
13544 }
13545
13546 #[test]
13547 fn this_inside_method_is_accepted_by_parser() {
13548 let (_ast, diags) = parse_str("class C { x: number; read(): number { return this.x; } }");
13549 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13550 }
13551 #[test]
13552 fn parse_void_expression_preserves_operand_and_precedence() {
13553 let (ast, diags) = parse_str("void effect() === undefined;");
13554 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13555 let expression = expr_of_single_stmt(&ast);
13556 let crate::ExprKind::Binary { lhs, rhs, .. } = expression.kind else {
13557 panic!("expected equality")
13558 };
13559 let crate::ExprKind::Void { operand } = ast.try_expr(lhs).unwrap().kind else {
13560 panic!("expected void")
13561 };
13562 assert!(matches!(
13563 ast.try_expr(operand).unwrap().kind,
13564 crate::ExprKind::Call { .. }
13565 ));
13566 assert!(matches!(
13567 ast.try_expr(rhs).unwrap().kind,
13568 crate::ExprKind::Identifier(_)
13569 ));
13570 }
13571
13572 #[test]
13573 fn parse_void_newline_and_object_operands() {
13574 for source in [
13575 "void\n0;",
13576 "void { value: 1 };",
13577 "void\n{ value: 1 };",
13578 "void /x/;",
13579 "void void 0;",
13580 ] {
13581 let (_, diags) = parse_str(source);
13582 assert!(diags.is_empty(), "{source}: {diags:?}");
13583 }
13584 let (_, diags) = parse_str("void; 0;");
13585 assert!(
13586 !diags.is_empty(),
13587 "an explicit semicolon cannot split void from its operand"
13588 );
13589 }
13590
13591 #[test]
13592 fn parse_optional_parameters_and_defaults_in_callable_forms() {
13593 for source in [
13594 "function f(x = 1): number { return x; }",
13595 "function f(x: number = 1, y: number): number { return y; }",
13596 "function f({ x } = { x: 1 }): number { return x; }",
13597 "const f = (x?: number) => x;",
13598 "const f = (x: number = effect()) => x;",
13599 "const f = function(x: number = effect()) { return x; };",
13600 "const f = { m(x: number = effect()): number { return x; } };",
13601 "const f = ([x = 1]: number[] = values) => x;",
13602 "let f: (x?: number, ...rest: string[]) => void = value;",
13603 "let f: { method?(x?: number): void } = value;",
13604 "function f(x: number = 1, y?: number): void {}",
13605 ] {
13606 let (_, diags) = parse_str(source);
13607 assert!(diags.is_empty(), "{source}: {diags:?}");
13608 }
13609 }
13610
13611 #[test]
13612 fn parse_invalid_optional_parameters() {
13613 for source in [
13614 "function f(x?: number, y: number): void {}",
13615 "const f = (x?: number, y: number) => y;",
13616 "let f: (x?: number, y: number) => void = value;",
13617 "function f(...x?: number[]): void {}",
13618 "function f(x?: number = 1): void {}",
13619 "const f = (x?: number = 1) => x;",
13620 "function f({x}?: T): void {}",
13621 ] {
13622 let (_, diags) = parse_str(source);
13623 assert!(!diags.is_empty(), "expected diagnostic for {source}");
13624 }
13625 }
13626
13627 #[test]
13628 fn parse_optional_tuple_forms_and_order() {
13629 for source in [
13630 "let x: [number, string?] = value;",
13631 "let x: [x?: number, y?: string] = value;",
13632 "let x: readonly [(number | null)?, string?] = value;",
13633 ] {
13634 let (_, diags) = parse_str(source);
13635 assert!(diags.is_empty(), "{source}: {diags:?}");
13636 }
13637 for source in [
13638 "let x: [number?, string] = value;",
13639 "let x: [x: number?] = value;",
13640 ] {
13641 let (_, diags) = parse_str(source);
13642 assert!(!diags.is_empty(), "expected diagnostic for {source}");
13643 }
13644 }
13645
13646 #[test]
13647 fn parse_array_pattern_defaults_preserve_holes() {
13648 let (ast, diags) = parse_str("const [, a = effect(), b] = values;");
13649 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13650 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
13651 panic!("expected pattern")
13652 };
13653 let crate::Binding::Array {
13654 elems, defaults, ..
13655 } = binding
13656 else {
13657 panic!("expected array")
13658 };
13659 assert_eq!(elems.len(), 3);
13660 assert_eq!(defaults.len(), elems.len());
13661 assert!(elems[0].is_none());
13662 assert!(defaults[0].is_none());
13663 assert!(defaults[1].is_some());
13664 assert!(defaults[2].is_none());
13665 }
13666
13667 #[test]
13668 fn parse_optional_class_method_forms() {
13669 let (ast, diags) =
13670 parse_str("class C { declared?(x?: number): void; implemented?(): void\n{} }");
13671 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13672 let members = class_members(&ast);
13673 assert!(matches!(
13674 &members[0],
13675 crate::ClassMember::Field {
13676 optional: true,
13677 initializer: None,
13678 ty: crate::TypeAnnotation {
13679 kind: crate::TypeAnnotationKind::Function { .. },
13680 ..
13681 },
13682 ..
13683 }
13684 ));
13685 assert!(matches!(
13686 &members[1],
13687 crate::ClassMember::Method { optional: true, .. }
13688 ));
13689 }
13690 #[test]
13691 fn defaults_retain_their_callable_receiver_scope() {
13692 for source in [
13693 "class C { value: number = 1; method(value: number = this.value): number { return value; } }",
13694 "class C { constructor(value: number = this.value) {} value: number = 1; }",
13695 "const f = function(this: { value: number }, value: number = this.value): number { return value; };",
13696 ] {
13697 let (ast, diagnostics) = parse_str(source);
13698 assert!(diagnostics.is_empty(), "{source}: {diagnostics:?}");
13699 assert!(
13700 ast.source_expressions()
13701 .iter()
13702 .any(|expr| matches!(expr.kind, ExprKind::This))
13703 );
13704 assert!(
13705 !ast.source_expressions()
13706 .iter()
13707 .any(|expr| matches!(expr.kind, ExprKind::ThisOutsideReceiver))
13708 );
13709 }
13710 }
13711}