1use crate::compiler_error::{CompileError, CompilerFailure, CompilerStage};
2use crate::{
3 ArrayLiteralElement, ArrowBody, Ast, BinOp, Binding, CatchClause, Diagnostic, EnumInitializer,
4 EnumMember, ExportedDecl, Expr, ExprId, ExprKind, FileId, Ident, ImportKind, ImportSpecifier,
5 ObjectLiteralField, ObjectLiteralMember, ObjectPatternField, ParamDecl, PostfixOp, Severity,
6 Span, Stmt, StmtId, StmtKind, SwitchCase, SwitchDefault, Token, TokenKind, TypeAnnotation,
7 TypeAnnotationField, TypeAnnotationKind, UnOp,
8};
9
10const MAX_ERRORS: usize = 20;
11const MAX_PARSE_DEPTH: usize = 128;
14
15const BUILT_IN_TYPE_NAMES: &[&str] = &[
17 "any",
18 "bigint",
19 "boolean",
20 "never",
21 "number",
22 "object",
23 "string",
24 "symbol",
25 "undefined",
26 "unknown",
27 "void",
28];
29
30#[derive(Clone, Copy, PartialEq)]
36enum TypePos {
37 Anywhere,
38 ArrowReturn,
39}
40
41pub fn parse(source: &str, tokens: Vec<Token>, file: FileId) -> (Ast, Vec<Diagnostic>) {
43 match parse_checked(source, tokens, file) {
44 Ok(result) => result,
45 Err(error) => (Ast::new(), error.into_diagnostics(file)),
46 }
47}
48
49pub fn parse_checked(
50 source: &str,
51 tokens: Vec<Token>,
52 file: FileId,
53) -> Result<(Ast, Vec<Diagnostic>), CompileError> {
54 crate::source::SourceError::check_source_len(source.len())
55 .map_err(|error| error.into_compiler_failure(CompilerStage::Parse))?;
56 validate_token_stream(source, &tokens, file).map_err(|message| CompilerFailure::Internal {
57 stage: CompilerStage::Parse,
58 span: None,
59 message: message.into(),
60 })?;
61 let eof = tokens
62 .last()
63 .cloned()
64 .unwrap_or_else(|| Token::new(TokenKind::Eof, Span::at(file)));
65 let mut p = Parser {
66 source,
67 tokens,
68 file,
69 pos: 0,
70 ast: Ast::new(),
71 diagnostics: Vec::new(),
72 block_depth: 0,
73 class_member_body_depth: 0,
74 function_expression_body_depth: 0,
75 recursion_depth: 0,
76 recursion_limit_span: None,
77 last_as_type_end: None,
78 eof,
79 fatal: None,
80 };
81 p.parse_program();
82 if let Some(span) = p.recursion_limit_span {
83 p.fatal = Some(CompilerFailure::Limit {
84 stage: CompilerStage::Parse,
85 span: Some(span),
86 message: "parser recursion limit exceeded".into(),
87 help: vec!["simplify nested syntax or split it into separate declarations".into()],
88 });
89 }
90 if let Some(fatal) = p.fatal {
91 return Err(CompileError {
92 diagnostics: p.diagnostics,
93 fatal: Some(fatal),
94 });
95 }
96 p.ast
97 .validate_source(source, file)
98 .map_err(|error| CompileError {
99 diagnostics: p.diagnostics.clone(),
100 fatal: Some(error.into_compiler_failure(CompilerStage::Parse)),
101 })?;
102 crate::tree_height::check_syntax(&p.ast).map_err(|fatal| CompileError {
103 diagnostics: p.diagnostics.clone(),
104 fatal: Some(fatal),
105 })?;
106 Ok((p.ast, p.diagnostics))
107}
108
109fn validate_token_stream(source: &str, tokens: &[Token], file: FileId) -> Result<(), &'static str> {
110 if !matches!(tokens.last().map(|token| &token.kind), Some(TokenKind::Eof)) {
111 return Err("token stream must end with EOF");
112 }
113 let mut previous = 0;
114 for (index, token) in tokens.iter().enumerate() {
115 let span = token.span;
116 if span.file != file
117 || span.start < previous
118 || span.start > span.end
119 || source.get(span.start as usize..span.end as usize).is_none()
120 {
121 return Err("token span is outside its source or out of order");
122 }
123 if matches!(token.kind, TokenKind::Eof) && index != tokens.len() - 1 {
124 return Err("tokens follow EOF");
125 }
126 if let Some(doc) = &token.leading_doc {
127 let span = doc.span;
128 if span.file != file
129 || span.start > span.end
130 || span.end > token.span.start
131 || source.get(span.start as usize..span.end as usize) != Some(doc.text.as_str())
132 || !doc.text.starts_with("/**")
133 || !doc.text.ends_with("*/")
134 {
135 return Err("documentation metadata does not match its source");
136 }
137 }
138 previous = span.start;
139 }
140 Ok(())
141}
142
143pub(crate) struct Parser<'a> {
144 source: &'a str,
145 tokens: Vec<Token>,
146 file: FileId,
147 pos: usize,
148 ast: Ast,
149 diagnostics: Vec<Diagnostic>,
150 block_depth: u32,
152 class_member_body_depth: u32,
154 function_expression_body_depth: u32,
155 recursion_depth: usize,
156 recursion_limit_span: Option<Span>,
157 last_as_type_end: Option<u32>,
161 eof: Token,
162 fatal: Option<CompilerFailure>,
163}
164
165impl<'a> Parser<'a> {
166 fn span(&self, start: u32, end: u32) -> Span {
167 Span {
168 file: self.file,
169 start,
170 end,
171 }
172 }
173
174 fn type_name(&mut self, span: Span) -> Option<Ident> {
175 match span.text(self.source, self.file) {
176 Ok(name) => Some(Ident {
177 name: name.to_string(),
178 span,
179 }),
180 Err(error) => {
181 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
182 None
183 }
184 }
185 }
186
187 fn parse_program(&mut self) {
188 while !self.is_at_eof() {
189 if self.fatal.is_some() || self.error_count() >= MAX_ERRORS {
190 break;
191 }
192 if matches!(self.peek().kind, TokenKind::Semicolon) {
193 self.advance();
194 continue;
195 }
196 let pos_before = self.pos;
197 if let Some(id) = self.parse_statement() {
198 self.ast.top_level.push(id);
199 } else {
200 if self.fatal.is_some() {
201 break;
202 }
203 self.recover();
204 if self.pos != pos_before && matches!(self.peek().kind, TokenKind::RightBrace) {
205 self.advance();
206 continue;
207 }
208 if self.pos == pos_before && !self.is_at_eof() {
210 self.advance();
211 }
212 }
213 }
214 }
215
216 fn parse_statement(&mut self) -> Option<StmtId> {
217 self.with_recursion_limit(Self::parse_statement_inner)
218 }
219
220 fn parse_statement_inner(&mut self) -> Option<StmtId> {
221 match self.peek().kind {
222 TokenKind::Let => self.parse_let_or_const(false),
223 TokenKind::Const => self.parse_let_or_const(true),
224 TokenKind::Function => self.parse_function_decl(),
225 TokenKind::Class => self.parse_class_decl(),
226 TokenKind::Interface => self.parse_interface_decl(),
227 TokenKind::Enum => self.parse_enum_decl(),
228 TokenKind::Identifier if self.at_type_alias_head() => self.parse_type_alias_decl(),
229 TokenKind::Export => self.parse_export(),
230 TokenKind::Import => self.parse_import_decl(),
231 TokenKind::If => self.parse_if(),
232 TokenKind::While => self.parse_while(),
233 TokenKind::Do => self.parse_do_while(),
234 TokenKind::For => self.parse_for(),
235 TokenKind::Switch => self.parse_switch(),
236 TokenKind::Break => self.parse_break(),
237 TokenKind::Continue => self.parse_continue(),
238 TokenKind::Return => self.parse_return(),
239 TokenKind::Throw => self.parse_throw(),
240 TokenKind::Try => self.parse_try(),
241 TokenKind::LeftBrace => self.parse_block(),
242 _ => self.parse_expression_statement(),
243 }
244 }
245
246 fn parse_expression_statement(&mut self) -> Option<StmtId> {
247 let expr_id = self.parse_expression()?;
248 let expr_span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
249 if !self.at_statement_end() {
250 let what = if matches!(
251 parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.kind,
252 ExprKind::Assign { .. }
253 ) {
254 "assignment"
255 } else {
256 "expression"
257 };
258 self.error_at_peek(format!("expected `;` after {what}"));
259 return None;
260 }
261 let end = self.finish_statement();
262 let span = self.span(expr_span.start, end);
263 let kind = self.statement_kind_for(expr_id)?;
264 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
265 }
266
267 fn statement_kind_for(&mut self, expr_id: ExprId) -> Option<StmtKind> {
270 let ExprKind::Assign {
271 target,
272 op,
273 op_span,
274 value,
275 } = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?
276 .kind
277 .clone()
278 else {
279 return Some(StmtKind::Expr(expr_id));
280 };
281 Some(
282 match (
283 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?
284 .kind
285 .clone(),
286 op,
287 ) {
288 (ExprKind::Identifier(target), None) => StmtKind::Assign { target, value },
289 (ExprKind::Identifier(target), Some(op)) => StmtKind::CompoundAssign {
290 target,
291 op,
292 op_span,
293 value,
294 },
295 (ExprKind::FieldAccess { receiver, name }, None) => StmtKind::AssignField {
296 receiver,
297 field_name: name,
298 value,
299 },
300 (ExprKind::FieldAccess { receiver, name }, Some(op)) => {
301 StmtKind::CompoundAssignField {
302 receiver,
303 field_name: name,
304 op,
305 op_span,
306 value,
307 }
308 }
309 (ExprKind::IndexAccess { receiver, index }, None) => StmtKind::AssignIndex {
310 receiver,
311 index,
312 value,
313 },
314 (ExprKind::IndexAccess { receiver, index }, Some(op)) => {
315 StmtKind::CompoundAssignIndex {
316 receiver,
317 index,
318 op,
319 op_span,
320 value,
321 }
322 }
323 _ => return self.invariant_failure("invalid assignment target"),
324 },
325 )
326 }
327
328 fn parse_let_or_const(&mut self, is_const: bool) -> Option<StmtId> {
329 let doc = self.take_leading_doc();
330 let kw = self.advance();
331
332 let binding = match self.peek().kind {
333 TokenKind::LeftBrace | TokenKind::LeftBracket => Some(self.parse_binding()?),
334 _ => None,
335 };
336
337 let name = if binding.is_none() {
338 let name_tok = self.expect_identifier("expected identifier after `let`/`const`")?;
339 Some(self.ident_from_token(&name_tok))
340 } else {
341 None
342 };
343
344 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
345 self.advance();
346 Some(self.parse_type_annotation()?)
347 } else {
348 None
349 };
350
351 let implicit = !is_const
352 && ty.is_some()
353 && !matches!(self.peek().kind, TokenKind::Equals)
354 && matches!(self.peek().kind, TokenKind::Semicolon);
355 let value = if let (true, Some(name)) = (implicit, &name) {
356 let value = parse_arena_result(
358 self.ast.try_push_expr(Expr {
359 kind: ExprKind::Identifier(Ident {
360 name: "undefined".to_string(),
361 span: name.span,
362 }),
363 span: name.span,
364 }),
365 &mut self.fatal,
366 )?;
367 self.ast.implicit_initializers.insert(value);
368 self.ast.synthetic_undefined.insert(value);
369 value
370 } else {
371 if !matches!(self.peek().kind, TokenKind::Equals) {
372 let (msg, help) = if is_const {
373 (
374 "`const` declaration requires an initializer",
375 vec!["const x: T = expr;".to_string()],
376 )
377 } else {
378 (
379 "`let` declaration requires an initializer",
380 vec![
381 "let x: T = expr;".to_string(),
382 "or declare one that starts empty: `let x: T | undefined;`".to_string(),
383 ],
384 )
385 };
386 self.error_at_peek_with_help(msg, help);
387 return None;
388 }
389 self.advance();
390 self.parse_expression()?
391 };
392
393 if !self.at_statement_end() {
394 self.error_at_peek("expected `;` after declaration");
395 return None;
396 }
397 let end = self.finish_statement();
398
399 let span = self.span(kw.span.start, end);
400 let kind = match (binding, name) {
401 (Some(binding), _) => {
402 if is_const {
403 StmtKind::ConstPattern {
404 binding,
405 ty,
406 value,
407 doc,
408 }
409 } else {
410 StmtKind::LetPattern {
411 binding,
412 ty,
413 value,
414 doc,
415 }
416 }
417 }
418 (None, Some(name)) => {
419 if is_const {
420 StmtKind::Const {
421 name,
422 ty,
423 value,
424 doc,
425 }
426 } else {
427 StmtKind::Let {
428 name,
429 ty,
430 value,
431 doc,
432 }
433 }
434 }
435 (None, None) => return self.invariant_failure("either binding or name must be Some"),
436 };
437 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
438 }
439
440 fn parse_binding(&mut self) -> Option<Binding> {
442 match self.peek().kind {
443 TokenKind::LeftBrace => self.parse_object_binding(),
444 TokenKind::LeftBracket => self.parse_array_binding(),
445 _ => {
446 self.error_at_peek("expected `{` or `[` to start a destructuring pattern");
447 None
448 }
449 }
450 }
451
452 fn parse_object_binding(&mut self) -> Option<Binding> {
453 let open = self.advance();
454 let mut fields: Vec<ObjectPatternField> = Vec::new();
455 let mut rest: Option<Ident> = None;
456
457 if matches!(self.peek().kind, TokenKind::RightBrace) {
458 self.error_at_peek("empty object destructuring pattern");
459 return None;
460 }
461
462 loop {
463 if matches!(self.peek().kind, TokenKind::DotDotDot) {
464 let dots = self.advance();
465 let name_tok = self.expect_identifier("expected identifier after `...`")?;
466 let name = self.ident_from_token(&name_tok);
467 if !matches!(self.peek().kind, TokenKind::RightBrace) {
468 self.error_at_peek_with_help(
469 "rest element must be the last element in a destructuring pattern",
470 vec!["move `...` after every other binding".to_string()],
471 );
472 return None;
473 }
474 rest = Some(Ident {
475 name: name.name,
476 span: self.span(dots.span.start, name.span.end),
477 });
478 break;
479 }
480
481 let source_tok = self.expect_property_name("expected field name in object pattern")?;
482 let source = self.ident_from_token(&source_tok);
483 let field_span_start = source.span.start;
484
485 let (local, field_end) = if matches!(self.peek().kind, TokenKind::Colon) {
486 self.advance();
487 match self.peek().kind {
488 TokenKind::LeftBrace | TokenKind::LeftBracket => {
489 self.error_at_peek_with_help(
490 "nested destructuring is not supported",
491 vec![
492 "destructure once and access nested fields explicitly: const { a } = obj; const inner = a.b;"
493 .to_string(),
494 ],
495 );
496 return None;
497 }
498 _ => {
499 let local_tok =
500 self.expect_identifier("expected identifier after `:` in pattern")?;
501 let local = self.ident_from_token(&local_tok);
502 let end = local.span.end;
503 (local, end)
504 }
505 }
506 } else {
507 if !matches!(source_tok.kind, TokenKind::Identifier) {
508 self.error_at_peek("expected `:` after keyword field name in object pattern");
509 return None;
510 }
511 self.reject_strict_mode_reserved_word(&source_tok);
512 let end = source.span.end;
513 (source.clone(), end)
514 };
515
516 let default = if matches!(self.peek().kind, TokenKind::Equals) {
517 self.advance();
518 Some(self.parse_expression()?)
519 } else {
520 None
521 };
522 let field_end = match default {
523 Some(value) => {
524 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?
525 .span
526 .end
527 }
528 None => field_end,
529 };
530
531 fields.push(ObjectPatternField {
532 source,
533 local,
534 default,
535 span: self.span(field_span_start, field_end),
536 });
537
538 match self.peek().kind {
539 TokenKind::Comma => {
540 self.advance();
541 if matches!(self.peek().kind, TokenKind::RightBrace) {
542 break;
543 }
544 }
545 TokenKind::RightBrace => break,
546 _ => {
547 self.error_at_peek("expected `,` or `}` in object pattern");
548 return None;
549 }
550 }
551 }
552
553 if !matches!(self.peek().kind, TokenKind::RightBrace) {
554 self.error_at_peek("expected `}` to close object pattern");
555 return None;
556 }
557 let close = self.advance();
558
559 Some(Binding::Object {
560 fields,
561 rest,
562 span: self.span(open.span.start, close.span.end),
563 })
564 }
565
566 fn parse_array_binding(&mut self) -> Option<Binding> {
567 let open = self.advance();
568 let mut elems: Vec<Option<Ident>> = Vec::new();
569 let mut defaults = Vec::new();
570 let mut rest: Option<Ident> = None;
571
572 if matches!(self.peek().kind, TokenKind::RightBracket) {
573 self.error_at_peek("empty array destructuring pattern");
574 return None;
575 }
576
577 loop {
578 match self.peek().kind {
579 TokenKind::Comma => {
580 elems.push(None);
581 defaults.push(None);
582 self.advance();
583 if matches!(self.peek().kind, TokenKind::RightBracket) {
584 break;
585 }
586 continue;
587 }
588 TokenKind::RightBracket => break,
589 TokenKind::DotDotDot => {
590 let dots = self.advance();
591 let name_tok = self.expect_identifier("expected identifier after `...`")?;
592 let name = self.ident_from_token(&name_tok);
593 if !matches!(self.peek().kind, TokenKind::RightBracket) {
594 self.error_at_peek_with_help(
595 "rest element must be the last element in a destructuring pattern",
596 vec!["move `...` after every other binding".to_string()],
597 );
598 return None;
599 }
600 rest = Some(Ident {
601 name: name.name,
602 span: self.span(dots.span.start, name.span.end),
603 });
604 break;
605 }
606 TokenKind::LeftBrace | TokenKind::LeftBracket => {
607 self.error_at_peek_with_help(
608 "nested destructuring is not supported",
609 vec![
610 "destructure once and access nested elements explicitly: const [a] = arr; const inner = a[0];"
611 .to_string(),
612 ],
613 );
614 return None;
615 }
616 _ => {
617 let name_tok =
618 self.expect_identifier("expected identifier in array pattern")?;
619 let name = self.ident_from_token(&name_tok);
620
621 let default = if matches!(self.peek().kind, TokenKind::Equals) {
622 self.advance();
623 Some(self.parse_expression()?)
624 } else {
625 None
626 };
627 defaults.push(default);
628 elems.push(Some(name));
629
630 match self.peek().kind {
631 TokenKind::Comma => {
632 self.advance();
633 if matches!(self.peek().kind, TokenKind::RightBracket) {
634 break;
635 }
636 }
637 TokenKind::RightBracket => break,
638 _ => {
639 self.error_at_peek("expected `,` or `]` in array pattern");
640 return None;
641 }
642 }
643 }
644 }
645 }
646
647 if !matches!(self.peek().kind, TokenKind::RightBracket) {
648 self.error_at_peek("expected `]` to close array pattern");
649 return None;
650 }
651 let close = self.advance();
652
653 Some(Binding::Array {
654 elems,
655 defaults,
656 rest,
657 span: self.span(open.span.start, close.span.end),
658 })
659 }
660
661 fn parse_function_decl(&mut self) -> Option<StmtId> {
662 let doc = self.take_leading_doc();
663 let kw = self.advance();
664
665 let name_tok = self.expect_identifier("expected function name")?;
666 let name = self.ident_from_token(&name_tok);
667
668 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
669 self.parse_generic_param_list()?
670 } else {
671 Vec::new()
672 };
673
674 if !matches!(self.peek().kind, TokenKind::LeftParen) {
675 self.error_at_peek("expected `(` after function name");
676 return None;
677 }
678 self.advance();
679
680 let params = self.parse_param_list(false)?;
681 self.advance(); if !matches!(self.peek().kind, TokenKind::Colon) {
684 self.error_at_peek_with_help(
685 "expected `:` and return type",
686 vec!["function name(): T { … }".to_string()],
687 );
688 return None;
689 }
690 self.advance();
691 let (return_type, type_predicate) =
692 self.parse_predicate_or_return_type(TypePos::Anywhere)?;
693
694 let body = self.parse_outer_this_boundary(Self::parse_block)?;
697 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
698 .span
699 .end;
700
701 parse_arena_result(
702 self.ast.try_push_stmt(Stmt {
703 kind: StmtKind::Function {
704 name,
705 generics,
706 params,
707 return_type,
708 type_predicate,
709 body,
710 doc,
711 },
712 span: self.span(kw.span.start, body_end),
713 }),
714 &mut self.fatal,
715 )
716 }
717
718 fn parse_class_decl(&mut self) -> Option<StmtId> {
719 let doc = self.take_leading_doc();
720 let kw = self.advance();
721 if self.block_depth > 0 {
724 self.error_at_with_help(
725 kw.span,
726 "a `class` must be declared at the top level of the module",
727 vec!["move this `class` outside any block or function body".to_string()],
728 );
729 }
730 let name = self.parse_type_decl_name("a", "class")?;
731
732 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
733 self.parse_generic_param_list()?
734 } else {
735 Vec::new()
736 };
737
738 let extends = if matches!(self.peek().kind, TokenKind::Extends) {
739 self.advance();
740 Some(self.parse_type_annotation()?)
741 } else {
742 None
743 };
744
745 let mut implements: Vec<crate::TypeAnnotation> = Vec::new();
746 if matches!(self.peek().kind, TokenKind::Implements) {
747 self.advance();
748 loop {
749 implements.push(self.parse_type_annotation()?);
750 if matches!(self.peek().kind, TokenKind::Comma) {
751 self.advance();
752 continue;
753 }
754 break;
755 }
756 }
757
758 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
759 self.error_at_peek("expected `{` after class header");
760 return None;
761 }
762 self.advance();
763
764 let mut members: Vec<crate::ClassMember> = Vec::new();
765 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
766 if matches!(self.peek().kind, TokenKind::Semicolon) {
768 self.advance();
769 continue;
770 }
771 members.push(self.parse_class_member()?);
772 }
773
774 if !matches!(self.peek().kind, TokenKind::RightBrace) {
775 self.error_at_peek("expected `}` to close class");
776 return None;
777 }
778 let close = self.advance();
779
780 parse_arena_result(
781 self.ast.try_push_stmt(Stmt {
782 kind: StmtKind::ClassDecl {
783 name,
784 generics,
785 extends,
786 implements,
787 members,
788 doc,
789 },
790 span: self.span(kw.span.start, close.span.end),
791 }),
792 &mut self.fatal,
793 )
794 }
795
796 fn parse_class_member(&mut self) -> Option<crate::ClassMember> {
797 let doc = self.take_leading_doc();
798 let member_start = self.peek().span.start;
799
800 if self.peek_identifier_text_is("protected") {
802 let tok = self.advance();
803 self.error_at_with_help(
804 tok.span,
805 "`protected` is not supported",
806 vec!["use `private` or `public`".to_string()],
807 );
808 return None;
809 }
810 if self.peek_identifier_text_is("abstract") {
811 let tok = self.advance();
812 self.error_at_with_help(
813 tok.span,
814 "abstract classes are not supported",
815 vec!["provide a concrete implementation instead of `abstract`".to_string()],
816 );
817 return None;
818 }
819
820 let modifiers = self.parse_class_modifiers();
821
822 for (kw, kind) in [
825 ("get", crate::AccessorKind::Get),
826 ("set", crate::AccessorKind::Set),
827 ] {
828 if self.peek_identifier_text_is(kw)
829 && is_property_name(&self.peek_at(1).kind)
830 && matches!(self.peek_at(2).kind, TokenKind::LeftParen)
831 {
832 if let Some(span) = modifiers.readonly {
833 self.reject_readonly_modifier(span);
834 }
835 if let Some(span) = modifiers.static_span {
836 self.error_at_with_help(
837 span,
838 "static accessors are not supported",
839 vec!["use a static method: `static name(): T { … }`".to_string()],
840 );
841 return None;
842 }
843 return self.parse_accessor(member_start, modifiers, kind, doc);
844 }
845 }
846
847 if self.peek_identifier_text_is("constructor")
849 && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
850 {
851 if let Some(span) = modifiers.static_span {
852 self.error_at_with_help(
853 span,
854 "a constructor cannot be `static`",
855 vec![
856 "remove `static` — the constructor already belongs to the class, not \
857 instances"
858 .to_string(),
859 ],
860 );
861 return None;
862 }
863 if let Some(span) = modifiers.readonly {
864 self.error_at_with_help(
865 span,
866 "a constructor cannot be `readonly`",
867 vec!["remove `readonly`; it applies to fields".to_string()],
868 );
869 return None;
870 }
871 return self.parse_constructor(member_start, modifiers.visibility, doc);
872 }
873
874 let name = self.expect_property_ident("expected class member name")?;
875 let optional_span = if matches!(self.peek().kind, TokenKind::Question) {
876 Some(self.advance().span)
877 } else {
878 None
879 };
880 let optional = optional_span.is_some();
881
882 if matches!(self.peek().kind, TokenKind::LessThan | TokenKind::LeftParen) {
884 if let Some(span) = modifiers.readonly {
885 self.reject_readonly_modifier(span);
886 }
887 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
888 self.parse_generic_param_list()?
889 } else {
890 Vec::new()
891 };
892 if !matches!(self.peek().kind, TokenKind::LeftParen) {
893 self.error_at_peek("expected `(` to start the method parameter list");
894 return None;
895 }
896 self.advance();
897 let params = self.parse_class_member_params(false)?;
898 self.advance(); if !matches!(self.peek().kind, TokenKind::Colon) {
900 self.error_at_peek_with_help(
901 "expected `:` and return type",
902 vec!["method name(): T { … }".to_string()],
903 );
904 return None;
905 }
906 self.advance();
907 let return_type = self.parse_type_annotation()?;
908 if self.at_inserted_semicolon() && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
909 {
910 self.advance();
911 }
912 if optional
913 && matches!(
914 self.peek().kind,
915 TokenKind::Semicolon | TokenKind::RightBrace
916 )
917 {
918 if !generics.is_empty() {
919 self.error_at(
920 name.span,
921 "generic optional method declarations are not supported",
922 );
923 return None;
924 }
925 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
926 self.advance().span.end
927 } else {
928 return_type.span.end
929 };
930 let ty = self.method_declaration_type(
931 params,
932 return_type,
933 self.span(name.span.start, end),
934 )?;
935 return Some(crate::ClassMember::Field {
936 name,
937 modifiers,
938 optional: true,
939 ty,
940 initializer: None,
941 span: self.span(member_start, end),
942 doc,
943 });
944 }
945 let body = self.parse_class_member_body()?;
946 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
947 .span
948 .end;
949 return Some(crate::ClassMember::Method {
950 name,
951 optional,
952 modifiers,
953 generics,
954 params,
955 return_type,
956 body,
957 span: self.span(member_start, body_end),
958 doc,
959 });
960 }
961
962 if let Some(span) = optional_span
964 && modifiers.static_span.is_some()
965 {
966 self.error_at_with_help(
967 span,
968 "a static field cannot be optional",
969 vec!["a static field must be initialized; give it a value with `= …`".to_string()],
970 );
971 return None;
972 }
973 if !matches!(self.peek().kind, TokenKind::Colon) {
974 if matches!(self.peek().kind, TokenKind::Equals) {
975 self.error_at_peek_with_help(
976 "class fields require a type annotation",
977 vec!["add `: T` before the initializer, e.g. `name: string = …`".to_string()],
978 );
979 } else {
980 self.error_at_peek("expected `:` and a type for the class field");
981 }
982 return None;
983 }
984 self.advance();
985 let ty = self.parse_type_annotation()?;
986 let initializer = if matches!(self.peek().kind, TokenKind::Equals) {
987 self.advance();
988 self.class_member_body_depth += 1;
991 let init = self.parse_expression();
992 self.class_member_body_depth -= 1;
993 Some(init?)
994 } else {
995 None
996 };
997 if !matches!(self.peek().kind, TokenKind::Semicolon) {
998 self.error_at_peek("expected `;` after class field");
999 return None;
1000 }
1001 let semi = self.advance();
1002 Some(crate::ClassMember::Field {
1003 name,
1004 modifiers,
1005 optional,
1006 ty,
1007 initializer,
1008 span: self.span(member_start, semi.span.end),
1009 doc,
1010 })
1011 }
1012
1013 fn parse_constructor(
1014 &mut self,
1015 member_start: u32,
1016 visibility: crate::Visibility,
1017 doc: Option<crate::DocComment>,
1018 ) -> Option<crate::ClassMember> {
1019 self.advance(); self.advance(); let params = self.parse_class_member_params(true)?;
1022 self.advance(); if matches!(self.peek().kind, TokenKind::Colon) {
1024 let colon = self.advance();
1025 let _ = self.parse_type_annotation();
1026 self.error_at_with_help(
1027 colon.span,
1028 "a constructor cannot declare a return type",
1029 vec![
1030 "drop the `: T` — a constructor always returns the class instance".to_string(),
1031 ],
1032 );
1033 }
1034 let body = self.parse_class_member_body()?;
1035 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1036 .span
1037 .end;
1038 Some(crate::ClassMember::Constructor {
1039 visibility,
1040 params,
1041 body,
1042 span: self.span(member_start, body_end),
1043 doc,
1044 })
1045 }
1046
1047 fn parse_accessor(
1050 &mut self,
1051 member_start: u32,
1052 modifiers: crate::ClassModifiers,
1053 kind: crate::AccessorKind,
1054 doc: Option<crate::DocComment>,
1055 ) -> Option<crate::ClassMember> {
1056 self.advance(); let name = self.expect_property_ident("expected accessor name")?;
1058 self.advance(); let mut params = self.parse_class_member_params(false)?;
1060 self.advance(); let param = match kind {
1062 crate::AccessorKind::Get => {
1063 if !params.is_empty() {
1064 self.error_at(name.span, "a getter cannot declare parameters");
1065 }
1066 None
1067 }
1068 crate::AccessorKind::Set => {
1069 if params.len() != 1 {
1070 self.error_at(name.span, "a setter must declare exactly one parameter");
1071 return None;
1072 }
1073 Some(Box::new(params.remove(0)))
1074 }
1075 };
1076 let return_type = if matches!(self.peek().kind, TokenKind::Colon) {
1077 self.advance();
1078 Some(self.parse_type_annotation()?)
1079 } else {
1080 if kind == crate::AccessorKind::Get {
1084 self.error_at_peek_with_help(
1085 "expected `:` and return type",
1086 vec!["get name(): T { … }".to_string()],
1087 );
1088 }
1089 None
1090 };
1091 let body = self.parse_class_member_body()?;
1092 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1093 .span
1094 .end;
1095 Some(crate::ClassMember::Accessor {
1096 name,
1097 modifiers,
1098 kind,
1099 param,
1100 return_type,
1101 body,
1102 span: self.span(member_start, body_end),
1103 doc,
1104 })
1105 }
1106
1107 fn parse_class_modifiers(&mut self) -> crate::ClassModifiers {
1113 let mut visibility = crate::Visibility::Public;
1114 let mut visibility_span: Option<Span> = None;
1115 let mut readonly: Option<Span> = None;
1116 let mut static_span: Option<Span> = None;
1117 loop {
1118 if self.peek_word_is_class_modifier("public")
1119 || self.peek_word_is_class_modifier("private")
1120 {
1121 let (kind, word) = if self.peek_identifier_text_is("private") {
1122 (crate::Visibility::Private, "private")
1123 } else {
1124 (crate::Visibility::Public, "public")
1125 };
1126 let tok = self.advance();
1127 if visibility_span.is_some() {
1128 self.error_at_with_help(
1129 tok.span,
1130 "a class member may have at most one visibility modifier",
1131 vec!["keep a single `public` or `private`".to_string()],
1132 );
1133 continue;
1134 }
1135 let preceding = static_span
1138 .map(|_| "static")
1139 .or(readonly.map(|_| "readonly"));
1140 if let Some(preceding) = preceding {
1141 self.error_at_with_help(
1142 tok.span,
1143 format!("`{word}` must come before `{preceding}`"),
1144 vec![format!("write `{word} {preceding} <name>`")],
1145 );
1146 }
1147 visibility = kind;
1148 visibility_span = Some(tok.span);
1149 continue;
1150 }
1151 if self.peek_word_is_class_modifier("static") {
1152 let tok = self.advance();
1153 if static_span.is_some() {
1154 self.error_at(tok.span, "duplicate `static` modifier");
1155 } else {
1156 static_span = Some(tok.span);
1157 }
1158 continue;
1159 }
1160 if self.peek_word_is_class_modifier("readonly") {
1161 let tok = self.advance();
1162 if readonly.is_some() {
1163 self.error_at(tok.span, "duplicate `readonly` modifier");
1164 } else {
1165 readonly = Some(tok.span);
1166 }
1167 continue;
1168 }
1169 break;
1170 }
1171 crate::ClassModifiers {
1172 visibility,
1173 visibility_span,
1174 readonly,
1175 static_span,
1176 }
1177 }
1178
1179 fn peek_word_is_class_modifier(&self, word: &str) -> bool {
1185 if !self.peek_identifier_text_is(word) {
1186 return false;
1187 }
1188 if word != "static" && self.line_break_after_peek() {
1189 return false;
1190 }
1191 let next = &self.peek_at(1).kind;
1192 is_property_name(next) || matches!(next, TokenKind::StringLiteral(_))
1193 }
1194
1195 fn parse_class_member_params(
1196 &mut self,
1197 allow_param_properties: bool,
1198 ) -> Option<Vec<ParamDecl>> {
1199 let saved = self.class_member_body_depth;
1200 self.class_member_body_depth = 1;
1201 let params = self.parse_param_list(allow_param_properties);
1202 self.class_member_body_depth = saved;
1203 params
1204 }
1205
1206 fn parse_class_member_body(&mut self) -> Option<StmtId> {
1207 self.class_member_body_depth += 1;
1208 let body = self.parse_block();
1209 self.class_member_body_depth -= 1;
1210 body
1211 }
1212
1213 fn parse_interface_decl(&mut self) -> Option<StmtId> {
1214 let doc = self.take_leading_doc();
1215 let kw = self.advance();
1216 let name = self.parse_type_decl_name("an", "interface")?;
1217
1218 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1219 self.parse_generic_param_list()?
1220 } else {
1221 Vec::new()
1222 };
1223
1224 let mut extends = Vec::new();
1225 if matches!(self.peek().kind, TokenKind::Extends) {
1226 self.advance();
1227 loop {
1228 extends.push(self.parse_type_annotation()?);
1229 if !matches!(self.peek().kind, TokenKind::Comma) {
1230 break;
1231 }
1232 self.advance();
1233 }
1234 }
1235 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1236 self.error_at_peek("expected `{` after interface name");
1237 return None;
1238 }
1239 self.advance();
1240
1241 let mut members: Vec<crate::InterfaceMember> = Vec::new();
1242 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
1243 && !self.peek_starts_declaration()
1244 {
1245 let member_doc = self.take_leading_doc();
1249 if matches!(self.peek().kind, TokenKind::LeftParen) {
1250 let open = self.advance();
1251 let params = self.parse_param_list(false)?;
1252 self.advance();
1253 if !matches!(self.peek().kind, TokenKind::Colon) {
1254 self.error_at_peek("expected `:` and return type");
1255 return None;
1256 }
1257 self.advance();
1258 let return_type = self.parse_type_annotation()?;
1259 let end = self.finish_interface_member(return_type.span.end)?;
1260 let span = self.span(open.span.start, end);
1261 let sentinel = Ident {
1262 name: "@call".to_string(),
1263 span: open.span,
1264 };
1265 members.push(crate::InterfaceMember::Method {
1266 name: sentinel,
1267 optional: false,
1268 generics: Vec::new(),
1269 params,
1270 return_type,
1271 span,
1272 doc: member_doc,
1273 });
1274 continue;
1275 }
1276 if matches!(self.peek().kind, TokenKind::LessThan) {
1277 self.error_at_peek("generic call signatures are not yet supported");
1278 return None;
1279 }
1280 let member_start = self.peek().span.start;
1281 let readonly = self.eat_readonly_property_modifier();
1282 if self.peek_is_parameterless_index_signature() {
1283 self.skip_parameterless_index_signature(member_start)?;
1284 self.finish_interface_member(self.prev_token_end())?;
1285 continue;
1286 }
1287 if matches!(self.peek().kind, TokenKind::LeftBracket) {
1288 let signature = self.parse_index_signature(readonly)?;
1289 self.finish_interface_member(signature.span.end)?;
1290 members.push(crate::InterfaceMember::IndexSignature(signature));
1291 continue;
1292 }
1293 let member_name = self.expect_property_ident("expected interface member name")?;
1294 let optional = matches!(self.peek().kind, TokenKind::Question);
1295 if optional {
1296 self.advance();
1297 }
1298 if matches!(self.peek().kind, TokenKind::Colon) {
1299 self.advance();
1300 let ty = self.parse_type_annotation()?;
1301 let end = self.finish_interface_member(ty.span.end)?;
1302 let span = self.span(member_name.span.start, end);
1303 members.push(crate::InterfaceMember::Property {
1304 name: member_name,
1305 ty,
1306 optional,
1307 readonly,
1308 span,
1309 doc: member_doc,
1310 });
1311 continue;
1312 }
1313 if readonly {
1314 self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
1315 }
1316 let m_generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1317 self.parse_generic_param_list()?
1318 } else {
1319 Vec::new()
1320 };
1321 if !matches!(self.peek().kind, TokenKind::LeftParen) {
1322 self.error_at_peek(
1323 "expected `(` to start a method signature or `:` to start a property",
1324 );
1325 return None;
1326 }
1327 self.advance();
1328 let params = self.parse_param_list(false)?;
1329 self.advance();
1330 if !matches!(self.peek().kind, TokenKind::Colon) {
1331 self.error_at_peek("expected `:` and return type");
1332 return None;
1333 }
1334 self.advance();
1335 let return_type = self.parse_type_annotation()?;
1336 let end = self.finish_interface_member(return_type.span.end)?;
1337 let span = self.span(member_name.span.start, end);
1338 if optional {
1339 if !m_generics.is_empty() {
1340 self.error_at(
1341 member_name.span,
1342 "generic optional method declarations are not supported",
1343 );
1344 return None;
1345 }
1346 let ty = self.method_declaration_type(params, return_type, span)?;
1347 members.push(crate::InterfaceMember::Property {
1348 name: member_name,
1349 ty,
1350 optional: true,
1351 readonly,
1352 span,
1353 doc: member_doc,
1354 });
1355 } else {
1356 members.push(crate::InterfaceMember::Method {
1357 name: member_name,
1358 optional,
1359 generics: m_generics,
1360 params,
1361 return_type,
1362 span,
1363 doc: member_doc,
1364 });
1365 }
1366 }
1367
1368 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1369 self.error_at_peek("expected `}` to close interface");
1370 return None;
1371 }
1372 let close = self.advance();
1373
1374 parse_arena_result(
1375 self.ast.try_push_stmt(Stmt {
1376 kind: StmtKind::InterfaceDecl {
1377 name,
1378 generics,
1379 extends,
1380 members,
1381 doc,
1382 },
1383 span: self.span(kw.span.start, close.span.end),
1384 }),
1385 &mut self.fatal,
1386 )
1387 }
1388
1389 fn method_declaration_type(
1390 &mut self,
1391 params: Vec<ParamDecl>,
1392 return_type: TypeAnnotation,
1393 span: Span,
1394 ) -> Option<TypeAnnotation> {
1395 let mut fields = Vec::new();
1396 for param in params {
1397 if param.pattern.is_some() || param.default.is_some() {
1398 self.error_at(
1399 param.name.span,
1400 "a method declaration cannot contain a parameter pattern or default value",
1401 );
1402 return None;
1403 }
1404 let Some(ty) = param.ty else {
1405 return self.invariant_failure("method parameter annotation was not retained");
1406 };
1407 fields.push(TypeAnnotationField {
1408 name: param.name,
1409 ty,
1410 optional: param.optional,
1411 readonly: false,
1412 rest: param.rest,
1413 method: false,
1414 });
1415 }
1416 Some(TypeAnnotation {
1417 kind: TypeAnnotationKind::Function {
1418 params: fields,
1419 return_type: Box::new(return_type),
1420 },
1421 span,
1422 })
1423 }
1424
1425 fn finish_interface_member(&mut self, body_end: u32) -> Option<u32> {
1426 self.finish_type_member(body_end, "expected `;`, `,`, or `}` after interface member")
1427 }
1428
1429 fn finish_type_member(&mut self, body_end: u32, message: &str) -> Option<u32> {
1435 match self.peek().kind {
1436 TokenKind::Semicolon | TokenKind::Comma => Some(self.advance().span.end),
1437 TokenKind::RightBrace => Some(body_end),
1438 _ if self.line_break_before_peek() => Some(body_end),
1439 _ => {
1440 self.error_at_peek(message);
1441 None
1442 }
1443 }
1444 }
1445
1446 fn parse_type_alias_decl(&mut self) -> Option<StmtId> {
1447 let doc = self.take_leading_doc();
1448 let kw = self.advance();
1449 let name = self.parse_type_decl_name("a", "type alias")?;
1450
1451 let generics: Vec<Ident> = if matches!(self.peek().kind, TokenKind::LessThan) {
1452 self.parse_generic_param_list()?
1453 } else {
1454 Vec::new()
1455 };
1456
1457 if !matches!(self.peek().kind, TokenKind::Equals) {
1458 self.error_at_peek("expected `=` after type alias name");
1459 return None;
1460 }
1461 self.advance();
1462
1463 let ty = self.parse_type_annotation()?;
1464
1465 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1468 self.advance().span.end
1469 } else if self.line_break_before_peek() {
1470 self.prev_token_end()
1471 } else {
1472 self.error_at_peek("expected `;` after type alias body");
1473 return None;
1474 };
1475
1476 parse_arena_result(
1477 self.ast.try_push_stmt(Stmt {
1478 kind: StmtKind::TypeAliasDecl {
1479 name,
1480 generics,
1481 ty,
1482 doc,
1483 },
1484 span: self.span(kw.span.start, end),
1485 }),
1486 &mut self.fatal,
1487 )
1488 }
1489
1490 fn parse_enum_decl(&mut self) -> Option<StmtId> {
1492 #[derive(Copy, Clone)]
1493 enum Kind {
1494 Number,
1495 String,
1496 }
1497 let doc = self.take_leading_doc();
1498 let kw = self.advance();
1499 let name = self.parse_type_decl_name("an", "enum")?;
1500
1501 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1502 self.error_at_peek("expected `{` after enum name");
1503 return None;
1504 }
1505 self.advance();
1506
1507 let mut members: Vec<EnumMember> = Vec::new();
1508 let mut first_explicit: Option<(Kind, Span)> = None;
1509 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1510 if matches!(self.peek().kind, TokenKind::Semicolon) {
1512 self.advance();
1513 continue;
1514 }
1515 let member_doc = self.take_leading_doc();
1516 let member_name_tok = self.expect_identifier_name("expected enum member name")?;
1517 let member_name = self.ident_from_token(&member_name_tok);
1518
1519 let value = if matches!(self.peek().kind, TokenKind::Equals) {
1520 self.advance();
1521 let init = self.parse_enum_initializer()?;
1522 let (this_kind, this_span) = match &init {
1523 EnumInitializer::Number { span, .. } => (Kind::Number, *span),
1524 EnumInitializer::String { span, .. } => (Kind::String, *span),
1525 };
1526 match first_explicit {
1527 None => first_explicit = Some((this_kind, this_span)),
1528 Some((prev, _)) => {
1529 let mismatched = matches!(
1530 (prev, this_kind),
1531 (Kind::Number, Kind::String) | (Kind::String, Kind::Number)
1532 );
1533 if mismatched {
1534 self.error_at(
1535 this_span,
1536 "mixed numeric and string enum members are not allowed",
1537 );
1538 }
1539 }
1540 }
1541 Some(init)
1542 } else {
1543 None
1544 };
1545
1546 let end_span = value
1547 .as_ref()
1548 .map_or(member_name.span, super::ast::EnumInitializer::span);
1549 let member_span = self.span(member_name.span.start, end_span.end);
1550 members.push(EnumMember {
1551 name: member_name,
1552 value,
1553 span: member_span,
1554 doc: member_doc,
1555 });
1556
1557 match self.peek().kind {
1558 TokenKind::Comma | TokenKind::Semicolon => {
1561 self.advance();
1562 }
1563 TokenKind::RightBrace | TokenKind::Eof => {}
1564 _ => {
1565 self.error_at_peek("expected `,` or `}` after enum member");
1566 return None;
1567 }
1568 }
1569 }
1570
1571 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1572 self.error_at_peek("expected `}` to close enum");
1573 return None;
1574 }
1575 let close = self.advance();
1576
1577 parse_arena_result(
1578 self.ast.try_push_stmt(Stmt {
1579 kind: StmtKind::EnumDecl { name, members, doc },
1580 span: self.span(kw.span.start, close.span.end),
1581 }),
1582 &mut self.fatal,
1583 )
1584 }
1585
1586 fn parse_export(&mut self) -> Option<StmtId> {
1593 let export_tok = self.advance();
1594 let export_span = export_tok.span;
1595
1596 let nested = self.block_depth > 0;
1597 if nested {
1598 self.error_at_with_help(
1599 export_span,
1600 "`export` statements must appear at the top of the file",
1601 vec!["move this `export` to the top level of the module".to_string()],
1602 );
1603 }
1606
1607 if self.peek_identifier_text_is("type")
1610 && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1611 {
1612 self.advance();
1613 }
1614
1615 let declares = matches!(
1616 self.peek().kind,
1617 TokenKind::Function
1618 | TokenKind::Class
1619 | TokenKind::Const
1620 | TokenKind::Let
1621 | TokenKind::Interface
1622 | TokenKind::Enum
1623 ) || self.at_type_alias_head();
1624 if declares {
1625 if export_tok.leading_doc.is_some() && self.peek().leading_doc.is_none() {
1628 if let Some(token) = self.tokens.get_mut(self.pos) {
1629 token.leading_doc = export_tok.leading_doc;
1630 } else {
1631 return self.invariant_failure("export cursor is outside token stream");
1632 }
1633 }
1634 let id = self.parse_statement()?;
1635 if nested {
1636 return None;
1637 }
1638 self.ast.exported_decls.push(ExportedDecl {
1639 stmt: id,
1640 export_span,
1641 });
1642 return Some(id);
1643 }
1644
1645 match self.peek().kind {
1646 TokenKind::LeftBrace => {
1647 let doc = export_tok
1650 .leading_doc
1651 .as_ref()
1652 .and_then(|raw| self.parse_doc(raw))
1653 .or_else(|| self.take_leading_doc());
1654 self.parse_export_from(export_span, nested, doc)
1655 }
1656 TokenKind::Default => {
1657 self.error_at_peek_with_help(
1658 "`export default` is not supported",
1659 vec![
1660 "default exports are out of scope; drop `default` — a named \
1661 `export` marks the declaration visible to sibling modules"
1662 .to_string(),
1663 ],
1664 );
1665 None
1666 }
1667 _ => {
1668 self.error_at_peek_with_help(
1669 "expected a declaration after `export`",
1670 vec![
1671 "`export` may precede `function`, `class`, `const`, `let`, `type`, \
1672 `interface`, or `enum`, or take the form `export { … }`"
1673 .to_string(),
1674 ],
1675 );
1676 None
1677 }
1678 }
1679 }
1680
1681 fn parse_export_from(
1684 &mut self,
1685 export_span: Span,
1686 nested: bool,
1687 doc: Option<crate::DocComment>,
1688 ) -> Option<StmtId> {
1689 let (specs, open_span) = self.parse_specifier_list(SpecifierList::Export)?;
1690 if specs.is_empty() {
1691 self.error_at_with_help(
1692 open_span,
1693 "empty export specifier list",
1694 vec!["name what you re-export: `export { name } from \"./util\";`".to_string()],
1695 );
1696 return None;
1697 }
1698
1699 let source = if self.peek_identifier_text_is("from") {
1700 self.advance();
1701 let module_tok = self.peek().clone();
1702 let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1703 s.clone()
1704 } else {
1705 self.error_at_peek("expected module specifier string after `from`");
1706 return None;
1707 };
1708 self.advance();
1709 Some((module, module_tok.span))
1710 } else {
1711 None
1712 };
1713
1714 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1715 self.error_at_peek("expected `;` after export statement");
1716 return None;
1717 }
1718 let semi = self.advance();
1719
1720 if nested {
1721 return None;
1722 }
1723
1724 parse_arena_result(
1725 self.ast.try_push_stmt(Stmt {
1726 kind: StmtKind::ExportFrom { specs, source, doc },
1727 span: self.span(export_span.start, semi.span.end),
1728 }),
1729 &mut self.fatal,
1730 )
1731 }
1732
1733 fn parse_import_decl(&mut self) -> Option<StmtId> {
1734 let doc = self.take_leading_doc();
1735 let kw = self.advance();
1736 let kw_span = kw.span;
1737
1738 let nested = self.block_depth > 0;
1739 if nested {
1740 self.error_at_with_help(
1741 kw_span,
1742 "`import` statements must appear at the top of the file",
1743 vec!["move this `import` outside any block / function body".to_string()],
1744 );
1745 }
1747
1748 let kind = self.parse_import_kind()?;
1749
1750 if !self.peek_identifier_text_is("from") {
1751 self.error_at_peek("expected `from` after import specifier list");
1752 return None;
1753 }
1754 self.advance();
1755
1756 let module_tok = self.peek().clone();
1757 let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1758 s.clone()
1759 } else {
1760 self.error_at_peek("expected module specifier string after `from`");
1761 return None;
1762 };
1763 let module_span = module_tok.span;
1764 self.advance();
1765
1766 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1767 self.error_at_peek("expected `;` after import statement");
1768 return None;
1769 }
1770 let semi = self.advance();
1771
1772 if nested {
1773 return None;
1774 }
1775
1776 parse_arena_result(
1777 self.ast.try_push_stmt(Stmt {
1778 kind: StmtKind::Import {
1779 module,
1780 module_span,
1781 kind,
1782 doc,
1783 },
1784 span: self.span(kw_span.start, semi.span.end),
1785 }),
1786 &mut self.fatal,
1787 )
1788 }
1789
1790 fn parse_import_kind(&mut self) -> Option<ImportKind> {
1791 if self.peek_identifier_text_is("type")
1796 && match self.peek_at(1).kind {
1797 TokenKind::LeftBrace | TokenKind::Star => true,
1798 TokenKind::Identifier => {
1799 !(self.peek_at_is_word(1, "from")
1800 && matches!(self.peek_at(2).kind, TokenKind::StringLiteral(_)))
1801 }
1802 _ => false,
1803 }
1804 {
1805 self.advance();
1806 }
1807
1808 match self.peek().kind {
1809 TokenKind::LeftBrace => self.parse_named_imports(),
1810 TokenKind::Identifier => {
1811 let local_tok = self.advance();
1812 let local_name = self.ident_from_token(&local_tok);
1813 if matches!(self.peek().kind, TokenKind::Comma) {
1814 self.error_at_peek_with_help(
1815 "combining default and named imports is not supported",
1816 vec!["use two separate `import` statements".to_string()],
1817 );
1818 return None;
1819 }
1820 Some(ImportKind::Namespace { local_name })
1821 }
1822 TokenKind::Star => {
1825 self.advance();
1826 if !self.peek_identifier_text_is("as") {
1827 self.error_at_peek_with_help(
1828 "expected `as <name>` after `import *`",
1829 vec![
1830 "name the namespace binding: \
1831 `import * as ns from \"pkg\";` (or use the \
1832 short form: `import ns from \"pkg\";`)"
1833 .to_string(),
1834 ],
1835 );
1836 return None;
1837 }
1838 self.advance();
1839 let local_tok = self.expect_identifier("expected namespace name after `as`")?;
1840 let local_name = self.ident_from_token(&local_tok);
1841 Some(ImportKind::Namespace { local_name })
1842 }
1843 TokenKind::StringLiteral(_) => {
1844 self.error_at_peek_with_help(
1845 "side-effect-only imports are not supported",
1846 vec![
1847 "name what you import: `import { name } from \"pkg\";` \
1848 or `import ns from \"pkg\";`"
1849 .to_string(),
1850 ],
1851 );
1852 None
1853 }
1854 _ => {
1855 self.error_at_peek("expected `{` or namespace name after `import`");
1856 None
1857 }
1858 }
1859 }
1860
1861 fn parse_named_imports(&mut self) -> Option<ImportKind> {
1862 let (specs, open_span) = self.parse_specifier_list(SpecifierList::Import)?;
1863 if specs.is_empty() {
1864 self.error_at_with_help(
1865 open_span,
1866 "empty import specifier list",
1867 vec!["name what you import: `import { name } from \"pkg\";`".to_string()],
1868 );
1869 return None;
1870 }
1871 Some(ImportKind::Named(specs))
1872 }
1873
1874 fn parse_specifier_list(
1880 &mut self,
1881 list: SpecifierList,
1882 ) -> Option<(Vec<ImportSpecifier>, Span)> {
1883 let what = list.noun();
1884 let open = self.advance();
1885 let mut specs: Vec<ImportSpecifier> = Vec::new();
1886 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1887 if self.peek_identifier_text_is("type")
1890 && matches!(self.peek_at(1).kind, TokenKind::Identifier)
1891 && !self.peek_at_is_word(1, "as")
1892 {
1893 self.advance();
1894 }
1895 let imported_tok =
1896 self.expect_identifier_name(&format!("expected {what} specifier name"))?;
1897 let imported_name = self.ident_from_token(&imported_tok);
1898
1899 let binds_local = list == SpecifierList::Import;
1900 let local_name = if self.peek_identifier_text_is("as") {
1901 self.advance();
1902 let local_tok = self.expect_identifier_name("expected local name after `as`")?;
1903 if binds_local {
1904 self.reject_strict_mode_reserved_word(&local_tok);
1905 }
1906 self.ident_from_token(&local_tok)
1907 } else {
1908 if binds_local {
1909 self.reject_strict_mode_reserved_word(&imported_tok);
1910 }
1911 imported_name.clone()
1912 };
1913
1914 specs.push(ImportSpecifier {
1915 imported_name,
1916 local_name,
1917 });
1918
1919 match self.peek().kind {
1920 TokenKind::Comma => {
1921 self.advance();
1922 }
1923 TokenKind::RightBrace | TokenKind::Eof => {}
1924 _ => {
1925 self.error_at_peek(format!("expected `,` or `}}` after {what} specifier"));
1926 return None;
1927 }
1928 }
1929 }
1930
1931 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1932 self.error_at_peek(format!("expected `}}` to close {what} specifier list"));
1933 return None;
1934 }
1935 self.advance();
1936
1937 Some((specs, open.span))
1938 }
1939
1940 fn parse_enum_initializer(&mut self) -> Option<EnumInitializer> {
1941 let neg = if matches!(self.peek().kind, TokenKind::Minus) {
1942 Some(self.advance())
1943 } else {
1944 None
1945 };
1946 let tok = self.advance();
1947 match &tok.kind {
1948 TokenKind::NumberLiteral(n) => {
1949 let value = if neg.is_some() { -n } else { *n };
1950 let value = if value == 0.0 { 0.0 } else { value };
1952 let start = neg.as_ref().map_or(tok.span.start, |t| t.span.start);
1953 Some(EnumInitializer::Number {
1954 value,
1955 span: self.span(start, tok.span.end),
1956 })
1957 }
1958 TokenKind::StringLiteral(s) => {
1959 if let Some(neg_tok) = &neg {
1960 self.error_at(
1961 self.span(neg_tok.span.start, tok.span.end),
1962 "cannot negate a string enum initializer",
1963 );
1964 return None;
1965 }
1966 Some(EnumInitializer::String {
1967 value: s.clone(),
1968 span: tok.span,
1969 })
1970 }
1971 _ => {
1972 let span = neg.map_or(tok.span, |t| self.span(t.span.start, tok.span.end));
1973 self.error_at(
1974 span,
1975 "expected a number or string literal for enum initializer",
1976 );
1977 None
1978 }
1979 }
1980 }
1981
1982 fn is_contextual_type_operator(&self, index: usize, operator: &str) -> bool {
1990 let Some(tok) = self.tokens.get(index) else {
1991 return false;
1992 };
1993 if !self.token_is_word(tok, operator) {
1994 return false;
1995 }
1996 matches!(
1997 self.tokens.get(index + 1).map(|t| &t.kind),
1998 Some(
1999 TokenKind::Identifier
2000 | TokenKind::Void
2001 | TokenKind::LeftBrace
2002 | TokenKind::LeftParen
2003 | TokenKind::LeftBracket
2004 | TokenKind::StringLiteral(_)
2005 | TokenKind::NumberLiteral(_)
2006 | TokenKind::BooleanLiteral(_)
2007 | TokenKind::NullLiteral
2008 )
2009 )
2010 }
2011
2012 fn parse_type_argument_list(&mut self) -> Option<(Vec<TypeAnnotation>, u32)> {
2013 let lt = self.advance();
2014 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2015 self.error_at(lt.span, "empty generic argument list `<>` is not allowed");
2016 return None;
2017 }
2018 let mut args = Vec::new();
2019 loop {
2020 args.push(self.parse_type_annotation()?);
2021 match self.peek().kind {
2022 TokenKind::Comma => {
2023 self.advance();
2024 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2025 let gt = self.advance();
2026 return Some((args, gt.span.end));
2027 }
2028 }
2029 TokenKind::GreaterThan => {
2030 let gt = self.advance();
2031 return Some((args, gt.span.end));
2032 }
2033 _ => {
2034 self.error_at(
2035 lt.span,
2036 "expected `,` or `>` to close generic argument list",
2037 );
2038 return None;
2039 }
2040 }
2041 }
2042 }
2043
2044 fn parse_type_decl_name(&mut self, article: &str, noun: &str) -> Option<Ident> {
2048 let name_tok = self.expect_identifier(&format!("expected {noun} name"))?;
2049 let name = self.ident_from_token(&name_tok);
2050 if BUILT_IN_TYPE_NAMES.contains(&name.name.as_str()) {
2051 self.error_at_with_help(
2052 name.span,
2053 format!(
2054 "`{}` is a built-in type and can't be used as {article} {noun} name",
2055 name.name
2056 ),
2057 vec![format!("rename the {noun}")],
2058 );
2059 }
2060 Some(name)
2061 }
2062
2063 fn parse_generic_param_list(&mut self) -> Option<Vec<Ident>> {
2064 let lt = self.advance();
2065 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2066 self.error_at(lt.span, "empty generic parameter list `<>` is not allowed");
2067 return None;
2068 }
2069 let mut generics = Vec::new();
2070 loop {
2071 let name_tok = self.expect_identifier("expected generic parameter name")?;
2072 let name = self.ident_from_token(&name_tok);
2073 if generics.iter().any(|g: &Ident| g.name == name.name) {
2077 self.error_at_with_help(
2078 name.span,
2079 format!("duplicate type parameter `{}`", name.name),
2080 vec!["give each type parameter its own name".to_string()],
2081 );
2082 } else {
2083 generics.push(name);
2084 }
2085 match self.peek().kind {
2086 TokenKind::Comma => {
2087 self.advance();
2088 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2089 self.advance();
2090 return Some(generics);
2091 }
2092 }
2093 TokenKind::GreaterThan => {
2094 self.advance();
2095 return Some(generics);
2096 }
2097 _ => {
2098 self.error_at_peek("expected `,` or `>`");
2099 return None;
2100 }
2101 }
2102 }
2103 }
2104
2105 fn parse_param_list(&mut self, allow_param_properties: bool) -> Option<Vec<ParamDecl>> {
2106 if matches!(self.peek().kind, TokenKind::RightParen) {
2107 return Some(Vec::new());
2108 }
2109 let mut params = Vec::new();
2110 loop {
2111 let param = self.parse_param(allow_param_properties)?;
2112 let is_rest = param.rest;
2113 params.push(param);
2114 match self.peek().kind {
2115 TokenKind::Comma => {
2116 if is_rest {
2117 let comma = self.advance();
2118 self.error_at(comma.span, "rest parameter must be the last parameter");
2119 return None;
2120 }
2121 self.advance();
2122 if matches!(self.peek().kind, TokenKind::RightParen) {
2123 break;
2124 }
2125 }
2126 TokenKind::RightParen => break,
2127 _ => {
2128 self.error_at_peek("expected `,` or `)`");
2129 return None;
2130 }
2131 }
2132 }
2133 self.check_parameter_order(params.iter().map(ParamOrder::of_param));
2134 Some(params)
2135 }
2136
2137 fn parse_param(&mut self, allow_param_properties: bool) -> Option<ParamDecl> {
2138 let modifiers = if self.peek_word_is_class_modifier("public")
2142 || self.peek_word_is_class_modifier("private")
2143 || self.peek_word_is_class_modifier("readonly")
2144 {
2145 let span = self.peek().span;
2146 let parsed = self.parse_class_modifiers();
2147 if allow_param_properties {
2148 Some(parsed)
2149 } else {
2150 self.error_at_with_help(
2151 span,
2152 "parameter properties are only allowed in a constructor",
2153 vec![
2154 "move the `public`/`private`/`readonly` modifier to a constructor parameter"
2155 .to_string(),
2156 ],
2157 );
2158 None
2159 }
2160 } else {
2161 None
2162 };
2163
2164 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
2165 self.advance();
2166 true
2167 } else {
2168 false
2169 };
2170 if modifiers.is_some() && rest {
2171 self.error_at_peek("a parameter property cannot be a rest parameter");
2172 return None;
2173 }
2174
2175 let (name, pattern) = match self.peek().kind {
2178 TokenKind::LeftBrace | TokenKind::LeftBracket => {
2179 if rest {
2180 self.error_at_peek("rest parameter cannot be destructured");
2181 return None;
2182 }
2183 if modifiers.is_some() {
2184 self.error_at_peek("a parameter property cannot be destructured");
2185 return None;
2186 }
2187 let binding = self.parse_binding()?;
2188 let placeholder = Ident {
2189 name: String::new(),
2190 span: binding.span(),
2191 };
2192 (placeholder, Some(binding))
2193 }
2194 _ => {
2195 let name_tok = self.expect_identifier(if rest {
2196 "expected identifier after `...`"
2197 } else {
2198 "expected parameter name"
2199 })?;
2200 let name = self.ident_from_token(&name_tok);
2201 (name, None)
2202 }
2203 };
2204
2205 let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
2206 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2207 self.advance();
2208 Some(self.parse_type_annotation()?)
2209 } else if !rest && matches!(self.peek().kind, TokenKind::Equals) {
2210 None
2211 } else {
2212 if rest {
2213 self.error_at_peek_with_help(
2214 "rest parameter requires a type annotation",
2215 vec!["function name(...args: T[]): R { … }".to_string()],
2216 );
2217 } else {
2218 self.error_at_peek_with_help(
2219 "parameter requires a type annotation",
2220 vec!["add a type (`x: T`) or a default initializer (`x = value`)".to_string()],
2221 );
2222 }
2223 return None;
2224 };
2225 let default = self.parse_parameter_default(rest, optional)?;
2226 Some(ParamDecl {
2227 name,
2228 ty,
2229 default,
2230 optional,
2231 pattern,
2232 rest,
2233 modifiers,
2234 })
2235 }
2236
2237 fn parse_parameter_optional(&mut self, rest: bool, pattern: bool) -> Option<bool> {
2238 if !matches!(self.peek().kind, TokenKind::Question) {
2239 return Some(false);
2240 }
2241 let question = self.advance();
2242 if rest || pattern {
2243 self.error_at(
2244 question.span,
2245 if rest {
2246 "a rest parameter cannot be optional"
2247 } else {
2248 "a destructured parameter cannot be optional; use a default value"
2249 },
2250 );
2251 return None;
2252 }
2253 Some(true)
2254 }
2255
2256 fn parse_parameter_default(&mut self, rest: bool, optional: bool) -> Option<Option<ExprId>> {
2259 if !matches!(self.peek().kind, TokenKind::Equals) {
2260 return Some(None);
2261 }
2262 if rest {
2263 self.error_rest_parameter_default();
2264 return None;
2265 }
2266 if optional {
2267 self.error_at_peek_with_help(
2268 "an optional parameter cannot have a default value",
2269 vec!["drop the `?`: a parameter with a default is already optional".to_string()],
2270 );
2271 }
2272 self.advance();
2273 Some(Some(self.parse_expression()?))
2274 }
2275
2276 fn error_rest_parameter_default(&mut self) {
2278 self.error_at_peek_with_help(
2279 "rest parameter cannot have a default value",
2280 vec!["omit the `= …`; an unspecified rest defaults to `[]`".to_string()],
2281 );
2282 }
2283
2284 fn check_parameter_order<'p>(&mut self, params: impl IntoIterator<Item = ParamOrder<'p>>) {
2288 let mut follows_optional = false;
2289 for param in params {
2290 if param.optional {
2291 follows_optional = true;
2292 continue;
2293 }
2294 if !follows_optional || !param.required {
2295 continue;
2296 }
2297 let label = match param.name {
2298 Some(name) => format!("parameter `{name}`"),
2299 None => "destructured parameter".to_string(),
2300 };
2301 self.error_at_with_help(
2302 param.span,
2303 format!("required {label} cannot follow an optional parameter"),
2304 vec!["make it optional too (`x?: T`), give it a default (`x: T = …`), or move it before the optional parameters".to_string()],
2305 );
2306 return;
2307 }
2308 }
2309
2310 fn eat_asi_semicolon_before(&mut self, next: TokenKind) {
2315 if matches!(self.peek().kind, TokenKind::Semicolon) && self.peek_at(1).kind == next {
2316 self.advance();
2317 }
2318 }
2319
2320 fn parse_block(&mut self) -> Option<StmtId> {
2321 self.eat_asi_semicolon_before(TokenKind::LeftBrace);
2322 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2323 self.error_at_peek("expected `{`");
2324 return None;
2325 }
2326 let open = self.advance();
2327 self.block_depth += 1;
2328
2329 let mut stmts = Vec::new();
2330 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2331 if matches!(self.peek().kind, TokenKind::Semicolon) {
2332 self.advance();
2333 continue;
2334 }
2335 let pos_before = self.pos;
2336 if let Some(id) = self.parse_statement() {
2337 stmts.push(id);
2338 } else {
2339 self.recover();
2340 if self.pos == pos_before && !self.is_at_eof() {
2341 self.advance();
2342 }
2343 }
2344 }
2345
2346 self.block_depth -= 1;
2347 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2348 self.error_at_peek("expected `}`");
2349 return None;
2350 }
2351 let close = self.advance();
2352
2353 let span = self.span(open.span.start, close.span.end);
2354 parse_arena_result(
2355 self.ast.try_push_stmt(Stmt {
2356 kind: StmtKind::Block(stmts),
2357 span,
2358 }),
2359 &mut self.fatal,
2360 )
2361 }
2362
2363 fn parse_control_body(&mut self) -> Option<StmtId> {
2368 if matches!(self.peek().kind, TokenKind::LeftBrace) {
2369 return self.parse_block();
2370 }
2371 let keyword = match self.peek().kind {
2376 TokenKind::Let => Some(("a", "let")),
2377 TokenKind::Const => Some(("a", "const")),
2378 TokenKind::Function => Some(("a", "function")),
2379 TokenKind::Interface => Some(("an", "interface")),
2381 TokenKind::Enum => Some(("an", "enum")),
2382 _ if self.peek_identifier_text_is("type") => Some(("a", "type")),
2383 _ => None,
2384 };
2385 let declares = match self.peek_at(1).kind {
2389 TokenKind::Identifier => true,
2390 TokenKind::LeftBracket | TokenKind::LeftBrace => !matches!(keyword, Some((_, "type"))),
2391 _ => false,
2392 };
2393 if let Some((article, keyword)) = keyword
2394 && declares
2395 {
2396 self.error_at_peek_with_help(
2397 format!(
2398 "{article} `{keyword}` declaration can't be the body of a statement without braces"
2399 ),
2400 vec![format!("wrap it in braces: `{{ {keyword} … }}`")],
2401 );
2402 }
2403 self.block_depth += 1;
2404 let stmt = self.parse_statement();
2405 self.block_depth -= 1;
2406 let stmt = stmt?;
2407 let span = parse_arena_result(self.ast.try_stmt(stmt), &mut self.fatal)?.span;
2408 parse_arena_result(
2409 self.ast.try_push_stmt(Stmt {
2410 kind: StmtKind::Block(vec![stmt]),
2411 span,
2412 }),
2413 &mut self.fatal,
2414 )
2415 }
2416
2417 fn parse_loop_body(&mut self) -> Option<StmtId> {
2420 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2421 return self.parse_control_body();
2422 }
2423 self.empty_body_at_semicolon()
2424 }
2425
2426 fn parse_branch_body(&mut self, keyword: &str) -> Option<StmtId> {
2430 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2431 return self.parse_control_body();
2432 }
2433 self.error_at_peek_with_help(
2434 format!("`{keyword}` has an empty body"),
2435 vec![format!("remove the `;` after `{keyword}`")],
2436 );
2437 self.empty_body_at_semicolon()
2438 }
2439
2440 fn empty_body_at_semicolon(&mut self) -> Option<StmtId> {
2442 let semi = self.advance();
2443 parse_arena_result(
2444 self.ast.try_push_stmt(Stmt {
2445 kind: StmtKind::Block(vec![]),
2446 span: semi.span,
2447 }),
2448 &mut self.fatal,
2449 )
2450 }
2451
2452 fn parse_if(&mut self) -> Option<StmtId> {
2453 self.with_recursion_limit(Self::parse_if_inner)
2454 }
2455
2456 fn parse_if_inner(&mut self) -> Option<StmtId> {
2457 let kw = self.advance();
2458 let condition = self.parse_paren_condition()?;
2459 let then_block = self.parse_branch_body("if")?;
2460
2461 let else_block = if matches!(self.peek().kind, TokenKind::Else) {
2462 self.advance();
2463 if matches!(self.peek().kind, TokenKind::If) {
2464 Some(self.parse_if()?)
2465 } else {
2466 Some(self.parse_branch_body("else")?)
2467 }
2468 } else {
2469 None
2470 };
2471
2472 let end = match else_block {
2473 Some(id) => {
2474 parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
2475 .span
2476 .end
2477 }
2478 None => {
2479 parse_arena_result(self.ast.try_stmt(then_block), &mut self.fatal)?
2480 .span
2481 .end
2482 }
2483 };
2484
2485 parse_arena_result(
2486 self.ast.try_push_stmt(Stmt {
2487 kind: StmtKind::If {
2488 condition,
2489 then_block,
2490 else_block,
2491 },
2492 span: self.span(kw.span.start, end),
2493 }),
2494 &mut self.fatal,
2495 )
2496 }
2497
2498 fn parse_while(&mut self) -> Option<StmtId> {
2499 let kw = self.advance();
2500 let condition = self.parse_paren_condition()?;
2501 let body = self.parse_loop_body()?;
2502 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2503 .span
2504 .end;
2505
2506 parse_arena_result(
2507 self.ast.try_push_stmt(Stmt {
2508 kind: StmtKind::While { condition, body },
2509 span: self.span(kw.span.start, body_end),
2510 }),
2511 &mut self.fatal,
2512 )
2513 }
2514
2515 fn parse_for(&mut self) -> Option<StmtId> {
2517 let kw = self.advance();
2518 if !matches!(self.peek().kind, TokenKind::LeftParen) {
2519 self.error_at_peek("expected `(` after `for`");
2520 return None;
2521 }
2522 self.advance();
2523
2524 if self.scan_for_of_shape() {
2525 self.parse_for_of_tail(kw)
2526 } else {
2527 self.parse_c_for_tail(kw)
2528 }
2529 }
2530
2531 fn scan_for_of_shape(&self) -> bool {
2533 if !matches!(self.peek().kind, TokenKind::Let | TokenKind::Const) {
2534 return false;
2535 }
2536 let (start, mut depth) = match self.peek_at(1).kind {
2538 TokenKind::Identifier => match self.peek_at(2).kind {
2539 TokenKind::Identifier if self.peek_at_is_word(2, "of") => return true,
2540 TokenKind::Colon => (3, 0i32),
2541 _ => return false,
2542 },
2543 TokenKind::LeftBracket | TokenKind::LeftBrace => (2, 1i32),
2544 _ => return false,
2545 };
2546 let mut i = start;
2547 loop {
2548 match self.peek_at(i).kind {
2549 TokenKind::Eof => return false,
2550 TokenKind::LessThan
2551 | TokenKind::LeftParen
2552 | TokenKind::LeftBracket
2553 | TokenKind::LeftBrace => {
2554 depth += 1;
2555 }
2556 TokenKind::GreaterThan
2557 | TokenKind::RightParen
2558 | TokenKind::RightBracket
2559 | TokenKind::RightBrace => {
2560 if depth == 0 {
2561 return false;
2562 }
2563 depth -= 1;
2564 }
2565 TokenKind::Identifier if depth == 0 && self.peek_at_is_word(i, "of") => {
2568 return true;
2569 }
2570 TokenKind::Equals | TokenKind::Semicolon if depth == 0 => return false,
2571 _ => {}
2572 }
2573 i += 1;
2574 }
2575 }
2576
2577 fn parse_c_for_tail(&mut self, kw: Token) -> Option<StmtId> {
2579 let init = if matches!(self.peek().kind, TokenKind::Semicolon) {
2580 self.advance();
2581 None
2582 } else {
2583 let init_id = match self.peek().kind {
2584 TokenKind::Let => self.parse_let_or_const(false)?,
2585 TokenKind::Const => self.parse_let_or_const(true)?,
2586 _ => self.parse_expression_statement()?,
2587 };
2588 Some(init_id)
2589 };
2590 let for_stmt = self.finish_c_for(kw, init);
2591 if for_stmt.is_none()
2592 && let Some(init) = init
2593 {
2594 self.discard_detached_pattern(init);
2595 }
2596 for_stmt
2597 }
2598
2599 fn finish_c_for(&mut self, kw: Token, init: Option<StmtId>) -> Option<StmtId> {
2600 let condition = if matches!(self.peek().kind, TokenKind::Semicolon) {
2601 None
2602 } else {
2603 Some(self.parse_expression()?)
2604 };
2605 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2606 self.error_at_peek("expected `;` after `for` condition");
2607 return None;
2608 }
2609 self.advance();
2610
2611 let update = if matches!(self.peek().kind, TokenKind::RightParen) {
2612 None
2613 } else {
2614 Some(self.parse_for_update_stmt()?)
2615 };
2616 if !matches!(self.peek().kind, TokenKind::RightParen) {
2617 self.error_at_peek("expected `)` after `for` update");
2618 return None;
2619 }
2620 self.advance();
2621
2622 let body = self.parse_loop_body()?;
2623 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2624 .span
2625 .end;
2626 parse_arena_result(
2627 self.ast.try_push_stmt(Stmt {
2628 kind: StmtKind::For {
2629 init,
2630 condition,
2631 update,
2632 body,
2633 },
2634 span: self.span(kw.span.start, body_end),
2635 }),
2636 &mut self.fatal,
2637 )
2638 }
2639
2640 fn discard_detached_pattern(&mut self, init: StmtId) {
2643 let Some(stmt) = parse_arena_result(self.ast.try_stmt_mut(init), &mut self.fatal) else {
2644 return;
2645 };
2646 if matches!(
2647 stmt.kind,
2648 StmtKind::LetPattern { .. } | StmtKind::ConstPattern { .. }
2649 ) {
2650 stmt.kind = StmtKind::Block(Vec::new());
2651 }
2652 }
2653
2654 fn parse_for_update_stmt(&mut self) -> Option<StmtId> {
2655 let expr_id = self.parse_expression()?;
2656 let span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
2657 let kind = self.statement_kind_for(expr_id)?;
2658 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
2659 }
2660
2661 fn parse_for_of_tail(&mut self, kw: Token) -> Option<StmtId> {
2662 let binding_tok = self.advance();
2663 let binding_kind = match binding_tok.kind {
2664 TokenKind::Let => crate::BindingKind::Let,
2665 TokenKind::Const => crate::BindingKind::Const,
2666 _ => return self.invariant_failure("scan_for_of_shape gated this"),
2667 };
2668 let (name, binding) = match self.peek().kind {
2669 TokenKind::LeftBracket => {
2670 let b = self.parse_binding()?;
2671 (None, Some(b))
2672 }
2673 TokenKind::LeftBrace => {
2674 let b = self.parse_binding()?;
2675 self.error_at(
2676 b.span(),
2677 "object destructuring is not supported in `for-of`; use array destructuring or unpack inside the loop body",
2678 );
2679 return None;
2680 }
2681 _ => {
2682 let name_tok =
2683 self.expect_identifier("expected identifier after `let`/`const` in `for-of`")?;
2684 (Some(self.ident_from_token(&name_tok)), None)
2685 }
2686 };
2687 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2688 self.advance();
2689 Some(self.parse_type_annotation()?)
2690 } else {
2691 None
2692 };
2693 if !self.peek_identifier_text_is("of") {
2694 self.error_at_peek("expected `of` in `for-of`");
2695 return None;
2696 }
2697 self.advance();
2698
2699 let iter = self.parse_expression()?;
2700 if !matches!(self.peek().kind, TokenKind::RightParen) {
2701 self.error_at_peek("expected `)` after `for-of` iterable");
2702 return None;
2703 }
2704 self.advance();
2705
2706 let body = self.parse_loop_body()?;
2707 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2708 .span
2709 .end;
2710 let kind = match (binding, name) {
2711 (Some(binding), _) => StmtKind::ForOfPattern {
2712 binding_kind,
2713 binding,
2714 ty,
2715 iter,
2716 body,
2717 },
2718 (None, Some(name)) => StmtKind::ForOf {
2719 binding_kind,
2720 name,
2721 ty,
2722 iter,
2723 body,
2724 },
2725 (None, None) => return self.invariant_failure("either binding or name must be Some"),
2726 };
2727 parse_arena_result(
2728 self.ast.try_push_stmt(Stmt {
2729 kind,
2730 span: self.span(kw.span.start, body_end),
2731 }),
2732 &mut self.fatal,
2733 )
2734 }
2735
2736 fn parse_do_while(&mut self) -> Option<StmtId> {
2737 let kw = self.advance();
2738 let body = self.parse_loop_body()?;
2739 self.eat_asi_semicolon_before(TokenKind::While);
2740 if !matches!(self.peek().kind, TokenKind::While) {
2741 self.error_at_peek("expected `while` after `do` body");
2742 return None;
2743 }
2744 self.advance();
2745 let condition = self.parse_paren_condition()?;
2746 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
2750 self.advance().span.end
2751 } else {
2752 parse_arena_result(self.ast.try_expr(condition), &mut self.fatal)?
2753 .span
2754 .end
2755 };
2756 parse_arena_result(
2757 self.ast.try_push_stmt(Stmt {
2758 kind: StmtKind::DoWhile { body, condition },
2759 span: self.span(kw.span.start, end),
2760 }),
2761 &mut self.fatal,
2762 )
2763 }
2764
2765 fn parse_switch(&mut self) -> Option<StmtId> {
2767 let kw = self.advance();
2768 if !matches!(self.peek().kind, TokenKind::LeftParen) {
2769 self.error_at_peek("expected `(` after `switch`");
2770 return None;
2771 }
2772 self.advance();
2773 let discriminant = self.parse_expression()?;
2774 if !matches!(self.peek().kind, TokenKind::RightParen) {
2775 self.error_at_peek("expected `)` after `switch` discriminant");
2776 return None;
2777 }
2778 self.advance();
2779
2780 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2781 self.error_at_peek("expected `{` to start `switch` body");
2782 return None;
2783 }
2784 let open = self.advance();
2785 self.block_depth += 1;
2786
2787 let mut cases: Vec<SwitchCase> = Vec::new();
2788 let mut default: Option<SwitchDefault> = None;
2789 let mut pending_values: Vec<ExprId> = Vec::new();
2790 let mut pending_label_start: Option<u32> = None;
2791 let mut pending_default: Option<Span> = None;
2792
2793 loop {
2794 match self.peek().kind {
2795 TokenKind::Eof => break,
2796 TokenKind::Semicolon => {
2798 self.advance();
2799 }
2800 TokenKind::RightBrace => {
2801 if pending_label_start.is_some() {
2803 self.error_at_peek(
2804 "expected case body before `}` — case labels must be followed by statements",
2805 );
2806 return None;
2807 }
2808 break;
2809 }
2810 TokenKind::Case => {
2811 let case_kw = self.advance();
2812 if pending_label_start.is_none() {
2813 pending_label_start = Some(case_kw.span.start);
2814 }
2815 let value = self.parse_expression()?;
2816 if !matches!(self.peek().kind, TokenKind::Colon) {
2817 self.error_at_peek("expected `:` after `case` label");
2818 return None;
2819 }
2820 self.advance();
2821 pending_values.push(value);
2822 }
2823 TokenKind::Default => {
2824 let kw_tok = self.advance();
2825 if default.is_some() {
2826 self.error_at(kw_tok.span, "duplicate `default` clause in `switch`");
2827 return None;
2828 }
2829 if !matches!(self.peek().kind, TokenKind::Colon) {
2830 self.error_at_peek("expected `:` after `default`");
2831 return None;
2832 }
2833 self.advance();
2834 if pending_label_start.is_none() {
2835 pending_label_start = Some(kw_tok.span.start);
2836 }
2837 pending_default = Some(kw_tok.span);
2838 }
2839 _ => {
2840 if pending_label_start.is_none() {
2841 self.error_at_peek(
2842 "expected `case` or `default` at the start of a `switch` body",
2843 );
2844 return None;
2845 }
2846 let Some(label_start) = pending_label_start else {
2847 return self.invariant_failure("switch arm has no label");
2848 };
2849 let body = self.parse_switch_arm_body(label_start)?;
2850 let arm_span =
2851 parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
2852 if !pending_values.is_empty() {
2853 let first = parse_arena_result(
2854 self.ast.try_expr(pending_values[0]),
2855 &mut self.fatal,
2856 )?
2857 .span
2858 .start;
2859 let case_span = self.span(first, arm_span.end);
2860 cases.push(SwitchCase {
2861 values: std::mem::take(&mut pending_values),
2862 body,
2863 span: case_span,
2864 });
2865 }
2866 if let Some(def_kw_span) = pending_default.take() {
2867 default = Some(SwitchDefault {
2868 body,
2869 span: self.span(def_kw_span.start, arm_span.end),
2870 });
2871 }
2872 pending_label_start = None;
2873 }
2874 }
2875 }
2876
2877 self.block_depth -= 1;
2878 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2879 self.error_at_peek("expected `}` to close `switch` body");
2880 return None;
2881 }
2882 let close = self.advance();
2883
2884 let _ = open;
2885 parse_arena_result(
2886 self.ast.try_push_stmt(Stmt {
2887 kind: StmtKind::Switch {
2888 discriminant,
2889 cases,
2890 default,
2891 },
2892 span: self.span(kw.span.start, close.span.end),
2893 }),
2894 &mut self.fatal,
2895 )
2896 }
2897
2898 fn parse_switch_arm_body(&mut self, body_start: u32) -> Option<StmtId> {
2900 let mut stmts: Vec<StmtId> = Vec::new();
2901 let mut end_offset = body_start;
2902 loop {
2903 if matches!(
2904 self.peek().kind,
2905 TokenKind::Case | TokenKind::Default | TokenKind::RightBrace | TokenKind::Eof
2906 ) {
2907 break;
2908 }
2909 if matches!(self.peek().kind, TokenKind::Semicolon) {
2910 end_offset = self.peek().span.end;
2911 self.advance();
2912 continue;
2913 }
2914 let pos_before = self.pos;
2915 if let Some(id) = self.parse_statement() {
2916 end_offset = parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
2917 .span
2918 .end;
2919 stmts.push(id);
2920 } else {
2921 self.recover();
2922 if self.pos == pos_before && !self.is_at_eof() {
2923 self.advance();
2924 }
2925 end_offset = end_offset.max(self.prev_token_end());
2928 }
2929 }
2930 let span = self.span(body_start, end_offset);
2931 parse_arena_result(
2932 self.ast.try_push_stmt(Stmt {
2933 kind: StmtKind::Block(stmts),
2934 span,
2935 }),
2936 &mut self.fatal,
2937 )
2938 }
2939
2940 fn parse_break(&mut self) -> Option<StmtId> {
2941 let kw = self.advance();
2942 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2943 self.error_at_peek("expected `;` after `break`");
2944 return None;
2945 }
2946 let semi = self.advance();
2947 parse_arena_result(
2948 self.ast.try_push_stmt(Stmt {
2949 kind: StmtKind::Break,
2950 span: self.span(kw.span.start, semi.span.end),
2951 }),
2952 &mut self.fatal,
2953 )
2954 }
2955
2956 fn parse_continue(&mut self) -> Option<StmtId> {
2957 let kw = self.advance();
2958 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2959 self.error_at_peek("expected `;` after `continue`");
2960 return None;
2961 }
2962 let semi = self.advance();
2963 parse_arena_result(
2964 self.ast.try_push_stmt(Stmt {
2965 kind: StmtKind::Continue,
2966 span: self.span(kw.span.start, semi.span.end),
2967 }),
2968 &mut self.fatal,
2969 )
2970 }
2971
2972 fn parse_return(&mut self) -> Option<StmtId> {
2973 let kw = self.advance();
2974
2975 let value = if matches!(self.peek().kind, TokenKind::Semicolon) {
2976 None
2977 } else {
2978 Some(self.parse_expression()?)
2979 };
2980
2981 if !self.at_statement_end() {
2982 self.error_at_peek("expected `;` after return");
2983 return None;
2984 }
2985 let end = self.finish_statement();
2986
2987 parse_arena_result(
2988 self.ast.try_push_stmt(Stmt {
2989 kind: StmtKind::Return(value),
2990 span: self.span(kw.span.start, end),
2991 }),
2992 &mut self.fatal,
2993 )
2994 }
2995
2996 fn parse_throw(&mut self) -> Option<StmtId> {
2997 let kw = self.advance();
2998
2999 if matches!(self.peek().kind, TokenKind::Semicolon) {
3000 self.error_at_peek_with_help(
3001 "expected expression after `throw`",
3002 vec!["throw new Error(\"message\");".to_string()],
3003 );
3004 return None;
3005 }
3006
3007 let value = self.parse_expression()?;
3008
3009 if !self.at_statement_end() {
3010 self.error_at_peek("expected `;` after `throw`");
3011 return None;
3012 }
3013 let end = self.finish_statement();
3014
3015 parse_arena_result(
3016 self.ast.try_push_stmt(Stmt {
3017 kind: StmtKind::Throw { value },
3018 span: self.span(kw.span.start, end),
3019 }),
3020 &mut self.fatal,
3021 )
3022 }
3023
3024 fn parse_try(&mut self) -> Option<StmtId> {
3025 let kw = self.advance();
3026
3027 let body = self.parse_block()?;
3028
3029 let mut catches: Vec<CatchClause> = Vec::new();
3030 let mut finally: Option<StmtId> = None;
3031 let mut tail_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3032 .span
3033 .end;
3034
3035 loop {
3036 match self.peek().kind {
3037 TokenKind::Catch => {
3038 if finally.is_some() {
3039 self.error_at_peek("`catch` clause must appear before `finally`");
3040 return None;
3041 }
3042 let clause = self.parse_catch_clause()?;
3043 tail_end = clause.span.end;
3044 catches.push(clause);
3045 }
3046 TokenKind::Finally => {
3047 if finally.is_some() {
3048 self.error_at_peek("duplicate `finally` clause");
3049 return None;
3050 }
3051 self.advance();
3052 let block = self.parse_block()?;
3053 tail_end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
3054 .span
3055 .end;
3056 finally = Some(block);
3057 }
3058 _ => break,
3059 }
3060 }
3061
3062 if catches.is_empty() && finally.is_none() {
3063 self.error_at(
3064 self.span(kw.span.start, tail_end),
3065 "try requires at least one of `catch` or `finally`",
3066 );
3067 return None;
3068 }
3069
3070 parse_arena_result(
3071 self.ast.try_push_stmt(Stmt {
3072 kind: StmtKind::Try {
3073 body,
3074 catches,
3075 finally,
3076 },
3077 span: self.span(kw.span.start, tail_end),
3078 }),
3079 &mut self.fatal,
3080 )
3081 }
3082
3083 fn parse_catch_clause(&mut self) -> Option<CatchClause> {
3084 let kw = self.advance();
3085
3086 let (binding, ty) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
3087 (
3089 Ident {
3090 name: "#catch".into(),
3091 span: kw.span,
3092 },
3093 None,
3094 )
3095 } else {
3096 self.parse_catch_binding()?
3097 };
3098
3099 let body = self.parse_block()?;
3100 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3101 .span
3102 .end;
3103
3104 Some(CatchClause {
3105 binding,
3106 ty,
3107 body,
3108 span: self.span(kw.span.start, body_end),
3109 })
3110 }
3111
3112 fn parse_catch_binding(&mut self) -> Option<(Ident, Option<TypeAnnotation>)> {
3113 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3114 self.error_at_peek("expected `(` after `catch`");
3115 return None;
3116 }
3117 self.advance();
3118 let name_tok = self.expect_identifier("expected catch binding name")?;
3119 let binding = self.ident_from_token(&name_tok);
3120 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3121 self.advance();
3122 if self.reject_any_catch_annotation() {
3123 None
3125 } else {
3126 Some(self.parse_type_annotation()?)
3127 }
3128 } else {
3129 None
3130 };
3131 if !matches!(self.peek().kind, TokenKind::RightParen) {
3132 self.error_at_peek("expected `)` after catch binding");
3133 return None;
3134 }
3135 self.advance();
3136 Some((binding, ty))
3137 }
3138
3139 fn reject_any_catch_annotation(&mut self) -> bool {
3142 let is_bare_any = self.peek_identifier_text_is("any")
3143 && matches!(self.peek_at(1).kind, TokenKind::RightParen);
3144 if !is_bare_any {
3145 return false;
3146 }
3147 let any_tok = self.advance();
3148 self.error_at_with_help(
3150 any_tok.span,
3151 "`any` is not supported",
3152 vec![
3153 "write `catch (e)` to catch every thrown error, or name an error class, \
3154 as in `catch (e: RangeError)`, to catch only that type"
3155 .to_string(),
3156 ],
3157 );
3158 true
3159 }
3160
3161 fn parse_paren_condition(&mut self) -> Option<ExprId> {
3162 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3163 self.error_at_peek("expected `(`");
3164 return None;
3165 }
3166 self.advance();
3167 let expr = self.parse_expression()?;
3168 if !matches!(self.peek().kind, TokenKind::RightParen) {
3169 self.error_at_peek("expected `)`");
3170 return None;
3171 }
3172 self.advance();
3173 Some(expr)
3174 }
3175
3176 fn parse_predicate_or_return_type(
3177 &mut self,
3178 type_pos: TypePos,
3179 ) -> Option<(
3180 Option<TypeAnnotation>,
3181 Option<crate::TypePredicateAnnotation>,
3182 )> {
3183 if matches!(self.peek().kind, TokenKind::Identifier) && self.peek_at_is_word(1, "is") {
3184 let param_tok = self.advance();
3185 let param = self.ident_from_token(¶m_tok);
3186 self.advance();
3187 let asserted = self.parse_type_annotation_in(type_pos)?;
3188 let span = self.span(param.span.start, asserted.span.end);
3189 return Some((
3190 None,
3191 Some(crate::TypePredicateAnnotation {
3192 param,
3193 asserted,
3194 span,
3195 }),
3196 ));
3197 }
3198 Some((Some(self.parse_type_annotation_in(type_pos)?), None))
3199 }
3200
3201 fn parse_type_annotation(&mut self) -> Option<TypeAnnotation> {
3202 self.parse_type_annotation_in(TypePos::Anywhere)
3203 }
3204
3205 fn parse_type_annotation_in(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3206 let leading_pipe = matches!(self.peek().kind, TokenKind::Pipe);
3208 if leading_pipe {
3209 self.advance();
3210 }
3211 let first = self.parse_type_array(type_pos)?;
3212 if !matches!(self.peek().kind, TokenKind::Pipe) {
3213 return Some(first);
3214 }
3215 let start = first.span.start;
3216 let mut end = first.span.end;
3217 let mut members = vec![first];
3218 while matches!(self.peek().kind, TokenKind::Pipe) {
3219 self.advance();
3220 let member = self.parse_type_array(type_pos)?;
3221 end = member.span.end;
3222 members.push(member);
3223 }
3224 Some(TypeAnnotation {
3225 kind: TypeAnnotationKind::Union(members),
3226 span: self.span(start, end),
3227 })
3228 }
3229
3230 fn parse_type_array(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3231 self.with_recursion_limit(|parser| parser.parse_type_array_inner(type_pos))
3232 }
3233
3234 fn parse_type_array_inner(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3235 if self.is_contextual_type_operator(self.pos, "keyof") {
3239 let kw = self.advance();
3240 let operand = self.parse_type_array(type_pos)?;
3241 let span = self.span(kw.span.start, operand.span.end);
3242 return Some(TypeAnnotation {
3243 kind: TypeAnnotationKind::KeyOf(Box::new(operand)),
3244 span,
3245 });
3246 }
3247 if self.is_contextual_type_operator(self.pos, "readonly") {
3248 return self.parse_readonly_type(type_pos);
3249 }
3250
3251 let mut ty = match self.peek().kind {
3252 TokenKind::Typeof => {
3258 let kw = self.advance();
3259 let first = self.expect_identifier("expected a value name after `typeof`")?;
3260 let mut path = vec![self.type_name(first.span)?];
3261 let mut end = first.span.end;
3262 while matches!(self.peek().kind, TokenKind::Dot) {
3263 self.advance();
3264 let seg = self.expect_identifier_name("expected a property name after `.`")?;
3265 end = seg.span.end;
3266 path.push(self.type_name(seg.span)?);
3267 }
3268 TypeAnnotation {
3269 kind: TypeAnnotationKind::TypeOf { path },
3270 span: self.span(kw.span.start, end),
3271 }
3272 }
3273 TokenKind::Identifier | TokenKind::Void => {
3274 let leading_is_identifier = matches!(self.peek().kind, TokenKind::Identifier);
3276 let tok = self.advance();
3277 let first_span = tok.span;
3278 let mut path_end = first_span.end;
3279 let first_name = self.type_name(first_span)?;
3280 let mut path = vec![first_name.clone()];
3281 if leading_is_identifier {
3282 while matches!(self.peek().kind, TokenKind::Dot) {
3283 self.advance();
3284 if !matches!(self.peek().kind, TokenKind::Identifier) {
3285 self.error_at_peek("expected identifier after `.` in type name");
3286 return None;
3287 }
3288 let seg = self.advance();
3289 path_end = seg.span.end;
3290 path.push(self.type_name(seg.span)?);
3291 }
3292 }
3293 let (args, end) = if matches!(self.peek().kind, TokenKind::LessThan)
3295 && !self.line_break_before_peek()
3296 {
3297 self.parse_type_argument_list()?
3298 } else {
3299 (Vec::new(), path_end)
3300 };
3301 let outer_span = self.span(first_span.start, end);
3302 let kind = if path.len() == 1 {
3303 if first_name.name == "any" {
3304 self.error_at_with_help(
3305 first_span,
3306 "`any` is not supported",
3307 vec![
3308 "annotate a concrete type instead — an object shape \
3309 like `{ id: string }`, a named `interface`, or \
3310 `unknown` (a safe dynamic type you narrow before use)"
3311 .to_string(),
3312 ],
3313 );
3314 }
3315 TypeAnnotationKind::Name {
3316 name: first_name,
3317 args,
3318 }
3319 } else {
3320 TypeAnnotationKind::Qualified { path, args }
3321 };
3322 TypeAnnotation {
3323 kind,
3324 span: outer_span,
3325 }
3326 }
3327 TokenKind::NullLiteral => {
3329 let tok = self.advance();
3330 TypeAnnotation {
3331 kind: TypeAnnotationKind::Name {
3332 name: self.type_name(tok.span)?,
3333 args: Vec::new(),
3334 },
3335 span: tok.span,
3336 }
3337 }
3338 TokenKind::StringLiteral(_) => {
3339 let tok = self.advance();
3340 let span = tok.span;
3341 let TokenKind::StringLiteral(s) = tok.kind else {
3342 unreachable!("advance preserves the matched StringLiteral variant");
3344 };
3345 TypeAnnotation {
3346 kind: TypeAnnotationKind::StringLiteral(s),
3347 span,
3348 }
3349 }
3350 TokenKind::NumberLiteral(_) => {
3351 let tok = self.advance();
3352 let span = tok.span;
3353 let TokenKind::NumberLiteral(v) = tok.kind else {
3354 unreachable!("advance preserves the matched NumberLiteral variant");
3356 };
3357 let canonical = if v == 0.0 { 0.0 } else { v };
3359 TypeAnnotation {
3360 kind: TypeAnnotationKind::NumberLiteral(crate::types::LiteralF64(canonical)),
3361 span,
3362 }
3363 }
3364 TokenKind::BigIntLiteral(_) => {
3365 let tok = self.advance();
3366 let TokenKind::BigIntLiteral(digits) = tok.kind else {
3367 unreachable!("advance preserves the matched BigIntLiteral variant");
3369 };
3370 TypeAnnotation {
3371 kind: TypeAnnotationKind::BigIntLiteral(digits),
3372 span: tok.span,
3373 }
3374 }
3375 TokenKind::Minus
3376 if matches!(
3377 self.peek_at(1).kind,
3378 TokenKind::NumberLiteral(_) | TokenKind::BigIntLiteral(_)
3379 ) =>
3380 {
3381 let minus = self.advance().span;
3382 let tok = self.advance();
3383 let span = self.span(minus.start, tok.span.end);
3384 let kind =
3385 match tok.kind {
3386 TokenKind::NumberLiteral(v) => TypeAnnotationKind::NumberLiteral(
3387 crate::types::LiteralF64(if v == 0.0 { 0.0 } else { -v }),
3388 ),
3389 TokenKind::BigIntLiteral(digits) => TypeAnnotationKind::BigIntLiteral(
3390 crate::types::negate_bigint_digits(&digits),
3391 ),
3392 _ => unreachable!("guarded to a numeric literal after `-`"),
3394 };
3395 TypeAnnotation { kind, span }
3396 }
3397 TokenKind::BooleanLiteral(value) => {
3398 let span = self.advance().span;
3399 TypeAnnotation {
3400 kind: TypeAnnotationKind::BooleanLiteral(value),
3401 span,
3402 }
3403 }
3404 TokenKind::LeftBrace => self.parse_object_type_annotation()?,
3405 TokenKind::LeftParen if self.paren_at_opens_function_type(self.pos, type_pos) => {
3406 self.parse_function_type_annotation()?
3407 }
3408 TokenKind::LeftParen => self.parse_grouped_or_function_type()?,
3409 TokenKind::LeftBracket => self.parse_tuple_type_annotation()?,
3411 _ => {
3412 self.error_at_peek("expected type");
3413 return None;
3414 }
3415 };
3416 let mut array_suffixes = 0usize;
3419 while matches!(self.peek().kind, TokenKind::LeftBracket) && !self.line_break_before_peek() {
3422 array_suffixes += 1;
3423 if self.recursion_depth.saturating_add(array_suffixes) >= MAX_PARSE_DEPTH {
3424 self.stop_at_recursion_limit();
3425 return None;
3426 }
3427 let open = self.advance();
3428 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3429 self.error_at(open.span, "expected `]` to close array type");
3430 return None;
3431 }
3432 let close = self.advance();
3433 let span = self.span(ty.span.start, close.span.end);
3434 ty = TypeAnnotation {
3435 kind: TypeAnnotationKind::Array(Box::new(ty)),
3436 span,
3437 };
3438 }
3439 Some(ty)
3440 }
3441
3442 fn parse_readonly_type(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3446 let kw = self.advance();
3447 let opens_group = matches!(self.peek().kind, TokenKind::LeftParen);
3448 let operand = self.parse_type_array(type_pos)?;
3449 let grouped = opens_group
3452 && matches!(
3453 self.pos
3454 .checked_sub(1)
3455 .and_then(|index| self.tokens.get(index))
3456 .map(|token| &token.kind),
3457 Some(TokenKind::RightParen)
3458 );
3459 if grouped
3460 || !matches!(
3461 operand.kind,
3462 TypeAnnotationKind::Array(_) | TypeAnnotationKind::Tuple(_)
3463 )
3464 {
3465 self.error_at_with_help(
3466 kw.span,
3467 "`readonly` only applies to array and tuple types",
3468 vec![
3469 "write `readonly T[]` or `readonly [A, B]`; to protect an object's \
3470 properties, mark each one `readonly` instead"
3471 .to_string(),
3472 ],
3473 );
3474 return Some(operand);
3475 }
3476 let span = self.span(kw.span.start, operand.span.end);
3477 Some(TypeAnnotation {
3478 kind: TypeAnnotationKind::Readonly(Box::new(operand)),
3479 span,
3480 })
3481 }
3482
3483 fn paren_at_opens_function_type(&self, index: usize, type_pos: TypePos) -> bool {
3494 let after = |offset: usize| self.tokens.get(index + offset).map(|t| &t.kind);
3495 match (after(1), after(2)) {
3496 (Some(TokenKind::RightParen | TokenKind::DotDotDot), _) => true,
3498 (Some(TokenKind::Identifier), Some(TokenKind::Colon | TokenKind::Question)) => true,
3500 (Some(TokenKind::Identifier), Some(TokenKind::Comma | TokenKind::RightParen)) => {
3509 type_pos == TypePos::Anywhere
3510 && self.index_after_matching_paren(index).is_some_and(|i| {
3511 matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow))
3512 })
3513 }
3514 _ => false,
3516 }
3517 }
3518
3519 fn parse_grouped_or_function_type(&mut self) -> Option<TypeAnnotation> {
3524 let open = self.pos;
3525 let diagnostics_before = self.diagnostics.len();
3526 if let Some(ty) = self.parse_grouped_type_annotation() {
3527 return Some(ty);
3528 }
3529 let arrow_follows = self
3530 .index_after_matching_paren(open)
3531 .is_some_and(|i| matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)));
3532 if !arrow_follows {
3533 return None;
3534 }
3535 self.pos = open;
3536 self.diagnostics.truncate(diagnostics_before);
3537 self.parse_function_type_annotation()
3538 }
3539
3540 fn parse_grouped_type_annotation(&mut self) -> Option<TypeAnnotation> {
3543 let open = self.advance();
3544 let inner = self.parse_type_annotation()?;
3545 if !matches!(self.peek().kind, TokenKind::RightParen) {
3546 self.error_at_peek("expected `)` to close a parenthesized type");
3547 return None;
3548 }
3549 let close = self.advance();
3550 Some(TypeAnnotation {
3551 span: self.span(open.span.start, close.span.end),
3552 ..inner
3553 })
3554 }
3555
3556 fn parse_function_type_annotation(&mut self) -> Option<TypeAnnotation> {
3558 let open = self.advance();
3559 let params = self.parse_function_type_params()?;
3560 if !matches!(self.peek().kind, TokenKind::Arrow) {
3561 self.error_at_peek("expected `=>` in function-type annotation");
3562 return None;
3563 }
3564 self.advance();
3565 let return_type = self.parse_type_annotation()?;
3566 let end = return_type.span.end;
3567 Some(TypeAnnotation {
3568 kind: TypeAnnotationKind::Function {
3569 params,
3570 return_type: Box::new(return_type),
3571 },
3572 span: self.span(open.span.start, end),
3573 })
3574 }
3575
3576 fn parse_function_type_params(&mut self) -> Option<Vec<TypeAnnotationField>> {
3580 let mut params: Vec<TypeAnnotationField> = Vec::new();
3581 if !matches!(self.peek().kind, TokenKind::RightParen) {
3582 loop {
3583 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
3584 self.advance();
3585 true
3586 } else {
3587 false
3588 };
3589 let name_tok = self.expect_identifier(if rest {
3590 "expected identifier after `...` in function-type annotation"
3591 } else {
3592 "expected parameter name in function-type annotation"
3593 })?;
3594 let name = self.ident_from_token(&name_tok);
3595 let optional = self.parse_parameter_optional(rest, false)?;
3596 if !matches!(self.peek().kind, TokenKind::Colon) {
3597 if rest {
3598 self.error_at_peek_with_help(
3599 "rest parameter requires a type annotation",
3600 vec!["(...args: T[]) => R".to_string()],
3601 );
3602 } else {
3603 self.error_at_peek(
3604 "expected `:` after parameter name (function-type params \
3605 require named annotations in v1)",
3606 );
3607 }
3608 return None;
3609 }
3610 self.advance();
3611 let ty = self.parse_type_annotation()?;
3612 if rest && !is_rest_array_annotation(&ty) {
3613 self.error_at(ty.span, "rest parameter type must be an array");
3614 return None;
3615 }
3616 if matches!(self.peek().kind, TokenKind::Equals) {
3617 if rest {
3618 self.error_rest_parameter_default();
3619 } else {
3620 self.error_at_peek(
3621 "default values in function-type annotations are not yet supported",
3622 );
3623 }
3624 return None;
3625 }
3626 params.push(TypeAnnotationField {
3627 name,
3628 ty,
3629 optional,
3630 readonly: false,
3631 rest,
3632 method: false,
3633 });
3634 match self.peek().kind {
3635 TokenKind::Comma => {
3636 if rest {
3637 let comma = self.advance();
3638 self.error_at(comma.span, "rest parameter must be the last parameter");
3639 return None;
3640 }
3641 self.advance();
3642 if matches!(self.peek().kind, TokenKind::RightParen) {
3643 break;
3644 }
3645 }
3646 TokenKind::RightParen => break,
3647 _ => {
3648 self.error_at_peek("expected `,` or `)`");
3649 return None;
3650 }
3651 }
3652 }
3653 self.check_parameter_order(params.iter().map(ParamOrder::of_field));
3654 }
3655 if !matches!(self.peek().kind, TokenKind::RightParen) {
3656 self.error_at_peek("expected `)`");
3657 return None;
3658 }
3659 self.advance();
3660 Some(params)
3661 }
3662
3663 fn parse_tuple_type_annotation(&mut self) -> Option<TypeAnnotation> {
3664 let open = self.advance();
3665 if matches!(self.peek().kind, TokenKind::RightBracket) {
3666 let close = self.peek().span;
3667 self.error_at(
3668 self.span(open.span.start, close.end),
3669 "tuple types must have at least one element",
3670 );
3671 return None;
3672 }
3673 let mut elements: Vec<TypeAnnotation> = Vec::new();
3674 loop {
3675 if matches!(self.peek().kind, TokenKind::DotDotDot) {
3676 self.error_at_with_help(
3677 self.peek().span,
3678 "rest elements in tuple types are not supported",
3679 vec!["use an array type `T[]` for a list of varying length".to_string()],
3680 );
3681 return None;
3682 }
3683 let label = self.skip_tuple_element_label();
3686 let mut ty = self.parse_type_annotation()?;
3687 let suffix_optional = matches!(self.peek().kind, TokenKind::Question);
3688 if suffix_optional && label != TupleElementLabel::None {
3689 self.error_at_peek_with_help(
3690 "put the optional marker after the tuple element label",
3691 vec!["write `name?: T` instead of `name: T?`".to_string()],
3692 );
3693 return None;
3694 }
3695 let optional = label == TupleElementLabel::Optional || suffix_optional;
3696 if optional {
3697 let end = if suffix_optional {
3698 self.advance().span.end
3699 } else {
3700 ty.span.end
3701 };
3702 let span = self.span(ty.span.start, end);
3703 ty = TypeAnnotation {
3704 kind: TypeAnnotationKind::Optional(Box::new(ty)),
3705 span,
3706 };
3707 } else if elements
3708 .iter()
3709 .any(|element| matches!(element.kind, TypeAnnotationKind::Optional(_)))
3710 {
3711 self.error_at(
3712 ty.span,
3713 "a required tuple element cannot follow an optional element",
3714 );
3715 return None;
3716 }
3717 elements.push(ty);
3718 match self.peek().kind {
3719 TokenKind::Comma => {
3720 self.advance();
3721 if matches!(self.peek().kind, TokenKind::RightBracket) {
3722 break;
3723 }
3724 }
3725 TokenKind::RightBracket => break,
3726 _ => {
3727 self.error_at_peek("expected `,` or `]`");
3728 return None;
3729 }
3730 }
3731 }
3732 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3733 self.error_at_peek("expected `]`");
3734 return None;
3735 }
3736 let close = self.advance();
3737 Some(TypeAnnotation {
3738 kind: TypeAnnotationKind::Tuple(elements),
3739 span: self.span(open.span.start, close.span.end),
3740 })
3741 }
3742
3743 fn skip_tuple_element_label(&mut self) -> TupleElementLabel {
3745 if !is_property_name(&self.peek().kind) {
3746 return TupleElementLabel::None;
3747 }
3748 match self.peek_at(1).kind {
3749 TokenKind::Colon => {
3750 self.advance();
3751 self.advance();
3752 TupleElementLabel::Required
3753 }
3754 TokenKind::Question if matches!(self.peek_at(2).kind, TokenKind::Colon) => {
3755 self.advance();
3756 self.advance();
3757 self.advance();
3758 TupleElementLabel::Optional
3759 }
3760 _ => TupleElementLabel::None,
3761 }
3762 }
3763
3764 fn peek_is_parameterless_index_signature(&self) -> bool {
3767 matches!(self.peek().kind, TokenKind::LeftBracket)
3768 && matches!(self.peek_at(1).kind, TokenKind::RightBracket)
3769 }
3770
3771 fn skip_parameterless_index_signature(&mut self, member_start: u32) -> Option<()> {
3775 self.advance();
3776 let close = self.advance();
3777 self.error_at_with_help(
3778 self.span(member_start, close.span.end),
3779 "an index signature must declare exactly one parameter",
3780 vec!["write `[key: string]: V`".to_string()],
3781 );
3782 if matches!(self.peek().kind, TokenKind::Colon) {
3783 self.advance();
3784 self.parse_type_annotation()?;
3785 }
3786 Some(())
3787 }
3788
3789 fn parse_index_signature(&mut self, readonly: bool) -> Option<crate::IndexSignatureAnnotation> {
3790 let open = self.advance();
3791 self.expect_property_ident("expected index parameter name")?;
3792 if !matches!(self.peek().kind, TokenKind::Colon) {
3793 self.error_at_peek("expected `:` after index parameter name");
3794 return None;
3795 }
3796 self.advance();
3797 if !self.peek_identifier_text_is("string") {
3798 self.error_at_peek("index signatures require string keys; use `[key: string]: V`");
3799 return None;
3800 }
3801 self.advance();
3802 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3803 self.error_at_peek("expected `]` after string index key type");
3804 return None;
3805 }
3806 self.advance();
3807 if !matches!(self.peek().kind, TokenKind::Colon) {
3808 self.error_at_peek("expected `:` before index value type");
3809 return None;
3810 }
3811 self.advance();
3812 let value = self.parse_type_annotation()?;
3813 Some(crate::IndexSignatureAnnotation {
3814 span: self.span(open.span.start, value.span.end),
3815 value,
3816 readonly,
3817 })
3818 }
3819
3820 fn parse_object_type_annotation(&mut self) -> Option<TypeAnnotation> {
3821 let open = self.advance();
3822 let mut fields: Vec<TypeAnnotationField> = Vec::new();
3823 let mut index = None;
3824 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
3825 && !self.peek_starts_declaration()
3826 {
3827 let member_start = self.peek().span.start;
3828 let readonly = self.eat_readonly_property_modifier();
3829 if self.peek_is_parameterless_index_signature() {
3830 self.skip_parameterless_index_signature(member_start)?;
3831 } else if self.peek_is_construct_signature() {
3832 self.reject_construct_signature()?;
3833 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
3834 let signature = self.parse_index_signature(readonly)?;
3835 if index.is_some() {
3836 self.error_at_with_help(
3837 signature.span,
3838 "duplicate string index signature",
3839 vec![],
3840 );
3841 }
3842 index = Some(Box::new(signature));
3843 } else {
3844 let field = self.parse_object_type_field(readonly, member_start)?;
3845 if let Some(existing) = fields.iter().find(|f| f.name.name == field.name.name) {
3846 self.diagnostics.push(Diagnostic {
3847 severity: Severity::Error,
3848 span: field.name.span,
3849 message: format!("duplicate field `{}` in object type", field.name.name),
3850 help: vec![],
3851 notes: vec![(existing.name.span, "first defined here".into())],
3852 });
3853 }
3854 fields.push(field);
3855 }
3856 self.finish_type_member(self.prev_token_end(), "expected `;`, `,`, or `}`")?;
3857 }
3858 if !matches!(self.peek().kind, TokenKind::RightBrace) {
3859 self.error_at_peek("expected `}`");
3860 return None;
3861 }
3862 let close = self.advance();
3863 Some(TypeAnnotation {
3864 kind: TypeAnnotationKind::Object { index, fields },
3865 span: self.span(open.span.start, close.span.end),
3866 })
3867 }
3868
3869 fn peek_starts_declaration(&self) -> bool {
3875 if matches!(self.peek().kind, TokenKind::Export)
3876 && matches!(
3877 self.peek_at(1).kind,
3878 TokenKind::Default | TokenKind::LeftBrace | TokenKind::Star
3879 )
3880 {
3881 return true;
3882 }
3883 let mut offset = 0;
3884 while self.peek_at_is_declaration_modifier(offset) {
3885 offset += 1;
3886 }
3887 let declaration_keyword = matches!(
3888 self.peek_at(offset).kind,
3889 TokenKind::Function
3890 | TokenKind::Class
3891 | TokenKind::Let
3892 | TokenKind::Const
3893 | TokenKind::Enum
3894 | TokenKind::Interface
3895 ) || self.peek_at_is_word(offset, "type");
3896 declaration_keyword && matches!(self.peek_at(offset + 1).kind, TokenKind::Identifier)
3897 }
3898
3899 fn peek_at_is_declaration_modifier(&self, offset: usize) -> bool {
3901 matches!(self.peek_at(offset).kind, TokenKind::Export)
3902 || ["declare", "async", "abstract"]
3903 .iter()
3904 .any(|word| self.peek_at_is_word(offset, word))
3905 }
3906
3907 fn peek_is_construct_signature(&self) -> bool {
3912 matches!(self.peek().kind, TokenKind::New)
3913 && matches!(
3914 self.peek_at(1).kind,
3915 TokenKind::LeftParen | TokenKind::LessThan
3916 )
3917 }
3918
3919 fn reject_construct_signature(&mut self) -> Option<()> {
3923 let keyword = self.advance();
3924 self.error_at_with_help(
3925 keyword.span,
3926 "construct signatures are not supported in object types",
3927 vec![
3928 "declare an `interface` with the `new (…)` signature, or pass a factory \
3929 function such as `() => T`"
3930 .to_string(),
3931 ],
3932 );
3933 if matches!(self.peek().kind, TokenKind::LessThan) {
3934 self.parse_generic_param_list()?;
3935 }
3936 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3937 self.error_at_peek("expected `(` after construct signature type parameters");
3938 return None;
3939 }
3940 self.parse_object_type_method_signature(keyword.span.start)?;
3941 Some(())
3942 }
3943
3944 fn parse_object_type_field(
3946 &mut self,
3947 readonly: bool,
3948 member_start: u32,
3949 ) -> Option<TypeAnnotationField> {
3950 let name = self.expect_property_ident("expected field name in object type")?;
3951 let optional = matches!(self.peek().kind, TokenKind::Question);
3953 if optional {
3954 self.advance();
3955 }
3956 let method = matches!(self.peek().kind, TokenKind::LeftParen);
3957 let ty = if method {
3958 if readonly {
3959 self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
3960 }
3961 self.parse_object_type_method_signature(name.span.start)?
3962 } else {
3963 if !matches!(self.peek().kind, TokenKind::Colon) {
3964 self.error_at_peek("expected `:` after field name");
3965 return None;
3966 }
3967 self.advance();
3968 self.parse_type_annotation()?
3969 };
3970 Some(TypeAnnotationField {
3971 name,
3972 ty,
3973 optional,
3974 readonly,
3975 rest: false,
3976 method,
3977 })
3978 }
3979
3980 fn parse_object_type_method_signature(&mut self, start: u32) -> Option<TypeAnnotation> {
3985 self.advance();
3986 let params = self.parse_function_type_params()?;
3987 if !matches!(self.peek().kind, TokenKind::Colon) {
3988 self.error_at_peek("expected `:` and return type after method parameters");
3989 return None;
3990 }
3991 self.advance();
3992 let return_type = self.parse_type_annotation()?;
3993 let end = return_type.span.end;
3994 Some(TypeAnnotation {
3995 kind: TypeAnnotationKind::Function {
3996 params,
3997 return_type: Box::new(return_type),
3998 },
3999 span: self.span(start, end),
4000 })
4001 }
4002
4003 fn expect_identifier(&mut self, message: &str) -> Option<Token> {
4006 let tok = self.expect_identifier_name(message)?;
4007 self.reject_strict_mode_reserved_word(&tok);
4008 Some(tok)
4009 }
4010
4011 fn expect_identifier_name(&mut self, message: &str) -> Option<Token> {
4014 if matches!(self.peek().kind, TokenKind::Identifier) {
4015 return Some(self.advance());
4016 }
4017 if is_reserved_identifier_word(&self.peek().kind) {
4018 let tok = self.advance();
4019 let keyword = &self.source[tok.span.start as usize..tok.span.end as usize];
4020 self.error_at_with_help(
4021 tok.span,
4022 format!("`{keyword}` is a reserved keyword and can't be used as a name"),
4023 vec![format!(
4024 "rename it, for example: `{}`",
4025 reserved_keyword_rename_example(keyword)
4026 )],
4027 );
4028 return None;
4029 }
4030 self.error_at_peek(message.to_string());
4031 None
4032 }
4033
4034 fn reject_strict_mode_reserved_word(&mut self, tok: &Token) {
4037 let word = &self.source[tok.span.start as usize..tok.span.end as usize];
4038 if !STRICT_MODE_RESERVED_WORDS.contains(&word) {
4039 return;
4040 }
4041 self.error_at_with_help(
4042 tok.span,
4043 format!("`{word}` is a reserved word in strict mode and can't be used as a name"),
4044 vec![format!(
4045 "rename it, for example: `{}`",
4046 reserved_keyword_rename_example(word)
4047 )],
4048 );
4049 }
4050
4051 fn ident_from_token(&self, tok: &Token) -> Ident {
4052 Ident {
4053 name: self.source[tok.span.start as usize..tok.span.end as usize].to_string(),
4054 span: tok.span,
4055 }
4056 }
4057
4058 fn property_ident_from_token(&self, tok: &Token) -> Ident {
4059 if let TokenKind::StringLiteral(s) = &tok.kind {
4060 Ident {
4061 name: s.clone(),
4062 span: tok.span,
4063 }
4064 } else {
4065 self.ident_from_token(tok)
4066 }
4067 }
4068
4069 fn expect_property_name(&mut self, message: &str) -> Option<Token> {
4070 if !is_property_name(&self.peek().kind) {
4071 self.error_at_peek(message.to_string());
4072 return None;
4073 }
4074 Some(self.advance())
4075 }
4076
4077 fn expect_property_ident(&mut self, message: &str) -> Option<Ident> {
4078 if !is_property_name(&self.peek().kind)
4079 && !matches!(self.peek().kind, TokenKind::StringLiteral(_))
4080 {
4081 self.error_at_peek(message.to_string());
4082 return None;
4083 }
4084 let tok = self.advance();
4085 Some(self.property_ident_from_token(&tok))
4086 }
4087
4088 fn readonly_modifier_span(&self, member_start: u32) -> Span {
4091 self.span(member_start, member_start + "readonly".len() as u32)
4092 }
4093
4094 fn reject_readonly_modifier(&mut self, span: Span) {
4097 self.error_at_with_help(
4098 span,
4099 "`readonly` can only modify a property or index signature",
4100 vec!["remove `readonly`".to_string()],
4101 );
4102 }
4103
4104 fn eat_readonly_property_modifier(&mut self) -> bool {
4105 if self.peek_identifier_text_is("readonly")
4106 && !self.line_break_after_peek()
4107 && (self.peek_starts_property_after_readonly()
4108 || matches!(self.peek_at(1).kind, TokenKind::LeftBracket))
4109 {
4110 self.advance();
4111 return true;
4112 }
4113 false
4114 }
4115
4116 fn peek_starts_property_after_readonly(&self) -> bool {
4117 let name = &self.peek_at(1).kind;
4118 let after_name = &self.peek_at(2).kind;
4119 let after_optional = &self.peek_at(3).kind;
4120 let starts_member_type =
4123 |kind: &TokenKind| matches!(kind, TokenKind::Colon | TokenKind::LeftParen);
4124 (is_property_name(name) || matches!(name, TokenKind::StringLiteral(_)))
4125 && (starts_member_type(after_name)
4126 || matches!(after_name, TokenKind::Question) && starts_member_type(after_optional))
4127 }
4128
4129 fn peek_identifier_text_is(&self, expected: &str) -> bool {
4130 self.token_is_word(self.peek(), expected)
4131 }
4132
4133 fn peek_at_is_word(&self, offset: usize, expected: &str) -> bool {
4134 self.token_is_word(self.peek_at(offset), expected)
4135 }
4136
4137 fn token_is_word(&self, tok: &Token, expected: &str) -> bool {
4138 matches!(tok.kind, TokenKind::Identifier)
4139 && &self.source[tok.span.start as usize..tok.span.end as usize] == expected
4140 }
4141
4142 fn at_delete_operator(&self) -> bool {
4149 if !self.peek_identifier_text_is("delete") {
4150 return false;
4151 }
4152 if !matches!(
4153 self.peek_at(1).kind,
4154 TokenKind::Identifier | TokenKind::This
4155 ) {
4156 return false;
4157 }
4158 let next_is_cast_keyword =
4159 self.peek_at_is_word(1, "as") || self.peek_at_is_word(1, "satisfies");
4160 !next_is_cast_keyword || cannot_start_type(&self.peek_at(2).kind)
4161 }
4162
4163 fn at_type_alias_head(&self) -> bool {
4167 self.peek_identifier_text_is("type")
4168 && matches!(self.peek_at(1).kind, TokenKind::Identifier)
4169 }
4170
4171 fn parse_expression(&mut self) -> Option<ExprId> {
4174 self.with_recursion_limit(Self::parse_expression_inner)
4175 }
4176
4177 fn parse_expression_inner(&mut self) -> Option<ExprId> {
4178 let written = self.parse_conditional()?;
4179 let shift_assignment = self.peek_shift().filter(|shift| shift.assignment);
4180 if !is_assign_lookahead(&self.peek().kind) && shift_assignment.is_none() {
4181 return Some(written);
4182 }
4183 let target_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4184 let target = self.assignment_target(written)?;
4185 let op_tok = self.advance();
4186 let mut op_span = op_tok.span;
4187 let op = if let Some(ShiftToken { op, tokens, .. }) = shift_assignment {
4188 for _ in 1..tokens {
4189 op_span.end = self.advance().span.end;
4190 }
4191 Some(op)
4192 } else {
4193 compound_op_for_token(&op_tok.kind)
4194 };
4195 let value = self.parse_expression()?;
4196 let value_span = parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
4197 parse_arena_result(
4198 self.ast.try_push_expr(Expr {
4199 kind: ExprKind::Assign {
4200 target,
4201 op,
4202 op_span,
4203 value,
4204 },
4205 span: self.span(target_span.start, value_span.end),
4206 }),
4207 &mut self.fatal,
4208 )
4209 }
4210
4211 fn assignment_target(&mut self, written: ExprId) -> Option<ExprId> {
4214 let mut target = written;
4215 while let ExprKind::Paren(inner) =
4216 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind
4217 {
4218 target = inner;
4219 }
4220 if matches!(
4221 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind,
4222 ExprKind::Identifier(_) | ExprKind::FieldAccess { .. } | ExprKind::IndexAccess { .. }
4223 ) {
4224 return Some(target);
4225 }
4226 let error_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4227 self.error_at_with_help(
4228 error_span,
4229 "invalid assignment target",
4230 vec![
4231 "assign to a variable, a field (`o.f = …`), or an element (`a[i] = …`)".to_string(),
4232 ],
4233 );
4234 None
4235 }
4236
4237 fn parse_conditional(&mut self) -> Option<ExprId> {
4238 if self.is_arrow_start() {
4239 return self.parse_arrow();
4240 }
4241 let cond = self.parse_binary(0)?;
4242 if !matches!(self.peek().kind, TokenKind::Question) {
4244 return Some(cond);
4245 }
4246 self.advance();
4247 let then_ = self.parse_expression()?;
4248 if !matches!(self.peek().kind, TokenKind::Colon) {
4249 self.error_at_peek_with_help(
4250 "expected `:` to complete ternary expression",
4251 vec!["const x = cond ? then : else;".to_string()],
4252 );
4253 return None;
4254 }
4255 self.advance();
4256 let else_ = self.parse_expression()?;
4257 let cond_span = parse_arena_result(self.ast.try_expr(cond), &mut self.fatal)?.span;
4258 let else_span = parse_arena_result(self.ast.try_expr(else_), &mut self.fatal)?.span;
4259 parse_arena_result(
4260 self.ast.try_push_expr(Expr {
4261 kind: ExprKind::Ternary { cond, then_, else_ },
4262 span: self.span(cond_span.start, else_span.end),
4263 }),
4264 &mut self.fatal,
4265 )
4266 }
4267
4268 fn is_arrow_start(&self) -> bool {
4269 match self.peek().kind {
4270 TokenKind::Identifier => matches!(self.peek_at(1).kind, TokenKind::Arrow),
4271 TokenKind::LeftParen => self.find_arrow_after_matching_paren(),
4272 _ => false,
4273 }
4274 }
4275
4276 fn index_after_matching_paren(&self, index: usize) -> Option<usize> {
4279 if !matches!(
4280 self.tokens.get(index).map(|t| &t.kind),
4281 Some(TokenKind::LeftParen)
4282 ) {
4283 return None;
4284 }
4285 self.scan_past_balanced(
4286 index,
4287 |k| matches!(k, TokenKind::LeftParen),
4288 |k| matches!(k, TokenKind::RightParen),
4289 )
4290 }
4291
4292 fn find_arrow_after_matching_paren(&self) -> bool {
4293 let Some(i) = self.index_after_matching_paren(self.pos) else {
4294 return false;
4295 };
4296 let after_type = if matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Colon)) {
4297 match self.scan_past_type_annotation(i + 1, TypePos::ArrowReturn) {
4298 Some(p) => p,
4299 None => return false,
4300 }
4301 } else {
4302 i
4303 };
4304 matches!(
4305 self.tokens.get(after_type).map(|t| &t.kind),
4306 Some(TokenKind::Arrow),
4307 )
4308 }
4309
4310 fn scan_past_type_annotation(&self, start: usize, type_pos: TypePos) -> Option<usize> {
4316 self.scan_past_type_annotation_at_depth(start, type_pos, 0)
4317 }
4318
4319 fn scan_past_type_annotation_at_depth(
4320 &self,
4321 start: usize,
4322 type_pos: TypePos,
4323 depth: usize,
4324 ) -> Option<usize> {
4325 if depth >= MAX_PARSE_DEPTH {
4326 return None;
4327 }
4328 if matches!(
4329 self.tokens.get(start).map(|t| &t.kind),
4330 Some(TokenKind::Identifier)
4331 ) && self
4332 .tokens
4333 .get(start + 1)
4334 .is_some_and(|t| self.token_is_word(t, "is"))
4335 {
4336 return self.scan_past_type_annotation_at_depth(start + 2, type_pos, depth + 1);
4337 }
4338 let start = if matches!(
4340 self.tokens.get(start).map(|t| &t.kind),
4341 Some(TokenKind::Pipe)
4342 ) {
4343 start + 1
4344 } else {
4345 start
4346 };
4347 let mut i = self.scan_past_single_type_member(start, type_pos, depth + 1)?;
4348 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Pipe)) {
4349 i = self.scan_past_single_type_member(i + 1, type_pos, depth + 1)?;
4350 }
4351 Some(i)
4352 }
4353
4354 fn scan_past_single_type_member(
4355 &self,
4356 start: usize,
4357 type_pos: TypePos,
4358 depth: usize,
4359 ) -> Option<usize> {
4360 if depth >= MAX_PARSE_DEPTH {
4361 return None;
4362 }
4363 if self.is_contextual_type_operator(start, "keyof")
4364 || self.is_contextual_type_operator(start, "readonly")
4365 {
4366 return self.scan_past_single_type_member(start + 1, type_pos, depth + 1);
4367 }
4368 let mut i = start;
4369 match self.tokens.get(i)?.kind {
4370 TokenKind::Identifier | TokenKind::Void => {
4371 i += 1;
4372 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Dot)) {
4373 if !matches!(
4374 self.tokens.get(i + 1).map(|t| &t.kind),
4375 Some(TokenKind::Identifier)
4376 ) {
4377 return None;
4378 }
4379 i += 2;
4380 }
4381 if matches!(
4382 self.tokens.get(i).map(|t| &t.kind),
4383 Some(TokenKind::LessThan)
4384 ) {
4385 i = self.scan_past_balanced(
4386 i,
4387 |k| matches!(k, TokenKind::LessThan),
4388 |k| matches!(k, TokenKind::GreaterThan),
4389 )?;
4390 }
4391 }
4392 TokenKind::NullLiteral
4393 | TokenKind::StringLiteral(_)
4394 | TokenKind::NumberLiteral(_)
4395 | TokenKind::BooleanLiteral(_) => {
4396 i += 1;
4397 }
4398 TokenKind::LeftBrace => {
4399 i = self.scan_past_balanced(
4400 i,
4401 |k| matches!(k, TokenKind::LeftBrace),
4402 |k| matches!(k, TokenKind::RightBrace),
4403 )?;
4404 }
4405 TokenKind::LeftBracket => {
4406 i = self.scan_past_balanced(
4407 i,
4408 |k| matches!(k, TokenKind::LeftBracket),
4409 |k| matches!(k, TokenKind::RightBracket),
4410 )?;
4411 }
4412 TokenKind::LeftParen => {
4417 let is_function_type = self.paren_at_opens_function_type(i, type_pos);
4418 i = self.scan_past_balanced(
4419 i,
4420 |k| matches!(k, TokenKind::LeftParen),
4421 |k| matches!(k, TokenKind::RightParen),
4422 )?;
4423 if is_function_type {
4424 if !matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)) {
4425 return None;
4426 }
4427 return self.scan_past_type_annotation_at_depth(
4428 i + 1,
4429 TypePos::Anywhere,
4430 depth + 1,
4431 );
4432 }
4433 }
4434 _ => return None,
4435 }
4436 while matches!(
4437 self.tokens.get(i).map(|t| &t.kind),
4438 Some(TokenKind::LeftBracket)
4439 ) {
4440 i += 1;
4441 if !matches!(
4442 self.tokens.get(i).map(|t| &t.kind),
4443 Some(TokenKind::RightBracket)
4444 ) {
4445 return None;
4446 }
4447 i += 1;
4448 }
4449 Some(i)
4450 }
4451
4452 fn scan_past_balanced(
4455 &self,
4456 start: usize,
4457 is_open: fn(&TokenKind) -> bool,
4458 is_close: fn(&TokenKind) -> bool,
4459 ) -> Option<usize> {
4460 let mut depth: i32 = 1;
4461 let mut i = start + 1;
4462 while i < self.tokens.len() && depth > 0 {
4463 let token = self.tokens.get(i)?;
4464 let kind = &token.kind;
4465 if is_open(kind) {
4466 depth += 1;
4467 } else if is_close(kind) {
4468 depth -= 1;
4469 } else if matches!(kind, TokenKind::Eof) {
4470 return None;
4471 }
4472 i += 1;
4473 }
4474 if depth == 0 { Some(i) } else { None }
4475 }
4476
4477 fn parse_arrow(&mut self) -> Option<ExprId> {
4478 let start_span = self.peek().span;
4479
4480 let params = match self.peek().kind {
4481 TokenKind::Identifier => {
4482 let name_tok = self.advance();
4483 let name = self.ident_from_token(&name_tok);
4484 vec![ParamDecl {
4485 name,
4486 ty: None,
4487 default: None,
4488 optional: false,
4489 pattern: None,
4490 rest: false,
4491 modifiers: None,
4492 }]
4493 }
4494 TokenKind::LeftParen => {
4495 self.advance();
4496 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4497 Vec::new()
4498 } else {
4499 self.parse_arrow_param_list()?
4500 };
4501 if !matches!(self.peek().kind, TokenKind::RightParen) {
4502 self.error_at_peek("expected `)`");
4503 return None;
4504 }
4505 self.advance();
4506 params
4507 }
4508 _ => return self.invariant_failure("is_arrow_start guarded the entry"),
4509 };
4510
4511 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4512 self.advance();
4513 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4514 } else {
4515 (None, None)
4516 };
4517
4518 if !matches!(self.peek().kind, TokenKind::Arrow) {
4519 self.error_at_peek("expected `=>` in arrow function");
4520 return None;
4521 }
4522 self.advance();
4523
4524 let (body, end_pos) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
4525 let block = self.parse_block()?;
4526 let span = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?.span;
4527 (ArrowBody::Block(block), span.end)
4528 } else {
4529 let expr = self.parse_expression()?;
4530 let span = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?.span;
4531 (ArrowBody::Expr(expr), span.end)
4532 };
4533
4534 parse_arena_result(
4535 self.ast.try_push_expr(Expr {
4536 kind: ExprKind::Arrow {
4537 params,
4538 return_type,
4539 type_predicate,
4540 body,
4541 },
4542 span: self.span(start_span.start, end_pos),
4543 }),
4544 &mut self.fatal,
4545 )
4546 }
4547
4548 fn parse_function_expression(&mut self) -> Option<ExprId> {
4551 let kw = self.advance();
4552
4553 if matches!(self.peek().kind, TokenKind::LessThan) {
4554 self.error_at_peek("generic function expressions are not supported");
4555 return None;
4556 }
4557
4558 let self_name = if matches!(self.peek().kind, TokenKind::Identifier) {
4559 let name_tok = self.advance();
4560 Some(self.ident_from_token(&name_tok))
4561 } else {
4562 None
4563 };
4564
4565 if !matches!(self.peek().kind, TokenKind::LeftParen) {
4566 self.error_at_peek("expected `(` after `function`");
4567 return None;
4568 }
4569 self.advance();
4570
4571 let this_type = if matches!(self.peek().kind, TokenKind::This) {
4572 self.advance();
4573 if !matches!(self.peek().kind, TokenKind::Colon) {
4574 self.error_at_peek("expected a type annotation after `this`");
4575 return None;
4576 }
4577 self.advance();
4578 let ty = self.parse_type_annotation()?;
4579 if matches!(self.peek().kind, TokenKind::Comma) {
4580 self.advance();
4581 } else if !matches!(self.peek().kind, TokenKind::RightParen) {
4582 self.error_at_peek("expected `,` or `)` after the `this` parameter");
4583 return None;
4584 }
4585 Some(ty)
4586 } else {
4587 None
4588 };
4589 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4590 Vec::new()
4591 } else {
4592 let saved_class = self.class_member_body_depth;
4593 let saved_function = self.function_expression_body_depth;
4594 self.class_member_body_depth = 0;
4595 self.function_expression_body_depth = 1;
4596 let params = self.parse_arrow_param_list();
4597 self.class_member_body_depth = saved_class;
4598 self.function_expression_body_depth = saved_function;
4599 params?
4600 };
4601 if !matches!(self.peek().kind, TokenKind::RightParen) {
4602 self.error_at_peek("expected `)`");
4603 return None;
4604 }
4605 self.advance();
4606
4607 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4608 self.advance();
4609 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4610 } else {
4611 (None, None)
4612 };
4613
4614 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
4615 self.error_at_peek("expected `{` to open the function body");
4616 return None;
4617 }
4618 let saved = self.class_member_body_depth;
4619 self.class_member_body_depth = 0;
4620 self.function_expression_body_depth += 1;
4621 let block = self.parse_block();
4622 self.function_expression_body_depth -= 1;
4623 self.class_member_body_depth = saved;
4624 let block = block?;
4625 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
4626 .span
4627 .end;
4628
4629 let function = parse_arena_result(
4630 self.ast.try_push_expr(Expr {
4631 kind: ExprKind::Arrow {
4632 params,
4633 return_type,
4634 type_predicate,
4635 body: ArrowBody::Block(block),
4636 },
4637 span: self.span(kw.span.start, end),
4638 }),
4639 &mut self.fatal,
4640 )?;
4641 parse_arena_result(
4642 self.ast.try_push_expr(Expr {
4643 kind: ExprKind::FunctionExpression {
4644 name: self_name,
4645 this_type,
4646 function,
4647 },
4648 span: self.span(kw.span.start, end),
4649 }),
4650 &mut self.fatal,
4651 )
4652 }
4653
4654 fn parse_outer_this_boundary<T>(
4657 &mut self,
4658 parse: impl FnOnce(&mut Self) -> Option<T>,
4659 ) -> Option<T> {
4660 let saved = self.class_member_body_depth;
4661 self.class_member_body_depth = 0;
4662 let saved_function = self.function_expression_body_depth;
4663 self.function_expression_body_depth = 0;
4664 let parsed = parse(self);
4665 self.class_member_body_depth = saved;
4666 self.function_expression_body_depth = saved_function;
4667 parsed
4668 }
4669
4670 fn parse_arrow_param_list(&mut self) -> Option<Vec<ParamDecl>> {
4671 let mut params = Vec::new();
4672 loop {
4673 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
4674 self.advance();
4675 true
4676 } else {
4677 false
4678 };
4679 let (name, pattern) = match self.peek().kind {
4682 TokenKind::LeftBrace | TokenKind::LeftBracket => {
4683 if rest {
4684 self.error_at_peek("rest parameter cannot be destructured");
4685 return None;
4686 }
4687 let binding = self.parse_binding()?;
4688 let placeholder = Ident {
4689 name: String::new(),
4690 span: binding.span(),
4691 };
4692 (placeholder, Some(binding))
4693 }
4694 _ => {
4695 let name_tok = self.expect_identifier(if rest {
4696 "expected identifier after `...`"
4697 } else {
4698 "expected parameter name"
4699 })?;
4700 let name = self.ident_from_token(&name_tok);
4701 (name, None)
4702 }
4703 };
4704 let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
4705 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
4706 self.advance();
4707 Some(self.parse_type_annotation()?)
4708 } else {
4709 None
4712 };
4713 let default = self.parse_parameter_default(rest, optional)?;
4714 params.push(ParamDecl {
4715 name,
4716 ty,
4717 default,
4718 optional,
4719 pattern,
4720 rest,
4721 modifiers: None,
4722 });
4723 match self.peek().kind {
4724 TokenKind::Comma => {
4725 if rest {
4726 let comma = self.advance();
4727 self.error_at(comma.span, "rest parameter must be the last parameter");
4728 return None;
4729 }
4730 self.advance();
4731 if matches!(self.peek().kind, TokenKind::RightParen) {
4732 break;
4733 }
4734 }
4735 TokenKind::RightParen => break,
4736 _ => {
4737 self.error_at_peek("expected `,` or `)`");
4738 return None;
4739 }
4740 }
4741 }
4742 self.check_parameter_order(params.iter().map(ParamOrder::of_param));
4743 Some(params)
4744 }
4745
4746 fn parse_binary(&mut self, min_prec: u8) -> Option<ExprId> {
4747 self.with_recursion_limit(|parser| parser.parse_binary_inner(min_prec))
4748 }
4749
4750 fn peek_shift(&self) -> Option<ShiftToken> {
4753 let first = self.peek();
4754 let second = self.peek_at(1);
4755 if first.span.end != second.span.start {
4756 return None;
4757 }
4758 match (&first.kind, &second.kind) {
4759 (TokenKind::LessThan, TokenKind::LessThan) => {
4760 Some(ShiftToken::new(BinOp::Shl, 2, false))
4761 }
4762 (TokenKind::LessThan, TokenKind::LessEquals) => {
4763 Some(ShiftToken::new(BinOp::Shl, 2, true))
4764 }
4765 (TokenKind::GreaterThan, TokenKind::GreaterEquals) => {
4766 Some(ShiftToken::new(BinOp::Shr, 2, true))
4767 }
4768 (TokenKind::GreaterThan, TokenKind::GreaterThan) => {
4769 let third = self.peek_at(2);
4770 if second.span.end == third.span.start {
4771 match third.kind {
4772 TokenKind::GreaterThan => {
4773 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, false));
4774 }
4775 TokenKind::GreaterEquals => {
4776 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, true));
4777 }
4778 _ => {}
4779 }
4780 }
4781 Some(ShiftToken::new(BinOp::Shr, 2, false))
4782 }
4783 _ => None,
4784 }
4785 }
4786
4787 fn peek_binary(&self) -> Option<BinaryToken> {
4788 if let Some(shift) = self.peek_shift() {
4789 return (!shift.assignment).then_some(BinaryToken {
4790 op: shift.op,
4791 precedence: SHIFT_PRECEDENCE,
4792 tokens: shift.tokens,
4793 });
4794 }
4795 peek_binop(&self.peek().kind).map(|(op, precedence)| BinaryToken {
4796 op,
4797 precedence,
4798 tokens: 1,
4799 })
4800 }
4801
4802 fn parse_binary_inner(&mut self, min_prec: u8) -> Option<ExprId> {
4803 let mut lhs = self.parse_cast()?;
4804 let mut last_op: Option<BinOp> = None;
4808 let mut reported_mixing = false;
4809 while let Some(BinaryToken {
4810 op,
4811 precedence: prec,
4812 tokens: count,
4813 }) = self.peek_binary()
4814 {
4815 if prec < min_prec {
4816 break;
4817 }
4818 if !reported_mixing
4819 && let Some(last) = last_op
4820 && mixed_logical(last, op)
4821 {
4822 reported_mixing = true;
4823 let op_span = self.peek().span;
4824 self.error_at_with_help(
4828 op_span,
4829 "mixing `??` with `||` / `&&` requires parentheses",
4830 vec![mixed_logical_help(last, op)],
4831 );
4832 }
4833 if op == BinOp::Pow
4834 && matches!(
4835 parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.kind,
4836 ExprKind::Unary { .. }
4837 | ExprKind::Typeof { .. }
4838 | ExprKind::Delete { .. }
4839 | ExprKind::Void { .. }
4840 )
4841 {
4842 let error_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
4843 self.error_at_with_help(
4844 error_span,
4845 "an unparenthesized unary expression cannot be the left operand of `**`",
4846 vec!["choose the grouping explicitly: `(-x) ** 2` or `-(x ** 2)`".into()],
4847 );
4848 return None;
4849 }
4850 let loose = matches!(self.peek().kind, TokenKind::EqEq | TokenKind::BangEq);
4851 for _ in 0..count {
4852 self.advance();
4853 }
4854 let rhs = self.parse_binary(right_operand_min_prec(op, prec))?;
4855 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
4856 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
4857 if loose {
4858 self.reject_loose_nullish_comparison(op, lhs, rhs)?;
4859 }
4860 lhs = parse_arena_result(
4861 self.ast.try_push_expr(Expr {
4862 kind: ExprKind::Binary { op, lhs, rhs },
4863 span: self.span(lhs_span.start, rhs_span.end),
4864 }),
4865 &mut self.fatal,
4866 )?;
4867 last_op = Some(op);
4868 }
4869 Some(lhs)
4870 }
4871
4872 fn parse_cast(&mut self) -> Option<ExprId> {
4874 let mut expr = self.parse_unary()?;
4875 while matches!(self.peek().kind, TokenKind::Instanceof)
4876 || self.peek_identifier_text_is("as")
4877 {
4878 let is_instanceof = matches!(self.peek().kind, TokenKind::Instanceof);
4879 self.advance();
4880 let ty = self.parse_type_annotation()?;
4881 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
4882 .span
4883 .start;
4884 let end = ty.span.end;
4885 let kind = if is_instanceof {
4886 ExprKind::InstanceOf { value: expr, ty }
4887 } else {
4888 self.last_as_type_end = Some(end);
4889 ExprKind::As { expr, ty }
4890 };
4891 expr = parse_arena_result(
4892 self.ast.try_push_expr(Expr {
4893 kind,
4894 span: self.span(start, end),
4895 }),
4896 &mut self.fatal,
4897 )?;
4898 }
4899 Some(expr)
4900 }
4901
4902 fn parse_unary(&mut self) -> Option<ExprId> {
4903 self.with_recursion_limit(Self::parse_unary_inner)
4904 }
4905
4906 fn parse_unary_inner(&mut self) -> Option<ExprId> {
4907 if matches!(self.peek().kind, TokenKind::Typeof) {
4910 return self.parse_prefix_expr(|operand| ExprKind::Typeof { operand });
4911 }
4912 if matches!(self.peek().kind, TokenKind::Void) {
4913 return self.parse_prefix_expr(|operand| ExprKind::Void { operand });
4914 }
4915 if self.at_delete_operator() {
4916 return self.parse_prefix_expr(|operand| ExprKind::Delete { operand });
4917 }
4918 if matches!(self.peek().kind, TokenKind::New) {
4919 let op_tok = self.advance();
4920 let mut callee = self.parse_atom()?;
4921 while matches!(self.peek().kind, TokenKind::Dot) {
4922 self.advance();
4923 if !matches!(self.peek().kind, TokenKind::Identifier) {
4924 self.error_at_peek("expected identifier after `.` in constructor name");
4925 return None;
4926 }
4927 let ident_tok = self.advance();
4928 let name = self.ident_from_token(&ident_tok);
4929 let receiver_span =
4930 parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
4931 let combined_span = self.span(receiver_span.start, ident_tok.span.end);
4932 callee = parse_arena_result(
4933 self.ast.try_push_expr(Expr {
4934 kind: ExprKind::FieldAccess {
4935 receiver: callee,
4936 name,
4937 },
4938 span: combined_span,
4939 }),
4940 &mut self.fatal,
4941 )?;
4942 }
4943 let type_args = if matches!(self.peek().kind, TokenKind::LessThan) {
4944 let saved_pos = self.pos;
4945 let saved_diag_len = self.diagnostics.len();
4946 if let Some(args) = self.try_parse_type_args_only() {
4947 Some(args)
4948 } else {
4949 self.pos = saved_pos;
4950 self.diagnostics.truncate(saved_diag_len);
4951 None
4952 }
4953 } else {
4954 None
4955 };
4956 if !matches!(self.peek().kind, TokenKind::LeftParen) {
4957 self.error_at_peek("expected `(` after constructor name in `new` expression");
4958 return None;
4959 }
4960 self.advance();
4961 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
4962 Vec::new()
4963 } else {
4964 self.parse_call_args()?
4965 };
4966 if !matches!(self.peek().kind, TokenKind::RightParen) {
4967 self.error_at_peek("expected `)`");
4968 return None;
4969 }
4970 let close = self.advance();
4971 let new_expr = parse_arena_result(
4972 self.ast.try_push_expr(Expr {
4973 kind: ExprKind::New {
4974 callee,
4975 type_args,
4976 args,
4977 },
4978 span: self.span(op_tok.span.start, close.span.end),
4979 }),
4980 &mut self.fatal,
4981 )?;
4982 return self.parse_postfix_from(new_expr);
4985 }
4986 let op = match self.peek().kind {
4987 TokenKind::Bang => UnOp::Not,
4988 TokenKind::Tilde => UnOp::BitNot,
4989 TokenKind::Minus => UnOp::Neg,
4990 TokenKind::Plus => UnOp::Pos,
4991 _ => return self.parse_postfix(),
4992 };
4993 self.parse_prefix_expr(|operand| ExprKind::Unary { op, operand })
4994 }
4995
4996 fn parse_prefix_expr(&mut self, kind: impl FnOnce(ExprId) -> ExprKind) -> Option<ExprId> {
4998 let op_tok = self.advance();
4999 if matches!(op_tok.kind, TokenKind::Void) && self.at_inserted_semicolon() {
5002 self.advance();
5003 }
5004 let operand = self.parse_unary()?;
5005 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5006 parse_arena_result(
5007 self.ast.try_push_expr(Expr {
5008 kind: kind(operand),
5009 span: self.span(op_tok.span.start, operand_span.end),
5010 }),
5011 &mut self.fatal,
5012 )
5013 }
5014
5015 fn parse_postfix(&mut self) -> Option<ExprId> {
5016 let expr = self.parse_atom()?;
5017 self.parse_postfix_from(expr)
5018 }
5019
5020 fn parse_postfix_from(&mut self, mut expr: ExprId) -> Option<ExprId> {
5021 let mut chain_parts: Option<(ExprId, Vec<crate::ChainPart>)> = None;
5025 loop {
5026 match self.peek().kind {
5027 TokenKind::LeftParen => {
5028 if let Some((_, parts)) = &mut chain_parts {
5029 let part = self.parse_chain_call_tail(false)?;
5030 parts.push(part);
5031 } else {
5032 expr = self.parse_call_tail(expr, None)?;
5033 }
5034 }
5035 TokenKind::Dot => {
5036 if let Some((_, parts)) = &mut chain_parts {
5037 let part = self.parse_chain_field_tail(false)?;
5038 parts.push(part);
5039 } else {
5040 expr = self.parse_field_access_tail(expr)?;
5041 }
5042 }
5043 TokenKind::LeftBracket => {
5044 if let Some((_, parts)) = &mut chain_parts {
5045 let part = self.parse_chain_index_tail(false)?;
5046 parts.push(part);
5047 } else {
5048 expr = self.parse_index_access_tail(expr)?;
5049 }
5050 }
5051 TokenKind::QuestionDot => {
5052 if chain_parts.is_none() {
5053 chain_parts = Some((expr, Vec::new()));
5054 }
5055 self.advance();
5056 let part = match self.peek().kind {
5057 TokenKind::LeftParen => self.parse_chain_call_tail(true)?,
5058 TokenKind::LeftBracket => self.parse_chain_index_tail(true)?,
5059 _ if is_property_name(&self.peek().kind) => {
5060 self.parse_chain_field_tail(true)?
5061 }
5062 _ => {
5063 self.error_at_peek(
5064 "expected field name, `(` for call, or `[` for index after `?.`",
5065 );
5066 return None;
5067 }
5068 };
5069 if let Some((_, parts)) = &mut chain_parts {
5070 parts.push(part);
5071 }
5072 }
5073 TokenKind::LessThan => {
5077 if chain_parts.is_some() {
5078 break;
5080 }
5081 let saved_pos = self.pos;
5082 let saved_diag_len = self.diagnostics.len();
5083 if let Some(call_id) = self.try_parse_generic_call_tail(expr) {
5084 expr = call_id;
5085 } else {
5086 self.pos = saved_pos;
5087 self.diagnostics.truncate(saved_diag_len);
5088 break;
5089 }
5090 }
5091 TokenKind::Bang => {
5092 if let Some((_, parts)) = &mut chain_parts {
5093 let tok = self.advance();
5094 parts.push(crate::ChainPart::NonNull { span: tok.span });
5095 continue;
5096 }
5097 let tok = self.advance();
5098 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5099 .span
5100 .start;
5101 expr = parse_arena_result(
5102 self.ast.try_push_expr(Expr {
5103 kind: ExprKind::PostfixUnary {
5104 op: PostfixOp::NonNullAssert,
5105 operand: expr,
5106 },
5107 span: self.span(start, tok.span.end),
5108 }),
5109 &mut self.fatal,
5110 )?;
5111 }
5112 TokenKind::PlusPlus | TokenKind::MinusMinus => {
5114 if chain_parts.is_some() {
5115 break;
5116 }
5117 let tok = self.advance();
5118 let op = if matches!(tok.kind, TokenKind::PlusPlus) {
5119 PostfixOp::Inc
5120 } else {
5121 PostfixOp::Dec
5122 };
5123 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5124 .span
5125 .start;
5126 expr = parse_arena_result(
5127 self.ast.try_push_expr(Expr {
5128 kind: ExprKind::PostfixUnary { op, operand: expr },
5129 span: self.span(start, tok.span.end),
5130 }),
5131 &mut self.fatal,
5132 )?;
5133 break;
5134 }
5135 _ => break,
5136 }
5137 }
5138 if let Some((base, parts)) = chain_parts {
5139 let base_span = parse_arena_result(self.ast.try_expr(base), &mut self.fatal)?.span;
5140 let end = parts.last().map_or(base_span.end, |p| p.span().end);
5141 Some(parse_arena_result(
5142 self.ast.try_push_expr(Expr {
5143 kind: ExprKind::OptionalChain { base, parts },
5144 span: self.span(base_span.start, end),
5145 }),
5146 &mut self.fatal,
5147 )?)
5148 } else {
5149 Some(expr)
5150 }
5151 }
5152
5153 fn parse_chain_field_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5155 let start_pos = self.peek().span.start;
5156 if !optional {
5157 self.advance();
5158 }
5159 let name_tok = if is_property_name(&self.peek().kind) {
5160 self.advance()
5161 } else {
5162 let what = if optional { "?." } else { "." };
5163 self.error_at_peek(format!("expected field name after `{what}`"));
5164 return None;
5165 };
5166 let name = self.ident_from_token(&name_tok);
5167 let end = name.span.end;
5168 Some(crate::ChainPart::Field {
5169 name,
5170 optional,
5171 span: self.span(start_pos, end),
5172 })
5173 }
5174
5175 fn parse_chain_index_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5176 let open = self.advance();
5177 let idx = self.parse_expression()?;
5178 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5179 self.error_at_peek("expected `]`");
5180 return None;
5181 }
5182 let close = self.advance();
5183 Some(crate::ChainPart::Index {
5184 idx,
5185 optional,
5186 span: self.span(open.span.start, close.span.end),
5187 })
5188 }
5189
5190 fn parse_chain_call_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5191 let open = self.advance();
5192 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5193 Vec::new()
5194 } else {
5195 self.parse_call_args()?
5196 };
5197 if !matches!(self.peek().kind, TokenKind::RightParen) {
5198 self.error_at_peek("expected `)`");
5199 return None;
5200 }
5201 let close = self.advance();
5202 Some(crate::ChainPart::Call {
5203 args,
5204 type_args: None,
5205 optional,
5206 span: self.span(open.span.start, close.span.end),
5207 })
5208 }
5209
5210 fn parse_field_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5211 self.advance();
5212 let name_tok = if is_property_name(&self.peek().kind) {
5214 self.advance()
5215 } else {
5216 self.error_at_peek("expected field name after `.`");
5217 return None;
5218 };
5219 let name = self.ident_from_token(&name_tok);
5220 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5221 parse_arena_result(
5222 self.ast.try_push_expr(Expr {
5223 kind: ExprKind::FieldAccess {
5224 receiver,
5225 name: name.clone(),
5226 },
5227 span: self.span(receiver_span.start, name.span.end),
5228 }),
5229 &mut self.fatal,
5230 )
5231 }
5232
5233 fn parse_index_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5234 self.advance();
5235 let index = self.parse_expression()?;
5236 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5237 self.error_at_peek("expected `]`");
5238 return None;
5239 }
5240 let close = self.advance();
5241 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5242 parse_arena_result(
5243 self.ast.try_push_expr(Expr {
5244 kind: ExprKind::IndexAccess { receiver, index },
5245 span: self.span(receiver_span.start, close.span.end),
5246 }),
5247 &mut self.fatal,
5248 )
5249 }
5250
5251 fn parse_call_tail(
5252 &mut self,
5253 callee: ExprId,
5254 type_args: Option<Vec<TypeAnnotation>>,
5255 ) -> Option<ExprId> {
5256 self.advance();
5257 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5258 Vec::new()
5259 } else {
5260 self.parse_call_args()?
5261 };
5262 if !matches!(self.peek().kind, TokenKind::RightParen) {
5263 self.error_at_peek("expected `)`");
5264 return None;
5265 }
5266 let close = self.advance();
5267
5268 let callee_span = parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5269 parse_arena_result(
5270 self.ast.try_push_expr(Expr {
5271 kind: ExprKind::Call {
5272 callee,
5273 type_args,
5274 args,
5275 },
5276 span: self.span(callee_span.start, close.span.end),
5277 }),
5278 &mut self.fatal,
5279 )
5280 }
5281
5282 fn try_parse_generic_call_tail(&mut self, callee: ExprId) -> Option<ExprId> {
5284 let type_args = self.try_parse_type_args_only()?;
5285 if !matches!(self.peek().kind, TokenKind::LeftParen) {
5286 return None;
5287 }
5288 self.parse_call_tail(callee, Some(type_args))
5289 }
5290
5291 fn try_parse_type_args_only(&mut self) -> Option<Vec<TypeAnnotation>> {
5292 if !matches!(self.peek().kind, TokenKind::LessThan) {
5293 return self.invariant_failure("type argument parser expected `<`");
5294 }
5295 self.advance();
5296
5297 if matches!(self.peek().kind, TokenKind::GreaterThan) {
5298 return None;
5299 }
5300
5301 let mut type_args = Vec::new();
5302 loop {
5303 let ta = self.parse_type_annotation()?;
5304 type_args.push(ta);
5305 match self.peek().kind {
5306 TokenKind::Comma => {
5307 self.advance();
5308 if matches!(self.peek().kind, TokenKind::GreaterThan) {
5309 self.advance();
5310 break;
5311 }
5312 }
5313 TokenKind::GreaterThan => {
5314 self.advance();
5315 break;
5316 }
5317 _ => return None,
5318 }
5319 }
5320 Some(type_args)
5321 }
5322
5323 fn parse_call_args(&mut self) -> Option<Vec<ExprId>> {
5324 let mut args = Vec::new();
5325 loop {
5326 let arg = self.parse_expression()?;
5327 args.push(arg);
5328 match self.peek().kind {
5329 TokenKind::Comma => {
5330 self.advance();
5331 if matches!(self.peek().kind, TokenKind::RightParen) {
5332 return Some(args);
5333 }
5334 }
5335 TokenKind::RightParen => return Some(args),
5336 _ => {
5337 self.error_at_peek("expected `,` or `)`");
5338 return None;
5339 }
5340 }
5341 }
5342 }
5343
5344 fn parse_angle_cast(&mut self) -> Option<ExprId> {
5351 let open = self.advance();
5352 let ty = self.parse_type_annotation()?;
5353 if !matches!(self.peek().kind, TokenKind::GreaterThan) {
5354 self.error_at_peek("expected `>` to close a type assertion");
5355 return None;
5356 }
5357 self.advance();
5358 let expr = self.parse_unary()?;
5359 let end = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5360 .span
5361 .end;
5362 parse_arena_result(
5363 self.ast.try_push_expr(Expr {
5364 kind: ExprKind::As { expr, ty },
5365 span: self.span(open.span.start, end),
5366 }),
5367 &mut self.fatal,
5368 )
5369 }
5370
5371 fn parse_atom(&mut self) -> Option<ExprId> {
5372 match self.peek().kind {
5373 TokenKind::LessThan => return self.parse_angle_cast(),
5378 TokenKind::LeftParen => return self.parse_paren(),
5379 TokenKind::LeftBrace => return self.parse_object_literal(),
5382 TokenKind::LeftBracket => return self.parse_array_literal(),
5383 TokenKind::TemplateHead(_) => return self.parse_template_literal(),
5384 TokenKind::This => return self.parse_this_or_super(true),
5385 TokenKind::Super => return self.parse_this_or_super(false),
5386 TokenKind::Function => return self.parse_function_expression(),
5387 _ => {}
5388 }
5389 if !matches!(
5390 self.peek().kind,
5391 TokenKind::NumberLiteral(_)
5392 | TokenKind::BigIntLiteral(_)
5393 | TokenKind::StringLiteral(_)
5394 | TokenKind::TemplateNoSubstitution(_)
5395 | TokenKind::BooleanLiteral(_)
5396 | TokenKind::NullLiteral
5397 | TokenKind::Identifier
5398 | TokenKind::RegexLiteral { .. }
5399 ) {
5400 self.error_at_peek("expected expression");
5401 return None;
5402 }
5403 let tok = self.advance();
5404 let span = tok.span;
5405 let kind = match tok.kind {
5406 TokenKind::NumberLiteral(v) => ExprKind::Number(v),
5407 TokenKind::BigIntLiteral(s) => ExprKind::BigInt(s),
5408 TokenKind::StringLiteral(s) => ExprKind::String(s),
5409 TokenKind::TemplateNoSubstitution(s) => ExprKind::String(s),
5410 TokenKind::BooleanLiteral(b) => ExprKind::Boolean(b),
5411 TokenKind::NullLiteral => ExprKind::Null,
5412 TokenKind::Identifier => ExprKind::Identifier(Ident {
5413 name: self.source[span.start as usize..span.end as usize].to_string(),
5414 span,
5415 }),
5416 TokenKind::RegexLiteral { source, flags } => ExprKind::Regex { source, flags },
5417 _ => unreachable!("advance preserves the accepted expression token"),
5419 };
5420 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5421 }
5422
5423 fn parse_this_or_super(&mut self, is_this: bool) -> Option<ExprId> {
5426 let tok = self.advance();
5427 let span = tok.span;
5428 if !is_this && self.class_member_body_depth == 0 {
5429 self.error_at_with_help(
5430 span,
5431 "`super` is only valid inside a class method or constructor body",
5432 vec![
5433 "call `super(...)` in a subclass constructor or `super.method(...)` \
5434 in a subclass method"
5435 .to_string(),
5436 ],
5437 );
5438 }
5439 let kind = if is_this
5440 && self.class_member_body_depth == 0
5441 && self.function_expression_body_depth == 0
5442 {
5443 ExprKind::ThisOutsideReceiver
5444 } else if is_this {
5445 ExprKind::This
5446 } else {
5447 ExprKind::Super
5448 };
5449 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5450 }
5451
5452 fn parse_template_literal(&mut self) -> Option<ExprId> {
5455 let head_tok = self.advance();
5456 let TokenKind::TemplateHead(head) = head_tok.kind else {
5457 unreachable!("template dispatch requires TemplateHead");
5458 };
5459 let start = head_tok.span.start;
5460 let mut parts: Vec<String> = vec![head];
5461 let mut exprs: Vec<ExprId> = Vec::new();
5462 let mut substitution_spans: Vec<Span> = Vec::new();
5463 let mut substitution_start = head_tok.span.end.saturating_sub(2);
5466 loop {
5467 let expr = self.parse_expression()?;
5468 exprs.push(expr);
5469 let closing = self.peek().span.start;
5470 let substitution_span = self.span(substitution_start, closing.saturating_add(1));
5471 match self.peek().kind.clone() {
5472 TokenKind::TemplateMiddle(s) => {
5473 let tok = self.advance();
5474 substitution_spans.push(substitution_span);
5475 substitution_start = tok.span.end.saturating_sub(2);
5476 parts.push(s);
5477 }
5478 TokenKind::TemplateTail(s) => {
5479 let tok = self.advance();
5480 substitution_spans.push(substitution_span);
5481 parts.push(s);
5482 return parse_arena_result(
5483 self.ast.try_push_expr(Expr {
5484 kind: ExprKind::TemplateLiteral {
5485 parts,
5486 exprs,
5487 substitution_spans,
5488 },
5489 span: self.span(start, tok.span.end),
5490 }),
5491 &mut self.fatal,
5492 );
5493 }
5494 _ => {
5495 self.error_at_peek("expected `}` to close template interpolation");
5496 return None;
5497 }
5498 }
5499 }
5500 }
5501
5502 fn parse_object_literal(&mut self) -> Option<ExprId> {
5503 let open = self.advance();
5504
5505 let mut members: Vec<ObjectLiteralMember> = Vec::new();
5506 if !matches!(self.peek().kind, TokenKind::RightBrace) {
5507 loop {
5508 if matches!(self.peek().kind, TokenKind::DotDotDot) {
5509 let dots = self.advance();
5510 let value = self.parse_expression()?;
5511 let value_span =
5512 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5513 members.push(ObjectLiteralMember::Spread {
5514 value,
5515 span: self.span(dots.span.start, value_span.end),
5516 });
5517 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
5518 self.advance();
5519 let key = self.parse_expression()?;
5520 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5521 self.error_at_peek("expected `]` after computed property key");
5522 return None;
5523 }
5524 self.advance();
5525 if !matches!(self.peek().kind, TokenKind::Colon) {
5526 self.error_at_peek("expected `:` after computed property key; computed methods are not supported");
5527 return None;
5528 }
5529 self.advance();
5530 let value = self.parse_expression()?;
5531 members.push(ObjectLiteralMember::Computed { key, value });
5532 } else {
5533 let field = self.parse_object_literal_field()?;
5534 if let Some(existing) = members.iter().find_map(|m| match m {
5537 ObjectLiteralMember::Field(f) if f.name.name == field.name.name => Some(f),
5538 _ => None,
5539 }) {
5540 self.diagnostics.push(Diagnostic {
5541 severity: Severity::Error,
5542 span: field.name.span,
5543 message: format!(
5544 "duplicate field `{}` in object literal",
5545 field.name.name
5546 ),
5547 help: vec![],
5548 notes: vec![(existing.name.span, "first defined here".into())],
5549 });
5550 }
5552 members.push(ObjectLiteralMember::Field(field));
5553 }
5554 if self.at_inserted_semicolon()
5555 && matches!(self.peek_at(1).kind, TokenKind::RightBrace)
5556 {
5557 self.advance();
5558 }
5559 match self.peek().kind {
5560 TokenKind::Comma => {
5561 self.advance();
5562 if matches!(self.peek().kind, TokenKind::RightBrace) {
5563 break;
5564 }
5565 }
5566 TokenKind::RightBrace => break,
5567 _ => {
5568 self.error_at_peek("expected `,` or `}`");
5569 return None;
5570 }
5571 }
5572 }
5573 }
5574
5575 if !matches!(self.peek().kind, TokenKind::RightBrace) {
5576 self.error_at_peek("expected `}`");
5577 return None;
5578 }
5579 let close = self.advance();
5580
5581 parse_arena_result(
5582 self.ast.try_push_expr(Expr {
5583 kind: ExprKind::ObjectLiteral { members },
5584 span: self.span(open.span.start, close.span.end),
5585 }),
5586 &mut self.fatal,
5587 )
5588 }
5589
5590 fn parse_object_literal_field(&mut self) -> Option<ObjectLiteralField> {
5591 let (name, shorthandable) = match self.peek().kind.clone() {
5592 TokenKind::Identifier => {
5593 let tok = self.advance();
5594 (self.ident_from_token(&tok), true)
5595 }
5596 kind if is_property_name(&kind) => {
5597 let tok = self.advance();
5598 (self.ident_from_token(&tok), false)
5599 }
5600 TokenKind::StringLiteral(_) => {
5601 let tok = self.advance();
5602 (self.property_ident_from_token(&tok), false)
5603 }
5604 _ => {
5605 self.error_at_peek("expected field name");
5606 return None;
5607 }
5608 };
5609
5610 if matches!(self.peek().kind, TokenKind::LeftParen) {
5616 let value = self.parse_method_shorthand_body(name.span)?;
5617 return Some(ObjectLiteralField { name, value });
5618 }
5619
5620 if !matches!(self.peek().kind, TokenKind::Colon) {
5621 if shorthandable {
5624 let value = parse_arena_result(
5625 self.ast.try_push_expr(Expr {
5626 kind: ExprKind::Identifier(name.clone()),
5627 span: name.span,
5628 }),
5629 &mut self.fatal,
5630 )?;
5631 return Some(ObjectLiteralField { name, value });
5632 }
5633 self.error_at_peek("expected `:` after field name");
5634 return None;
5635 }
5636 self.advance();
5637
5638 let value = self.parse_expression()?;
5639 Some(ObjectLiteralField { name, value })
5640 }
5641
5642 fn parse_method_shorthand_body(&mut self, name_span: Span) -> Option<ExprId> {
5645 self.advance();
5646
5647 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
5648 Vec::new()
5649 } else {
5650 self.parse_arrow_param_list()?
5651 };
5652 if !matches!(self.peek().kind, TokenKind::RightParen) {
5653 self.error_at_peek("expected `)`");
5654 return None;
5655 }
5656 self.advance();
5657
5658 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
5659 self.advance();
5660 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
5661 } else {
5662 (None, None)
5663 };
5664
5665 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
5666 self.error_at_peek("expected `{` to open the method body");
5667 return None;
5668 }
5669 let block = self.parse_outer_this_boundary(Self::parse_block)?;
5670 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
5671 .span
5672 .end;
5673
5674 parse_arena_result(
5675 self.ast.try_push_expr(Expr {
5676 kind: ExprKind::Arrow {
5677 params,
5678 return_type,
5679 type_predicate,
5680 body: ArrowBody::Block(block),
5681 },
5682 span: self.span(name_span.start, end),
5683 }),
5684 &mut self.fatal,
5685 )
5686 }
5687
5688 fn parse_array_literal(&mut self) -> Option<ExprId> {
5689 let open = self.advance();
5690
5691 let mut elements: Vec<ArrayLiteralElement> = Vec::new();
5692 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5693 loop {
5694 let element = if matches!(self.peek().kind, TokenKind::DotDotDot) {
5695 let dots = self.advance();
5696 let value = self.parse_expression()?;
5697 let value_span =
5698 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5699 ArrayLiteralElement::Spread {
5700 value,
5701 span: self.span(dots.span.start, value_span.end),
5702 }
5703 } else {
5704 ArrayLiteralElement::Value(self.parse_expression()?)
5705 };
5706 elements.push(element);
5707 match self.peek().kind {
5708 TokenKind::Comma => {
5709 self.advance();
5710 if matches!(self.peek().kind, TokenKind::RightBracket) {
5711 break;
5712 }
5713 }
5714 TokenKind::RightBracket => break,
5715 _ => {
5716 self.error_at_peek("expected `,` or `]`");
5717 return None;
5718 }
5719 }
5720 }
5721 }
5722
5723 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5724 self.error_at_peek("expected `]`");
5725 return None;
5726 }
5727 let close = self.advance();
5728
5729 parse_arena_result(
5730 self.ast.try_push_expr(Expr {
5731 kind: ExprKind::ArrayLiteral { elements },
5732 span: self.span(open.span.start, close.span.end),
5733 }),
5734 &mut self.fatal,
5735 )
5736 }
5737
5738 fn parse_paren(&mut self) -> Option<ExprId> {
5739 let open = self.advance();
5740 let inner = self.parse_expression()?;
5741 if !matches!(self.peek().kind, TokenKind::RightParen) {
5742 self.error_at_peek("expected `)`");
5743 return None;
5744 }
5745 let close = self.advance();
5746 let span = self.span(open.span.start, close.span.end);
5747 parse_arena_result(
5748 self.ast.try_push_expr(Expr {
5749 kind: ExprKind::Paren(inner),
5750 span,
5751 }),
5752 &mut self.fatal,
5753 )
5754 }
5755
5756 fn with_recursion_limit<T>(&mut self, parse: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
5757 if self.fatal.is_some() {
5758 return None;
5759 }
5760 if self.recursion_limit_span.is_some() || self.recursion_depth >= MAX_PARSE_DEPTH {
5761 self.stop_at_recursion_limit();
5762 return None;
5763 }
5764 self.recursion_depth += 1;
5765 let result = parse(self);
5766 self.recursion_depth -= 1;
5767 result
5768 }
5769
5770 fn stop_at_recursion_limit(&mut self) {
5771 self.recursion_limit_span.get_or_insert(self.peek().span);
5772 self.pos = self.tokens.len().saturating_sub(1);
5774 }
5775
5776 fn recover(&mut self) {
5777 while !self.is_at_eof() {
5778 match self.peek().kind {
5779 TokenKind::Semicolon => {
5780 self.advance();
5781 return;
5782 }
5783 TokenKind::RightBrace
5784 | TokenKind::Let
5785 | TokenKind::Const
5786 | TokenKind::Function
5787 | TokenKind::Export
5788 | TokenKind::If
5789 | TokenKind::While
5790 | TokenKind::Return
5791 | TokenKind::Try
5792 | TokenKind::Throw => return,
5793 _ => {
5794 self.advance();
5795 }
5796 }
5797 }
5798 }
5799
5800 fn peek(&self) -> &Token {
5801 self.tokens.get(self.pos).unwrap_or(&self.eof)
5802 }
5803
5804 fn peek_at(&self, offset: usize) -> &Token {
5805 self.pos
5806 .checked_add(offset)
5807 .and_then(|index| self.tokens.get(index))
5808 .unwrap_or(&self.eof)
5809 }
5810
5811 fn at_inserted_semicolon(&self) -> bool {
5813 let token = self.peek();
5814 matches!(token.kind, TokenKind::Semicolon) && token.span.start == token.span.end
5815 }
5816
5817 fn advance(&mut self) -> Token {
5818 let tok = self.peek().clone();
5819 if self.pos < self.tokens.len().saturating_sub(1) {
5820 self.pos += 1;
5821 }
5822 tok
5823 }
5824
5825 fn is_at_eof(&self) -> bool {
5826 self.fatal.is_some() || matches!(self.peek().kind, TokenKind::Eof)
5827 }
5828
5829 fn line_break_before_peek(&self) -> bool {
5831 self.source_has_line_break(self.prev_token_end(), self.peek().span.start)
5832 }
5833
5834 fn line_break_after_peek(&self) -> bool {
5836 self.source_has_line_break(self.peek().span.end, self.peek_at(1).span.start)
5837 }
5838
5839 fn source_has_line_break(&self, start: u32, end: u32) -> bool {
5842 self.source
5843 .get(start as usize..end as usize)
5844 .is_some_and(|gap| gap.contains(['\n', '\r']))
5845 }
5846
5847 fn at_statement_end(&self) -> bool {
5852 matches!(self.peek().kind, TokenKind::Semicolon)
5853 || (self.last_as_type_end == Some(self.prev_token_end())
5854 && self.line_break_before_peek())
5855 }
5856
5857 fn finish_statement(&mut self) -> u32 {
5860 if matches!(self.peek().kind, TokenKind::Semicolon) {
5861 return self.advance().span.end;
5862 }
5863 self.prev_token_end()
5864 }
5865
5866 fn prev_token_end(&self) -> u32 {
5869 self.tokens
5870 .get(self.pos.saturating_sub(1))
5871 .unwrap_or(&self.eof)
5872 .span
5873 .end
5874 }
5875
5876 fn take_leading_doc(&mut self) -> Option<crate::DocComment> {
5877 let parsed = self
5878 .peek()
5879 .leading_doc
5880 .as_ref()
5881 .map(crate::parse_doc_comment)
5882 .transpose();
5883 match parsed {
5884 Ok(doc) => doc,
5885 Err(error) => {
5886 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
5887 None
5888 }
5889 }
5890 }
5891
5892 fn parse_doc(&mut self, raw: &crate::RawDoc) -> Option<crate::DocComment> {
5893 match crate::parse_doc_comment(raw) {
5894 Ok(doc) => Some(doc),
5895 Err(error) => {
5896 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
5897 None
5898 }
5899 }
5900 }
5901
5902 fn error_at(&mut self, span: Span, message: impl Into<String>) {
5903 self.diagnostics.push(Diagnostic {
5904 severity: Severity::Error,
5905 span,
5906 message: message.into(),
5907 help: vec![],
5908 notes: vec![],
5909 });
5910 }
5911
5912 fn reject_loose_nullish_comparison(
5915 &mut self,
5916 op: BinOp,
5917 lhs: ExprId,
5918 rhs: ExprId,
5919 ) -> Option<()> {
5920 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5921 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
5922 let (nullish, operand, nullish_first) =
5923 match (self.nullish_operand(lhs)?, self.nullish_operand(rhs)?) {
5924 (Some(left), Some(right)) => {
5925 self.reject_nullish_against_nullish(op, lhs_span, rhs_span, [left, right]);
5926 return Some(());
5927 }
5928 (None, Some(nullish)) => (nullish, lhs, false),
5929 (Some(nullish), None) => (nullish, rhs, true),
5930 (None, None) => return Some(()),
5931 };
5932 let comparison = LooseComparison {
5933 nullish,
5934 nullish_first,
5935 negated: op == BinOp::NotEq,
5936 };
5937 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5938 let x = self.single_line_source(operand_span).unwrap_or("x");
5939 let strict = self.strict_suggestion(&comparison, x);
5940 let either = self.either_suggestion(&comparison, operand, x)?;
5941 let literal = comparison.nullish.literal.text();
5942 let written = self
5944 .single_line_source(self.span(lhs_span.start, rhs_span.end))
5945 .map_or_else(
5946 || {
5947 let written_op = if comparison.negated { "!=" } else { "==" };
5948 comparison.ordered(x, written_op, literal)
5949 },
5950 str::to_string,
5951 );
5952 self.error_at_with_help(
5953 self.span(lhs_span.start, rhs_span.end),
5954 format!(
5955 "`{written}` does not also match `{}`: `==` compares like `===`",
5956 comparison.nullish.literal.other().text()
5957 ),
5958 vec![
5959 either,
5960 format!("to test for `{literal}` alone, write `{strict}`"),
5961 ],
5962 );
5963 Some(())
5964 }
5965
5966 fn reject_nullish_against_nullish(
5970 &mut self,
5971 op: BinOp,
5972 lhs: Span,
5973 rhs: Span,
5974 sides: [NullishOperand; 2],
5975 ) {
5976 let span = self.span(lhs.start, rhs.end);
5977 let subject = self.single_line_source(span).map_or_else(
5980 || "this comparison".to_string(),
5981 |written| format!("`{written}`"),
5982 );
5983 let value = if op == BinOp::NotEq { "false" } else { "true" };
5984 let effects: Vec<String> = sides
5985 .iter()
5986 .filter_map(|side| side.effect)
5987 .map(|effect| {
5988 self.single_line_source(effect.operand).map_or_else(
5989 || "the `void` operand's expression".to_string(),
5990 |statement| format!("`{statement};`"),
5991 )
5992 })
5993 .collect();
5994 let help = if effects.is_empty() {
5995 format!("in TypeScript this is always `{value}`; write `{value}`")
5996 } else {
5997 format!(
5998 "in TypeScript this is always `{value}`; run {} {}, then use `{value}`",
5999 effects.join(" and "),
6000 if effects.len() > 1 {
6001 "each as its own statement"
6002 } else {
6003 "as its own statement"
6004 }
6005 )
6006 };
6007 self.error_at_with_help(
6008 span,
6009 format!("{subject} compares two nullish values: `==` compares like `===`"),
6010 vec![help],
6011 );
6012 }
6013
6014 fn strict_suggestion(&self, comparison: &LooseComparison, x: &str) -> String {
6017 let eq = comparison.strict_op();
6018 match comparison.nullish.effect {
6019 Some(effect) => {
6020 let void = self.single_line_source(effect.void).unwrap_or("void expr");
6021 comparison.ordered(x, eq, void)
6022 }
6023 None => format!("{x} {eq} {}", comparison.nullish.literal.text()),
6024 }
6025 }
6026
6027 fn either_suggestion(
6031 &mut self,
6032 comparison: &LooseComparison,
6033 operand: ExprId,
6034 x: &str,
6035 ) -> Option<String> {
6036 let mut steps = Vec::new();
6037 let tested = if self.repeats_without_effect(operand)? {
6038 x
6039 } else {
6040 steps.push(format!("bind `{x}` to a `const` (`const value = {x};`)"));
6041 "value"
6042 };
6043 if let Some(effect) = comparison.nullish.effect {
6044 let statement = self.single_line_source(effect.operand).unwrap_or("expr");
6045 let step = format!("run `{statement};` as its own statement");
6046 if comparison.nullish_first {
6047 steps.insert(0, step);
6048 } else {
6049 steps.push(step);
6050 }
6051 }
6052 let eq = comparison.strict_op();
6053 let joiner = if comparison.negated { "&&" } else { "||" };
6054 let both = format!("{tested} {eq} null {joiner} {tested} {eq} undefined");
6055 Some(if steps.is_empty() {
6056 format!("to test for either, write `{both}`")
6057 } else {
6058 format!(
6059 "to test for either, first {}, then write `{both}`",
6060 steps.join(", then ")
6061 )
6062 })
6063 }
6064
6065 fn repeats_without_effect(&mut self, id: ExprId) -> Option<bool> {
6068 let unwrapped = self.unparenthesized(id)?;
6069 let expr = parse_arena_result(self.ast.try_expr(unwrapped), &mut self.fatal)?;
6070 if is_literal(&expr.kind) {
6071 return Some(true);
6072 }
6073 if self
6074 .nullish_operand(unwrapped)?
6075 .is_some_and(|nullish| nullish.effect.is_none())
6076 {
6077 return Some(true);
6078 }
6079 self.is_reference_path(id)
6080 }
6081
6082 fn nullish_operand(&mut self, id: ExprId) -> Option<Option<NullishOperand>> {
6084 let id = self.unparenthesized(id)?;
6085 let expr = parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?;
6086 let span = expr.span;
6087 Some(match &expr.kind {
6088 ExprKind::Null => Some(NullishOperand {
6089 literal: Nullish::Null,
6090 effect: None,
6091 }),
6092 ExprKind::Identifier(ident) if ident.name == "undefined" => Some(NullishOperand {
6093 literal: Nullish::Undefined,
6094 effect: None,
6095 }),
6096 ExprKind::Void { operand } => {
6097 let operand_id = self.unparenthesized(*operand)?;
6098 let operand =
6099 parse_arena_result(self.ast.try_expr(operand_id), &mut self.fatal)?.clone();
6100 let effect_free =
6102 is_literal(&operand.kind) || self.is_reference_path(operand_id)?;
6103 let effect = (!effect_free).then_some(VoidEffect {
6104 void: span,
6105 operand: operand.span,
6106 });
6107 Some(NullishOperand {
6108 literal: Nullish::Undefined,
6109 effect,
6110 })
6111 }
6112 _ => None,
6113 })
6114 }
6115
6116 fn is_reference_path(&mut self, id: ExprId) -> Option<bool> {
6119 let mut id = id;
6120 loop {
6121 match &parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6122 ExprKind::Identifier(_) | ExprKind::This => return Some(true),
6123 ExprKind::FieldAccess { receiver, .. } => id = *receiver,
6124 ExprKind::Paren(inner) => id = *inner,
6125 _ => return Some(false),
6126 }
6127 }
6128 }
6129
6130 fn unparenthesized(&mut self, id: ExprId) -> Option<ExprId> {
6131 let mut id = id;
6132 loop {
6133 match parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6134 ExprKind::Paren(inner) => id = inner,
6135 _ => return Some(id),
6136 }
6137 }
6138 }
6139
6140 fn single_line_source(&self, span: Span) -> Option<&'a str> {
6141 self.source
6142 .get(span.start as usize..span.end as usize)
6143 .filter(|text| !text.contains('\n'))
6144 }
6145
6146 fn error_at_with_help(&mut self, span: Span, message: impl Into<String>, help: Vec<String>) {
6147 self.diagnostics.push(Diagnostic {
6148 severity: Severity::Error,
6149 span,
6150 message: message.into(),
6151 help,
6152 notes: vec![],
6153 });
6154 }
6155
6156 fn invariant_failure<T>(&mut self, message: &str) -> Option<T> {
6157 self.fatal.get_or_insert_with(|| CompilerFailure::Internal {
6158 stage: CompilerStage::Parse,
6159 span: None,
6160 message: message.into(),
6161 });
6162 self.pos = self.tokens.len();
6163 None
6164 }
6165
6166 fn error_at_peek(&mut self, message: impl Into<String>) {
6167 let span = self.peek().span;
6168 self.error_at(span, message);
6169 }
6170
6171 fn error_at_peek_with_help(&mut self, message: impl Into<String>, help: Vec<String>) {
6172 let span = self.peek().span;
6173 self.error_at_with_help(span, message, help);
6174 }
6175
6176 fn error_count(&self) -> usize {
6177 self.diagnostics
6178 .iter()
6179 .filter(|d| d.severity == Severity::Error)
6180 .count()
6181 }
6182}
6183
6184fn is_property_name(kind: &TokenKind) -> bool {
6185 matches!(
6186 kind,
6187 TokenKind::Identifier
6188 | TokenKind::BooleanLiteral(_)
6189 | TokenKind::NullLiteral
6190 | TokenKind::Let
6191 | TokenKind::Const
6192 | TokenKind::Function
6193 | TokenKind::If
6194 | TokenKind::Else
6195 | TokenKind::While
6196 | TokenKind::Do
6197 | TokenKind::For
6198 | TokenKind::Break
6199 | TokenKind::Continue
6200 | TokenKind::Return
6201 | TokenKind::Switch
6202 | TokenKind::Case
6203 | TokenKind::Default
6204 | TokenKind::Void
6205 | TokenKind::Interface
6206 | TokenKind::Enum
6207 | TokenKind::In
6208 | TokenKind::Typeof
6209 | TokenKind::Import
6210 | TokenKind::Export
6211 | TokenKind::New
6212 | TokenKind::Try
6213 | TokenKind::Catch
6214 | TokenKind::Finally
6215 | TokenKind::Throw
6216 | TokenKind::Class
6217 | TokenKind::Extends
6218 | TokenKind::Implements
6219 | TokenKind::Super
6220 | TokenKind::This
6221 )
6222}
6223
6224fn is_reserved_identifier_word(kind: &TokenKind) -> bool {
6225 matches!(
6226 kind,
6227 TokenKind::BooleanLiteral(_)
6228 | TokenKind::NullLiteral
6229 | TokenKind::Let
6230 | TokenKind::Const
6231 | TokenKind::Function
6232 | TokenKind::If
6233 | TokenKind::Else
6234 | TokenKind::While
6235 | TokenKind::Do
6236 | TokenKind::For
6237 | TokenKind::Break
6238 | TokenKind::Continue
6239 | TokenKind::Return
6240 | TokenKind::Switch
6241 | TokenKind::Case
6242 | TokenKind::Default
6243 | TokenKind::Export
6244 | TokenKind::Void
6245 | TokenKind::Interface
6246 | TokenKind::Enum
6247 | TokenKind::In
6248 | TokenKind::Typeof
6249 | TokenKind::Import
6250 | TokenKind::New
6251 | TokenKind::Try
6252 | TokenKind::Catch
6253 | TokenKind::Finally
6254 | TokenKind::Throw
6255 | TokenKind::Class
6256 | TokenKind::Extends
6257 | TokenKind::Implements
6258 | TokenKind::Super
6259 | TokenKind::This
6260 )
6261}
6262
6263#[derive(Clone, Copy, PartialEq, Eq)]
6267enum SpecifierList {
6268 Import,
6269 Export,
6270}
6271
6272impl SpecifierList {
6273 fn noun(self) -> &'static str {
6274 match self {
6275 SpecifierList::Import => "import",
6276 SpecifierList::Export => "export",
6277 }
6278 }
6279}
6280
6281const STRICT_MODE_RESERVED_WORDS: [&str; 6] = [
6285 "package",
6286 "private",
6287 "protected",
6288 "public",
6289 "static",
6290 "yield",
6291];
6292
6293fn reserved_keyword_rename_example(keyword: &str) -> &'static str {
6294 match keyword {
6295 "default" => "defaultValue",
6296 _ => "valueName",
6297 }
6298}
6299
6300fn mixed_logical(prev: BinOp, next: BinOp) -> bool {
6301 let prev_logical = matches!(prev, BinOp::Or | BinOp::And);
6302 let next_logical = matches!(next, BinOp::Or | BinOp::And);
6303 let prev_nullish = matches!(prev, BinOp::NullishCoalesce);
6304 let next_nullish = matches!(next, BinOp::NullishCoalesce);
6305 (prev_logical && next_nullish) || (prev_nullish && next_logical)
6306}
6307
6308fn mixed_logical_help(prev: BinOp, next: BinOp) -> String {
6311 let symbol = |op: BinOp| if op == BinOp::And { "&&" } else { "||" };
6312 let (first, second) = if prev == BinOp::NullishCoalesce {
6313 ("??", symbol(next))
6314 } else {
6315 (symbol(prev), "??")
6316 };
6317 format!(
6318 "add parentheses to choose the grouping: `(a {first} b) {second} c` or `a {first} (b {second} c)`"
6319 )
6320}
6321
6322const LOGICAL_AND_PREC: u8 = 2;
6323
6324#[derive(Clone, Copy)]
6326struct NullishOperand {
6327 literal: Nullish,
6328 effect: Option<VoidEffect>,
6330}
6331
6332#[derive(Clone, Copy, PartialEq, Eq)]
6333enum Nullish {
6334 Null,
6335 Undefined,
6336}
6337
6338impl Nullish {
6339 fn text(self) -> &'static str {
6340 match self {
6341 Nullish::Null => "null",
6342 Nullish::Undefined => "undefined",
6343 }
6344 }
6345
6346 fn other(self) -> Self {
6348 match self {
6349 Nullish::Null => Nullish::Undefined,
6350 Nullish::Undefined => Nullish::Null,
6351 }
6352 }
6353}
6354
6355struct LooseComparison {
6357 nullish: NullishOperand,
6358 nullish_first: bool,
6360 negated: bool,
6362}
6363
6364impl LooseComparison {
6365 fn strict_op(&self) -> &'static str {
6366 if self.negated { "!==" } else { "===" }
6367 }
6368
6369 fn ordered(&self, x: &str, op: &str, nullish: &str) -> String {
6371 if self.nullish_first {
6372 format!("{nullish} {op} {x}")
6373 } else {
6374 format!("{x} {op} {nullish}")
6375 }
6376 }
6377}
6378
6379#[derive(Clone, Copy)]
6380struct VoidEffect {
6381 void: Span,
6383 operand: Span,
6385}
6386
6387fn is_literal(kind: &ExprKind) -> bool {
6388 matches!(
6389 kind,
6390 ExprKind::Number(_)
6391 | ExprKind::BigInt(_)
6392 | ExprKind::String(_)
6393 | ExprKind::Boolean(_)
6394 | ExprKind::Null
6395 )
6396}
6397
6398struct ParamOrder<'p> {
6400 optional: bool,
6401 required: bool,
6403 name: Option<&'p str>,
6405 span: Span,
6406}
6407
6408impl<'p> ParamOrder<'p> {
6409 fn of_param(param: &'p ParamDecl) -> Self {
6410 let (name, span) = match ¶m.pattern {
6411 Some(binding) => (None, binding.span()),
6412 None => (Some(param.name.name.as_str()), param.name.span),
6413 };
6414 Self {
6415 optional: param.optional,
6416 required: !param.is_omittable() && !param.rest,
6417 name,
6418 span,
6419 }
6420 }
6421
6422 fn of_field(field: &'p TypeAnnotationField) -> Self {
6423 Self {
6424 optional: field.optional,
6425 required: !field.optional && !field.rest,
6426 name: Some(field.name.name.as_str()),
6427 span: field.name.span,
6428 }
6429 }
6430}
6431
6432struct BinaryToken {
6434 op: BinOp,
6435 precedence: u8,
6436 tokens: usize,
6437}
6438
6439struct ShiftToken {
6441 op: BinOp,
6442 tokens: usize,
6443 assignment: bool,
6445}
6446
6447impl ShiftToken {
6448 fn new(op: BinOp, tokens: usize, assignment: bool) -> Self {
6449 Self {
6450 op,
6451 tokens,
6452 assignment,
6453 }
6454 }
6455}
6456
6457const SHIFT_PRECEDENCE: u8 = 8;
6459
6460#[derive(Clone, Copy, PartialEq)]
6461enum TupleElementLabel {
6462 None,
6463 Required,
6465 Optional,
6467}
6468
6469fn peek_binop(kind: &TokenKind) -> Option<(BinOp, u8)> {
6470 Some(match kind {
6471 TokenKind::QuestionQuestion => (BinOp::NullishCoalesce, 0),
6472 TokenKind::PipePipe => (BinOp::Or, 1),
6473 TokenKind::AmpAmp => (BinOp::And, LOGICAL_AND_PREC),
6474 TokenKind::Pipe => (BinOp::BitOr, 3),
6475 TokenKind::Caret => (BinOp::BitXor, 4),
6476 TokenKind::Amp => (BinOp::BitAnd, 5),
6477 TokenKind::EqEqEq | TokenKind::EqEq => (BinOp::Eq, 6),
6478 TokenKind::BangEqEq | TokenKind::BangEq => (BinOp::NotEq, 6),
6479 TokenKind::LessThan => (BinOp::Lt, 7),
6480 TokenKind::GreaterThan => (BinOp::Gt, 7),
6481 TokenKind::LessEquals => (BinOp::Le, 7),
6482 TokenKind::GreaterEquals => (BinOp::Ge, 7),
6483 TokenKind::In => (BinOp::In, 7),
6484 TokenKind::Plus => (BinOp::Add, 9),
6487 TokenKind::Minus => (BinOp::Sub, 9),
6488 TokenKind::Star => (BinOp::Mul, 10),
6489 TokenKind::Slash => (BinOp::Div, 10),
6490 TokenKind::Percent => (BinOp::Rem, 10),
6491 TokenKind::StarStar => (BinOp::Pow, 11),
6493 _ => return None,
6494 })
6495}
6496
6497fn cannot_start_type(kind: &TokenKind) -> bool {
6501 matches!(
6502 kind,
6503 TokenKind::Dot
6504 | TokenKind::QuestionDot
6505 | TokenKind::LeftBracket
6506 | TokenKind::Bang
6507 | TokenKind::Semicolon
6508 | TokenKind::RightParen
6509 | TokenKind::RightBrace
6510 | TokenKind::Comma
6511 | TokenKind::Eof
6512 )
6513}
6514
6515fn right_operand_min_prec(op: BinOp, prec: u8) -> u8 {
6519 match op {
6520 BinOp::Pow => prec,
6521 BinOp::NullishCoalesce => LOGICAL_AND_PREC + 1,
6522 _ => prec + 1,
6523 }
6524}
6525
6526fn compound_op_for_token(kind: &TokenKind) -> Option<BinOp> {
6527 Some(match kind {
6528 TokenKind::PlusEquals => BinOp::Add,
6529 TokenKind::MinusEquals => BinOp::Sub,
6530 TokenKind::StarEquals => BinOp::Mul,
6531 TokenKind::SlashEquals => BinOp::Div,
6532 TokenKind::PercentEquals => BinOp::Rem,
6533 TokenKind::StarStarEquals => BinOp::Pow,
6534 TokenKind::AmpEquals => BinOp::BitAnd,
6535 TokenKind::PipeEquals => BinOp::BitOr,
6536 TokenKind::CaretEquals => BinOp::BitXor,
6537 _ => return None,
6538 })
6539}
6540
6541fn is_assign_lookahead(kind: &TokenKind) -> bool {
6542 matches!(kind, TokenKind::Equals) || compound_op_for_token(kind).is_some()
6543}
6544
6545fn parse_arena_result<T>(
6548 result: Result<T, crate::arena::ArenaError>,
6549 fatal: &mut Option<CompilerFailure>,
6550) -> Option<T> {
6551 match result {
6552 Ok(value) => Some(value),
6553 Err(error) => {
6554 fatal.get_or_insert_with(|| error.into_compiler_failure(CompilerStage::Parse));
6555 None
6556 }
6557 }
6558}
6559
6560fn is_rest_array_annotation(ty: &crate::TypeAnnotation) -> bool {
6562 use crate::ast::TypeAnnotationKind;
6563 match &ty.kind {
6564 TypeAnnotationKind::Array(_) => true,
6565 TypeAnnotationKind::Readonly(inner) => matches!(inner.kind, TypeAnnotationKind::Array(_)),
6566 _ => false,
6567 }
6568}
6569
6570#[cfg(test)]
6571mod tests {
6572 use super::{MAX_ERRORS, MAX_PARSE_DEPTH, Parser, parse};
6573 use crate::source::Sources;
6574 use crate::{Asi, Ast, ExprKind, FileId, ImportKind, StmtKind, Token, TokenKind, diagnostics};
6575
6576 const F: FileId = FileId(0);
6577
6578 #[test]
6579 fn malformed_direct_token_streams_return_typed_failures() {
6580 use crate::compiler_error::CompilerFailure;
6581 for tokens in [
6582 Vec::new(),
6583 vec![Token::new(
6584 TokenKind::Identifier,
6585 crate::Span::new(F, 0, 2).unwrap(),
6586 )],
6587 vec![
6588 Token::new(
6589 TokenKind::Identifier,
6590 crate::Span {
6591 file: F,
6592 start: 1,
6593 end: 2,
6594 },
6595 ),
6596 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
6597 ],
6598 vec![
6599 Token::new(TokenKind::Eof, crate::Span::at(F)),
6600 Token::new(TokenKind::Eof, crate::Span::at(F)),
6601 ],
6602 vec![
6603 Token::new(
6604 TokenKind::Identifier,
6605 crate::Span {
6606 file: F,
6607 start: 2,
6608 end: 0,
6609 },
6610 ),
6611 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
6612 ],
6613 vec![Token::new(TokenKind::Eof, crate::Span::at(FileId(9)))],
6614 ] {
6615 let error = super::parse_checked("é", tokens, F).expect_err("invalid token metadata");
6616 assert!(matches!(
6617 error.fatal,
6618 Some(CompilerFailure::Internal { .. })
6619 ));
6620 }
6621 }
6622
6623 #[test]
6624 fn malformed_documentation_metadata_returns_a_fatal_error() {
6625 let source = "/** hello */ function main(): void {}";
6626 let valid = tokens_of(source);
6627 for doc in [
6628 crate::RawDoc {
6629 text: "/** hi */".into(),
6630 span: crate::Span {
6631 file: F,
6632 start: u32::MAX,
6633 end: u32::MAX,
6634 },
6635 },
6636 crate::RawDoc {
6637 text: "/** different */".into(),
6638 span: crate::Span::new(F, 0, 12).unwrap(),
6639 },
6640 crate::RawDoc {
6641 text: "/** hello */".into(),
6642 span: crate::Span::new(FileId(9), 0, 12).unwrap(),
6643 },
6644 ] {
6645 let mut tokens = valid.clone();
6646 tokens[0].leading_doc = Some(doc);
6647 let error = super::parse_checked(source, tokens, F).expect_err("invalid doc metadata");
6648 assert!(error.to_string().contains("documentation metadata"));
6649 }
6650 assert!(super::parse_checked(source, valid, F).is_ok());
6651 }
6652
6653 #[test]
6654 fn invalid_parser_dispatch_survives_diagnostic_rollback() {
6655 let mut parser = parser_from_source("let x = 1;");
6656 assert!(parser.try_parse_type_args_only().is_none());
6657 parser.diagnostics.clear();
6658 assert!(parser.fatal.is_some());
6659 assert!(parser.parse_expression().is_none());
6660 }
6661
6662 #[test]
6663 fn invalid_arena_read_stops_parser_and_survives_diagnostic_rollback() {
6664 use crate::{
6665 ExprId,
6666 compiler_error::{CompilerFailure, CompilerStage},
6667 };
6668 let mut parser = parser_from_source("let x = 1;");
6669 assert!(parser.statement_kind_for(ExprId(u32::MAX)).is_none());
6670 assert!(matches!(
6671 parser.fatal,
6672 Some(CompilerFailure::Internal {
6673 stage: CompilerStage::Parse,
6674 span: None,
6675 ..
6676 })
6677 ));
6678 parser.diagnostics.clear();
6679 let position = parser.pos;
6680 assert!(parser.parse_statement().is_none());
6681 parser.recover();
6682 assert_eq!(parser.pos, position);
6683 assert!(parser.ast.expr_ids().unwrap().next().is_none());
6684 }
6685
6686 fn tokens_of(source: &str) -> Vec<Token> {
6687 let mut asi = Asi::new(source, crate::FileId(0));
6688 let mut tokens = Vec::new();
6689 loop {
6690 let tok = asi.next_token();
6691 let is_eof = matches!(tok.kind, TokenKind::Eof);
6692 tokens.push(tok);
6693 if is_eof {
6694 break;
6695 }
6696 }
6697 let diags = asi.into_diagnostics();
6698 assert!(diags.is_empty(), "unexpected lexer diagnostics: {diags:?}");
6699 tokens
6700 }
6701
6702 fn parse_str(source: &str) -> (Ast, Vec<crate::Diagnostic>) {
6703 parse(source, tokens_of(source), crate::FileId(0))
6704 }
6705
6706 fn parser_from_source(source: &str) -> Parser<'_> {
6707 Parser {
6708 source,
6709 tokens: tokens_of(source),
6710 file: F,
6711 pos: 0,
6712 ast: Ast::new(),
6713 diagnostics: Vec::new(),
6714 block_depth: 0,
6715 class_member_body_depth: 0,
6716 function_expression_body_depth: 0,
6717 recursion_depth: 0,
6718 recursion_limit_span: None,
6719 last_as_type_end: None,
6720 eof: Token::new(
6721 TokenKind::Eof,
6722 crate::Span::new(F, source.len() as u32, source.len() as u32).unwrap(),
6723 ),
6724 fatal: None,
6725 }
6726 }
6727
6728 #[test]
6729 fn recursion_budget_accepts_boundary_and_restores_depth() {
6730 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH - 1));
6731 let mut parser = parser_from_source(&source);
6732 assert!(parser.parse_unary().is_some());
6733 assert_eq!(parser.recursion_depth, 0);
6734 assert!(parser.recursion_limit_span.is_none());
6735
6736 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH));
6737 let mut parser = parser_from_source(&source);
6738 assert!(parser.parse_unary().is_none());
6739 assert_eq!(parser.recursion_depth, 0);
6740 assert!(parser.recursion_limit_span.is_some());
6741 }
6742
6743 #[test]
6744 fn parse_empty_input() {
6745 let (ast, diags) = parse_str("");
6746 assert!(diags.is_empty());
6747 assert!(ast.top_level.is_empty());
6748 }
6749
6750 #[test]
6751 fn any_type_is_rejected() {
6752 for src in [
6755 "function main(): void { const x: any = 1; }",
6756 "function main(): void { const x = 1 as any; }",
6757 "function main(): void { const x: any[] = []; }",
6758 ] {
6759 let (_ast, diags) = parse_str(src);
6760 assert!(
6761 diags
6762 .iter()
6763 .any(|d| d.message.contains("`any` is not supported")),
6764 "expected an `any` rejection for {src:?}, got: {diags:?}",
6765 );
6766 assert!(
6767 diags
6768 .iter()
6769 .any(|d| d.help.iter().any(|h| h.contains("`unknown`"))),
6770 "expected the fix-shape help for {src:?}, got: {diags:?}",
6771 );
6772 }
6773 }
6774
6775 #[test]
6776 fn any_catch_binding_help_suggests_catch_forms() {
6777 let (_ast, diags) = parse_str("function main(): void { try { } catch (e: any) { } }");
6780 let any_diags: Vec<_> = diags
6781 .iter()
6782 .filter(|d| d.message.contains("`any` is not supported"))
6783 .collect();
6784 assert_eq!(any_diags.len(), 1, "got: {diags:?}");
6785 assert!(
6786 any_diags[0].help.iter().any(|h| h.contains("`catch (e)`")),
6787 "got: {diags:?}"
6788 );
6789 assert!(
6790 !any_diags[0].help.iter().any(|h| h.contains("`unknown`")),
6791 "got: {diags:?}"
6792 );
6793 }
6794
6795 #[test]
6796 fn arrow_return_type_scanner_matches_type_grammar() {
6797 for ty in [
6802 "number",
6803 "void",
6804 "null",
6805 "ns.Foo",
6806 "Map<string, number>",
6807 "ns.Foo<string>[]",
6808 "number[]",
6809 "[number, number]",
6810 "[number, [string, boolean]]",
6811 "[number, string][]",
6812 "{ a: number }",
6813 "{ a: number }[]",
6814 "\"on\" | \"off\"",
6815 "1 | 2",
6816 "Point | null",
6817 "| number | string",
6818 "(n: number) => number",
6819 "(n: number) => [number, number]",
6820 "readonly number[]",
6821 "readonly number[][]",
6822 "readonly [number, string]",
6823 "readonly number[] | null",
6824 "[first: number, second: string]",
6825 "[new: number, string]",
6826 "keyof { a: number }",
6827 ] {
6828 let annotation = format!("function f(): void {{ const a: {ty} = x; }}");
6829 let (_ast, diags) = parse_str(&annotation);
6830 assert!(
6831 diags.is_empty(),
6832 "annotation grammar rejected {ty:?}: {diags:?}",
6833 );
6834
6835 let arrow = format!("function f(): void {{ const g = (): {ty} => x; }}");
6836 let (_ast, diags) = parse_str(&arrow);
6837 assert!(
6838 diags.is_empty(),
6839 "arrow lookahead rejected return type {ty:?}: {diags:?}",
6840 );
6841 }
6842
6843 let (_ast, diags) =
6845 parse_str("function f(): void { const g = (v: unknown): v is string => x; }");
6846 assert!(
6847 diags.is_empty(),
6848 "arrow lookahead rejected predicate return: {diags:?}",
6849 );
6850 }
6851
6852 #[test]
6853 fn bare_semicolons_are_silent_empty_statements() {
6854 let (ast, diags) = parse_str(";");
6855 assert!(diags.is_empty());
6856 assert!(ast.top_level.is_empty());
6857 }
6858
6859 #[test]
6860 fn parse_single_non_expression_token_errors() {
6861 let (ast, diags) = parse_str("}");
6862 assert_eq!(diags.len(), 1);
6863 assert_eq!(diags[0].message, "expected expression");
6864 assert!(ast.top_level.is_empty());
6865 }
6866
6867 #[test]
6868 fn recovery_across_many_bad_starts() {
6869 let (ast, diags) = parse_str("} } } x;");
6870 assert_eq!(diags.len(), 3);
6871 for d in &diags {
6872 assert_eq!(d.message, "expected expression");
6873 }
6874 assert_eq!(ast.top_level.len(), 1);
6875 }
6876
6877 #[test]
6878 fn recovery_consumes_semicolon() {
6879 let mut p = parser_from_source("foo ;");
6880 p.error_at_peek("test");
6881 p.recover();
6882 assert!(matches!(
6883 p.peek().kind,
6884 TokenKind::Semicolon | TokenKind::Eof
6885 ));
6886 assert!(matches!(p.peek().kind, TokenKind::Eof));
6887 }
6888
6889 #[test]
6890 fn recovery_stops_at_right_brace() {
6891 let mut p = parser_from_source("foo }");
6892 p.error_at_peek("test");
6893 p.recover();
6894 assert!(matches!(p.peek().kind, TokenKind::RightBrace));
6895 }
6896
6897 #[test]
6898 fn recovery_stops_at_let_keyword() {
6899 let mut p = parser_from_source("foo let x");
6900 p.error_at_peek("test");
6901 p.recover();
6902 assert!(matches!(p.peek().kind, TokenKind::Let));
6903 }
6904
6905 #[test]
6906 fn recovery_stops_at_function_keyword() {
6907 let mut p = parser_from_source("foo function bar");
6908 p.error_at_peek("test");
6909 p.recover();
6910 assert!(matches!(p.peek().kind, TokenKind::Function));
6911 }
6912
6913 #[test]
6914 fn error_cap_at_20() {
6915 let source = "} ".repeat(25);
6916 let (_, diags) = parse_str(&source);
6917 assert_eq!(diags.len(), MAX_ERRORS);
6918 }
6919
6920 #[test]
6921 fn driver_makes_progress_past_unmatched_brace() {
6922 let (ast, diags) = parse_str("} x;");
6923 assert_eq!(diags.len(), 1);
6924 assert_eq!(diags[0].message, "expected expression");
6925 assert_eq!(ast.top_level.len(), 1);
6926 }
6927
6928 #[test]
6929 fn snapshot_multi_error_render() {
6930 let source = "foo bar;\nbaz qux;\nhello world;";
6931 let (_, diags) = parse_str(source);
6932 let (sources, _) = Sources::single("script.subm", source).unwrap();
6933 let rendered: String = diags
6934 .iter()
6935 .map(|d| diagnostics::render(d, &sources))
6936 .collect::<Vec<_>>()
6937 .join("\n");
6938 insta::assert_snapshot!(rendered);
6939 }
6940
6941 fn single_stmt(ast: &Ast) -> &crate::Stmt {
6942 assert_eq!(ast.top_level.len(), 1);
6943 ast.try_stmt(ast.top_level[0]).unwrap()
6944 }
6945
6946 fn expr_of_single_stmt(ast: &Ast) -> &crate::Expr {
6947 let stmt = single_stmt(ast);
6948 match stmt.kind {
6949 crate::StmtKind::Expr(id) => ast.try_expr(id).unwrap(),
6950 _ => panic!("expected expression statement, got {:?}", stmt.kind),
6951 }
6952 }
6953
6954 #[test]
6955 fn parse_number_literal() {
6956 let (ast, diags) = parse_str("42;");
6957 assert!(diags.is_empty());
6958 let expr = expr_of_single_stmt(&ast);
6959 assert_eq!(expr.kind, crate::ExprKind::Number(42.0));
6960 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
6961 }
6962
6963 #[test]
6964 fn parse_string_literal() {
6965 let (ast, diags) = parse_str(r#""s";"#);
6966 assert!(diags.is_empty());
6967 let expr = expr_of_single_stmt(&ast);
6968 assert_eq!(expr.kind, crate::ExprKind::String("s".to_string()));
6969 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
6970 }
6971
6972 #[test]
6973 fn parse_boolean_true() {
6974 let (ast, diags) = parse_str("true;");
6975 assert!(diags.is_empty());
6976 let expr = expr_of_single_stmt(&ast);
6977 assert_eq!(expr.kind, crate::ExprKind::Boolean(true));
6978 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
6979 }
6980
6981 #[test]
6982 fn parse_boolean_false() {
6983 let (ast, diags) = parse_str("false;");
6984 assert!(diags.is_empty());
6985 let expr = expr_of_single_stmt(&ast);
6986 assert_eq!(expr.kind, crate::ExprKind::Boolean(false));
6987 assert_eq!(expr.span, crate::Span::new(F, 0, 5).unwrap());
6988 }
6989
6990 #[test]
6991 fn parse_null_literal() {
6992 let (ast, diags) = parse_str("null;");
6993 assert!(diags.is_empty());
6994 let expr = expr_of_single_stmt(&ast);
6995 assert_eq!(expr.kind, crate::ExprKind::Null);
6996 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
6997 }
6998
6999 #[test]
7000 fn parse_identifier() {
7001 let (ast, diags) = parse_str("x;");
7002 assert!(diags.is_empty());
7003 let expr = expr_of_single_stmt(&ast);
7004 assert!(matches!(expr.kind, crate::ExprKind::Identifier(_)));
7005 assert_eq!(expr.span, crate::Span::new(F, 0, 1).unwrap());
7006 }
7007
7008 #[test]
7009 fn reserved_keywords_remain_invalid_declaration_names() {
7010 let (_ast, diags) = parse_str("function default(): void {}");
7011 assert_eq!(diags.len(), 1, "{diags:#?}");
7012 assert_eq!(
7013 diags[0].message,
7014 "`default` is a reserved keyword and can't be used as a name"
7015 );
7016 assert!(
7017 diags[0]
7018 .help
7019 .iter()
7020 .any(|h| h == "rename it, for example: `defaultValue`"),
7021 "expected rename help, got: {diags:?}"
7022 );
7023 }
7024
7025 #[test]
7026 fn reserved_keywords_remain_invalid_parameter_names() {
7027 let (_ast, diags) = parse_str("function f(default: string): void {}");
7028 assert_eq!(diags.len(), 1, "{diags:#?}");
7029 assert_eq!(
7030 diags[0].message,
7031 "`default` is a reserved keyword and can't be used as a name"
7032 );
7033 assert!(
7034 diags[0]
7035 .help
7036 .iter()
7037 .any(|h| h == "rename it, for example: `defaultValue`"),
7038 "expected rename help, got: {diags:?}"
7039 );
7040 }
7041
7042 #[test]
7043 fn contextual_keywords_are_valid_names() {
7044 for src in [
7045 "const type = \"x\";",
7046 "const from = 1;",
7047 "let of = 2;",
7048 "const as = 3;",
7049 "const is = 4;",
7050 "function f(from: string): void {}",
7051 ] {
7052 let (_ast, diags) = parse_str(src);
7053 assert!(
7054 diags.is_empty(),
7055 "unexpected diagnostics for {src:?}: {diags:#?}"
7056 );
7057 }
7058 }
7059
7060 #[test]
7061 fn type_alias_dispatch_table() {
7062 let (ast, diags) = parse_str("type X = number;");
7063 assert!(diags.is_empty(), "{diags:#?}");
7064 assert!(matches!(
7065 ast.try_stmt(ast.top_level[0]).unwrap().kind,
7066 crate::StmtKind::TypeAliasDecl { .. }
7067 ));
7068
7069 for src in ["type = 5;", "type;", "type(1);", "type < 3;", "type.foo;"] {
7071 let (ast, diags) = parse_str(src);
7072 assert!(
7073 diags.is_empty(),
7074 "unexpected diagnostics for {src:?}: {diags:#?}"
7075 );
7076 assert!(
7077 !matches!(
7078 ast.try_stmt(ast.top_level[0]).unwrap().kind,
7079 crate::StmtKind::TypeAliasDecl { .. }
7080 ),
7081 "{src:?} must not parse as a type alias"
7082 );
7083 }
7084
7085 let (_ast, diags) = parse_str("type X;");
7086 assert!(
7087 diags
7088 .iter()
7089 .any(|d| d.message == "expected `=` after type alias name"),
7090 "bare `type X` should commit to the alias diagnostic, got: {diags:#?}"
7091 );
7092 }
7093
7094 #[test]
7095 fn export_type_alias_dispatch() {
7096 let (ast, diags) = parse_str("export type X = number;");
7097 assert!(diags.is_empty(), "{diags:#?}");
7098 assert_eq!(ast.exported_decls.len(), 1);
7099 assert!(matches!(
7100 ast.try_stmt(ast.exported_decls[0].stmt).unwrap().kind,
7101 crate::StmtKind::TypeAliasDecl { .. }
7102 ));
7103
7104 let (_ast, diags) = parse_str("export type = 5;");
7105 assert!(
7106 diags
7107 .iter()
7108 .any(|d| d.message == "expected a declaration after `export`"),
7109 "`export type = 5` must not become an exported assignment, got: {diags:#?}"
7110 );
7111 }
7112
7113 #[test]
7114 fn parse_paren_simple() {
7115 let (ast, diags) = parse_str("(x);");
7116 assert!(diags.is_empty());
7117 let expr = expr_of_single_stmt(&ast);
7118 let crate::ExprKind::Paren(inner_id) = expr.kind else {
7119 panic!("expected Paren, got {:?}", expr.kind);
7120 };
7121 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7122 let inner = ast.try_expr(inner_id).unwrap();
7123 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7124 assert_eq!(inner.span, crate::Span::new(F, 1, 2).unwrap());
7125 }
7126
7127 #[test]
7128 fn parse_paren_nested() {
7129 let (ast, diags) = parse_str("((x));");
7130 assert!(diags.is_empty());
7131 let outer = expr_of_single_stmt(&ast);
7132 let crate::ExprKind::Paren(mid_id) = outer.kind else {
7133 panic!("expected outer Paren");
7134 };
7135 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7136 let mid = ast.try_expr(mid_id).unwrap();
7137 let crate::ExprKind::Paren(inner_id) = mid.kind else {
7138 panic!("expected inner Paren");
7139 };
7140 assert_eq!(mid.span, crate::Span::new(F, 1, 4).unwrap());
7141 let inner = ast.try_expr(inner_id).unwrap();
7142 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7143 assert_eq!(inner.span, crate::Span::new(F, 2, 3).unwrap());
7144 }
7145
7146 #[test]
7147 fn parse_sequence_of_atoms() {
7148 let (ast, diags) = parse_str(r#"42; "hi"; true; null; x; (x);"#);
7149 assert!(diags.is_empty());
7150 assert_eq!(ast.top_level.len(), 6);
7151 insta::assert_debug_snapshot!(ast);
7152 }
7153
7154 #[test]
7155 fn missing_semicolon_after_expression() {
7156 let (ast, diags) = parse_str("42 43");
7157 assert_eq!(diags.len(), 1);
7158 assert_eq!(diags[0].message, "expected `;` after expression");
7159 assert!(ast.top_level.is_empty());
7160 }
7161
7162 #[test]
7163 fn empty_paren_diagnoses() {
7164 let (ast, diags) = parse_str("();");
7165 assert_eq!(diags.len(), 1);
7166 assert_eq!(diags[0].message, "expected expression");
7167 assert!(ast.top_level.is_empty());
7168 }
7169
7170 #[test]
7171 fn unterminated_paren_diagnoses() {
7172 let (ast, diags) = parse_str("(x;");
7173 assert_eq!(diags.len(), 1);
7174 assert_eq!(diags[0].message, "expected `)`");
7175 assert!(ast.top_level.is_empty());
7176 }
7177
7178 fn binary(e: &crate::Expr) -> (crate::BinOp, crate::ExprId, crate::ExprId) {
7179 match e.kind {
7180 crate::ExprKind::Binary { op, lhs, rhs } => (op, lhs, rhs),
7181 _ => panic!("expected Binary, got {:?}", e.kind),
7182 }
7183 }
7184
7185 #[test]
7186 fn parse_binary_add() {
7187 let (ast, diags) = parse_str("a + b;");
7188 assert!(diags.is_empty());
7189 let outer = expr_of_single_stmt(&ast);
7190 let (op, lhs, rhs) = binary(outer);
7191 assert_eq!(op, crate::BinOp::Add);
7192 assert!(matches!(
7193 ast.try_expr(lhs).unwrap().kind,
7194 crate::ExprKind::Identifier(_)
7195 ));
7196 assert!(matches!(
7197 ast.try_expr(rhs).unwrap().kind,
7198 crate::ExprKind::Identifier(_)
7199 ));
7200 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7201 }
7202
7203 #[test]
7204 fn bitwise_precedence_and_shift_adjacency() {
7205 let (ast, diags) = parse_str("a | b ^ c & d == e >> f + g;");
7206 assert!(diags.is_empty(), "{diags:?}");
7207 let (op, _, mut rhs) = binary(expr_of_single_stmt(&ast));
7208 assert_eq!(op, crate::BinOp::BitOr);
7209 for expected in [
7210 crate::BinOp::BitXor,
7211 crate::BinOp::BitAnd,
7212 crate::BinOp::Eq,
7213 crate::BinOp::Shr,
7214 crate::BinOp::Add,
7215 ] {
7216 let (op, _, next) = binary(ast.try_expr(rhs).unwrap());
7217 assert_eq!(op, expected);
7218 rhs = next;
7219 }
7220 for source in ["a > > b;", "a >/*gap*/> b;", "a < < b;"] {
7221 let (_, diags) = parse_str(source);
7222 assert!(!diags.is_empty(), "{source}");
7223 }
7224 }
7225
7226 #[test]
7227 fn bitwise_shift_assignment_span() {
7228 let (ast, diags) = parse_str("a >>>= b;");
7229 assert!(diags.is_empty(), "{diags:?}");
7230 let crate::StmtKind::CompoundAssign { op, op_span, .. } = single_stmt(&ast).kind else {
7231 panic!("expected assignment");
7232 };
7233 assert_eq!(op, crate::BinOp::UnsignedShr);
7234 assert_eq!(op_span, crate::Span::new(F, 2, 6).unwrap());
7235 }
7236
7237 #[test]
7238 fn precedence_mul_binds_tighter_than_add() {
7239 let (ast, diags) = parse_str("a + b * c;");
7240 assert!(diags.is_empty());
7241 let outer = expr_of_single_stmt(&ast);
7242 let (op, lhs, rhs) = binary(outer);
7243 assert_eq!(op, crate::BinOp::Add);
7244 assert!(matches!(
7245 ast.try_expr(lhs).unwrap().kind,
7246 crate::ExprKind::Identifier(_)
7247 ));
7248 let (rop, rlhs, rrhs) = binary(ast.try_expr(rhs).unwrap());
7249 assert_eq!(rop, crate::BinOp::Mul);
7250 assert!(matches!(
7251 ast.try_expr(rlhs).unwrap().kind,
7252 crate::ExprKind::Identifier(_)
7253 ));
7254 assert!(matches!(
7255 ast.try_expr(rrhs).unwrap().kind,
7256 crate::ExprKind::Identifier(_)
7257 ));
7258 }
7259
7260 #[test]
7261 fn precedence_mul_left_of_add() {
7262 let (ast, diags) = parse_str("a * b + c;");
7263 assert!(diags.is_empty());
7264 let outer = expr_of_single_stmt(&ast);
7265 let (op, lhs, rhs) = binary(outer);
7266 assert_eq!(op, crate::BinOp::Add);
7267 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7268 assert_eq!(lop, crate::BinOp::Mul);
7269 assert!(matches!(
7270 ast.try_expr(rhs).unwrap().kind,
7271 crate::ExprKind::Identifier(_)
7272 ));
7273 }
7274
7275 #[test]
7276 fn left_associative_same_precedence() {
7277 let (ast, diags) = parse_str("a - b - c;");
7278 assert!(diags.is_empty());
7279 let outer = expr_of_single_stmt(&ast);
7280 let (op, lhs, rhs) = binary(outer);
7281 assert_eq!(op, crate::BinOp::Sub);
7282 let (lop, llhs, lrhs) = binary(ast.try_expr(lhs).unwrap());
7283 assert_eq!(lop, crate::BinOp::Sub);
7284 assert!(matches!(
7285 ast.try_expr(llhs).unwrap().kind,
7286 crate::ExprKind::Identifier(_)
7287 ));
7288 assert!(matches!(
7289 ast.try_expr(lrhs).unwrap().kind,
7290 crate::ExprKind::Identifier(_)
7291 ));
7292 assert!(matches!(
7293 ast.try_expr(rhs).unwrap().kind,
7294 crate::ExprKind::Identifier(_)
7295 ));
7296 }
7297
7298 #[test]
7299 fn equality_inside_logical() {
7300 let (ast, diags) = parse_str("a === b && c === d;");
7301 assert!(diags.is_empty());
7302 let outer = expr_of_single_stmt(&ast);
7303 let (op, lhs, rhs) = binary(outer);
7304 assert_eq!(op, crate::BinOp::And);
7305 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7306 assert_eq!(lop, crate::BinOp::Eq);
7307 let (rop, ..) = binary(ast.try_expr(rhs).unwrap());
7308 assert_eq!(rop, crate::BinOp::Eq);
7309 }
7310
7311 #[test]
7312 fn double_equal_is_same_variant_as_triple() {
7313 let (ast, _) = parse_str("a == b;");
7314 let outer = expr_of_single_stmt(&ast);
7315 let (op, ..) = binary(outer);
7316 assert_eq!(op, crate::BinOp::Eq);
7317 }
7318
7319 #[test]
7320 fn bang_eq_is_not_eq() {
7321 let (ast, _) = parse_str("a != b;");
7322 let outer = expr_of_single_stmt(&ast);
7323 let (op, ..) = binary(outer);
7324 assert_eq!(op, crate::BinOp::NotEq);
7325 }
7326
7327 #[test]
7328 fn comparison_has_higher_prec_than_logical() {
7329 let (ast, _) = parse_str("a < b && c;");
7330 let outer = expr_of_single_stmt(&ast);
7331 let (op, lhs, _) = binary(outer);
7332 assert_eq!(op, crate::BinOp::And);
7333 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7334 assert_eq!(lop, crate::BinOp::Lt);
7335 }
7336
7337 #[test]
7338 fn full_precedence_chain_snapshot() {
7339 let (ast, diags) = parse_str("a || b && c === d < e + f * g;");
7340 assert!(diags.is_empty());
7341 insta::assert_debug_snapshot!(ast);
7342 }
7343
7344 #[test]
7345 fn paren_overrides_precedence() {
7346 let (ast, _) = parse_str("(a + b) * c;");
7347 let outer = expr_of_single_stmt(&ast);
7348 let (op, lhs, rhs) = binary(outer);
7349 assert_eq!(op, crate::BinOp::Mul);
7350 let crate::ExprKind::Paren(inner_id) = ast.try_expr(lhs).unwrap().kind else {
7351 panic!("expected Paren on lhs");
7352 };
7353 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7354 assert_eq!(inner_op, crate::BinOp::Add);
7355 assert!(matches!(
7356 ast.try_expr(rhs).unwrap().kind,
7357 crate::ExprKind::Identifier(_)
7358 ));
7359 }
7360
7361 #[test]
7362 fn binary_missing_rhs_diagnoses() {
7363 let (ast, diags) = parse_str("a +;");
7364 assert_eq!(diags.len(), 1);
7365 assert_eq!(diags[0].message, "expected expression");
7366 assert!(ast.top_level.is_empty());
7367 }
7368
7369 fn unary(e: &crate::Expr) -> (crate::UnOp, crate::ExprId) {
7370 match e.kind {
7371 crate::ExprKind::Unary { op, operand } => (op, operand),
7372 _ => panic!("expected Unary, got {:?}", e.kind),
7373 }
7374 }
7375
7376 #[test]
7377 fn parse_unary_not() {
7378 let (ast, _) = parse_str("!x;");
7379 let outer = expr_of_single_stmt(&ast);
7380 let (op, operand) = unary(outer);
7381 assert_eq!(op, crate::UnOp::Not);
7382 assert!(matches!(
7383 ast.try_expr(operand).unwrap().kind,
7384 crate::ExprKind::Identifier(_)
7385 ));
7386 assert_eq!(outer.span, crate::Span::new(F, 0, 2).unwrap());
7387 }
7388
7389 #[test]
7390 fn parse_unary_neg() {
7391 let (ast, _) = parse_str("-x;");
7392 let (op, _) = unary(expr_of_single_stmt(&ast));
7393 assert_eq!(op, crate::UnOp::Neg);
7394 }
7395
7396 #[test]
7397 fn parse_unary_pos() {
7398 let (ast, _) = parse_str("+x;");
7399 let (op, _) = unary(expr_of_single_stmt(&ast));
7400 assert_eq!(op, crate::UnOp::Pos);
7401 }
7402
7403 #[test]
7404 fn repeated_unary_not() {
7405 let (ast, _) = parse_str("!!x;");
7406 let outer = expr_of_single_stmt(&ast);
7407 let (op, operand) = unary(outer);
7408 assert_eq!(op, crate::UnOp::Not);
7409 let (inner_op, inner_operand) = unary(ast.try_expr(operand).unwrap());
7410 assert_eq!(inner_op, crate::UnOp::Not);
7411 assert!(matches!(
7412 ast.try_expr(inner_operand).unwrap().kind,
7413 crate::ExprKind::Identifier(_)
7414 ));
7415 }
7416
7417 #[test]
7418 fn unary_binds_tighter_than_binary() {
7419 let (ast, _) = parse_str("-x + 1;");
7420 let outer = expr_of_single_stmt(&ast);
7421 let (op, lhs, rhs) = binary(outer);
7422 assert_eq!(op, crate::BinOp::Add);
7423 let (unop, _) = unary(ast.try_expr(lhs).unwrap());
7424 assert_eq!(unop, crate::UnOp::Neg);
7425 assert_eq!(
7426 ast.try_expr(rhs).unwrap().kind,
7427 crate::ExprKind::Number(1.0)
7428 );
7429 }
7430
7431 #[test]
7432 fn unary_over_paren() {
7433 let (ast, _) = parse_str("-(x + 1);");
7434 let outer = expr_of_single_stmt(&ast);
7435 let (op, operand) = unary(outer);
7436 assert_eq!(op, crate::UnOp::Neg);
7437 let crate::ExprKind::Paren(inner_id) = ast.try_expr(operand).unwrap().kind else {
7438 panic!("expected Paren");
7439 };
7440 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7441 assert_eq!(inner_op, crate::BinOp::Add);
7442 }
7443
7444 #[test]
7445 fn parse_is_as_expression_rejected() {
7446 let (_, diags) = parse_str("x is number;");
7447 assert!(!diags.is_empty(), "expected parse error for `x is T`");
7448 }
7449
7450 fn as_cast(e: &crate::Expr) -> (crate::ExprId, &crate::TypeAnnotation) {
7451 match &e.kind {
7452 crate::ExprKind::As { expr, ty } => (*expr, ty),
7453 other => panic!("expected ExprKind::As, got {other:?}"),
7454 }
7455 }
7456
7457 #[test]
7458 fn parse_instanceof_class() {
7459 let (ast, diags) = parse_str("x instanceof Foo;");
7460 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7461 let outer = expr_of_single_stmt(&ast);
7462 let crate::ExprKind::InstanceOf { value, ty } = &outer.kind else {
7463 panic!("expected ExprKind::InstanceOf, got {:?}", outer.kind);
7464 };
7465 assert!(matches!(
7466 ast.try_expr(*value).unwrap().kind,
7467 crate::ExprKind::Identifier(_)
7468 ));
7469 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7470 }
7471
7472 #[test]
7473 fn parse_instanceof_binds_tighter_than_logical_and() {
7474 let (ast, diags) = parse_str("a instanceof B && c;");
7476 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7477 let outer = expr_of_single_stmt(&ast);
7478 let (op, lhs, rhs) = binary(outer);
7479 assert_eq!(op, crate::BinOp::And);
7480 assert!(matches!(
7481 ast.try_expr(lhs).unwrap().kind,
7482 crate::ExprKind::InstanceOf { .. }
7483 ));
7484 assert!(matches!(
7485 ast.try_expr(rhs).unwrap().kind,
7486 crate::ExprKind::Identifier(_)
7487 ));
7488 }
7489
7490 #[test]
7493 fn parse_angle_cast_lowers_to_the_as_node() {
7494 let (ast, diags) = parse_str("<number>x;");
7495 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7496 let outer = expr_of_single_stmt(&ast);
7497 let (inner, ty) = as_cast(outer);
7498 assert!(matches!(
7499 ast.try_expr(inner).unwrap().kind,
7500 crate::ExprKind::Identifier(_)
7501 ));
7502 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7503 }
7504
7505 #[test]
7509 fn a_less_than_comparison_is_not_read_as_a_cast() {
7510 let (ast, diags) = parse_str("a < b;");
7511 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7512 let e = expr_of_single_stmt(&ast);
7513 assert!(matches!(e.kind, crate::ExprKind::Binary { .. }));
7514 }
7515
7516 #[test]
7517 fn parse_typeof_in_type_position() {
7518 let (_ast, diags) = parse_str("let a: typeof b = 1;");
7519 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7520 }
7521
7522 #[test]
7523 fn parse_typeof_with_a_dotted_path() {
7524 let (_ast, diags) = parse_str("let a: typeof b.c.d = 1;");
7525 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7526 }
7527
7528 #[test]
7529 fn parse_as_primitive() {
7530 let (ast, diags) = parse_str("x as number;");
7531 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7532 let outer = expr_of_single_stmt(&ast);
7533 let (inner, ty) = as_cast(outer);
7534 assert!(matches!(
7535 ast.try_expr(inner).unwrap().kind,
7536 crate::ExprKind::Identifier(_)
7537 ));
7538 match &ty.kind {
7539 crate::TypeAnnotationKind::Name { args, .. } => {
7540 assert!(args.is_empty());
7541 }
7542 other => panic!("expected Name target, got {other:?}"),
7543 }
7544 assert_eq!(outer.span, crate::Span::new(F, 0, 11).unwrap());
7545 }
7546
7547 #[test]
7548 fn parse_as_object_literal_type() {
7549 let (ast, diags) = parse_str("x as { a: number };");
7550 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7551 let outer = expr_of_single_stmt(&ast);
7552 let (_, ty) = as_cast(outer);
7553 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Object { .. }));
7554 }
7555
7556 #[test]
7557 fn parse_as_array_short() {
7558 let (ast, diags) = parse_str("x as string[];");
7559 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7560 let outer = expr_of_single_stmt(&ast);
7561 let (_, ty) = as_cast(outer);
7562 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
7563 }
7564
7565 #[test]
7566 fn parse_as_array_generic() {
7567 let (ast, diags) = parse_str("x as T[];");
7568 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7569 let outer = expr_of_single_stmt(&ast);
7570 let (_, ty) = as_cast(outer);
7571 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
7572 }
7573
7574 #[test]
7575 fn parse_as_generic_instantiation() {
7576 let (ast, diags) = parse_str("x as Foo<Bar>;");
7577 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7578 let outer = expr_of_single_stmt(&ast);
7579 let (_, ty) = as_cast(outer);
7580 match &ty.kind {
7581 crate::TypeAnnotationKind::Name { args, .. } => {
7582 assert_eq!(args.len(), 1, "Foo<Bar> has one type arg");
7583 }
7584 other => panic!("expected Name with type args, got {other:?}"),
7585 }
7586 }
7587
7588 #[test]
7589 fn parse_as_precedence_higher_than_additive() {
7590 let (ast, diags) = parse_str("x as number + 1;");
7591 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7592 let outer = expr_of_single_stmt(&ast);
7593 let (op, lhs, rhs) = binary(outer);
7594 assert_eq!(op, crate::BinOp::Add);
7595 assert!(matches!(
7596 ast.try_expr(lhs).unwrap().kind,
7597 crate::ExprKind::As { .. }
7598 ));
7599 assert!(matches!(
7600 ast.try_expr(rhs).unwrap().kind,
7601 crate::ExprKind::Number(_)
7602 ));
7603 }
7604
7605 #[test]
7606 fn parse_as_precedence_lower_than_unary() {
7607 let (ast, diags) = parse_str("!x as boolean;");
7608 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7609 let outer = expr_of_single_stmt(&ast);
7610 let (inner, _) = as_cast(outer);
7611 assert!(matches!(
7612 ast.try_expr(inner).unwrap().kind,
7613 crate::ExprKind::Unary {
7614 op: crate::UnOp::Not,
7615 ..
7616 }
7617 ));
7618 }
7619
7620 #[test]
7621 fn parse_as_left_associative_chain() {
7622 let (ast, diags) = parse_str("x as A as B;");
7623 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7624 let outer = expr_of_single_stmt(&ast);
7625 let (inner, _outer_ty) = as_cast(outer);
7626 let (innermost, _inner_ty) = as_cast(ast.try_expr(inner).unwrap());
7627 assert!(matches!(
7628 ast.try_expr(innermost).unwrap().kind,
7629 crate::ExprKind::Identifier(_)
7630 ));
7631 }
7632
7633 #[test]
7634 fn parse_as_inside_call_arg() {
7635 let (ast, diags) = parse_str("f(x as T);");
7636 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7637 let outer = expr_of_single_stmt(&ast);
7638 match &outer.kind {
7639 crate::ExprKind::Call { args, .. } => {
7640 assert_eq!(args.len(), 1);
7641 assert!(matches!(
7642 ast.try_expr(args[0]).unwrap().kind,
7643 crate::ExprKind::As { .. }
7644 ));
7645 }
7646 other => panic!("expected Call, got {other:?}"),
7647 }
7648 }
7649
7650 #[test]
7651 fn parse_as_after_field_access() {
7652 let (ast, diags) = parse_str("obj.field as T;");
7653 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7654 let outer = expr_of_single_stmt(&ast);
7655 let (inner, _) = as_cast(outer);
7656 assert!(matches!(
7657 ast.try_expr(inner).unwrap().kind,
7658 crate::ExprKind::FieldAccess { .. }
7659 ));
7660 }
7661
7662 #[test]
7663 fn parse_type_guard_return_annotation_on_function() {
7664 let src = "function isCircle(s: Shape): s is Circle { return true; }";
7665 let (ast, diags) = parse_str(src);
7666 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7667 let stmt = single_stmt(&ast);
7668 match stmt.kind {
7669 crate::StmtKind::Function {
7670 ref return_type,
7671 ref type_predicate,
7672 ..
7673 } => {
7674 assert!(
7675 return_type.is_none(),
7676 "predicate form leaves return_type empty",
7677 );
7678 let tp = type_predicate.as_ref().expect("predicate parsed");
7679 assert_eq!(tp.param.name, "s");
7680 assert!(matches!(
7681 tp.asserted.kind,
7682 crate::TypeAnnotationKind::Name { .. }
7683 ));
7684 assert_eq!(tp.span, crate::Span::new(F, 29, 40).unwrap());
7685 }
7686 _ => panic!("expected Function"),
7687 }
7688 }
7689
7690 #[test]
7691 fn parse_type_guard_return_annotation_on_arrow() {
7692 let src = "const f = (s: Shape): s is Circle => true;";
7693 let (ast, diags) = parse_str(src);
7694 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7695 let value = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
7696 crate::StmtKind::Const { value, .. } => *value,
7697 _ => panic!("expected const"),
7698 };
7699 match &ast.try_expr(value).unwrap().kind {
7700 crate::ExprKind::Arrow {
7701 return_type,
7702 type_predicate,
7703 ..
7704 } => {
7705 assert!(return_type.is_none());
7706 let tp = type_predicate.as_ref().expect("predicate parsed");
7707 assert_eq!(tp.param.name, "s");
7708 }
7709 other => panic!("expected Arrow, got {other:?}"),
7710 }
7711 }
7712
7713 #[test]
7714 fn parse_plain_return_type_unaffected_by_predicate_parser() {
7715 let src = "function f(s: Shape): boolean { return true; }";
7716 let (ast, diags) = parse_str(src);
7717 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7718 let stmt = single_stmt(&ast);
7719 match stmt.kind {
7720 crate::StmtKind::Function {
7721 ref return_type,
7722 ref type_predicate,
7723 ..
7724 } => {
7725 assert!(return_type.is_some(), "plain form has return_type");
7726 assert!(type_predicate.is_none(), "plain form has no type_predicate",);
7727 }
7728 _ => panic!("expected Function"),
7729 }
7730 }
7731
7732 #[test]
7733 fn parse_type_guard_rejects_non_return_position() {
7734 let (_, diags) = parse_str("let x: y is Foo = null;");
7735 assert!(
7736 !diags.is_empty(),
7737 "expected parse error for `is` outside return-type position",
7738 );
7739 }
7740
7741 #[test]
7742 fn parse_typeof_prefix() {
7743 let (ast, diags) = parse_str("typeof x;");
7744 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7745 let outer = expr_of_single_stmt(&ast);
7746 let crate::ExprKind::Typeof { operand } = outer.kind else {
7747 panic!("expected Typeof, got {:?}", outer.kind);
7748 };
7749 assert!(matches!(
7750 ast.try_expr(operand).unwrap().kind,
7751 crate::ExprKind::Identifier(_)
7752 ));
7753 assert_eq!(outer.span, crate::Span::new(F, 0, 8).unwrap());
7754 }
7755
7756 #[test]
7757 fn parse_typeof_in_equality() {
7758 let (ast, diags) = parse_str(r#"typeof x === "number";"#);
7759 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7760 let outer = expr_of_single_stmt(&ast);
7761 let (op, lhs, rhs) = binary(outer);
7762 assert_eq!(op, crate::BinOp::Eq);
7763 assert!(matches!(
7764 ast.try_expr(lhs).unwrap().kind,
7765 crate::ExprKind::Typeof { .. }
7766 ));
7767 assert!(matches!(
7768 ast.try_expr(rhs).unwrap().kind,
7769 crate::ExprKind::String(ref s) if s == "number"
7770 ));
7771 }
7772
7773 #[test]
7774 fn parse_let_without_type() {
7775 let (ast, diags) = parse_str("let x = 1;");
7776 assert!(diags.is_empty());
7777 let stmt = single_stmt(&ast);
7778 assert_eq!(stmt.span, crate::Span::new(F, 0, 10).unwrap());
7779 match stmt.kind {
7780 crate::StmtKind::Let {
7781 ref name,
7782 ref ty,
7783 value,
7784 ..
7785 } => {
7786 assert_eq!(name.name, "x");
7787 assert_eq!(name.span, crate::Span::new(F, 4, 5).unwrap());
7788 assert!(ty.is_none());
7789 assert_eq!(
7790 ast.try_expr(value).unwrap().kind,
7791 crate::ExprKind::Number(1.0)
7792 );
7793 }
7794 _ => panic!("expected Let"),
7795 }
7796 }
7797
7798 #[test]
7799 fn parse_const_with_type_and_string() {
7800 let (ast, diags) = parse_str(r#"const y: string = "hi";"#);
7801 assert!(diags.is_empty());
7802 let stmt = single_stmt(&ast);
7803 match stmt.kind {
7804 crate::StmtKind::Const {
7805 ref name,
7806 ref ty,
7807 value,
7808 ..
7809 } => {
7810 assert_eq!(name.name, "y");
7811 assert_eq!(name.span, crate::Span::new(F, 6, 7).unwrap());
7812 let ty = ty.as_ref().expect("expected type annotation");
7813 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7814 assert_eq!(ty.span, crate::Span::new(F, 9, 15).unwrap());
7815 assert_eq!(
7816 ast.try_expr(value).unwrap().kind,
7817 crate::ExprKind::String("hi".to_string())
7818 );
7819 }
7820 _ => panic!("expected Const"),
7821 }
7822 }
7823
7824 #[test]
7825 fn parse_let_with_type_and_binary_initializer() {
7826 let (ast, diags) = parse_str("let z: number = 1 + 2;");
7827 assert!(diags.is_empty());
7828 let stmt = single_stmt(&ast);
7829 match stmt.kind {
7830 crate::StmtKind::Let { ref ty, value, .. } => {
7831 assert!(ty.is_some());
7832 let (op, ..) = binary(ast.try_expr(value).unwrap());
7833 assert_eq!(op, crate::BinOp::Add);
7834 }
7835 _ => panic!("expected Let"),
7836 }
7837 }
7838
7839 #[test]
7840 fn void_type_annotation_is_accepted_syntactically() {
7841 let (ast, diags) = parse_str("let v: void = null;");
7842 assert!(diags.is_empty());
7843 let stmt = single_stmt(&ast);
7844 match stmt.kind {
7845 crate::StmtKind::Let { ref ty, .. } => {
7846 let ty = ty.as_ref().unwrap();
7847 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7848 assert_eq!(ty.span, crate::Span::new(F, 7, 11).unwrap());
7849 }
7850 _ => panic!("expected Let"),
7851 }
7852 }
7853
7854 #[test]
7855 fn const_missing_initializer_diagnoses() {
7856 let (ast, diags) = parse_str("const x;");
7857 assert_eq!(diags.len(), 1);
7858 assert_eq!(
7859 diags[0].message,
7860 "`const` declaration requires an initializer"
7861 );
7862 assert!(ast.top_level.is_empty());
7863 }
7864
7865 #[test]
7866 fn parse_const_object_shorthand_pattern() {
7867 let (ast, diags) = parse_str("const { a, b } = obj;");
7868 assert!(diags.is_empty(), "unexpected: {diags:?}");
7869 let stmt = single_stmt(&ast);
7870 match stmt.kind {
7871 crate::StmtKind::ConstPattern {
7872 binding:
7873 crate::Binding::Object {
7874 ref fields,
7875 ref rest,
7876 ..
7877 },
7878 ..
7879 } => {
7880 assert_eq!(fields.len(), 2);
7881 assert_eq!(fields[0].source.name, "a");
7882 assert_eq!(fields[0].local.name, "a");
7883 assert_eq!(fields[1].source.name, "b");
7884 assert_eq!(fields[1].local.name, "b");
7885 assert!(rest.is_none());
7886 }
7887 _ => panic!("expected ConstPattern Object, got {:?}", stmt.kind),
7888 }
7889 }
7890
7891 #[test]
7892 fn parse_const_object_pattern_keyword_source_key() {
7893 let (ast, diags) = parse_str("const { type: statusType, default: fallback } = obj;");
7894 assert!(diags.is_empty(), "unexpected: {diags:?}");
7895 let stmt = single_stmt(&ast);
7896 let crate::StmtKind::ConstPattern {
7897 binding: crate::Binding::Object { ref fields, .. },
7898 ..
7899 } = stmt.kind
7900 else {
7901 panic!("expected object const pattern");
7902 };
7903
7904 let names: Vec<(&str, &str)> = fields
7905 .iter()
7906 .map(|f| (f.source.name.as_str(), f.local.name.as_str()))
7907 .collect();
7908 assert_eq!(names, vec![("type", "statusType"), ("default", "fallback")]);
7909 }
7910
7911 #[test]
7912 fn parse_const_object_pattern_keyword_shorthand_rejected() {
7913 let (_ast, diags) = parse_str("const { default } = obj;");
7914 assert!(
7915 diags
7916 .iter()
7917 .any(|d| d.message == "expected `:` after keyword field name in object pattern"),
7918 "expected keyword-shorthand diagnostic, got: {diags:?}"
7919 );
7920 }
7921
7922 #[test]
7923 fn parse_const_object_renamed_pattern() {
7924 let (ast, diags) = parse_str("const { a: x, b: y } = obj;");
7925 assert!(diags.is_empty(), "unexpected: {diags:?}");
7926 match single_stmt(&ast).kind {
7927 crate::StmtKind::ConstPattern {
7928 binding: crate::Binding::Object { ref fields, .. },
7929 ..
7930 } => {
7931 assert_eq!(fields[0].source.name, "a");
7932 assert_eq!(fields[0].local.name, "x");
7933 assert_eq!(fields[1].source.name, "b");
7934 assert_eq!(fields[1].local.name, "y");
7935 }
7936 ref other => panic!("expected ConstPattern Object, got {other:?}"),
7937 }
7938 }
7939
7940 #[test]
7941 fn parse_const_object_with_rest() {
7942 let (ast, diags) = parse_str("const { a, ...rest } = obj;");
7943 assert!(diags.is_empty(), "unexpected: {diags:?}");
7944 match single_stmt(&ast).kind {
7945 crate::StmtKind::ConstPattern {
7946 binding:
7947 crate::Binding::Object {
7948 ref fields,
7949 ref rest,
7950 ..
7951 },
7952 ..
7953 } => {
7954 assert_eq!(fields.len(), 1);
7955 assert_eq!(fields[0].source.name, "a");
7956 let r = rest.as_ref().expect("expected rest");
7957 assert_eq!(r.name, "rest");
7958 }
7959 ref other => panic!("expected ConstPattern Object, got {other:?}"),
7960 }
7961 }
7962
7963 #[test]
7964 fn parse_const_array_pattern() {
7965 let (ast, diags) = parse_str("const [x, y] = arr;");
7966 assert!(diags.is_empty(), "unexpected: {diags:?}");
7967 match single_stmt(&ast).kind {
7968 crate::StmtKind::ConstPattern {
7969 binding:
7970 crate::Binding::Array {
7971 ref elems,
7972 ref rest,
7973 ..
7974 },
7975 ..
7976 } => {
7977 assert_eq!(elems.len(), 2);
7978 assert_eq!(elems[0].as_ref().unwrap().name, "x");
7979 assert_eq!(elems[1].as_ref().unwrap().name, "y");
7980 assert!(rest.is_none());
7981 }
7982 ref other => panic!("expected ConstPattern Array, got {other:?}"),
7983 }
7984 }
7985
7986 #[test]
7987 fn parse_const_array_with_hole_and_rest() {
7988 let (ast, diags) = parse_str("const [, x, ...rest] = arr;");
7989 assert!(diags.is_empty(), "unexpected: {diags:?}");
7990 match single_stmt(&ast).kind {
7991 crate::StmtKind::ConstPattern {
7992 binding:
7993 crate::Binding::Array {
7994 ref elems,
7995 ref rest,
7996 ..
7997 },
7998 ..
7999 } => {
8000 assert_eq!(elems.len(), 2);
8001 assert!(elems[0].is_none(), "first slot should be a hole");
8002 assert_eq!(elems[1].as_ref().unwrap().name, "x");
8003 assert_eq!(rest.as_ref().unwrap().name, "rest");
8004 }
8005 ref other => panic!("expected ConstPattern Array, got {other:?}"),
8006 }
8007 }
8008
8009 #[test]
8010 fn parse_let_object_pattern() {
8011 let (ast, diags) = parse_str("let { a, b } = obj;");
8012 assert!(diags.is_empty(), "unexpected: {diags:?}");
8013 assert!(matches!(
8014 single_stmt(&ast).kind,
8015 crate::StmtKind::LetPattern {
8016 binding: crate::Binding::Object { .. },
8017 ..
8018 }
8019 ));
8020 }
8021
8022 #[test]
8023 fn parse_function_param_object_pattern() {
8024 let src = "function f({ a, b }: T): void { return; }";
8025 let (ast, diags) = parse_str(src);
8026 assert!(diags.is_empty(), "unexpected: {diags:?}");
8027 match single_stmt(&ast).kind {
8028 crate::StmtKind::Function { ref params, .. } => {
8029 assert_eq!(params.len(), 1);
8030 let p = ¶ms[0];
8031 assert!(p.pattern.is_some(), "expected pattern param");
8032 assert_eq!(p.name.name, "", "pattern placeholder uses empty name");
8033 match p.pattern.as_ref().unwrap() {
8034 crate::Binding::Object { fields, .. } => {
8035 assert_eq!(fields.len(), 2);
8036 assert_eq!(fields[0].source.name, "a");
8037 }
8038 other => panic!("expected Object binding, got {other:?}"),
8039 }
8040 }
8041 ref other => panic!("expected Function, got {other:?}"),
8042 }
8043 }
8044
8045 #[test]
8046 fn parse_arrow_param_array_pattern() {
8047 let src = "const g = ([x, y]: number[]): number => x + y;";
8048 let (_ast, diags) = parse_str(src);
8049 assert!(diags.is_empty(), "unexpected: {diags:?}");
8050 }
8051
8052 #[test]
8053 fn parse_destructure_nested_rejected() {
8054 let (_ast, diags) = parse_str("const { a: { b } } = obj;");
8055 assert!(
8056 diags
8057 .iter()
8058 .any(|d| d.message.contains("nested destructuring")),
8059 "expected nested-destructuring diagnostic, got: {diags:?}",
8060 );
8061 }
8062
8063 #[test]
8064 fn parse_destructure_array_nested_rejected() {
8065 let (_ast, diags) = parse_str("const [[a]] = arr;");
8066 assert!(
8067 diags
8068 .iter()
8069 .any(|d| d.message.contains("nested destructuring")),
8070 "expected nested-destructuring diagnostic, got: {diags:?}",
8071 );
8072 }
8073
8074 #[test]
8075 fn parse_destructure_pattern_defaults() {
8076 let (ast, diags) = parse_str("const { a = 1, b: c = 2 } = obj;");
8077 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8078 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
8079 panic!("expected pattern")
8080 };
8081 let crate::Binding::Object { fields, .. } = binding else {
8082 panic!("expected object")
8083 };
8084 assert_eq!(fields.len(), 2);
8085 assert!(fields.iter().all(|field| field.default.is_some()));
8086 assert_eq!(fields[1].local.name, "c");
8087 }
8088
8089 #[test]
8090 fn parse_destructure_param_default() {
8091 let (ast, diags) = parse_str("function f({ a = 1 }: T = obj): void { return; }");
8092 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8093 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
8094 panic!("expected function")
8095 };
8096 assert!(params[0].default.is_some());
8097 assert!(params[0].pattern.is_some());
8098 }
8099
8100 #[test]
8101 fn parse_destructure_rest_not_last_rejected() {
8102 let (_ast, diags) = parse_str("const { ...rest, a } = obj;");
8103 assert!(
8104 diags
8105 .iter()
8106 .any(|d| d.message.contains("rest element must be the last element")),
8107 "expected rest-not-last diagnostic, got: {diags:?}",
8108 );
8109 }
8110
8111 #[test]
8112 fn parse_destructure_empty_object_rejected() {
8113 let (_ast, diags) = parse_str("const { } = obj;");
8114 assert!(
8115 diags
8116 .iter()
8117 .any(|d| d.message.contains("empty object destructuring pattern")),
8118 "expected empty-pattern diagnostic, got: {diags:?}",
8119 );
8120 }
8121
8122 #[test]
8123 fn parse_ternary_simple() {
8124 let (ast, diags) = parse_str("const x: number = a ? 1 : 2;");
8125 assert!(diags.is_empty(), "unexpected: {diags:?}");
8126 let stmt = single_stmt(&ast);
8127 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8128 panic!("expected Const, got {:?}", stmt.kind);
8129 };
8130 assert!(matches!(
8131 ast.try_expr(value).unwrap().kind,
8132 crate::ExprKind::Ternary { .. }
8133 ));
8134 }
8135
8136 #[test]
8137 fn parse_ternary_nested_right_assoc() {
8138 let (ast, diags) = parse_str("const x: number = a ? 1 : b ? 2 : 3;");
8139 assert!(diags.is_empty(), "unexpected: {diags:?}");
8140 let stmt = single_stmt(&ast);
8141 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8142 panic!("expected Const");
8143 };
8144 let crate::ExprKind::Ternary { else_, .. } = ast.try_expr(value).unwrap().kind else {
8145 panic!("expected outer Ternary");
8146 };
8147 assert!(matches!(
8148 ast.try_expr(else_).unwrap().kind,
8149 crate::ExprKind::Ternary { .. }
8150 ));
8151 }
8152
8153 #[test]
8154 fn parse_ternary_missing_colon_diagnoses() {
8155 let (_, diags) = parse_str("const x: number = a ? 1;");
8156 assert!(
8157 diags
8158 .iter()
8159 .any(|d| d.message.contains("expected `:` to complete ternary")),
8160 "expected colon diagnostic, got: {diags:?}",
8161 );
8162 }
8163
8164 #[test]
8165 fn parse_nullish_coalesce_binop() {
8166 let (ast, diags) = parse_str("const x: number = a ?? 1;");
8167 assert!(diags.is_empty(), "unexpected: {diags:?}");
8168 let stmt = single_stmt(&ast);
8169 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8170 panic!("expected Const");
8171 };
8172 match ast.try_expr(value).unwrap().kind {
8173 crate::ExprKind::Binary { op, .. } => {
8174 assert_eq!(op, crate::BinOp::NullishCoalesce);
8175 }
8176 ref other => panic!("expected Binary NullishCoalesce, got {other:?}"),
8177 }
8178 }
8179
8180 #[test]
8181 fn parse_nullish_left_associative() {
8182 let (ast, diags) = parse_str("const x: number = a ?? b ?? c;");
8183 assert!(diags.is_empty(), "unexpected: {diags:?}");
8184 let stmt = single_stmt(&ast);
8185 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8186 panic!("expected Const");
8187 };
8188 let crate::ExprKind::Binary { lhs, .. } = ast.try_expr(value).unwrap().kind else {
8189 panic!("expected outer Binary");
8190 };
8191 match ast.try_expr(lhs).unwrap().kind {
8192 crate::ExprKind::Binary { op, .. } => {
8193 assert_eq!(op, crate::BinOp::NullishCoalesce);
8194 }
8195 ref other => panic!("expected inner Binary, got {other:?}"),
8196 }
8197 }
8198
8199 #[test]
8200 fn parse_pow_is_right_associative() {
8201 let (ast, diags) = parse_str("const x: number = 2 ** 3 ** 2;");
8202 assert!(diags.is_empty(), "unexpected: {diags:?}");
8203 let stmt = single_stmt(&ast);
8204 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8205 panic!("expected Const");
8206 };
8207 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8208 panic!("expected outer Binary");
8209 };
8210 assert_eq!(op, crate::BinOp::Pow);
8211 match ast.try_expr(rhs).unwrap().kind {
8212 crate::ExprKind::Binary { op: inner_op, .. } => {
8213 assert_eq!(inner_op, crate::BinOp::Pow);
8214 }
8215 ref other => panic!("expected inner Pow Binary on RHS, got {other:?}"),
8216 }
8217 }
8218
8219 #[test]
8220 fn parse_pow_higher_than_multiply() {
8221 let (ast, diags) = parse_str("const x: number = 2 * 3 ** 2;");
8222 assert!(diags.is_empty(), "unexpected: {diags:?}");
8223 let stmt = single_stmt(&ast);
8224 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8225 panic!("expected Const");
8226 };
8227 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8228 panic!("expected outer Binary");
8229 };
8230 assert_eq!(op, crate::BinOp::Mul);
8231 match ast.try_expr(rhs).unwrap().kind {
8232 crate::ExprKind::Binary { op: inner_op, .. } => {
8233 assert_eq!(inner_op, crate::BinOp::Pow);
8234 }
8235 ref other => panic!("expected Pow on the RHS of *, got {other:?}"),
8236 }
8237 }
8238
8239 #[test]
8240 fn parse_compound_assign_ident() {
8241 let (ast, diags) = parse_str("function main(): void { let x: number = 0; x += 5; }");
8242 assert!(diags.is_empty(), "unexpected: {diags:?}");
8243 let func = ast.top_level.iter().find_map(|sid| {
8244 if let crate::StmtKind::Function { body, .. } = &ast.try_stmt(*sid).unwrap().kind {
8245 Some(*body)
8246 } else {
8247 None
8248 }
8249 });
8250 let crate::StmtKind::Block(stmts) = &ast.try_stmt(func.expect("main")).unwrap().kind else {
8251 panic!("expected function body to be a block");
8252 };
8253 let target = stmts
8254 .iter()
8255 .find_map(|sid| match &ast.try_stmt(*sid).unwrap().kind {
8256 crate::StmtKind::CompoundAssign { target, op, .. } => {
8257 Some((target.name.clone(), *op))
8258 }
8259 _ => None,
8260 });
8261 let (name, op) = target.expect("expected a CompoundAssign in the body");
8262 assert_eq!(name, "x");
8263 assert_eq!(op, crate::BinOp::Add);
8264 }
8265
8266 #[test]
8267 fn parse_nullish_mixed_with_or_rejected() {
8268 let (_, diags) = parse_str("const x: number = a || b ?? c;");
8269 assert!(
8270 diags
8271 .iter()
8272 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8273 "expected mixing diagnostic, got: {diags:?}",
8274 );
8275 }
8276
8277 #[test]
8278 fn parse_nullish_mixed_with_and_rejected() {
8279 let (_, diags) = parse_str("const x: number = a && b ?? c;");
8280 assert!(
8281 diags
8282 .iter()
8283 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8284 "expected mixing diagnostic, got: {diags:?}",
8285 );
8286 }
8287
8288 #[test]
8289 fn parse_nullish_followed_by_logical_rejected() {
8290 for (source, help) in [
8291 (
8292 "const x: number = a ?? b || c;",
8293 "`(a ?? b) || c` or `a ?? (b || c)`",
8294 ),
8295 (
8296 "const x: number = a ?? b && c;",
8297 "`(a ?? b) && c` or `a ?? (b && c)`",
8298 ),
8299 (
8300 "const x: number = a ?? b ?? c || d;",
8301 "`(a ?? b) || c` or `a ?? (b || c)`",
8302 ),
8303 (
8305 "const x: number = a || b ?? c || d;",
8306 "`(a || b) ?? c` or `a || (b ?? c)`",
8307 ),
8308 ] {
8309 let (_, diags) = parse_str(source);
8310 assert_eq!(diags.len(), 1, "diags for {source:?}: {diags:?}");
8311 assert!(
8312 diags[0].message.contains("mixing `??` with `||` / `&&`"),
8313 "message for {source:?}: {diags:?}",
8314 );
8315 assert!(
8316 diags[0].help.iter().any(|h| h.contains(help)),
8317 "help for {source:?}: {diags:?}",
8318 );
8319 }
8320 }
8321
8322 #[test]
8323 fn parse_nullish_operand_keeps_tighter_operators() {
8324 let (ast, diags) = parse_str("const x: boolean = a ?? b == c;");
8326 assert!(diags.is_empty(), "unexpected: {diags:?}");
8327 let stmt = single_stmt(&ast);
8328 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8329 panic!("expected Const");
8330 };
8331 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8332 panic!("expected outer Binary");
8333 };
8334 assert_eq!(op, crate::BinOp::NullishCoalesce);
8335 assert!(matches!(
8336 ast.try_expr(rhs).unwrap().kind,
8337 crate::ExprKind::Binary {
8338 op: crate::BinOp::Eq,
8339 ..
8340 }
8341 ));
8342 }
8343
8344 #[test]
8345 fn parse_nullish_with_parens_ok() {
8346 let (_, diags) = parse_str("const x: number = (a || b) ?? c;");
8347 assert!(diags.is_empty(), "unexpected: {diags:?}");
8348 }
8349
8350 #[test]
8351 fn parse_optional_field_chain() {
8352 let (ast, diags) = parse_str("const x: number = a?.b;");
8353 assert!(diags.is_empty(), "unexpected: {diags:?}");
8354 let stmt = single_stmt(&ast);
8355 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8356 panic!("expected Const");
8357 };
8358 match ast.try_expr(value).unwrap().kind {
8359 crate::ExprKind::OptionalChain { ref parts, .. } => {
8360 assert_eq!(parts.len(), 1);
8361 match &parts[0] {
8362 crate::ChainPart::Field { name, optional, .. } => {
8363 assert_eq!(name.name, "b");
8364 assert!(*optional);
8365 }
8366 other => panic!("expected Field, got {other:?}"),
8367 }
8368 }
8369 ref other => panic!("expected OptionalChain, got {other:?}"),
8370 }
8371 }
8372
8373 #[test]
8374 fn parse_optional_chain_continues_after_first_dot() {
8375 let (ast, diags) = parse_str("const x: number = a?.b.c;");
8376 assert!(diags.is_empty(), "unexpected: {diags:?}");
8377 let stmt = single_stmt(&ast);
8378 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8379 panic!("expected Const");
8380 };
8381 match ast.try_expr(value).unwrap().kind {
8382 crate::ExprKind::OptionalChain { ref parts, .. } => {
8383 assert_eq!(parts.len(), 2);
8384 if let crate::ChainPart::Field { optional, .. } = &parts[0] {
8385 assert!(*optional);
8386 }
8387 if let crate::ChainPart::Field { optional, .. } = &parts[1] {
8388 assert!(!*optional);
8389 }
8390 }
8391 ref other => panic!("expected OptionalChain, got {other:?}"),
8392 }
8393 }
8394
8395 #[test]
8396 fn parse_optional_chain_multiple_short_circuits() {
8397 let (ast, diags) = parse_str("const x: number = a?.b?.c;");
8398 assert!(diags.is_empty(), "unexpected: {diags:?}");
8399 let stmt = single_stmt(&ast);
8400 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8401 panic!("expected Const");
8402 };
8403 let crate::ExprKind::OptionalChain { parts, .. } = &ast.try_expr(value).unwrap().kind
8404 else {
8405 panic!("expected OptionalChain");
8406 };
8407 assert_eq!(parts.len(), 2);
8408 for p in parts {
8409 if let crate::ChainPart::Field { optional, .. } = p {
8410 assert!(*optional, "both parts should be optional");
8411 }
8412 }
8413 }
8414
8415 #[test]
8416 fn parse_optional_call_and_index() {
8417 let (_, diags) = parse_str("const x: number = f?.();");
8418 assert!(diags.is_empty(), "unexpected: {diags:?}");
8419 let (_, diags) = parse_str("const y: number = arr?.[0];");
8420 assert!(diags.is_empty(), "unexpected: {diags:?}");
8421 }
8422
8423 #[test]
8424 fn parse_optional_chain_terminates_at_questionmark_for_ternary() {
8425 let (ast, diags) = parse_str("const x: number = a?.b ? c : d;");
8426 assert!(diags.is_empty(), "unexpected: {diags:?}");
8427 let stmt = single_stmt(&ast);
8428 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8429 panic!("expected Const");
8430 };
8431 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8432 panic!(
8433 "expected top-level Ternary, got {:?}",
8434 ast.try_expr(value).unwrap().kind
8435 );
8436 };
8437 assert!(matches!(
8438 ast.try_expr(cond).unwrap().kind,
8439 crate::ExprKind::OptionalChain { .. }
8440 ));
8441 }
8442
8443 #[test]
8444 fn parse_nullish_below_ternary() {
8445 let (ast, diags) = parse_str("const x: number = a ?? b ? c : d;");
8446 assert!(diags.is_empty(), "unexpected: {diags:?}");
8447 let stmt = single_stmt(&ast);
8448 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8449 panic!("expected Const");
8450 };
8451 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8452 panic!("expected top-level Ternary");
8453 };
8454 match ast.try_expr(cond).unwrap().kind {
8455 crate::ExprKind::Binary { op, .. } => {
8456 assert_eq!(op, crate::BinOp::NullishCoalesce);
8457 }
8458 ref other => panic!("expected Binary ??, got {other:?}"),
8459 }
8460 }
8461
8462 #[test]
8463 fn let_missing_initializer_diagnoses() {
8464 let (ast, diags) = parse_str("let x;");
8465 assert_eq!(diags.len(), 1);
8466 assert_eq!(
8467 diags[0].message,
8468 "`let` declaration requires an initializer"
8469 );
8470 assert!(ast.top_level.is_empty());
8471 }
8472
8473 #[test]
8474 fn missing_identifier_after_let_diagnoses() {
8475 let (ast, diags) = parse_str("let = 1;");
8476 assert_eq!(diags.len(), 1);
8477 assert_eq!(diags[0].message, "expected identifier after `let`/`const`");
8478 assert!(ast.top_level.is_empty());
8479 }
8480
8481 #[test]
8482 fn missing_type_after_colon_diagnoses() {
8483 let (ast, diags) = parse_str("let x: = 1;");
8484 assert_eq!(diags.len(), 1);
8485 assert_eq!(diags[0].message, "expected type");
8486 assert!(ast.top_level.is_empty());
8487 }
8488
8489 #[test]
8490 fn missing_semicolon_after_declaration_diagnoses() {
8491 let (ast, diags) = parse_str("let x = 1 y;");
8492 assert_eq!(diags.len(), 1);
8493 assert_eq!(diags[0].message, "expected `;` after declaration");
8494 assert!(ast.top_level.is_empty());
8495 }
8496
8497 #[test]
8498 fn expression_statements_still_parse() {
8499 let (ast, diags) = parse_str("42;");
8500 assert!(diags.is_empty());
8501 assert_eq!(ast.top_level.len(), 1);
8502 }
8503
8504 #[test]
8505 fn snapshot_mixed_declarations() {
8506 let (ast, diags) =
8507 parse_str(r#"let x = 1; const y: string = "hi"; let z: number = 1 + 2;"#);
8508 assert!(diags.is_empty());
8509 assert_eq!(ast.top_level.len(), 3);
8510 insta::assert_debug_snapshot!(ast);
8511 }
8512
8513 #[test]
8514 fn parse_function_typical() {
8515 let (ast, diags) = parse_str("function f(a: number, b: string): boolean { }");
8516 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8517 let stmt = single_stmt(&ast);
8518 match stmt.kind {
8519 crate::StmtKind::Function {
8520 ref name,
8521 ref generics,
8522 ref params,
8523 ref return_type,
8524 body,
8525 ..
8526 } => {
8527 assert_eq!(name.name, "f");
8528 assert_eq!(name.span, crate::Span::new(F, 9, 10).unwrap());
8529 assert!(generics.is_empty());
8530 assert_eq!(params.len(), 2);
8531 assert_eq!(params[0].name.name, "a");
8532 assert_eq!(params[0].name.span, crate::Span::new(F, 11, 12).unwrap());
8533 let p0_ty = params[0]
8534 .ty
8535 .as_ref()
8536 .expect("function-decl param requires annotation");
8537 assert!(matches!(p0_ty.kind, crate::TypeAnnotationKind::Name { .. }));
8538 assert_eq!(p0_ty.span, crate::Span::new(F, 14, 20).unwrap()); assert_eq!(params[1].name.name, "b");
8540 assert_eq!(params[1].name.span, crate::Span::new(F, 22, 23).unwrap());
8541 let p1_ty = params[1]
8542 .ty
8543 .as_ref()
8544 .expect("function-decl param requires annotation");
8545 assert_eq!(p1_ty.span, crate::Span::new(F, 25, 31).unwrap()); let return_type = return_type.as_ref().expect("plain return type");
8547 assert!(matches!(
8548 return_type.kind,
8549 crate::TypeAnnotationKind::Name { .. }
8550 ));
8551 assert_eq!(return_type.span, crate::Span::new(F, 34, 41).unwrap()); let block = ast.try_stmt(body).unwrap();
8553 assert!(matches!(block.kind, crate::StmtKind::Block(ref v) if v.is_empty()));
8554 }
8555 _ => panic!("expected Function"),
8556 }
8557 }
8558
8559 #[test]
8560 fn parse_function_with_doc_comment() {
8561 let (ast, diags) = parse_str(
8562 "/**\n * Sum two numbers.\n * @param a First.\n * @param b Second.\n */\nfunction add(a: number, b: number): number { return a + b; }",
8563 );
8564 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8565 let stmt = single_stmt(&ast);
8566 match &stmt.kind {
8567 crate::StmtKind::Function { doc, .. } => {
8568 let d = doc.as_ref().expect("doc attached");
8569 assert_eq!(d.summary, "Sum two numbers.");
8570 assert_eq!(d.params.len(), 2);
8571 assert_eq!(d.params[0].name, "a");
8572 assert_eq!(d.params[1].name, "b");
8573 }
8574 _ => panic!("expected Function"),
8575 }
8576 }
8577
8578 #[test]
8579 fn parse_interface_member_docs() {
8580 let (ast, diags) = parse_str(
8581 "interface Foo {\n /** First method. */ first(): void;\n /** A field. */ field: number;\n}",
8582 );
8583 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8584 let stmt = single_stmt(&ast);
8585 match &stmt.kind {
8586 crate::StmtKind::InterfaceDecl { members, .. } => {
8587 assert_eq!(members.len(), 2);
8588 match &members[0] {
8589 crate::InterfaceMember::Method { doc, .. } => {
8590 assert_eq!(doc.as_ref().unwrap().summary, "First method.");
8591 }
8592 _ => panic!("expected Method first"),
8593 }
8594 match &members[1] {
8595 crate::InterfaceMember::Property { doc, .. } => {
8596 assert_eq!(doc.as_ref().unwrap().summary, "A field.");
8597 }
8598 _ => panic!("expected Property second"),
8599 }
8600 }
8601 _ => panic!("expected InterfaceDecl"),
8602 }
8603 }
8604
8605 #[test]
8606 fn parse_numeric_enum_all_implicit() {
8607 let (ast, diags) = parse_str("enum Direction { Up, Down, Left, Right }");
8608 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8609 let stmt = single_stmt(&ast);
8610 match &stmt.kind {
8611 crate::StmtKind::EnumDecl { name, members, doc } => {
8612 assert_eq!(name.name, "Direction");
8613 assert!(doc.is_none());
8614 assert_eq!(members.len(), 4);
8615 for (m, expected) in members.iter().zip(["Up", "Down", "Left", "Right"]) {
8616 assert_eq!(m.name.name, expected);
8617 assert!(m.value.is_none(), "expected no initializer on {expected}");
8618 }
8619 }
8620 _ => panic!("expected EnumDecl"),
8621 }
8622 }
8623
8624 #[test]
8625 fn parse_string_enum_all_explicit() {
8626 let (ast, diags) =
8627 parse_str("enum Status { Active = \"active\", Inactive = \"inactive\" }");
8628 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8629 let stmt = single_stmt(&ast);
8630 match &stmt.kind {
8631 crate::StmtKind::EnumDecl { name, members, .. } => {
8632 assert_eq!(name.name, "Status");
8633 assert_eq!(members.len(), 2);
8634 match &members[0].value {
8635 Some(crate::EnumInitializer::String { value, .. }) => {
8636 assert_eq!(value, "active");
8637 }
8638 other => panic!("expected String init, got {other:?}"),
8639 }
8640 match &members[1].value {
8641 Some(crate::EnumInitializer::String { value, .. }) => {
8642 assert_eq!(value, "inactive");
8643 }
8644 other => panic!("expected String init, got {other:?}"),
8645 }
8646 }
8647 _ => panic!("expected EnumDecl"),
8648 }
8649 }
8650
8651 #[test]
8652 fn parse_numeric_enum_with_negative_init() {
8653 let (ast, diags) = parse_str("enum D { Up = 1, Down = -1 }");
8654 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8655 let stmt = single_stmt(&ast);
8656 match &stmt.kind {
8657 crate::StmtKind::EnumDecl { members, .. } => {
8658 assert_eq!(members.len(), 2);
8659 match &members[0].value {
8660 Some(crate::EnumInitializer::Number { value, .. }) => {
8661 assert_eq!(*value, 1.0);
8662 }
8663 other => panic!("expected Number init, got {other:?}"),
8664 }
8665 match &members[1].value {
8666 Some(crate::EnumInitializer::Number { value, .. }) => {
8667 assert_eq!(*value, -1.0);
8668 }
8669 other => panic!("expected Number init, got {other:?}"),
8670 }
8671 }
8672 _ => panic!("expected EnumDecl"),
8673 }
8674 }
8675
8676 #[test]
8677 fn parse_enum_mixed_kinds_is_parse_error() {
8678 let (_ast, diags) = parse_str("enum Mix { A = 1, B = \"two\" }");
8679 assert!(
8680 diags
8681 .iter()
8682 .any(|d| d.message.contains("mixed numeric and string")),
8683 "expected mixed-kind diagnostic, got: {diags:?}"
8684 );
8685 }
8686
8687 #[test]
8688 fn parse_enum_member_docs() {
8689 let (ast, diags) = parse_str(
8690 "enum D {\n /** The first one. */ A = 1,\n /** The second one. */ B = 2,\n}",
8691 );
8692 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8693 let stmt = single_stmt(&ast);
8694 match &stmt.kind {
8695 crate::StmtKind::EnumDecl { members, .. } => {
8696 assert_eq!(members.len(), 2);
8697 assert_eq!(
8698 members[0].doc.as_ref().expect("first doc").summary,
8699 "The first one."
8700 );
8701 assert_eq!(
8702 members[1].doc.as_ref().expect("second doc").summary,
8703 "The second one."
8704 );
8705 }
8706 _ => panic!("expected EnumDecl"),
8707 }
8708 }
8709
8710 #[test]
8711 fn parse_enum_trailing_comma_ok() {
8712 let (ast, diags) = parse_str("enum D { A, B, }");
8713 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8714 let stmt = single_stmt(&ast);
8715 match &stmt.kind {
8716 crate::StmtKind::EnumDecl { members, .. } => assert_eq!(members.len(), 2),
8717 _ => panic!("expected EnumDecl"),
8718 }
8719 }
8720
8721 #[test]
8722 fn parse_function_empty_params() {
8723 let (ast, diags) = parse_str("function noop(): void { }");
8724 assert!(diags.is_empty());
8725 let stmt = single_stmt(&ast);
8726 match stmt.kind {
8727 crate::StmtKind::Function { ref params, .. } => assert!(params.is_empty()),
8728 _ => panic!("expected Function"),
8729 }
8730 }
8731
8732 #[test]
8733 fn parse_generic_function_single_param() {
8734 let (ast, diags) = parse_str("function identity<T>(x: T): T { return x; }");
8735 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8736 let stmt = single_stmt(&ast);
8737 match stmt.kind {
8738 crate::StmtKind::Function {
8739 ref name,
8740 ref generics,
8741 ref params,
8742 ..
8743 } => {
8744 assert_eq!(name.name, "identity");
8745 assert_eq!(generics.len(), 1);
8746 assert_eq!(generics[0].name, "T");
8747 assert_eq!(params.len(), 1);
8748 assert_eq!(params[0].name.name, "x");
8749 }
8750 _ => panic!("expected Function"),
8751 }
8752 }
8753
8754 #[test]
8755 fn parse_generic_function_multiple_params() {
8756 let (ast, diags) = parse_str("function pair<T, U>(a: T, b: U): T { return a; }");
8757 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8758 let stmt = single_stmt(&ast);
8759 match stmt.kind {
8760 crate::StmtKind::Function { ref generics, .. } => {
8761 assert_eq!(generics.len(), 2);
8762 assert_eq!(generics[0].name, "T");
8763 assert_eq!(generics[1].name, "U");
8764 }
8765 _ => panic!("expected Function"),
8766 }
8767 }
8768
8769 #[test]
8770 fn parse_non_generic_function_has_empty_generics() {
8771 let (ast, diags) = parse_str("function noop(): void { }");
8772 assert!(diags.is_empty());
8773 let stmt = single_stmt(&ast);
8774 match stmt.kind {
8775 crate::StmtKind::Function { ref generics, .. } => assert!(generics.is_empty()),
8776 _ => panic!("expected Function"),
8777 }
8778 }
8779
8780 #[test]
8781 fn parse_empty_generic_list_diagnoses() {
8782 let (_ast, diags) = parse_str("function f<>(): void { }");
8783 assert!(
8784 diags
8785 .iter()
8786 .any(|d| d.message.contains("empty generic parameter list")),
8787 "expected empty-generic-list diagnostic, got: {diags:?}",
8788 );
8789 }
8790
8791 #[test]
8792 fn parse_trailing_comma_in_generic_list_ok() {
8793 let (ast, diags) = parse_str("function f<T,>(): void { }");
8794 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8795 match single_stmt(&ast).kind {
8796 crate::StmtKind::Function { ref generics, .. } => assert_eq!(generics.len(), 1),
8797 _ => panic!("expected Function"),
8798 }
8799 }
8800
8801 #[test]
8802 fn parse_generic_call_single_type_arg() {
8803 let (ast, diags) = parse_str("identity<number>(42);");
8804 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8805 let stmt = single_stmt(&ast);
8806 let crate::StmtKind::Expr(call_id) = stmt.kind else {
8807 panic!("expected Expr stmt");
8808 };
8809 match &ast.try_expr(call_id).unwrap().kind {
8810 crate::ExprKind::Call {
8811 type_args, args, ..
8812 } => {
8813 let ta = type_args.as_ref().expect("type_args set for generic call");
8814 assert_eq!(ta.len(), 1);
8815 assert_eq!(args.len(), 1);
8816 }
8817 other => panic!("expected Call, got {other:?}"),
8818 }
8819 }
8820
8821 #[test]
8822 fn parse_generic_call_trailing_comma_ok() {
8823 let (ast, diags) = parse_str("identity<number,>(42);");
8824 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8825 let stmt = single_stmt(&ast);
8826 let crate::StmtKind::Expr(call_id) = stmt.kind else {
8827 panic!("expected Expr stmt");
8828 };
8829 match &ast.try_expr(call_id).unwrap().kind {
8830 crate::ExprKind::Call {
8831 type_args, args, ..
8832 } => {
8833 assert_eq!(type_args.as_ref().expect("type_args set").len(), 1);
8834 assert_eq!(args.len(), 1);
8835 }
8836 other => panic!("expected Call, got {other:?}"),
8837 }
8838 }
8839
8840 #[test]
8841 fn parse_generic_call_multi_type_args() {
8842 let (ast, diags) = parse_str("pair<number, string>(1, \"x\");");
8843 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8844 let stmt = single_stmt(&ast);
8845 let crate::StmtKind::Expr(call_id) = stmt.kind else {
8846 panic!("expected Expr stmt");
8847 };
8848 match &ast.try_expr(call_id).unwrap().kind {
8849 crate::ExprKind::Call { type_args, .. } => {
8850 let ta = type_args.as_ref().expect("type_args set for generic call");
8851 assert_eq!(ta.len(), 2);
8852 }
8853 other => panic!("expected Call, got {other:?}"),
8854 }
8855 }
8856
8857 #[test]
8858 fn parse_comparison_chain_unaffected_by_generic_lookahead() {
8859 let (ast, diags) = parse_str("let z = a < b > c;");
8860 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8861 assert!(
8862 !ast_contains_call_with_type_args(&ast),
8863 "should not have parsed any generic call",
8864 );
8865 }
8866
8867 #[test]
8868 fn parse_lt_with_paren_rhs_is_comparison_not_generic() {
8869 let (ast, diags) = parse_str("let z = a < (b);");
8870 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8871 assert!(!ast_contains_call_with_type_args(&ast));
8872 }
8873
8874 #[test]
8875 fn parse_generic_call_no_args() {
8876 let (ast, diags) = parse_str("none<number>();");
8877 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8878 let stmt = single_stmt(&ast);
8879 let crate::StmtKind::Expr(call_id) = stmt.kind else {
8880 panic!("expected Expr stmt");
8881 };
8882 match &ast.try_expr(call_id).unwrap().kind {
8883 crate::ExprKind::Call {
8884 type_args, args, ..
8885 } => {
8886 assert_eq!(type_args.as_ref().unwrap().len(), 1);
8887 assert!(args.is_empty());
8888 }
8889 other => panic!("expected Call, got {other:?}"),
8890 }
8891 }
8892
8893 fn ast_contains_call_with_type_args(ast: &crate::Ast) -> bool {
8894 for id in 0..ast.top_level.len() {
8895 let stmt = ast.try_stmt(ast.top_level[id]).unwrap();
8896 if walk_stmt_for_typed_call(ast, stmt) {
8897 return true;
8898 }
8899 }
8900 false
8901 }
8902
8903 fn walk_stmt_for_typed_call(ast: &crate::Ast, stmt: &crate::Stmt) -> bool {
8904 match &stmt.kind {
8905 crate::StmtKind::Let { value, .. } | crate::StmtKind::Const { value, .. } => {
8906 walk_expr_for_typed_call(ast, *value)
8907 }
8908 crate::StmtKind::Expr(e) => walk_expr_for_typed_call(ast, *e),
8909 crate::StmtKind::Block(stmts) => stmts
8910 .iter()
8911 .any(|sid| walk_stmt_for_typed_call(ast, ast.try_stmt(*sid).unwrap())),
8912 _ => false,
8913 }
8914 }
8915
8916 fn walk_expr_for_typed_call(ast: &crate::Ast, id: crate::ExprId) -> bool {
8917 match &ast.try_expr(id).unwrap().kind {
8918 crate::ExprKind::Call {
8919 type_args: Some(_), ..
8920 } => true,
8921 crate::ExprKind::Call { callee, args, .. } => {
8922 walk_expr_for_typed_call(ast, *callee)
8923 || args.iter().any(|a| walk_expr_for_typed_call(ast, *a))
8924 }
8925 crate::ExprKind::Binary { lhs, rhs, .. } => {
8926 walk_expr_for_typed_call(ast, *lhs) || walk_expr_for_typed_call(ast, *rhs)
8927 }
8928 crate::ExprKind::Unary { operand, .. } => walk_expr_for_typed_call(ast, *operand),
8929 crate::ExprKind::Paren(e) => walk_expr_for_typed_call(ast, *e),
8930 crate::ExprKind::FieldAccess { receiver, .. } => {
8931 walk_expr_for_typed_call(ast, *receiver)
8932 }
8933 crate::ExprKind::IndexAccess { receiver, index } => {
8934 walk_expr_for_typed_call(ast, *receiver) || walk_expr_for_typed_call(ast, *index)
8935 }
8936 _ => false,
8937 }
8938 }
8939
8940 #[test]
8941 fn parse_function_body_with_statements() {
8942 let (ast, diags) = parse_str("function g(x: number): number { let y = x; y; }");
8943 assert!(diags.is_empty());
8944 let stmt = single_stmt(&ast);
8945 match stmt.kind {
8946 crate::StmtKind::Function { body, .. } => {
8947 let block = ast.try_stmt(body).unwrap();
8948 match block.kind {
8949 crate::StmtKind::Block(ref v) => assert_eq!(v.len(), 2),
8950 _ => panic!("expected Block"),
8951 }
8952 }
8953 _ => panic!("expected Function"),
8954 }
8955 }
8956
8957 #[test]
8958 fn function_missing_return_type_diagnoses() {
8959 let (ast, diags) = parse_str("function f() { }");
8960 assert!(!diags.is_empty());
8961 assert_eq!(diags[0].message, "expected `:` and return type");
8962 assert!(ast.top_level.is_empty());
8963 }
8964
8965 #[test]
8966 fn function_missing_param_type_diagnoses() {
8967 let (ast, diags) = parse_str("function f(a): void { }");
8968 assert!(!diags.is_empty());
8969 assert_eq!(diags[0].message, "parameter requires a type annotation");
8970 assert!(ast.top_level.is_empty());
8971 }
8972
8973 #[test]
8974 fn function_missing_name_diagnoses() {
8975 let (ast, diags) = parse_str("function (x: number): void { }");
8976 assert!(!diags.is_empty());
8977 assert_eq!(diags[0].message, "expected function name");
8978 assert!(ast.top_level.is_empty());
8979 }
8980
8981 #[test]
8982 fn function_trailing_param_comma_ok() {
8983 let (ast, diags) = parse_str("function f(a: number,): void { }");
8984 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8985 match single_stmt(&ast).kind {
8986 crate::StmtKind::Function { ref params, .. } => assert_eq!(params.len(), 1),
8987 _ => panic!("expected Function"),
8988 }
8989 }
8990
8991 #[test]
8992 fn function_missing_open_brace_diagnoses() {
8993 let (ast, diags) = parse_str("function f(): void");
8994 assert_eq!(diags.len(), 1);
8995 assert_eq!(diags[0].message, "expected `{`");
8996 assert!(ast.top_level.is_empty());
8997 }
8998
8999 #[test]
9000 fn snapshot_function_declaration() {
9001 let (ast, diags) =
9002 parse_str("function add(a: number, b: number): number { let sum = a + b; sum; }");
9003 assert!(diags.is_empty());
9004 insta::assert_debug_snapshot!(ast);
9005 }
9006
9007 #[test]
9008 fn rest_param_basic_parses() {
9009 let (_, diags) = parse_str("function sum(...nums: number[]): number { return 0; }");
9010 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9011 }
9012
9013 #[test]
9014 fn rest_param_with_fixed_prefix_parses() {
9015 let (_, diags) =
9016 parse_str("function tag(label: string, ...vals: number[]): string { return label; }");
9017 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9018 }
9019
9020 #[test]
9021 fn rest_param_in_arrow_parses() {
9022 let (_, diags) = parse_str("const f = (...n: number[]) => n.length;");
9023 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9024 }
9025
9026 #[test]
9027 fn rest_param_in_function_type_annotation_parses() {
9028 let (_, diags) =
9029 parse_str("let f: (a: number, ...rest: string[]) => void = (a, ...r) => {};");
9030 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9031 }
9032
9033 #[test]
9034 fn rest_param_not_last_rejected() {
9035 let (_, diags) = parse_str("function f(...xs: number[], y: number): void {}");
9036 assert!(
9037 diags.iter().any(|d| d
9038 .message
9039 .contains("rest parameter must be the last parameter")),
9040 "expected 'must be last' diagnostic, got {diags:?}",
9041 );
9042 }
9043
9044 #[test]
9045 fn rest_param_with_default_rejected() {
9046 let (_, diags) = parse_str("function f(...xs: number[] = []): void {}");
9047 assert!(
9048 diags.iter().any(|d| d
9049 .message
9050 .contains("rest parameter cannot have a default value")),
9051 "expected default-rejection diagnostic, got {diags:?}",
9052 );
9053 }
9054
9055 #[test]
9056 fn rest_param_without_annotation_rejected() {
9057 let (_, diags) = parse_str("function f(...xs): void {}");
9058 assert!(
9059 diags.iter().any(|d| d
9060 .message
9061 .contains("rest parameter requires a type annotation")),
9062 "expected missing-annotation diagnostic, got {diags:?}",
9063 );
9064 }
9065
9066 #[test]
9067 fn rest_param_destructured_rejected() {
9068 let (_, diags) = parse_str("function f(...{a, b}): void {}");
9069 assert!(
9070 diags
9071 .iter()
9072 .any(|d| d.message.contains("rest parameter cannot be destructured")),
9073 "expected destructured-rejection diagnostic, got {diags:?}",
9074 );
9075 }
9076
9077 #[test]
9078 fn rest_param_in_function_type_with_non_array_rejected() {
9079 let (_, diags) = parse_str("let f: (...xs: number) => void = () => {};");
9080 assert!(
9081 diags
9082 .iter()
9083 .any(|d| d.message.contains("rest parameter type must be an array")),
9084 "expected non-array-type diagnostic, got {diags:?}",
9085 );
9086 }
9087
9088 fn call(e: &crate::Expr) -> (crate::ExprId, &[crate::ExprId]) {
9089 match e.kind {
9090 crate::ExprKind::Call {
9091 callee, ref args, ..
9092 } => (callee, args.as_slice()),
9093 _ => panic!("expected Call, got {:?}", e.kind),
9094 }
9095 }
9096
9097 #[test]
9098 fn parse_call_no_args() {
9099 let (ast, diags) = parse_str("f();");
9100 assert!(diags.is_empty());
9101 let outer = expr_of_single_stmt(&ast);
9102 let (callee, args) = call(outer);
9103 assert!(matches!(
9104 ast.try_expr(callee).unwrap().kind,
9105 crate::ExprKind::Identifier(_)
9106 ));
9107 assert!(args.is_empty());
9108 assert_eq!(outer.span, crate::Span::new(F, 0, 3).unwrap());
9109 }
9110
9111 #[test]
9112 fn parse_call_one_arg() {
9113 let (ast, diags) = parse_str("f(1);");
9114 assert!(diags.is_empty());
9115 let outer = expr_of_single_stmt(&ast);
9116 let (callee, args) = call(outer);
9117 assert!(matches!(
9118 ast.try_expr(callee).unwrap().kind,
9119 crate::ExprKind::Identifier(_)
9120 ));
9121 assert_eq!(args.len(), 1);
9122 assert_eq!(
9123 ast.try_expr(args[0]).unwrap().kind,
9124 crate::ExprKind::Number(1.0)
9125 );
9126 }
9127
9128 #[test]
9129 fn parse_call_three_args() {
9130 let (ast, diags) = parse_str("f(1, 2, 3);");
9131 assert!(diags.is_empty());
9132 let outer = expr_of_single_stmt(&ast);
9133 let (_callee, args) = call(outer);
9134 assert_eq!(args.len(), 3);
9135 assert_eq!(
9136 ast.try_expr(args[0]).unwrap().kind,
9137 crate::ExprKind::Number(1.0)
9138 );
9139 assert_eq!(
9140 ast.try_expr(args[1]).unwrap().kind,
9141 crate::ExprKind::Number(2.0)
9142 );
9143 assert_eq!(
9144 ast.try_expr(args[2]).unwrap().kind,
9145 crate::ExprKind::Number(3.0)
9146 );
9147 }
9148
9149 #[test]
9150 fn parse_call_nested() {
9151 let (ast, diags) = parse_str("f(g(x));");
9152 assert!(diags.is_empty());
9153 let outer = expr_of_single_stmt(&ast);
9154 let (_callee, args) = call(outer);
9155 assert_eq!(args.len(), 1);
9156 let (_inner_callee, inner_args) = call(ast.try_expr(args[0]).unwrap());
9157 assert_eq!(inner_args.len(), 1);
9158 assert!(matches!(
9159 ast.try_expr(inner_args[0]).unwrap().kind,
9160 crate::ExprKind::Identifier(_)
9161 ));
9162 }
9163
9164 #[test]
9165 fn parse_curried_call() {
9166 let (ast, diags) = parse_str("f()(x);");
9167 assert!(diags.is_empty());
9168 let outer = expr_of_single_stmt(&ast);
9169 let (callee, args) = call(outer);
9170 assert_eq!(args.len(), 1);
9171 let (_inner_callee, inner_args) = call(ast.try_expr(callee).unwrap());
9172 assert!(inner_args.is_empty());
9173 }
9174
9175 #[test]
9176 fn unary_binds_looser_than_call() {
9177 let (ast, diags) = parse_str("-f();");
9178 assert!(diags.is_empty());
9179 let outer = expr_of_single_stmt(&ast);
9180 let (op, operand) = unary(outer);
9181 assert_eq!(op, crate::UnOp::Neg);
9182 let (_callee, args) = call(ast.try_expr(operand).unwrap());
9183 assert!(args.is_empty());
9184 }
9185
9186 #[test]
9187 fn call_inside_binary() {
9188 let (ast, diags) = parse_str("f() + 1;");
9189 assert!(diags.is_empty());
9190 let outer = expr_of_single_stmt(&ast);
9191 let (op, lhs, rhs) = binary(outer);
9192 assert_eq!(op, crate::BinOp::Add);
9193 let (_callee, args) = call(ast.try_expr(lhs).unwrap());
9194 assert!(args.is_empty());
9195 assert_eq!(
9196 ast.try_expr(rhs).unwrap().kind,
9197 crate::ExprKind::Number(1.0)
9198 );
9199 }
9200
9201 #[test]
9202 fn call_with_complex_args() {
9203 let (ast, diags) = parse_str("f(1 + 2, g(x));");
9204 assert!(diags.is_empty());
9205 let outer = expr_of_single_stmt(&ast);
9206 let (_callee, args) = call(outer);
9207 assert_eq!(args.len(), 2);
9208 let (op, ..) = binary(ast.try_expr(args[0]).unwrap());
9209 assert_eq!(op, crate::BinOp::Add);
9210 let (_, inner_args) = call(ast.try_expr(args[1]).unwrap());
9211 assert_eq!(inner_args.len(), 1);
9212 }
9213
9214 #[test]
9215 fn call_trailing_comma_ok() {
9216 let (ast, diags) = parse_str("f(1, 2,);");
9217 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9218 let (_callee, args) = call(expr_of_single_stmt(&ast));
9219 assert_eq!(args.len(), 2);
9220 }
9221
9222 #[test]
9223 fn unclosed_call_diagnoses() {
9224 let (ast, diags) = parse_str("f(1, 2");
9225 assert!(!diags.is_empty());
9226 assert!(diags[0].message == "expected `,` or `)`" || diags[0].message == "expected `)`");
9227 assert!(ast.top_level.is_empty());
9228 }
9229
9230 #[test]
9231 fn call_expression_statement() {
9232 let (ast, diags) = parse_str(r#"greet("world");"#);
9233 assert!(diags.is_empty());
9234 let outer = expr_of_single_stmt(&ast);
9235 let (callee, args) = call(outer);
9236 assert!(matches!(
9237 ast.try_expr(callee).unwrap().kind,
9238 crate::ExprKind::Identifier(_)
9239 ));
9240 assert_eq!(args.len(), 1);
9241 assert_eq!(
9242 ast.try_expr(args[0]).unwrap().kind,
9243 crate::ExprKind::String("world".to_string())
9244 );
9245 }
9246
9247 #[test]
9248 fn snapshot_multi_call_program() {
9249 let (ast, diags) = parse_str("f(); g(1); h(f(2), 3);");
9250 assert!(diags.is_empty());
9251 assert_eq!(ast.top_level.len(), 3);
9252 insta::assert_debug_snapshot!(ast);
9253 }
9254
9255 #[test]
9256 fn parse_if_simple() {
9257 let (ast, diags) = parse_str("if (a) { }");
9258 assert!(diags.is_empty());
9259 let stmt = single_stmt(&ast);
9260 match stmt.kind {
9261 crate::StmtKind::If {
9262 condition,
9263 then_block,
9264 else_block,
9265 } => {
9266 assert!(matches!(
9267 ast.try_expr(condition).unwrap().kind,
9268 crate::ExprKind::Identifier(_)
9269 ));
9270 assert!(matches!(
9271 ast.try_stmt(then_block).unwrap().kind,
9272 crate::StmtKind::Block(ref v) if v.is_empty()
9273 ));
9274 assert!(else_block.is_none());
9275 }
9276 _ => panic!("expected If"),
9277 }
9278 }
9279
9280 #[test]
9281 fn parse_if_else() {
9282 let (ast, diags) = parse_str("if (a) { } else { }");
9283 assert!(diags.is_empty());
9284 let stmt = single_stmt(&ast);
9285 match stmt.kind {
9286 crate::StmtKind::If { else_block, .. } => {
9287 let else_id = else_block.expect("expected else block");
9288 assert!(matches!(
9289 ast.try_stmt(else_id).unwrap().kind,
9290 crate::StmtKind::Block(ref v) if v.is_empty()
9291 ));
9292 }
9293 _ => panic!("expected If"),
9294 }
9295 }
9296
9297 #[test]
9298 fn parse_else_if_chain() {
9299 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
9300 assert!(diags.is_empty());
9301 let stmt = single_stmt(&ast);
9302 let crate::StmtKind::If {
9303 else_block: Some(inner_id),
9304 ..
9305 } = stmt.kind
9306 else {
9307 panic!("expected outer If with else_block");
9308 };
9309 let inner = ast.try_stmt(inner_id).unwrap();
9310 let crate::StmtKind::If {
9311 else_block: Some(tail_id),
9312 ..
9313 } = inner.kind
9314 else {
9315 panic!("expected inner If for `else if`");
9316 };
9317 assert!(matches!(
9318 ast.try_stmt(tail_id).unwrap().kind,
9319 crate::StmtKind::Block(_)
9320 ));
9321 }
9322
9323 #[test]
9324 fn parse_if_with_body_statements() {
9325 let (ast, diags) = parse_str("if (a) { let x = 1; }");
9326 assert!(diags.is_empty());
9327 let stmt = single_stmt(&ast);
9328 match stmt.kind {
9329 crate::StmtKind::If { then_block, .. } => {
9330 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind
9331 else {
9332 panic!("expected Block")
9333 };
9334 assert_eq!(body.len(), 1);
9335 }
9336 _ => panic!("expected If"),
9337 }
9338 }
9339
9340 #[test]
9341 fn parse_if_braceless_body() {
9342 let (ast, diags) = parse_str("if (a) return b;");
9343 assert!(diags.is_empty(), "{diags:?}");
9344 let stmt = single_stmt(&ast);
9345 let crate::StmtKind::If {
9346 then_block,
9347 else_block,
9348 ..
9349 } = stmt.kind
9350 else {
9351 panic!("expected If");
9352 };
9353 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9354 panic!("braceless body should wrap in a Block");
9355 };
9356 assert_eq!(body.len(), 1);
9357 assert!(matches!(
9358 ast.try_stmt(body[0]).unwrap().kind,
9359 crate::StmtKind::Return(_)
9360 ));
9361 assert!(else_block.is_none());
9362 }
9363
9364 #[test]
9365 fn parse_braceless_dangling_else_binds_nearest_if() {
9366 let (ast, diags) = parse_str("if (a) if (b) x(); else y();");
9368 assert!(diags.is_empty(), "{diags:?}");
9369 let crate::StmtKind::If {
9370 then_block,
9371 else_block: outer_else,
9372 ..
9373 } = single_stmt(&ast).kind
9374 else {
9375 panic!("expected outer If");
9376 };
9377 assert!(outer_else.is_none(), "else must bind to the inner if");
9378 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9379 panic!("expected wrapped Block");
9380 };
9381 let crate::StmtKind::If {
9382 else_block: Some(_),
9383 ..
9384 } = ast.try_stmt(body[0]).unwrap().kind
9385 else {
9386 panic!("inner If should own the else");
9387 };
9388 }
9389
9390 #[test]
9391 fn parse_while_braceless_body() {
9392 let (ast, diags) = parse_str("while (a) x();");
9393 assert!(diags.is_empty(), "{diags:?}");
9394 let crate::StmtKind::While { body, .. } = single_stmt(&ast).kind else {
9395 panic!("expected While");
9396 };
9397 assert!(matches!(
9398 ast.try_stmt(body).unwrap().kind,
9399 crate::StmtKind::Block(ref v) if v.len() == 1
9400 ));
9401 }
9402
9403 #[test]
9404 fn parse_loop_with_empty_body() {
9405 for src in [
9406 "while (a());",
9407 "for (const x of xs);",
9408 "for (let i = 0; i < 3; i++);",
9409 "do ; while (a());",
9410 ] {
9411 let (ast, diags) = parse_str(src);
9412 assert!(diags.is_empty(), "{src}: {diags:?}");
9413 let body = match single_stmt(&ast).kind {
9414 crate::StmtKind::While { body, .. }
9415 | crate::StmtKind::DoWhile { body, .. }
9416 | crate::StmtKind::For { body, .. }
9417 | crate::StmtKind::ForOf { body, .. } => body,
9418 ref other => panic!("{src}: expected a loop, got {other:?}"),
9419 };
9420 assert!(
9421 matches!(ast.try_stmt(body).unwrap().kind, crate::StmtKind::Block(ref v) if v.is_empty()),
9422 "{src}"
9423 );
9424 }
9425 }
9426
9427 #[test]
9428 fn parse_if_with_empty_body_is_an_error() {
9429 let (_ast, diags) = parse_str("if (a);");
9430 assert_eq!(diags[0].message, "`if` has an empty body");
9431 }
9432
9433 #[test]
9434 fn parse_empty_branch_body_reports_once() {
9435 let (_ast, diags) = parse_str("if (a) ; else b();");
9436 assert_eq!(diags.len(), 1, "{diags:?}");
9437 assert_eq!(diags[0].message, "`if` has an empty body");
9438 let (_ast, diags) = parse_str("if (a) b(); else ;");
9439 assert_eq!(diags.len(), 1, "{diags:?}");
9440 assert_eq!(diags[0].message, "`else` has an empty body");
9441 let (_ast, diags) = parse_str("if (a) ; else if (b) ; else ;");
9443 let messages: Vec<_> = diags.iter().map(|d| d.message.as_str()).collect();
9444 assert_eq!(
9445 messages,
9446 [
9447 "`if` has an empty body",
9448 "`if` has an empty body",
9449 "`else` has an empty body"
9450 ]
9451 );
9452 }
9453
9454 #[test]
9455 fn parse_for_of_braceless_body() {
9456 let (ast, diags) = parse_str("for (const x of xs) f(x);");
9457 assert!(diags.is_empty(), "{diags:?}");
9458 let crate::StmtKind::ForOf { body, .. } = single_stmt(&ast).kind else {
9459 panic!("expected ForOf");
9460 };
9461 assert!(matches!(
9462 ast.try_stmt(body).unwrap().kind,
9463 crate::StmtKind::Block(ref v) if v.len() == 1
9464 ));
9465 }
9466
9467 #[test]
9468 fn parse_nested_if() {
9469 let (ast, diags) = parse_str("if (a) { if (b) { } }");
9470 assert!(diags.is_empty());
9471 let outer = single_stmt(&ast);
9472 let crate::StmtKind::If { then_block, .. } = outer.kind else {
9473 panic!("expected outer If");
9474 };
9475 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9476 panic!("expected Block")
9477 };
9478 assert_eq!(body.len(), 1);
9479 assert!(matches!(
9480 ast.try_stmt(body[0]).unwrap().kind,
9481 crate::StmtKind::If { .. }
9482 ));
9483 }
9484
9485 #[test]
9486 fn if_missing_open_paren_diagnoses() {
9487 let (ast, diags) = parse_str("if a { }");
9488 assert!(!diags.is_empty());
9489 assert_eq!(diags[0].message, "expected `(`");
9490 assert!(ast.top_level.is_empty());
9491 }
9492
9493 #[test]
9494 fn if_missing_close_paren_diagnoses() {
9495 let (ast, diags) = parse_str("if (a { }");
9496 assert!(!diags.is_empty());
9497 assert_eq!(diags[0].message, "expected `)`");
9498 assert!(ast.top_level.is_empty());
9499 }
9500
9501 #[test]
9502 fn if_braceless_body_parses() {
9503 let (ast, diags) = parse_str("if (a) x;");
9504 assert!(diags.is_empty(), "{diags:?}");
9505 let crate::StmtKind::If { then_block, .. } = single_stmt(&ast).kind else {
9506 panic!("expected If");
9507 };
9508 assert!(matches!(
9509 ast.try_stmt(then_block).unwrap().kind,
9510 crate::StmtKind::Block(ref v) if v.len() == 1
9511 ));
9512 }
9513
9514 #[test]
9515 fn snapshot_else_if_chain() {
9516 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
9517 assert!(diags.is_empty());
9518 insta::assert_debug_snapshot!(ast);
9519 }
9520
9521 #[test]
9522 fn parse_while_simple() {
9523 let (ast, diags) = parse_str("while (a) { }");
9524 assert!(diags.is_empty());
9525 let stmt = single_stmt(&ast);
9526 match stmt.kind {
9527 crate::StmtKind::While { condition, body } => {
9528 assert!(matches!(
9529 ast.try_expr(condition).unwrap().kind,
9530 crate::ExprKind::Identifier(_)
9531 ));
9532 assert!(matches!(
9533 ast.try_stmt(body).unwrap().kind,
9534 crate::StmtKind::Block(ref v) if v.is_empty()
9535 ));
9536 }
9537 _ => panic!("expected While"),
9538 }
9539 }
9540
9541 #[test]
9542 fn parse_while_with_body() {
9543 let (ast, diags) = parse_str("while (a) { x; y; }");
9544 assert!(diags.is_empty());
9545 let stmt = single_stmt(&ast);
9546 let crate::StmtKind::While { body, .. } = stmt.kind else {
9547 panic!("expected While");
9548 };
9549 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
9550 panic!("expected Block")
9551 };
9552 assert_eq!(stmts.len(), 2);
9553 }
9554
9555 #[test]
9556 fn while_missing_paren_diagnoses() {
9557 let (ast, diags) = parse_str("while a { }");
9558 assert!(!diags.is_empty());
9559 assert_eq!(diags[0].message, "expected `(`");
9560 assert!(ast.top_level.is_empty());
9561 }
9562
9563 #[test]
9564 fn parse_for_all_slots() {
9565 let (ast, diags) = parse_str("for (let i = 0; i < 10; i = i + 1) { x; }");
9566 assert!(diags.is_empty(), "{diags:?}");
9567 let stmt = single_stmt(&ast);
9568 let crate::StmtKind::For {
9569 init,
9570 condition,
9571 update,
9572 body,
9573 } = stmt.kind
9574 else {
9575 panic!("expected For");
9576 };
9577 assert!(init.is_some(), "init missing");
9578 assert!(condition.is_some(), "condition missing");
9579 assert!(update.is_some(), "update missing");
9580 assert!(matches!(
9581 ast.try_stmt(body).unwrap().kind,
9582 crate::StmtKind::Block(_)
9583 ));
9584 }
9585
9586 #[test]
9587 fn parse_for_empty_slots() {
9588 let (ast, diags) = parse_str("for (;;) { break; }");
9589 assert!(diags.is_empty(), "{diags:?}");
9590 let stmt = single_stmt(&ast);
9591 let crate::StmtKind::For {
9592 init,
9593 condition,
9594 update,
9595 ..
9596 } = stmt.kind
9597 else {
9598 panic!("expected For");
9599 };
9600 assert!(init.is_none() && condition.is_none() && update.is_none());
9601 }
9602
9603 #[test]
9604 fn parse_for_of_const_binding() {
9605 let (ast, diags) = parse_str("for (const x of arr) { y; }");
9606 assert!(diags.is_empty(), "{diags:?}");
9607 let stmt = single_stmt(&ast);
9608 let crate::StmtKind::ForOf {
9609 binding_kind,
9610 ref name,
9611 ref ty,
9612 ..
9613 } = stmt.kind
9614 else {
9615 panic!("expected ForOf");
9616 };
9617 assert!(matches!(binding_kind, crate::BindingKind::Const));
9618 assert_eq!(name.name, "x");
9619 assert!(ty.is_none());
9620 }
9621
9622 #[test]
9623 fn parse_for_of_let_with_annotation() {
9624 let (ast, diags) = parse_str("for (let x: number of arr) { y; }");
9625 assert!(diags.is_empty(), "{diags:?}");
9626 let stmt = single_stmt(&ast);
9627 let crate::StmtKind::ForOf {
9628 binding_kind,
9629 ref ty,
9630 ..
9631 } = stmt.kind
9632 else {
9633 panic!("expected ForOf");
9634 };
9635 assert!(matches!(binding_kind, crate::BindingKind::Let));
9636 assert!(ty.is_some());
9637 }
9638
9639 #[test]
9640 fn parse_do_while_simple() {
9641 let (ast, diags) = parse_str("do { x; } while (a);");
9642 assert!(diags.is_empty(), "{diags:?}");
9643 let stmt = single_stmt(&ast);
9644 assert!(matches!(stmt.kind, crate::StmtKind::DoWhile { .. }));
9645 }
9646
9647 #[test]
9648 fn parse_break_and_continue() {
9649 let (ast, diags) = parse_str("while (a) { break; continue; }");
9650 assert!(diags.is_empty(), "{diags:?}");
9651 let stmt = single_stmt(&ast);
9652 let crate::StmtKind::While { body, .. } = stmt.kind else {
9653 panic!("expected While");
9654 };
9655 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
9656 panic!("expected Block");
9657 };
9658 assert!(matches!(
9659 ast.try_stmt(stmts[0]).unwrap().kind,
9660 crate::StmtKind::Break
9661 ));
9662 assert!(matches!(
9663 ast.try_stmt(stmts[1]).unwrap().kind,
9664 crate::StmtKind::Continue
9665 ));
9666 }
9667
9668 #[test]
9669 fn parse_bare_return_in_function() {
9670 let (ast, diags) = parse_str("function f(): void { return; }");
9671 assert!(diags.is_empty());
9672 let f = single_stmt(&ast);
9673 let crate::StmtKind::Function { body, .. } = f.kind else {
9674 panic!("expected Function");
9675 };
9676 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
9677 panic!("expected Block")
9678 };
9679 assert_eq!(stmts.len(), 1);
9680 match ast.try_stmt(stmts[0]).unwrap().kind {
9681 crate::StmtKind::Return(None) => {}
9682 _ => panic!("expected Return(None)"),
9683 }
9684 }
9685
9686 #[test]
9687 fn parse_return_with_value() {
9688 let (ast, diags) = parse_str("function f(): number { return 1; }");
9689 assert!(diags.is_empty());
9690 let f = single_stmt(&ast);
9691 let crate::StmtKind::Function { body, .. } = f.kind else {
9692 panic!("expected Function");
9693 };
9694 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
9695 panic!("expected Block")
9696 };
9697 match ast.try_stmt(stmts[0]).unwrap().kind {
9698 crate::StmtKind::Return(Some(value)) => {
9699 assert_eq!(
9700 ast.try_expr(value).unwrap().kind,
9701 crate::ExprKind::Number(1.0)
9702 );
9703 }
9704 _ => panic!("expected Return(Some)"),
9705 }
9706 }
9707
9708 #[test]
9709 fn parse_return_with_expression() {
9710 let (ast, diags) = parse_str("function f(): number { return 1 + 2; }");
9711 assert!(diags.is_empty());
9712 let f = single_stmt(&ast);
9713 let crate::StmtKind::Function { body, .. } = f.kind else {
9714 panic!("expected Function");
9715 };
9716 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
9717 panic!("expected Block")
9718 };
9719 match ast.try_stmt(stmts[0]).unwrap().kind {
9720 crate::StmtKind::Return(Some(value)) => {
9721 let (op, ..) = binary(ast.try_expr(value).unwrap());
9722 assert_eq!(op, crate::BinOp::Add);
9723 }
9724 _ => panic!("expected Return(Some(Binary))"),
9725 }
9726 }
9727
9728 #[test]
9729 fn return_missing_semicolon_diagnoses() {
9730 let (_, diags) = parse_str("function f(): number { return x x; }");
9731 assert!(!diags.is_empty());
9732 assert_eq!(diags[0].message, "expected `;` after return");
9733 }
9734
9735 #[test]
9736 fn parse_bare_block() {
9737 let (ast, diags) = parse_str("{ x; y; }");
9738 assert!(diags.is_empty());
9739 let stmt = single_stmt(&ast);
9740 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
9741 panic!("expected Block");
9742 };
9743 assert_eq!(stmts.len(), 2);
9744 }
9745
9746 #[test]
9747 fn parse_empty_bare_block() {
9748 let (ast, diags) = parse_str("{ }");
9749 assert!(diags.is_empty());
9750 let stmt = single_stmt(&ast);
9751 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
9752 panic!("expected Block");
9753 };
9754 assert!(stmts.is_empty());
9755 }
9756
9757 #[test]
9758 fn parse_nested_bare_blocks() {
9759 let (ast, diags) = parse_str("{ { } }");
9760 assert!(diags.is_empty());
9761 let outer = single_stmt(&ast);
9762 let crate::StmtKind::Block(ref stmts) = outer.kind else {
9763 panic!("expected outer Block");
9764 };
9765 assert_eq!(stmts.len(), 1);
9766 assert!(matches!(
9767 ast.try_stmt(stmts[0]).unwrap().kind,
9768 crate::StmtKind::Block(ref v) if v.is_empty()
9769 ));
9770 }
9771
9772 #[test]
9773 fn snapshot_function_with_if_and_return() {
9774 let source = "function isPositive(n: number): boolean {\n if (n > 0) {\n return true;\n } else {\n return false;\n }\n}";
9775 let (ast, diags) = parse_str(source);
9776 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9777 insta::assert_debug_snapshot!(ast);
9778 }
9779
9780 fn object_literal(e: &crate::Expr) -> &[crate::ObjectLiteralMember] {
9781 match e.kind {
9782 crate::ExprKind::ObjectLiteral { ref members } => members.as_slice(),
9783 _ => panic!("expected ObjectLiteral, got {:?}", e.kind),
9784 }
9785 }
9786
9787 fn object_literal_field(m: &crate::ObjectLiteralMember) -> &crate::ObjectLiteralField {
9788 match m {
9789 crate::ObjectLiteralMember::Field(f) => f,
9790 crate::ObjectLiteralMember::Spread { .. }
9791 | crate::ObjectLiteralMember::Computed { .. } => {
9792 panic!("expected Field member, got Spread")
9793 }
9794 }
9795 }
9796
9797 fn array_literal(e: &crate::Expr) -> &[crate::ArrayLiteralElement] {
9798 match e.kind {
9799 crate::ExprKind::ArrayLiteral { ref elements } => elements.as_slice(),
9800 _ => panic!("expected ArrayLiteral, got {:?}", e.kind),
9801 }
9802 }
9803
9804 fn array_literal_value(el: &crate::ArrayLiteralElement) -> crate::ExprId {
9805 match el {
9806 crate::ArrayLiteralElement::Value(id) => *id,
9807 crate::ArrayLiteralElement::Spread { .. } => {
9808 panic!("expected Value element, got Spread")
9809 }
9810 }
9811 }
9812
9813 #[test]
9814 fn parse_empty_object_literal() {
9815 let (ast, diags) = parse_str("let x = {};");
9816 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9817 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9818 panic!("expected Let");
9819 };
9820 assert!(object_literal(ast.try_expr(value).unwrap()).is_empty());
9821 }
9822
9823 #[test]
9824 fn parse_object_literal_simple() {
9825 let (ast, diags) = parse_str("let p = { x: 1, y: 2 };");
9826 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9827 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9828 panic!("expected Let");
9829 };
9830 let members = object_literal(ast.try_expr(value).unwrap());
9831 assert_eq!(members.len(), 2);
9832 let f0 = object_literal_field(&members[0]);
9833 let f1 = object_literal_field(&members[1]);
9834 assert_eq!(f0.name.name, "x");
9835 assert_eq!(f1.name.name, "y");
9836 assert_eq!(
9837 ast.try_expr(f0.value).unwrap().kind,
9838 crate::ExprKind::Number(1.0)
9839 );
9840 assert_eq!(
9841 ast.try_expr(f1.value).unwrap().kind,
9842 crate::ExprKind::Number(2.0)
9843 );
9844 }
9845
9846 #[test]
9847 fn parse_object_literal_keyword_keys() {
9848 let (ast, diags) = parse_str("let p = { type: 1, default: 2, null: 3, true: 4 };");
9849 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9850 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9851 panic!("expected Let");
9852 };
9853 let names: Vec<&str> = object_literal(ast.try_expr(value).unwrap())
9854 .iter()
9855 .map(object_literal_field)
9856 .map(|f| f.name.name.as_str())
9857 .collect();
9858 assert_eq!(names, vec!["type", "default", "null", "true"]);
9859 }
9860
9861 #[test]
9862 fn parse_object_literal_string_key() {
9863 let (ast, diags) = parse_str(r#"let p = { "hello": 1 };"#);
9864 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9865 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9866 panic!("expected Let");
9867 };
9868 let members = object_literal(ast.try_expr(value).unwrap());
9869 assert_eq!(members.len(), 1);
9870 let f0 = object_literal_field(&members[0]);
9871 assert_eq!(f0.name.name, "hello");
9872 }
9873
9874 #[test]
9875 fn parse_object_literal_string_key_decodes_escape() {
9876 let (ast, diags) = parse_str(r#"let p = { "content\u002dtype": 1 };"#);
9877 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9878 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9879 panic!("expected Let");
9880 };
9881 let members = object_literal(ast.try_expr(value).unwrap());
9882 assert_eq!(members.len(), 1);
9883 let f0 = object_literal_field(&members[0]);
9884 assert_eq!(f0.name.name, "content-type");
9885 }
9886
9887 #[test]
9888 fn parse_object_literal_shorthand() {
9889 let (ast, diags) = parse_str("let p = { x, y: 2 };");
9890 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9891 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9892 panic!("expected Let");
9893 };
9894 let members = object_literal(ast.try_expr(value).unwrap());
9895 assert_eq!(members.len(), 2);
9896 let f0 = object_literal_field(&members[0]);
9897 assert_eq!(f0.name.name, "x");
9898 assert_eq!(
9900 ast.try_expr(f0.value).unwrap().kind,
9901 crate::ExprKind::Identifier(crate::Ident {
9902 name: "x".into(),
9903 span: f0.name.span,
9904 })
9905 );
9906 let f1 = object_literal_field(&members[1]);
9907 assert_eq!(f1.name.name, "y");
9908 assert_eq!(
9909 ast.try_expr(f1.value).unwrap().kind,
9910 crate::ExprKind::Number(2.0)
9911 );
9912 }
9913
9914 #[test]
9915 fn parse_object_literal_string_key_shorthand_rejected() {
9916 let (_ast, diags) = parse_str(r#"let p = { "x" };"#);
9917 assert!(
9918 diags
9919 .iter()
9920 .any(|d| d.message.contains("expected `:` after field name")),
9921 "string-key shorthand should be rejected: {diags:?}"
9922 );
9923 }
9924
9925 #[test]
9926 fn parse_object_literal_trailing_comma() {
9927 let (ast, diags) = parse_str("let p = { x: 1, y: 2, };");
9928 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9929 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9930 panic!("expected Let");
9931 };
9932 assert_eq!(object_literal(ast.try_expr(value).unwrap()).len(), 2);
9933 }
9934
9935 #[test]
9936 fn parse_object_literal_duplicate_key_diagnoses() {
9937 let (_, diags) = parse_str("let p = { x: 1, x: 2 };");
9938 assert_eq!(diags.len(), 1);
9939 assert_eq!(diags[0].message, "duplicate field `x` in object literal");
9940 }
9941
9942 #[test]
9943 fn parse_empty_array_literal() {
9944 let (ast, diags) = parse_str("let xs = [];");
9945 assert!(diags.is_empty());
9946 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9947 panic!("expected Let");
9948 };
9949 assert!(array_literal(ast.try_expr(value).unwrap()).is_empty());
9950 }
9951
9952 #[test]
9953 fn parse_array_literal_simple() {
9954 let (ast, diags) = parse_str("let xs = [1, 2, 3];");
9955 assert!(diags.is_empty());
9956 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9957 panic!("expected Let");
9958 };
9959 let elements = array_literal(ast.try_expr(value).unwrap());
9960 assert_eq!(elements.len(), 3);
9961 assert_eq!(
9962 ast.try_expr(array_literal_value(&elements[0]))
9963 .unwrap()
9964 .kind,
9965 crate::ExprKind::Number(1.0)
9966 );
9967 assert_eq!(
9968 ast.try_expr(array_literal_value(&elements[2]))
9969 .unwrap()
9970 .kind,
9971 crate::ExprKind::Number(3.0)
9972 );
9973 }
9974
9975 #[test]
9976 fn parse_array_literal_trailing_comma() {
9977 let (ast, diags) = parse_str("let xs = [1, 2,];");
9978 assert!(diags.is_empty());
9979 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9980 panic!("expected Let");
9981 };
9982 assert_eq!(array_literal(ast.try_expr(value).unwrap()).len(), 2);
9983 }
9984
9985 #[test]
9986 fn parse_nested_array_in_object() {
9987 let (ast, diags) = parse_str("let p = { items: [1, 2] };");
9988 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9989 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9990 panic!("expected Let");
9991 };
9992 let members = object_literal(ast.try_expr(value).unwrap());
9993 let f0 = object_literal_field(&members[0]);
9994 assert_eq!(f0.name.name, "items");
9995 let inner = array_literal(ast.try_expr(f0.value).unwrap());
9996 assert_eq!(inner.len(), 2);
9997 }
9998
9999 #[test]
10000 fn parse_nested_object_in_array() {
10001 let (ast, diags) = parse_str("let xs = [{ x: 1 }, { x: 2 }];");
10002 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10003 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10004 panic!("expected Let");
10005 };
10006 let elements = array_literal(ast.try_expr(value).unwrap());
10007 assert_eq!(elements.len(), 2);
10008 assert_eq!(
10009 object_literal(ast.try_expr(array_literal_value(&elements[0])).unwrap()).len(),
10010 1
10011 );
10012 assert_eq!(
10013 object_literal(ast.try_expr(array_literal_value(&elements[1])).unwrap()).len(),
10014 1
10015 );
10016 }
10017
10018 #[test]
10019 fn brace_at_statement_start_is_block_not_object() {
10020 let (_, diags) = parse_str("{ x: 1 }");
10021 assert!(
10022 !diags.is_empty(),
10023 "expected a diagnostic for `x:` inside block"
10024 );
10025 }
10026
10027 fn field_access(e: &crate::Expr) -> (crate::ExprId, &str) {
10028 match e.kind {
10029 crate::ExprKind::FieldAccess { receiver, ref name } => (receiver, name.name.as_str()),
10030 _ => panic!("expected FieldAccess, got {:?}", e.kind),
10031 }
10032 }
10033
10034 fn index_access(e: &crate::Expr) -> (crate::ExprId, crate::ExprId) {
10035 match e.kind {
10036 crate::ExprKind::IndexAccess { receiver, index } => (receiver, index),
10037 _ => panic!("expected IndexAccess, got {:?}", e.kind),
10038 }
10039 }
10040
10041 #[test]
10042 fn parse_field_access_simple() {
10043 let (ast, diags) = parse_str("a.b;");
10044 assert!(diags.is_empty());
10045 let outer = expr_of_single_stmt(&ast);
10046 let (receiver, name) = field_access(outer);
10047 assert!(matches!(
10048 ast.try_expr(receiver).unwrap().kind,
10049 crate::ExprKind::Identifier(_)
10050 ));
10051 assert_eq!(name, "b");
10052 }
10053
10054 #[test]
10055 fn parse_field_access_keyword_name() {
10056 let (ast, diags) = parse_str("status.type.default.null;");
10057 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10058 let outer = expr_of_single_stmt(&ast);
10059 let (receiver, name) = field_access(outer);
10060 assert_eq!(name, "null");
10061 let (receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10062 assert_eq!(name, "default");
10063 let (_receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10064 assert_eq!(name, "type");
10065 }
10066
10067 #[test]
10068 fn parse_field_access_chain_left_assoc() {
10069 let (ast, diags) = parse_str("a.b.c;");
10070 assert!(diags.is_empty());
10071 let outer = expr_of_single_stmt(&ast);
10072 let (mid_id, c) = field_access(outer);
10073 assert_eq!(c, "c");
10074 let (a_id, b) = field_access(ast.try_expr(mid_id).unwrap());
10075 assert_eq!(b, "b");
10076 assert!(matches!(
10077 ast.try_expr(a_id).unwrap().kind,
10078 crate::ExprKind::Identifier(_)
10079 ));
10080 }
10081
10082 #[test]
10083 fn parse_index_access_simple() {
10084 let (ast, diags) = parse_str("a[0];");
10085 assert!(diags.is_empty());
10086 let outer = expr_of_single_stmt(&ast);
10087 let (receiver, idx) = index_access(outer);
10088 assert!(matches!(
10089 ast.try_expr(receiver).unwrap().kind,
10090 crate::ExprKind::Identifier(_)
10091 ));
10092 assert_eq!(
10093 ast.try_expr(idx).unwrap().kind,
10094 crate::ExprKind::Number(0.0)
10095 );
10096 }
10097
10098 #[test]
10099 fn parse_index_access_chain() {
10100 let (ast, diags) = parse_str("a[0][1];");
10101 assert!(diags.is_empty());
10102 let outer = expr_of_single_stmt(&ast);
10103 let (mid_id, one) = index_access(outer);
10104 assert_eq!(
10105 ast.try_expr(one).unwrap().kind,
10106 crate::ExprKind::Number(1.0)
10107 );
10108 let (a_id, zero) = index_access(ast.try_expr(mid_id).unwrap());
10109 assert_eq!(
10110 ast.try_expr(zero).unwrap().kind,
10111 crate::ExprKind::Number(0.0)
10112 );
10113 assert!(matches!(
10114 ast.try_expr(a_id).unwrap().kind,
10115 crate::ExprKind::Identifier(_)
10116 ));
10117 }
10118
10119 #[test]
10120 fn parse_mixed_access_and_call_chain() {
10121 let (ast, diags) = parse_str("a.b[c].d();");
10122 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10123 let outer = expr_of_single_stmt(&ast);
10124 let (callee, args) = call(outer);
10125 assert!(args.is_empty());
10126 let (idx_id, d_name) = field_access(ast.try_expr(callee).unwrap());
10127 assert_eq!(d_name, "d");
10128 let (b_id, c_idx) = index_access(ast.try_expr(idx_id).unwrap());
10129 assert!(matches!(
10130 ast.try_expr(c_idx).unwrap().kind,
10131 crate::ExprKind::Identifier(_)
10132 ));
10133 let (a_id, b_name) = field_access(ast.try_expr(b_id).unwrap());
10134 assert_eq!(b_name, "b");
10135 assert!(matches!(
10136 ast.try_expr(a_id).unwrap().kind,
10137 crate::ExprKind::Identifier(_)
10138 ));
10139 }
10140
10141 #[test]
10142 fn parse_call_then_field() {
10143 let (ast, diags) = parse_str("f().x;");
10144 assert!(diags.is_empty());
10145 let outer = expr_of_single_stmt(&ast);
10146 let (call_id, x_name) = field_access(outer);
10147 assert_eq!(x_name, "x");
10148 let (_callee, args) = call(ast.try_expr(call_id).unwrap());
10149 assert!(args.is_empty());
10150 }
10151
10152 #[test]
10153 fn parse_index_with_complex_expression() {
10154 let (ast, diags) = parse_str("a[i + 1];");
10155 assert!(diags.is_empty());
10156 let outer = expr_of_single_stmt(&ast);
10157 let (_, idx) = index_access(outer);
10158 let (op, ..) = binary(ast.try_expr(idx).unwrap());
10159 assert_eq!(op, crate::BinOp::Add);
10160 }
10161
10162 #[test]
10163 fn field_access_missing_name_diagnoses() {
10164 let (_, diags) = parse_str("a.;");
10165 assert!(!diags.is_empty());
10166 assert_eq!(diags[0].message, "expected field name after `.`");
10167 }
10168
10169 #[test]
10170 fn parse_postfix_increment() {
10171 let (ast, diags) = parse_str("x++;");
10172 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10173 let expr = expr_of_single_stmt(&ast);
10174 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10175 panic!("expected PostfixUnary, got {:?}", expr.kind);
10176 };
10177 assert_eq!(op, crate::PostfixOp::Inc);
10178 assert!(matches!(
10179 ast.try_expr(operand).unwrap().kind,
10180 crate::ExprKind::Identifier(_)
10181 ));
10182 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
10183 }
10184
10185 #[test]
10186 fn parse_postfix_decrement() {
10187 let (ast, diags) = parse_str("x--;");
10188 assert!(diags.is_empty());
10189 let expr = expr_of_single_stmt(&ast);
10190 let crate::ExprKind::PostfixUnary { op, .. } = expr.kind else {
10191 panic!("expected PostfixUnary");
10192 };
10193 assert_eq!(op, crate::PostfixOp::Dec);
10194 }
10195
10196 #[test]
10197 fn parse_postfix_non_null_assertion() {
10198 let (ast, diags) = parse_str("x!;");
10199 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10200 let expr = expr_of_single_stmt(&ast);
10201 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10202 panic!("expected PostfixUnary, got {:?}", expr.kind);
10203 };
10204 assert_eq!(op, crate::PostfixOp::NonNullAssert);
10205 assert!(matches!(
10206 ast.try_expr(operand).unwrap().kind,
10207 crate::ExprKind::Identifier(_)
10208 ));
10209 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
10210 }
10211
10212 #[test]
10213 fn parse_non_null_assertion_continues_chain() {
10214 let (ast, diags) = parse_str("x!.y;");
10215 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10216 let expr = expr_of_single_stmt(&ast);
10217 let crate::ExprKind::FieldAccess { receiver, name } = &expr.kind else {
10218 panic!("expected FieldAccess, got {:?}", expr.kind);
10219 };
10220 assert_eq!(name.name, "y");
10221 assert!(matches!(
10222 ast.try_expr(*receiver).unwrap().kind,
10223 crate::ExprKind::PostfixUnary {
10224 op: crate::PostfixOp::NonNullAssert,
10225 ..
10226 }
10227 ));
10228 }
10229
10230 #[test]
10231 fn parse_postfix_on_field_access() {
10232 let (ast, diags) = parse_str("o.f++;");
10233 assert!(diags.is_empty());
10234 let expr = expr_of_single_stmt(&ast);
10235 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
10236 panic!("expected PostfixUnary");
10237 };
10238 assert!(matches!(
10239 ast.try_expr(operand).unwrap().kind,
10240 crate::ExprKind::FieldAccess { .. }
10241 ));
10242 }
10243
10244 #[test]
10245 fn parse_postfix_on_index_access() {
10246 let (ast, diags) = parse_str("xs[0]++;");
10247 assert!(diags.is_empty());
10248 let expr = expr_of_single_stmt(&ast);
10249 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
10250 panic!("expected PostfixUnary");
10251 };
10252 assert!(matches!(
10253 ast.try_expr(operand).unwrap().kind,
10254 crate::ExprKind::IndexAccess { .. }
10255 ));
10256 }
10257
10258 #[test]
10259 fn parse_postfix_terminates_chain() {
10260 let (_, diags) = parse_str("x++.y;");
10261 assert!(!diags.is_empty());
10262 assert_eq!(diags[0].message, "expected `;` after expression");
10263 }
10264
10265 #[test]
10266 fn parse_postfix_in_binary_continues() {
10267 let (ast, diags) = parse_str("let y = x++ + 1;");
10268 assert!(diags.is_empty(), "unexpected: {diags:?}");
10269 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10270 panic!("expected Let");
10271 };
10272 let (op, lhs, _) = binary(ast.try_expr(value).unwrap());
10273 assert_eq!(op, crate::BinOp::Add);
10274 assert!(matches!(
10275 ast.try_expr(lhs).unwrap().kind,
10276 crate::ExprKind::PostfixUnary { .. }
10277 ));
10278 }
10279
10280 #[test]
10281 fn parse_index_assignment() {
10282 let (ast, diags) = parse_str("xs[0] = 1;");
10283 assert!(diags.is_empty(), "unexpected: {diags:?}");
10284 let stmt = single_stmt(&ast);
10285 let crate::StmtKind::AssignIndex {
10286 receiver,
10287 index,
10288 value,
10289 } = stmt.kind
10290 else {
10291 panic!("expected AssignIndex, got {:?}", stmt.kind);
10292 };
10293 assert!(matches!(
10294 ast.try_expr(receiver).unwrap().kind,
10295 crate::ExprKind::Identifier(_)
10296 ));
10297 assert_eq!(
10298 ast.try_expr(index).unwrap().kind,
10299 crate::ExprKind::Number(0.0)
10300 );
10301 assert_eq!(
10302 ast.try_expr(value).unwrap().kind,
10303 crate::ExprKind::Number(1.0)
10304 );
10305 }
10306
10307 #[test]
10308 fn parse_for_update_postfix() {
10309 let (ast, diags) = parse_str("function main(): void { for (let i = 0; i < 3; i++) {} }");
10310 assert!(diags.is_empty(), "unexpected: {diags:?}");
10311 assert_eq!(ast.top_level.len(), 1);
10312 }
10313
10314 #[test]
10315 fn parse_for_update_index_assignment() {
10316 let (ast, diags) =
10317 parse_str("function main(): void { for (let i = 0; i < 3; xs[i] = i) {} }");
10318 assert!(diags.is_empty(), "unexpected: {diags:?}");
10319 assert_eq!(ast.top_level.len(), 1);
10320 }
10321
10322 #[test]
10323 fn index_access_missing_close_bracket_diagnoses() {
10324 let (_, diags) = parse_str("a[1;");
10325 assert!(!diags.is_empty());
10326 assert_eq!(diags[0].message, "expected `]`");
10327 }
10328
10329 #[test]
10330 fn field_access_inside_object_literal_value_does_not_steal_dot() {
10331 let (ast, diags) = parse_str("let x = { a: 1 }.foo;");
10332 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10333 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10334 panic!("expected Let");
10335 };
10336 let (recv_id, name) = field_access(ast.try_expr(value).unwrap());
10337 assert_eq!(name, "foo");
10338 assert_eq!(object_literal(ast.try_expr(recv_id).unwrap()).len(), 1);
10339 }
10340
10341 fn type_of_let(stmt: &crate::Stmt) -> &crate::TypeAnnotation {
10342 match stmt.kind {
10343 crate::StmtKind::Let { ref ty, .. } => ty.as_ref().expect("expected type annotation"),
10344 _ => panic!("expected Let"),
10345 }
10346 }
10347
10348 #[test]
10349 fn parse_array_type_annotation() {
10350 let (ast, diags) = parse_str("let xs: number[] = null;");
10351 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10352 let ty = type_of_let(single_stmt(&ast));
10353 match ty.kind {
10354 crate::TypeAnnotationKind::Array(ref inner) => {
10355 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Name { .. }));
10356 }
10357 _ => panic!("expected Array, got {:?}", ty.kind),
10358 }
10359 }
10360
10361 #[test]
10362 fn parse_nested_array_type_annotation() {
10363 let (ast, diags) = parse_str("let xs: number[][] = null;");
10364 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10365 let ty = type_of_let(single_stmt(&ast));
10366 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
10367 panic!("expected outer Array");
10368 };
10369 let crate::TypeAnnotationKind::Array(ref inner2) = inner.kind else {
10370 panic!("expected inner Array");
10371 };
10372 assert!(matches!(
10373 inner2.kind,
10374 crate::TypeAnnotationKind::Name { .. }
10375 ));
10376 }
10377
10378 #[test]
10379 fn parse_object_type_annotation_simple() {
10380 let (ast, diags) = parse_str("let p: { x: number; y: number } = null;");
10381 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10382 let ty = type_of_let(single_stmt(&ast));
10383 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10384 panic!("expected Object, got {:?}", ty.kind);
10385 };
10386 assert_eq!(fields.len(), 2);
10387 assert_eq!(fields[0].name.name, "x");
10388 assert_eq!(fields[1].name.name, "y");
10389 }
10390
10391 #[test]
10392 fn readonly_is_rejected_on_methods_accessors_and_method_signatures() {
10393 let source = "class K {\n\
10394 readonly m(): number { return 1; }\n\
10395 public readonly n<T>(): number { return 1; }\n\
10396 readonly get g(): number { return 1; }\n\
10397 readonly p: number = 1;\n\
10398 }\n\
10399 type T = { readonly r(a: number): boolean; readonly f: () => void };\n\
10400 interface I { readonly s(): void; readonly q: number; }\n";
10401 let (_, diags) = parse_str(source);
10402 let lines: Vec<usize> = diags
10403 .iter()
10404 .map(|d| {
10405 assert_eq!(
10406 d.message,
10407 "`readonly` can only modify a property or index signature"
10408 );
10409 source[..d.span.start as usize].matches('\n').count() + 1
10410 })
10411 .collect();
10412 assert_eq!(lines, vec![2, 3, 4, 7, 8]);
10413 }
10414
10415 #[test]
10416 fn parse_object_type_method_members() {
10417 let (ast, diags) =
10420 parse_str("let p: { m(): number; opt?(): string; r(a: number): boolean } = null;");
10421 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10422 let ty = type_of_let(single_stmt(&ast));
10423 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10424 panic!("expected Object, got {:?}", ty.kind);
10425 };
10426 assert_eq!(fields.len(), 3);
10427 for field in fields {
10428 assert!(
10429 matches!(field.ty.kind, crate::TypeAnnotationKind::Function { .. }),
10430 "member `{}` should be function-typed, got {:?}",
10431 field.name.name,
10432 field.ty.kind,
10433 );
10434 }
10435 assert!(fields[1].optional, "`opt?()` is an optional member");
10436 let crate::TypeAnnotationKind::Function { ref params, .. } = fields[2].ty.kind else {
10437 unreachable!("checked above");
10438 };
10439 assert_eq!(params.len(), 1);
10440 assert_eq!(params[0].name.name, "a");
10441 }
10442
10443 #[test]
10444 fn parse_object_type_annotation_with_commas() {
10445 let (ast, diags) = parse_str("let p: { x: number, y: number } = null;");
10446 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10447 let ty = type_of_let(single_stmt(&ast));
10448 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10449 panic!("expected Object");
10450 };
10451 assert_eq!(fields.len(), 2);
10452 }
10453
10454 #[test]
10455 fn parse_object_type_annotation_empty() {
10456 let (ast, diags) = parse_str("let p: {} = null;");
10457 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10458 let ty = type_of_let(single_stmt(&ast));
10459 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10460 panic!("expected Object");
10461 };
10462 assert!(fields.is_empty());
10463 }
10464
10465 #[test]
10466 fn parse_object_type_with_optional_field() {
10467 let (ast, diags) = parse_str("let p: { id: number; nick?: string } = null;");
10468 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10469 let ty = type_of_let(single_stmt(&ast));
10470 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10471 panic!("expected Object");
10472 };
10473 assert_eq!(fields.len(), 2);
10474 assert_eq!(fields[0].name.name, "id");
10475 assert!(!fields[0].optional);
10476 assert_eq!(fields[1].name.name, "nick");
10477 assert!(fields[1].optional);
10478 }
10479
10480 #[test]
10481 fn parse_object_type_with_readonly_fields() {
10482 let (ast, diags) = parse_str(
10483 "let issue: { readonly id: string; readonly title?: string; body: string } = null;",
10484 );
10485 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10486 let ty = type_of_let(single_stmt(&ast));
10487 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10488 panic!("expected Object");
10489 };
10490 let fields: Vec<(&str, bool, bool)> = fields
10491 .iter()
10492 .map(|field| (field.name.name.as_str(), field.optional, field.readonly))
10493 .collect();
10494 assert_eq!(
10495 fields,
10496 vec![
10497 ("id", false, true),
10498 ("title", true, true),
10499 ("body", false, false),
10500 ]
10501 );
10502 }
10503
10504 #[test]
10505 fn parse_object_type_mixed_optional_and_required() {
10506 let (ast, diags) = parse_str("let p: { a?: number; b: string; c?: boolean } = null;");
10507 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10508 let ty = type_of_let(single_stmt(&ast));
10509 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10510 panic!("expected Object");
10511 };
10512 let opt: Vec<(&str, bool)> = fields
10513 .iter()
10514 .map(|f| (f.name.name.as_str(), f.optional))
10515 .collect();
10516 assert_eq!(opt, vec![("a", true), ("b", false), ("c", true)]);
10517 }
10518
10519 #[test]
10520 fn parse_object_type_keyword_fields() {
10521 let (ast, diags) =
10522 parse_str("let p: { type: string; default?: number; null: boolean } = null;");
10523 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10524 let ty = type_of_let(single_stmt(&ast));
10525 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10526 panic!("expected Object");
10527 };
10528 let opt: Vec<(&str, bool)> = fields
10529 .iter()
10530 .map(|f| (f.name.name.as_str(), f.optional))
10531 .collect();
10532 assert_eq!(
10533 opt,
10534 vec![("type", false), ("default", true), ("null", false)]
10535 );
10536 }
10537
10538 #[test]
10539 fn parse_object_type_quoted_fields() {
10540 let (ast, diags) =
10541 parse_str(r#"let p: { "content-type": string; "x\u002drequest-id"?: string } = null;"#);
10542 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10543 let ty = type_of_let(single_stmt(&ast));
10544 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10545 panic!("expected Object");
10546 };
10547 let opt: Vec<(&str, bool)> = fields
10548 .iter()
10549 .map(|f| (f.name.name.as_str(), f.optional))
10550 .collect();
10551 assert_eq!(opt, vec![("content-type", false), ("x-request-id", true)]);
10552 }
10553
10554 #[test]
10555 fn parse_interface_with_optional_property() {
10556 let (ast, diags) = parse_str("interface User { id: number; deletedAt?: string; }");
10557 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10558 let stmt = single_stmt(&ast);
10559 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
10560 panic!("expected interface decl");
10561 };
10562 let opt: Vec<(&str, bool)> = members
10563 .iter()
10564 .map(|m| match m {
10565 crate::InterfaceMember::Property { name, optional, .. } => {
10566 (name.name.as_str(), *optional)
10567 }
10568 _ => panic!("expected property"),
10569 })
10570 .collect();
10571 assert_eq!(opt, vec![("id", false), ("deletedAt", true)]);
10572 }
10573
10574 #[test]
10575 fn parse_interface_with_readonly_properties() {
10576 let (ast, diags) = parse_str(
10577 "interface Issue { readonly id: string; readonly title?: string; body: string; }",
10578 );
10579 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10580 let stmt = single_stmt(&ast);
10581 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
10582 panic!("expected interface decl");
10583 };
10584 let opt: Vec<(&str, bool, bool)> = members
10585 .iter()
10586 .map(|member| match member {
10587 crate::InterfaceMember::Property {
10588 name,
10589 optional,
10590 readonly,
10591 ..
10592 } => (name.name.as_str(), *optional, *readonly),
10593 _ => panic!("expected property"),
10594 })
10595 .collect();
10596 assert_eq!(
10597 opt,
10598 vec![
10599 ("id", false, true),
10600 ("title", true, true),
10601 ("body", false, false),
10602 ]
10603 );
10604 }
10605
10606 #[test]
10607 fn parse_readonly_property_name_without_modifier() {
10608 let (ast, diags) = parse_str(
10609 "interface Meta { readonly: boolean; }\nlet meta: { readonly: boolean } = null;",
10610 );
10611 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10612 let crate::StmtKind::InterfaceDecl { ref members, .. } =
10613 ast.try_stmt(ast.top_level[0]).unwrap().kind
10614 else {
10615 panic!("expected interface decl");
10616 };
10617 let crate::InterfaceMember::Property {
10618 ref name, readonly, ..
10619 } = members[0]
10620 else {
10621 panic!("expected property");
10622 };
10623 assert_eq!(name.name, "readonly");
10624 assert!(
10625 !readonly,
10626 "`readonly` here is the property name, not a modifier"
10627 );
10628
10629 let ty = type_of_let(ast.try_stmt(ast.top_level[1]).unwrap());
10630 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10631 panic!("expected Object");
10632 };
10633 assert_eq!(fields[0].name.name, "readonly");
10634 assert!(!fields[0].readonly);
10635 }
10636
10637 #[test]
10638 fn parse_interface_keyword_members() {
10639 let (ast, diags) =
10640 parse_str("interface IssueStatus { type: string; default(): string; null: boolean; }");
10641 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10642 let stmt = single_stmt(&ast);
10643 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
10644 panic!("expected interface decl");
10645 };
10646 let names: Vec<&str> = members
10647 .iter()
10648 .map(|m| match m {
10649 crate::InterfaceMember::Method { name, .. }
10650 | crate::InterfaceMember::Property { name, .. } => name.name.as_str(),
10651 crate::InterfaceMember::IndexSignature(_) => panic!("unexpected index signature"),
10652 })
10653 .collect();
10654 assert_eq!(names, vec!["type", "default", "null"]);
10655 }
10656
10657 #[test]
10658 fn parse_interface_quoted_property_members() {
10659 let (ast, diags) = parse_str(
10660 r#"interface Headers { "content-type": string; "x\u002drequest-id"?: string; }"#,
10661 );
10662 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10663 let stmt = single_stmt(&ast);
10664 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
10665 panic!("expected interface decl");
10666 };
10667 let names: Vec<(&str, bool)> = members
10668 .iter()
10669 .map(|m| match m {
10670 crate::InterfaceMember::Property { name, optional, .. } => {
10671 (name.name.as_str(), *optional)
10672 }
10673 _ => panic!("expected property"),
10674 })
10675 .collect();
10676 assert_eq!(names, vec![("content-type", false), ("x-request-id", true)]);
10677 }
10678
10679 #[test]
10680 fn parse_param_optional_marker() {
10681 let (ast, diags) = parse_str("function f(x?: number): void {}");
10682 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10683 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
10684 panic!("expected function")
10685 };
10686 assert!(params[0].optional);
10687 assert!(params[0].default.is_none());
10688 }
10689
10690 #[test]
10691 fn parse_interface_optional_method() {
10692 let (ast, diags) = parse_str("interface F { foo?(x?: number): void; }");
10693 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10694 let crate::StmtKind::InterfaceDecl { ref members, .. } = single_stmt(&ast).kind else {
10695 panic!("expected interface")
10696 };
10697 let crate::InterfaceMember::Property { optional, ty, .. } = &members[0] else {
10698 panic!("expected callable property")
10699 };
10700 assert!(*optional);
10701 let crate::TypeAnnotationKind::Function { params, .. } = &ty.kind else {
10702 panic!("expected function type")
10703 };
10704 assert!(params[0].optional);
10705 }
10706
10707 #[test]
10708 fn parse_interface_with_call_signature() {
10709 let (ast, diags) = parse_str("interface F { (s: string): number; }");
10710 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10711 let stmt = single_stmt(&ast);
10712 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
10713 panic!("expected interface decl");
10714 };
10715 assert_eq!(members.len(), 1);
10716 match &members[0] {
10717 crate::InterfaceMember::Method { name, params, .. } => {
10718 assert_eq!(name.name, "@call");
10719 assert_eq!(params.len(), 1);
10720 assert_eq!(params[0].name.name, "s");
10721 }
10722 other => panic!("expected method (call signature), got {other:?}"),
10723 }
10724 }
10725
10726 #[test]
10727 fn parse_interface_mixed_call_signature_and_method() {
10728 let (ast, diags) = parse_str("interface F { greet(): string; (x: number): number; }");
10729 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10730 let stmt = single_stmt(&ast);
10731 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
10732 panic!("expected interface decl");
10733 };
10734 let names: Vec<&str> = members
10735 .iter()
10736 .map(|m| match m {
10737 crate::InterfaceMember::Method { name, .. } => name.name.as_str(),
10738 _ => "<property>",
10739 })
10740 .collect();
10741 assert_eq!(names, vec!["greet", "@call"]);
10742 }
10743
10744 #[test]
10745 fn parse_interface_rejects_generic_call_signature() {
10746 let (_ast, diags) = parse_str("interface F { <T>(x: T): T; }");
10747 assert!(
10748 diags
10749 .iter()
10750 .any(|d| d.message.contains("generic call signatures")),
10751 "expected generic-call-signature diagnostic, got {diags:?}"
10752 );
10753 }
10754
10755 #[test]
10756 fn parse_object_type_with_array_field() {
10757 let (ast, diags) = parse_str("let p: { xs: number[] } = null;");
10758 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10759 let ty = type_of_let(single_stmt(&ast));
10760 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10761 panic!("expected Object");
10762 };
10763 assert_eq!(fields[0].name.name, "xs");
10764 assert!(matches!(
10765 fields[0].ty.kind,
10766 crate::TypeAnnotationKind::Array(_)
10767 ));
10768 }
10769
10770 #[test]
10771 fn parse_array_of_objects() {
10772 let (ast, diags) = parse_str("let xs: { x: number }[] = null;");
10773 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10774 let ty = type_of_let(single_stmt(&ast));
10775 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
10776 panic!("expected Array");
10777 };
10778 assert!(matches!(
10779 inner.kind,
10780 crate::TypeAnnotationKind::Object { .. }
10781 ));
10782 }
10783
10784 #[test]
10785 fn parse_tuple_type_annotation() {
10786 let (ast, diags) = parse_str("let p: [string, number] = null;");
10787 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10788 let ty = type_of_let(single_stmt(&ast));
10789 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
10790 panic!("expected Tuple, got {:?}", ty.kind);
10791 };
10792 assert_eq!(elems.len(), 2);
10793 assert!(matches!(
10794 elems[0].kind,
10795 crate::TypeAnnotationKind::Name { .. }
10796 ));
10797 assert!(matches!(
10798 elems[1].kind,
10799 crate::TypeAnnotationKind::Name { .. }
10800 ));
10801 }
10802
10803 #[test]
10804 fn parse_singleton_tuple_type() {
10805 let (ast, diags) = parse_str("let p: [number] = null;");
10806 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10807 let ty = type_of_let(single_stmt(&ast));
10808 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
10809 panic!("expected Tuple");
10810 };
10811 assert_eq!(elems.len(), 1);
10812 }
10813
10814 #[test]
10815 fn parse_nested_tuple_type() {
10816 let (ast, diags) = parse_str("let p: [[number, string], boolean] = null;");
10817 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10818 let ty = type_of_let(single_stmt(&ast));
10819 let crate::TypeAnnotationKind::Tuple(ref outer) = ty.kind else {
10820 panic!("expected outer Tuple");
10821 };
10822 assert_eq!(outer.len(), 2);
10823 let crate::TypeAnnotationKind::Tuple(ref inner) = outer[0].kind else {
10824 panic!("expected inner Tuple");
10825 };
10826 assert_eq!(inner.len(), 2);
10827 }
10828
10829 #[test]
10830 fn parse_array_of_tuple_type() {
10831 let (ast, diags) = parse_str("let xs: [string, number][] = null;");
10832 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10833 let ty = type_of_let(single_stmt(&ast));
10834 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
10835 panic!("expected outer Array, got {:?}", ty.kind);
10836 };
10837 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Tuple(_)));
10838 }
10839
10840 #[test]
10841 fn parse_trailing_comma_tuple_type() {
10842 let (ast, diags) = parse_str("let p: [number, string,] = null;");
10843 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10844 let ty = type_of_let(single_stmt(&ast));
10845 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
10846 panic!("expected Tuple");
10847 };
10848 assert_eq!(elems.len(), 2);
10849 }
10850
10851 #[test]
10852 fn parse_empty_tuple_type_rejected() {
10853 let (_ast, diags) = parse_str("let p: [] = null;");
10854 assert!(
10855 diags.iter().any(|d| d
10856 .message
10857 .contains("tuple types must have at least one element")),
10858 "expected empty-tuple diagnostic, got: {diags:?}"
10859 );
10860 }
10861
10862 #[test]
10863 fn parse_labeled_tuple_drops_labels() {
10864 let (ast, diags) = parse_str("let p: [first: number, string] = null;");
10865 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10866 let ty = type_of_let(single_stmt(&ast));
10867 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
10868 panic!("expected Tuple, got {:?}", ty.kind);
10869 };
10870 assert_eq!(elems.len(), 2);
10871 assert!(
10872 elems
10873 .iter()
10874 .all(|e| matches!(e.kind, crate::TypeAnnotationKind::Name { .. }))
10875 );
10876 }
10877
10878 #[test]
10879 fn parse_labeled_optional_tuple_element() {
10880 let (ast, diags) = parse_str("let p: [first: number, second?: string] = null;");
10881 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10882 let ty = type_of_let(single_stmt(&ast));
10883 let crate::TypeAnnotationKind::Tuple(elements) = &ty.kind else {
10884 panic!("expected tuple")
10885 };
10886 assert!(matches!(
10887 elements[1].kind,
10888 crate::TypeAnnotationKind::Optional(_)
10889 ));
10890 }
10891
10892 #[test]
10893 fn parse_readonly_binds_to_the_postfix_type() {
10894 let (ast, diags) = parse_str("let p: readonly number[][] | null = null;");
10895 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10896 let ty = type_of_let(single_stmt(&ast));
10897 let crate::TypeAnnotationKind::Union(ref members) = ty.kind else {
10898 panic!("expected Union, got {:?}", ty.kind);
10899 };
10900 let crate::TypeAnnotationKind::Readonly(ref operand) = members[0].kind else {
10901 panic!("expected Readonly, got {:?}", members[0].kind);
10902 };
10903 let crate::TypeAnnotationKind::Array(ref inner) = operand.kind else {
10904 panic!("expected Array, got {:?}", operand.kind);
10905 };
10906 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Array(_)));
10907 }
10908
10909 #[test]
10910 fn parse_readonly_as_a_type_name_is_not_the_operator() {
10911 let (ast, diags) = parse_str("let p: readonly = null;");
10912 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10913 let ty = type_of_let(single_stmt(&ast));
10914 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
10915 }
10916
10917 #[test]
10918 fn parse_generic_one_arg() {
10919 let (ast, diags) = parse_str("let x: Foo<number> = null;");
10920 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10921 let ty = type_of_let(single_stmt(&ast));
10922 let crate::TypeAnnotationKind::Name { ref name, ref args } = ty.kind else {
10923 panic!("expected Name, got {:?}", ty.kind);
10924 };
10925 assert_eq!(name.span, crate::Span::new(F, 7, 10).unwrap());
10926 assert_eq!(ty.span, crate::Span::new(F, 7, 18).unwrap());
10927 assert_eq!(args.len(), 1);
10928 assert!(matches!(
10929 args[0].kind,
10930 crate::TypeAnnotationKind::Name { .. }
10931 ));
10932 }
10933
10934 #[test]
10935 fn parse_generic_two_args() {
10936 let (ast, diags) = parse_str("let m: Map<string, number> = null;");
10937 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10938 let ty = type_of_let(single_stmt(&ast));
10939 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
10940 panic!("expected Name");
10941 };
10942 assert_eq!(args.len(), 2);
10943 }
10944
10945 #[test]
10946 fn parse_generic_nested() {
10947 let (ast, diags) = parse_str("let b: Box<Box<T>> = null;");
10948 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10949 let ty = type_of_let(single_stmt(&ast));
10950 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
10951 panic!("expected outer Name");
10952 };
10953 assert_eq!(args.len(), 1);
10954 let crate::TypeAnnotationKind::Name {
10955 args: ref inner, ..
10956 } = args[0].kind
10957 else {
10958 panic!("expected inner Name");
10959 };
10960 assert_eq!(inner.len(), 1);
10961 }
10962
10963 #[test]
10964 fn parse_generic_array_postfix() {
10965 let (ast, diags) = parse_str("let xs: Foo<number>[] = null;");
10966 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10967 let ty = type_of_let(single_stmt(&ast));
10968 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
10969 panic!("expected Array");
10970 };
10971 assert!(
10972 matches!(inner.kind, crate::TypeAnnotationKind::Name { ref args, .. } if args.len() == 1)
10973 );
10974 }
10975
10976 #[test]
10977 fn parse_generic_empty_diagnoses() {
10978 let (_ast, diags) = parse_str("let x: Foo<> = null;");
10979 assert!(
10980 diags
10981 .iter()
10982 .any(|d| d.message.contains("empty generic argument list")),
10983 "expected empty-generic-args diagnostic, got: {diags:?}"
10984 );
10985 }
10986
10987 #[test]
10988 fn parse_generic_trailing_comma_ok() {
10989 let (_ast, diags) = parse_str("let x: Foo<number,> = null;");
10990 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10991 }
10992
10993 #[test]
10994 fn parse_generic_missing_close_diagnoses() {
10995 let (_ast, diags) = parse_str("let x: Foo<number = null;");
10996 assert!(
10997 diags
10998 .iter()
10999 .any(|d| d.message.contains("expected `,` or `>`")),
11000 "expected unterminated-generic diagnostic, got: {diags:?}"
11001 );
11002 }
11003
11004 #[test]
11005 fn parse_qualified_two_segments() {
11006 let (ast, diags) = parse_str("let x: Foo.Bar = null;");
11007 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11008 let ty = type_of_let(single_stmt(&ast));
11009 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11010 panic!("expected Qualified, got {:?}", ty.kind);
11011 };
11012 assert_eq!(path.len(), 2);
11013 assert_eq!(path[0].span, crate::Span::new(F, 7, 10).unwrap());
11015 assert_eq!(path[1].span, crate::Span::new(F, 11, 14).unwrap());
11016 assert!(args.is_empty());
11017 assert_eq!(ty.span, crate::Span::new(F, 7, 14).unwrap());
11018 }
11019
11020 #[test]
11021 fn parse_qualified_three_segments() {
11022 let (ast, diags) = parse_str("let x: Foo.Bar.Baz = null;");
11023 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11024 let ty = type_of_let(single_stmt(&ast));
11025 let crate::TypeAnnotationKind::Qualified { ref path, .. } = ty.kind else {
11026 panic!("expected Qualified, got {:?}", ty.kind);
11027 };
11028 assert_eq!(path.len(), 3);
11029 }
11030
11031 #[test]
11032 fn parse_qualified_with_generic_args() {
11033 let (ast, diags) = parse_str("let x: Foo.Box<number> = null;");
11034 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11035 let ty = type_of_let(single_stmt(&ast));
11036 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11037 panic!("expected Qualified, got {:?}", ty.kind);
11038 };
11039 assert_eq!(path.len(), 2);
11040 assert_eq!(args.len(), 1);
11041 assert!(matches!(
11042 args[0].kind,
11043 crate::TypeAnnotationKind::Name { .. }
11044 ));
11045 }
11046
11047 #[test]
11048 fn parse_qualified_with_array_postfix() {
11049 let (ast, diags) = parse_str("let xs: Foo.Bar[] = null;");
11050 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11051 let ty = type_of_let(single_stmt(&ast));
11052 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11053 panic!("expected Array, got {:?}", ty.kind);
11054 };
11055 assert!(matches!(
11056 inner.kind,
11057 crate::TypeAnnotationKind::Qualified { .. }
11058 ));
11059 }
11060
11061 #[test]
11062 fn parse_qualified_trailing_dot_diagnoses() {
11063 let (_ast, diags) = parse_str("let x: Foo. = null;");
11064 assert!(
11065 diags.iter().any(|d| d
11066 .message
11067 .contains("expected identifier after `.` in type name")),
11068 "expected dotted-name diagnostic, got: {diags:?}"
11069 );
11070 }
11071
11072 #[test]
11073 fn parse_qualified_void_root_rejected_dot() {
11074 let (ast, diags) = parse_str("function f(): void.Bar { }");
11075 assert!(!diags.is_empty(), "expected a diagnostic for `void.Bar`");
11076 let _ = ast;
11077 }
11078
11079 fn union_members(ty: &crate::TypeAnnotation) -> &[crate::TypeAnnotation] {
11080 match &ty.kind {
11081 crate::TypeAnnotationKind::Union(members) => members,
11082 other => panic!("expected Union, got {other:?}"),
11083 }
11084 }
11085
11086 #[test]
11087 fn parse_union_two_members() {
11088 let (ast, diags) = parse_str("let x: number | string = null;");
11089 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11090 let ty = type_of_let(single_stmt(&ast));
11091 let members = union_members(ty);
11092 assert_eq!(members.len(), 2);
11093 assert!(matches!(
11094 members[0].kind,
11095 crate::TypeAnnotationKind::Name { .. }
11096 ));
11097 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
11099 members[1].kind,
11100 crate::TypeAnnotationKind::Name { .. }
11101 ));
11102 assert_eq!(members[1].span, crate::Span::new(F, 16, 22).unwrap()); assert_eq!(ty.span, crate::Span::new(F, 7, 22).unwrap());
11104 }
11105
11106 #[test]
11107 fn parse_union_three_members() {
11108 let (ast, diags) = parse_str("let x: A | B | C = null;");
11109 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11110 let ty = type_of_let(single_stmt(&ast));
11111 let members = union_members(ty);
11112 assert_eq!(members.len(), 3);
11113 for m in members {
11114 assert!(matches!(m.kind, crate::TypeAnnotationKind::Name { .. }));
11115 }
11116 }
11117
11118 #[test]
11119 fn parse_union_lower_than_array() {
11120 let (ast, diags) = parse_str("let x: number[] | string = null;");
11121 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11122 let ty = type_of_let(single_stmt(&ast));
11123 let members = union_members(ty);
11124 assert_eq!(members.len(), 2);
11125 assert!(
11126 matches!(members[0].kind, crate::TypeAnnotationKind::Array(_)),
11127 "expected first member to be Array, got {:?}",
11128 members[0].kind
11129 );
11130 assert!(matches!(
11131 members[1].kind,
11132 crate::TypeAnnotationKind::Name { .. }
11133 ));
11134 }
11135
11136 #[test]
11137 fn parse_union_with_null_member() {
11138 let (ast, diags) = parse_str("let x: number | null = null;");
11139 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11140 let ty = type_of_let(single_stmt(&ast));
11141 let members = union_members(ty);
11142 assert_eq!(members.len(), 2);
11143 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
11145 members[1].kind,
11146 crate::TypeAnnotationKind::Name { .. }
11147 ));
11148 assert_eq!(members[1].span, crate::Span::new(F, 16, 20).unwrap()); }
11150
11151 #[test]
11152 fn parse_union_in_function_return_type() {
11153 let (ast, diags) = parse_str("function f(): number | string { }");
11154 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11155 let stmt = single_stmt(&ast);
11156 match stmt.kind {
11157 crate::StmtKind::Function {
11158 ref return_type, ..
11159 } => {
11160 let return_type = return_type.as_ref().expect("plain return type");
11161 let members = union_members(return_type);
11162 assert_eq!(members.len(), 2);
11163 }
11164 _ => panic!("expected Function"),
11165 }
11166 }
11167
11168 #[test]
11169 fn parse_union_in_function_param_type() {
11170 let (ast, diags) = parse_str("function f(x: number | string): void { }");
11171 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11172 let stmt = single_stmt(&ast);
11173 match stmt.kind {
11174 crate::StmtKind::Function { ref params, .. } => {
11175 let p_ty = params[0].ty.as_ref().expect("param type");
11176 let members = union_members(p_ty);
11177 assert_eq!(members.len(), 2);
11178 }
11179 _ => panic!("expected Function"),
11180 }
11181 }
11182
11183 #[test]
11184 fn parse_union_in_object_field_type() {
11185 let (ast, diags) = parse_str("let p: { x: number | string } = null;");
11186 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11187 let ty = type_of_let(single_stmt(&ast));
11188 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11189 panic!("expected Object, got {:?}", ty.kind);
11190 };
11191 assert_eq!(fields.len(), 1);
11192 let members = union_members(&fields[0].ty);
11193 assert_eq!(members.len(), 2);
11194 }
11195
11196 #[test]
11197 fn parse_union_leading_pipe_is_forgiven() {
11198 let (ast, diags) = parse_str("let x: | number | string = null;");
11199 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11200 let ty = type_of_let(single_stmt(&ast));
11201 let members = union_members(ty);
11202 assert_eq!(members.len(), 2);
11203 }
11204
11205 #[test]
11206 fn parse_union_leading_pipe_single_member_unwraps() {
11207 let (ast, diags) = parse_str("let x: | number = null;");
11208 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11209 let ty = type_of_let(single_stmt(&ast));
11210 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11211 }
11212
11213 #[test]
11214 fn parse_union_trailing_pipe_diagnoses() {
11215 let (_, diags) = parse_str("let x: number | = null;");
11216 assert!(!diags.is_empty());
11217 assert_eq!(diags[0].message, "expected type");
11218 }
11219
11220 #[test]
11221 fn snapshot_union_type_mix() {
11222 let (ast, diags) = parse_str("let xs: number[] | string | null = null;");
11223 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11224 insta::assert_debug_snapshot!(ast);
11225 }
11226
11227 #[test]
11228 fn parse_object_type_duplicate_field_diagnoses() {
11229 let (_, diags) = parse_str("let p: { readonly x: number; x: string } = null;");
11230 assert!(!diags.is_empty());
11231 assert!(
11232 diags
11233 .iter()
11234 .any(|d| d.message == "duplicate field `x` in object type"),
11235 "expected duplicate-field diagnostic, got: {diags:?}"
11236 );
11237 }
11238
11239 #[test]
11240 fn snapshot_object_and_array_literals() {
11241 let (ast, diags) = parse_str(r#"let mix = [{ a: 1, b: "hi" }, {}];"#);
11242 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11243 insta::assert_debug_snapshot!(ast);
11244 }
11245
11246 #[test]
11247 fn snapshot_function_with_while() {
11248 let source = "function loop(n: number): void {\n while (n > 0) {\n log(n);\n }\n}";
11249 let (ast, diags) = parse_str(source);
11250 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11251 insta::assert_debug_snapshot!(ast);
11252 }
11253
11254 fn parse_arrow_const(
11255 source: &str,
11256 ) -> (
11257 Ast,
11258 Vec<crate::ParamDecl>,
11259 Option<crate::TypeAnnotation>,
11260 crate::ArrowBody,
11261 ) {
11262 let (ast, diags) = parse_str(source);
11263 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11264 assert_eq!(ast.top_level.len(), 1);
11265 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11266 let value = match &stmt.kind {
11267 StmtKind::Const { value, .. } => *value,
11268 _ => panic!("expected const declaration"),
11269 };
11270 let value = match ast.try_expr(value).unwrap().kind {
11271 ExprKind::FunctionExpression { function, .. } => function,
11272 _ => value,
11273 };
11274 let expr = ast.try_expr(value).unwrap().clone();
11275 let (params, return_type, body) = match expr.kind {
11276 ExprKind::Arrow {
11277 params,
11278 return_type,
11279 body,
11280 ..
11281 } => (params, return_type, body),
11282 other => panic!("expected Arrow, got {other:?}"),
11283 };
11284 (ast, params, return_type, body)
11285 }
11286
11287 #[test]
11288 fn parses_anonymous_function_expression_as_arrow() {
11289 let (ast, params, return_type, body) =
11290 parse_arrow_const("const f = function (x: number): number { return x + 1; };");
11291 assert_eq!(params.len(), 1);
11292 assert_eq!(params[0].name.name, "x");
11293 assert!(return_type.is_some());
11294 assert!(matches!(body, crate::ArrowBody::Block(_)));
11295 let _ = ast;
11296 }
11297
11298 #[test]
11299 fn parses_function_expression_with_no_params() {
11300 let (_ast, params, _return_type, body) =
11301 parse_arrow_const("const f = function (): void {};");
11302 assert!(params.is_empty());
11303 assert!(matches!(body, crate::ArrowBody::Block(_)));
11304 }
11305
11306 #[test]
11308 fn parses_named_function_expression_signature() {
11309 let (_ast, params, _return_type, _body) =
11310 parse_arrow_const("const f = function named(x: number): number { return x + 1; };");
11311 assert_eq!(params.len(), 1);
11312 }
11313
11314 #[test]
11316 fn accepts_a_named_function_expression_that_calls_itself() {
11317 let (_ast, diags) =
11318 parse_str("const f = function bar(x: number): number { return bar(x); };");
11319 assert!(diags.is_empty(), "{diags:?}");
11320 }
11321
11322 #[test]
11324 fn accepts_a_named_function_expression_using_the_name_as_a_property() {
11325 let (_ast, diags) = parse_str("const f = function bar(o: O): number { return o.bar; };");
11326 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11327 }
11328
11329 #[test]
11331 fn accepts_a_named_function_expression_using_the_name_as_an_object_key() {
11332 let (_ast, diags) = parse_str(
11333 "const f = function bar(x: number): number { const o = { bar: 1 }; return o.bar + x; };",
11334 );
11335 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11336 }
11337
11338 #[test]
11340 fn accepts_a_named_function_expression_with_its_name_in_a_string() {
11341 let (_ast, diags) =
11342 parse_str("const f = function bar(x: number): string { return \"bar\"; };");
11343 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11344 }
11345
11346 #[test]
11348 fn accepts_a_self_call_inside_a_template_substitution() {
11349 let (_ast, diags) =
11350 parse_str("const f = function bar(x: number): string { return `${bar(0)}`; };");
11351 assert!(diags.is_empty(), "{diags:?}");
11352 }
11353
11354 #[test]
11355 fn rejects_a_generic_function_expression() {
11356 let (_ast, diags) = parse_str("const f = function <T>(x: T): T { return x; };");
11357 assert!(
11358 diags
11359 .iter()
11360 .any(|d| d.message.contains("generic function expressions")),
11361 "generic function expression should be rejected: {diags:?}"
11362 );
11363 }
11364
11365 #[test]
11366 fn accepts_this_inside_a_function_expression_in_a_class_method() {
11367 let (_ast, diags) = parse_str(
11368 "class C { x: number = 1; m(): number { \
11369 const f = function (this: { x: number }): number { return this.x; }; return f(); } }",
11370 );
11371 assert!(diags.is_empty(), "{diags:?}");
11372 }
11373
11374 #[test]
11375 fn retains_this_boundary_inside_method_shorthand() {
11376 let (ast, diags) = parse_str(
11377 "class C { x: number = 1; m(): number { \
11378 const o = { x: 2, g(): number { return this.x; } }; return o.g(); } }",
11379 );
11380 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11381 assert!((0..ast.exprs_len()).any(|index| matches!(
11382 ast.try_expr(crate::ExprId(index as u32)).unwrap().kind,
11383 ExprKind::ThisOutsideReceiver
11384 )));
11385 }
11386
11387 #[test]
11388 fn this_still_parses_in_an_ordinary_class_method() {
11389 let (_ast, diags) = parse_str("class C { x: number = 1; m(): number { return this.x; } }");
11390 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11391 }
11392
11393 #[test]
11395 fn function_keyword_still_parses_as_an_object_key() {
11396 let (_ast, diags) = parse_str("let o = { function: 1 };");
11397 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11398 }
11399
11400 fn object_literal_member_values(source: &str) -> (Ast, Vec<crate::ExprId>) {
11401 let (ast, diags) = parse_str(source);
11402 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11403 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11404 let value = match &stmt.kind {
11405 StmtKind::Const { value, .. } => *value,
11406 other => panic!("expected a const declaration, got {other:?}"),
11407 };
11408 let members = match &ast.try_expr(value).unwrap().kind {
11409 ExprKind::ObjectLiteral { members } => members
11410 .iter()
11411 .map(crate::ObjectLiteralMember::value)
11412 .collect::<Vec<_>>(),
11413 other => panic!("expected ObjectLiteral, got {other:?}"),
11414 };
11415 (ast, members)
11416 }
11417
11418 #[test]
11419 fn parses_object_literal_method_shorthand_as_arrow() {
11420 let (ast, members) =
11421 object_literal_member_values("const o = { m(x: number): number { return x; } };");
11422 assert_eq!(members.len(), 1);
11423 match &ast.try_expr(members[0]).unwrap().kind {
11424 ExprKind::Arrow { params, body, .. } => {
11425 assert_eq!(params.len(), 1);
11426 assert_eq!(params[0].name.name, "x");
11427 assert!(matches!(body, crate::ArrowBody::Block(_)));
11428 }
11429 other => panic!("expected Arrow, got {other:?}"),
11430 }
11431 }
11432
11433 #[test]
11435 fn parses_method_shorthand_with_a_keyword_name() {
11436 let (ast, members) =
11437 object_literal_member_values("const o = { if(x: number): number { return x; } };");
11438 assert!(matches!(
11439 ast.try_expr(members[0]).unwrap().kind,
11440 ExprKind::Arrow { .. }
11441 ));
11442 }
11443
11444 #[test]
11445 fn parses_method_shorthand_alongside_plain_and_shorthand_properties() {
11446 let (ast, members) =
11447 object_literal_member_values("const o = { a: 1, m(): void {}, b: 2 };");
11448 assert_eq!(members.len(), 3);
11449 assert!(matches!(
11450 ast.try_expr(members[1]).unwrap().kind,
11451 ExprKind::Arrow { .. }
11452 ));
11453 }
11454
11455 #[test]
11456 fn parses_bare_ident_arrow_expression_body() {
11457 let (_ast, params, return_type, body) = parse_arrow_const("const f = x => x + 1;");
11458 assert_eq!(params.len(), 1);
11459 assert_eq!(params[0].name.name, "x");
11460 assert!(params[0].ty.is_none());
11461 assert!(return_type.is_none());
11462 assert!(matches!(body, crate::ArrowBody::Expr(_)));
11463 }
11464
11465 #[test]
11466 fn parses_zero_param_arrow() {
11467 let (_ast, params, return_type, body) = parse_arrow_const("const f = () => 0;");
11468 assert!(params.is_empty());
11469 assert!(return_type.is_none());
11470 assert!(matches!(body, crate::ArrowBody::Expr(_)));
11471 }
11472
11473 #[test]
11474 fn parses_paren_unannotated_arrow() {
11475 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x) => x;");
11476 assert_eq!(params.len(), 1);
11477 assert_eq!(params[0].name.name, "x");
11478 assert!(params[0].ty.is_none());
11479 }
11480
11481 #[test]
11482 fn parses_typed_param_arrow() {
11483 let (_ast, params, return_type, body) =
11484 parse_arrow_const("const f = (x: number) => x * 2;");
11485 assert_eq!(params.len(), 1);
11486 assert!(params[0].ty.is_some());
11487 assert!(return_type.is_none());
11488 assert!(matches!(body, crate::ArrowBody::Expr(_)));
11489 }
11490
11491 #[test]
11492 fn parses_typed_param_and_return_arrow() {
11493 let (_ast, params, return_type, body) =
11494 parse_arrow_const("const f = (x: number): number => x * 2;");
11495 assert_eq!(params.len(), 1);
11496 assert!(params[0].ty.is_some());
11497 assert!(return_type.is_some());
11498 assert!(matches!(body, crate::ArrowBody::Expr(_)));
11499 }
11500
11501 #[test]
11502 fn parses_multi_param_unannotated_arrow() {
11503 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x, y) => x + y;");
11504 assert_eq!(params.len(), 2);
11505 assert_eq!(params[0].name.name, "x");
11506 assert_eq!(params[1].name.name, "y");
11507 assert!(params[0].ty.is_none());
11508 assert!(params[1].ty.is_none());
11509 }
11510
11511 #[test]
11512 fn parses_multi_param_typed_arrow() {
11513 let (_ast, params, _return_type, _body) =
11514 parse_arrow_const("const f = (x: number, y: number) => x + y;");
11515 assert_eq!(params.len(), 2);
11516 assert!(params[0].ty.is_some());
11517 assert!(params[1].ty.is_some());
11518 }
11519
11520 #[test]
11521 fn parses_block_body_arrow() {
11522 let (_ast, params, _return_type, body) =
11523 parse_arrow_const("const f = (x: number) => { return x; };");
11524 assert_eq!(params.len(), 1);
11525 assert!(matches!(body, crate::ArrowBody::Block(_)));
11526 }
11527
11528 #[test]
11529 fn rejects_typed_bare_ident_arrow() {
11530 let (_ast, diags) = parse_str("const f = x: number => x;");
11531 assert!(
11532 !diags.is_empty(),
11533 "expected diagnostics for `x: number => …`",
11534 );
11535 }
11536
11537 #[test]
11538 fn arrow_inside_call_argument_parses() {
11539 let (ast, diags) = parse_str("f((x: number) => x + 1, 2);");
11540 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11541 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11542 let call = match &stmt.kind {
11543 StmtKind::Expr(eid) => ast.try_expr(*eid).unwrap(),
11544 _ => panic!("expected expression statement"),
11545 };
11546 let args = match &call.kind {
11547 ExprKind::Call { args, .. } => args.clone(),
11548 other => panic!("expected Call, got {other:?}"),
11549 };
11550 assert_eq!(args.len(), 2);
11551 assert!(matches!(
11552 ast.try_expr(args[0]).unwrap().kind,
11553 ExprKind::Arrow { .. }
11554 ));
11555 }
11556
11557 #[test]
11558 fn function_decl_param_still_requires_annotation() {
11559 let (_ast, diags) = parse_str("function f(x): number { return x; }");
11560 assert!(
11561 diags.iter().any(|d| d.message.contains("type annotation")),
11562 "expected `parameter requires a type annotation` diagnostic, got {diags:?}",
11563 );
11564 }
11565
11566 #[test]
11567 fn paren_expression_still_parses_when_no_arrow() {
11568 let (ast, diags) = parse_str("const a = (1 + 2);");
11569 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11570 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11571 let value = match &stmt.kind {
11572 StmtKind::Const { value, .. } => *value,
11573 _ => panic!("expected const declaration"),
11574 };
11575 assert!(matches!(
11576 ast.try_expr(value).unwrap().kind,
11577 ExprKind::Paren(_)
11578 ));
11579 }
11580
11581 #[test]
11582 fn parse_named_import_single() {
11583 let (ast, diags) = parse_str("import { v4 } from \"submilli:uuid\";");
11584 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11585 assert_eq!(ast.top_level.len(), 1);
11586 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11587 let (module, kind) = match &stmt.kind {
11588 StmtKind::Import { module, kind, .. } => (module, kind),
11589 other => panic!("expected Import, got {other:?}"),
11590 };
11591 assert_eq!(module, "submilli:uuid");
11592 let specs = match kind {
11593 ImportKind::Named(specs) => specs,
11594 other => panic!("expected Named, got {other:?}"),
11595 };
11596 assert_eq!(specs.len(), 1);
11597 assert_eq!(specs[0].imported_name.name, "v4");
11598 assert_eq!(specs[0].local_name.name, "v4");
11599 }
11600
11601 #[test]
11602 fn parse_named_import_multi_and_alias() {
11603 let (ast, diags) =
11604 parse_str("import { v4, v7 as makeId, validate } from \"submilli:uuid\";");
11605 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11606 let specs = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
11607 StmtKind::Import {
11608 kind: ImportKind::Named(s),
11609 ..
11610 } => s.clone(),
11611 other => panic!("expected named import, got {other:?}"),
11612 };
11613 assert_eq!(specs.len(), 3);
11614 assert_eq!(specs[0].imported_name.name, "v4");
11615 assert_eq!(specs[0].local_name.name, "v4");
11616 assert_eq!(specs[1].imported_name.name, "v7");
11617 assert_eq!(specs[1].local_name.name, "makeId");
11618 assert_eq!(specs[2].imported_name.name, "validate");
11619 assert_eq!(specs[2].local_name.name, "validate");
11620 }
11621
11622 #[test]
11623 fn parse_namespace_import() {
11624 let (ast, diags) = parse_str("import uuid from \"submilli:uuid\";");
11625 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11626 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
11627 StmtKind::Import { kind, .. } => kind.clone(),
11628 other => panic!("expected import, got {other:?}"),
11629 };
11630 let local = match kind {
11631 ImportKind::Namespace { local_name } => local_name,
11632 other => panic!("expected Namespace, got {other:?}"),
11633 };
11634 assert_eq!(local.name, "uuid");
11635 }
11636
11637 #[test]
11638 fn parse_wildcard_namespace_import_synonym() {
11639 let (ast, diags) = parse_str("import * as uuid from \"submilli:uuid\";");
11640 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11641 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
11642 StmtKind::Import { kind, .. } => kind.clone(),
11643 other => panic!("expected import, got {other:?}"),
11644 };
11645 let local = match kind {
11646 ImportKind::Namespace { local_name } => local_name,
11647 other => panic!("expected Namespace, got {other:?}"),
11648 };
11649 assert_eq!(local.name, "uuid");
11650 }
11651
11652 #[test]
11653 fn parse_wildcard_namespace_import_without_as_rejected() {
11654 let (_ast, diags) = parse_str("import * from \"submilli:uuid\";");
11655 assert!(
11656 diags
11657 .iter()
11658 .any(|d| d.message.contains("expected `as <name>`")),
11659 "expected missing-as diagnostic, got: {diags:?}",
11660 );
11661 }
11662
11663 #[test]
11664 fn parse_combined_default_and_named_rejected() {
11665 let (_ast, diags) = parse_str("import uuid, { v4 } from \"submilli:uuid\";");
11666 assert!(
11667 diags
11668 .iter()
11669 .any(|d| d.message.contains("combining default and named")),
11670 "expected combined-import diagnostic, got: {diags:?}",
11671 );
11672 }
11673
11674 #[test]
11675 fn parse_side_effect_import_rejected() {
11676 let (_ast, diags) = parse_str("import \"submilli:uuid\";");
11677 assert!(
11678 diags
11679 .iter()
11680 .any(|d| d.message.contains("side-effect-only imports")),
11681 "expected side-effect-import diagnostic, got: {diags:?}",
11682 );
11683 }
11684
11685 #[test]
11686 fn parse_empty_specifier_list_rejected() {
11687 let (_ast, diags) = parse_str("import {} from \"submilli:uuid\";");
11688 assert!(
11689 diags
11690 .iter()
11691 .any(|d| d.message.contains("empty import specifier list")),
11692 "expected empty-specifier diagnostic, got: {diags:?}",
11693 );
11694 }
11695
11696 #[test]
11697 fn parse_import_inside_function_body_rejected() {
11698 let (_ast, diags) =
11699 parse_str("function main(): void { import { v4 } from \"submilli:uuid\"; }");
11700 assert!(
11701 diags
11702 .iter()
11703 .any(|d| d.message.contains("must appear at the top of the file")),
11704 "expected top-level diagnostic, got: {diags:?}",
11705 );
11706 }
11707
11708 #[test]
11709 fn parse_missing_from_rejected() {
11710 let (_ast, diags) = parse_str("import { v4 } \"submilli:uuid\";");
11711 assert!(
11712 diags.iter().any(|d| d.message.contains("expected `from`")),
11713 "expected missing-from diagnostic, got: {diags:?}",
11714 );
11715 }
11716
11717 #[test]
11718 fn parse_missing_module_specifier_rejected() {
11719 let (_ast, diags) = parse_str("import { v4 } from;");
11720 assert!(
11721 diags
11722 .iter()
11723 .any(|d| d.message.contains("expected module specifier")),
11724 "expected missing-module diagnostic, got: {diags:?}",
11725 );
11726 }
11727
11728 #[test]
11729 fn parse_export_function_marks_exported() {
11730 let (ast, diags) = parse_str("export function main(): void {}");
11733 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11734 let stmt = single_stmt(&ast);
11735 assert!(
11736 matches!(stmt.kind, crate::StmtKind::Function { .. }),
11737 "expected Function decl, got {:?}",
11738 stmt.kind,
11739 );
11740 assert_eq!(ast.exported_decls.len(), 1, "one exported decl");
11741 assert_eq!(ast.exported_decls[0].stmt, ast.top_level[0]);
11742 }
11743
11744 #[test]
11745 fn parse_export_const_type_interface_enum_are_marked() {
11746 for src in [
11747 "export const X: number = 1;",
11748 "export type Id = string;",
11749 "export interface P { x: number; }",
11750 "export enum E { A, B }",
11751 ] {
11752 let (ast, diags) = parse_str(src);
11753 assert!(diags.is_empty(), "unexpected diags for {src:?}: {diags:?}");
11754 assert_eq!(ast.top_level.len(), 1, "one decl for {src:?}");
11755 assert_eq!(ast.exported_decls.len(), 1, "one exported decl for {src:?}");
11756 }
11757 }
11758
11759 #[test]
11760 fn parse_export_default_is_rejected_with_focused_message() {
11761 let (_ast, diags) = parse_str("export default function main(): void {}");
11762 assert!(
11763 diags
11764 .iter()
11765 .any(|d| d.message.contains("`export default` is not supported")),
11766 "expected focused export-default diagnostic, got: {diags:?}",
11767 );
11768 assert!(
11769 !diags.iter().any(|d| d.message.contains("expected `;`")),
11770 "the spurious missing-semicolon error must be gone: {diags:?}",
11771 );
11772 }
11773
11774 #[test]
11775 fn parse_export_list_parses_as_export_from() {
11776 let (ast, diags) = parse_str("function main(): void {}\nexport { main };");
11779 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11780 assert_eq!(ast.top_level.len(), 2, "function + export-from");
11781 let export = ast.try_stmt(ast.top_level[1]).unwrap();
11782 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
11783 panic!("expected ExportFrom, got {:?}", export.kind);
11784 };
11785 assert!(source.is_none(), "bare `export {{ x }}` has no source");
11786 assert_eq!(specs.len(), 1);
11787 assert_eq!(specs[0].imported_name.name, "main");
11788 assert_eq!(specs[0].local_name.name, "main");
11789 }
11790
11791 #[test]
11792 fn parse_export_from_with_source_and_alias_parses() {
11793 let (ast, diags) = parse_str("export { foo, bar as baz } from \"./util\";");
11794 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11795 let export = ast.try_stmt(ast.top_level[0]).unwrap();
11796 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
11797 panic!("expected ExportFrom, got {:?}", export.kind);
11798 };
11799 let Some((module, _)) = source else {
11800 panic!("expected a `from` source");
11801 };
11802 assert_eq!(module, "./util");
11803 assert_eq!(specs.len(), 2);
11804 assert_eq!(specs[1].imported_name.name, "bar");
11805 assert_eq!(specs[1].local_name.name, "baz");
11806 }
11807
11808 #[test]
11809 fn parse_export_inside_function_body_rejected() {
11810 let (_ast, diags) = parse_str("function main(): void { export const X: number = 1; }");
11811 assert!(
11812 diags
11813 .iter()
11814 .any(|d| d.message.contains("must appear at the top of the file")),
11815 "expected top-level diagnostic, got: {diags:?}",
11816 );
11817 }
11818
11819 #[test]
11820 fn parse_throw_with_expression() {
11821 let (ast, diags) = parse_str(r#"throw new Error("oops");"#);
11822 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11823 let stmt = single_stmt(&ast);
11824 let crate::StmtKind::Throw { value } = stmt.kind else {
11825 panic!("expected Throw, got {:?}", stmt.kind);
11826 };
11827 assert!(matches!(
11828 ast.try_expr(value).unwrap().kind,
11829 crate::ExprKind::New { .. }
11830 ));
11831 }
11832
11833 #[test]
11834 fn parse_new_with_dotted_callee() {
11835 let (ast, diags) = parse_str("new Temporal.Duration({});");
11836 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11837 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
11838 panic!("expected expression statement");
11839 };
11840 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
11841 panic!("expected New");
11842 };
11843 let crate::ExprKind::FieldAccess { name, .. } = &ast.try_expr(*callee).unwrap().kind else {
11844 panic!("expected FieldAccess callee");
11845 };
11846 assert_eq!(name.name, "Duration");
11847 }
11848
11849 #[test]
11850 fn parse_new_with_three_level_dotted_callee() {
11851 let (ast, diags) = parse_str("new Foo.Bar.Baz();");
11852 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11853 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
11854 panic!("expected expression statement");
11855 };
11856 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
11857 panic!("expected New");
11858 };
11859 let crate::ExprKind::FieldAccess { receiver, name } = &ast.try_expr(*callee).unwrap().kind
11860 else {
11861 panic!("expected outer FieldAccess");
11862 };
11863 assert_eq!(name.name, "Baz");
11864 let crate::ExprKind::FieldAccess { name: mid_name, .. } =
11865 &ast.try_expr(*receiver).unwrap().kind
11866 else {
11867 panic!("expected inner FieldAccess");
11868 };
11869 assert_eq!(mid_name.name, "Bar");
11870 }
11871
11872 #[test]
11873 fn parse_new_with_trailing_dot_diagnoses() {
11874 let (_ast, diags) = parse_str("new Foo.;");
11875 assert!(
11876 diags.iter().any(|d| d
11877 .message
11878 .contains("expected identifier after `.` in constructor name")),
11879 "expected trailing-dot diagnostic, got: {diags:?}",
11880 );
11881 }
11882
11883 #[test]
11884 fn throw_without_expression_diagnoses() {
11885 let (_ast, diags) = parse_str("throw;");
11886 assert!(
11887 diags
11888 .iter()
11889 .any(|d| d.message == "expected expression after `throw`"),
11890 "expected bare-throw diagnostic, got: {diags:?}",
11891 );
11892 }
11893
11894 #[test]
11895 fn parse_try_catch() {
11896 let (ast, diags) = parse_str("try { } catch (e: Error) { }");
11897 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11898 let stmt = single_stmt(&ast);
11899 let crate::StmtKind::Try {
11900 body,
11901 ref catches,
11902 ref finally,
11903 } = stmt.kind
11904 else {
11905 panic!("expected Try, got {:?}", stmt.kind);
11906 };
11907 assert!(matches!(
11908 ast.try_stmt(body).unwrap().kind,
11909 crate::StmtKind::Block(ref v) if v.is_empty()
11910 ));
11911 let [clause] = catches.as_slice() else {
11912 panic!("expected one catch clause, got {catches:?}");
11913 };
11914 assert_eq!(clause.binding.name, "e");
11915 assert!(clause.ty.is_some());
11916 assert!(finally.is_none());
11917 }
11918
11919 #[test]
11920 fn parse_try_finally() {
11921 let (ast, diags) = parse_str("try { } finally { }");
11922 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11923 let stmt = single_stmt(&ast);
11924 let crate::StmtKind::Try {
11925 ref catches,
11926 ref finally,
11927 ..
11928 } = stmt.kind
11929 else {
11930 panic!("expected Try");
11931 };
11932 assert!(catches.is_empty());
11933 let finally_id = finally.expect("finally block");
11934 assert!(matches!(
11935 ast.try_stmt(finally_id).unwrap().kind,
11936 crate::StmtKind::Block(_)
11937 ));
11938 }
11939
11940 #[test]
11941 fn parse_try_catch_finally() {
11942 let (ast, diags) = parse_str("try { } catch (e: Error) { } finally { }");
11943 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11944 let stmt = single_stmt(&ast);
11945 let crate::StmtKind::Try {
11946 ref catches,
11947 ref finally,
11948 ..
11949 } = stmt.kind
11950 else {
11951 panic!("expected Try");
11952 };
11953 assert_eq!(catches.len(), 1);
11954 assert!(finally.is_some());
11955 }
11956
11957 #[test]
11958 fn bare_try_diagnoses() {
11959 let (_ast, diags) = parse_str("try { }");
11960 assert!(
11961 diags
11962 .iter()
11963 .any(|d| d.message == "try requires at least one of `catch` or `finally`"),
11964 "expected bare-try diagnostic, got: {diags:?}",
11965 );
11966 }
11967
11968 #[test]
11969 fn parse_untyped_catch() {
11970 let (ast, diags) = parse_str("try { } catch (e) { }");
11971 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11972 let stmt = single_stmt(&ast);
11973 let crate::StmtKind::Try {
11974 ref catches,
11975 ref finally,
11976 ..
11977 } = stmt.kind
11978 else {
11979 panic!("expected Try");
11980 };
11981 let [clause] = catches.as_slice() else {
11982 panic!("expected one catch clause, got {catches:?}");
11983 };
11984 assert_eq!(clause.binding.name, "e");
11985 assert!(clause.ty.is_none());
11986 assert!(finally.is_none());
11987 }
11988
11989 #[test]
11990 fn parse_multiple_catch_clauses() {
11991 let (ast, diags) = parse_str("try { } catch (e: HttpError) { } catch (f) { }");
11992 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11993 let stmt = single_stmt(&ast);
11994 let crate::StmtKind::Try {
11995 ref catches,
11996 ref finally,
11997 ..
11998 } = stmt.kind
11999 else {
12000 panic!("expected Try");
12001 };
12002 let [first, second] = catches.as_slice() else {
12003 panic!("expected two catch clauses, got {catches:?}");
12004 };
12005 assert_eq!(first.binding.name, "e");
12006 assert!(first.ty.is_some());
12007 assert_eq!(second.binding.name, "f");
12008 assert!(second.ty.is_none());
12009 assert!(finally.is_none());
12010 }
12011
12012 #[test]
12013 fn catch_after_finally_diagnoses() {
12014 let (_ast, diags) = parse_str("try { } finally { } catch (e: Error) { }");
12015 assert!(
12016 !diags.is_empty(),
12017 "expected diagnostic for catch-after-finally"
12018 );
12019 }
12020
12021 #[test]
12022 fn catch_missing_open_paren_diagnoses() {
12023 let (_ast, diags) = parse_str("try { } catch e: Error) { }");
12024 assert!(
12025 diags
12026 .iter()
12027 .any(|d| d.message == "expected `(` after `catch`"),
12028 "expected open-paren diagnostic, got: {diags:?}",
12029 );
12030 }
12031
12032 fn class_members(ast: &Ast) -> &[crate::ClassMember] {
12033 match &single_stmt(ast).kind {
12034 StmtKind::ClassDecl { members, .. } => members,
12035 other => panic!("expected ClassDecl, got {other:?}"),
12036 }
12037 }
12038
12039 #[test]
12040 fn parse_animal_class() {
12041 let src = r#"
12042class Animal {
12043 name: string;
12044 private sound: string;
12045 readonly species: string;
12046
12047 constructor(name: string, sound: string, species: string) {
12048 this.name = name;
12049 this.sound = sound;
12050 this.species = species;
12051 }
12052
12053 speak(): string {
12054 return this.name + " says " + this.sound;
12055 }
12056}
12057"#;
12058 let (ast, diags) = parse_str(src);
12059 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12060 let StmtKind::ClassDecl {
12061 name,
12062 generics,
12063 extends,
12064 implements,
12065 members,
12066 ..
12067 } = &single_stmt(&ast).kind
12068 else {
12069 panic!("expected ClassDecl");
12070 };
12071 assert_eq!(name.name, "Animal");
12072 assert!(generics.is_empty());
12073 assert!(extends.is_none());
12074 assert!(implements.is_empty());
12075 assert_eq!(members.len(), 5);
12076
12077 let crate::ClassMember::Field {
12078 name, modifiers, ..
12079 } = &members[0]
12080 else {
12081 panic!("member 0 should be a field");
12082 };
12083 assert_eq!(name.name, "name");
12084 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12085 assert!(modifiers.readonly.is_none());
12086
12087 let crate::ClassMember::Field { modifiers, .. } = &members[1] else {
12088 panic!("member 1 should be a field");
12089 };
12090 assert_eq!(modifiers.visibility, crate::Visibility::Private);
12091
12092 let crate::ClassMember::Field { modifiers, .. } = &members[2] else {
12093 panic!("member 2 should be a field");
12094 };
12095 assert!(modifiers.readonly.is_some());
12096 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12097
12098 assert!(matches!(
12099 &members[3],
12100 crate::ClassMember::Constructor { .. }
12101 ));
12102
12103 let crate::ClassMember::Method { name, .. } = &members[4] else {
12104 panic!("member 4 should be a method");
12105 };
12106 assert_eq!(name.name, "speak");
12107 }
12108
12109 #[test]
12110 fn parse_dog_class_extends_super_and_void_method() {
12111 let src = r#"
12112class Dog extends Animal {
12113 private tricks: string[];
12114
12115 constructor(name: string) {
12116 super(name, "woof", "canis familiaris");
12117 this.tricks = [];
12118 }
12119
12120 learn(trick: string): void {
12121 this.tricks.push(trick);
12122 }
12123}
12124"#;
12125 let (ast, diags) = parse_str(src);
12126 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12127 let StmtKind::ClassDecl {
12128 extends, members, ..
12129 } = &single_stmt(&ast).kind
12130 else {
12131 panic!("expected ClassDecl");
12132 };
12133 assert!(extends.is_some(), "Dog should extend Animal");
12134 assert_eq!(members.len(), 3);
12135 assert!(matches!(&members[0], crate::ClassMember::Field { .. }));
12136 assert!(matches!(
12137 &members[1],
12138 crate::ClassMember::Constructor { .. }
12139 ));
12140 assert!(matches!(&members[2], crate::ClassMember::Method { .. }));
12141 }
12142
12143 #[test]
12144 fn parse_class_implements_list() {
12145 let (ast, diags) = parse_str("class C implements I, J { run(): void {} }");
12146 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12147 let StmtKind::ClassDecl { implements, .. } = &single_stmt(&ast).kind else {
12148 panic!("expected ClassDecl");
12149 };
12150 assert_eq!(implements.len(), 2);
12151 }
12152
12153 #[test]
12154 fn parse_class_extends_and_implements() {
12155 let (ast, diags) = parse_str("class C extends B implements I { run(): void {} }");
12156 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12157 let StmtKind::ClassDecl {
12158 extends,
12159 implements,
12160 ..
12161 } = &single_stmt(&ast).kind
12162 else {
12163 panic!("expected ClassDecl");
12164 };
12165 assert!(extends.is_some());
12166 assert_eq!(implements.len(), 1);
12167 }
12168
12169 #[test]
12170 fn parse_class_generics_optional_field_and_initializer() {
12171 let (ast, diags) = parse_str("class Box<T> { value?: T; items: T[] = []; }");
12172 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12173 let StmtKind::ClassDecl {
12174 generics, members, ..
12175 } = &single_stmt(&ast).kind
12176 else {
12177 panic!("expected ClassDecl");
12178 };
12179 assert_eq!(generics.len(), 1);
12180 assert_eq!(generics[0].name, "T");
12181
12182 let crate::ClassMember::Field {
12183 optional,
12184 initializer,
12185 ..
12186 } = &members[0]
12187 else {
12188 panic!("member 0 should be a field");
12189 };
12190 assert!(*optional);
12191 assert!(initializer.is_none());
12192
12193 let crate::ClassMember::Field { initializer, .. } = &members[1] else {
12194 panic!("member 1 should be a field");
12195 };
12196 assert!(initializer.is_some());
12197 }
12198
12199 #[test]
12200 fn export_class_is_marked() {
12201 let (ast, diags) = parse_str("export class Foo {}");
12202 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12203 assert_eq!(ast.top_level.len(), 1);
12204 assert_eq!(ast.exported_decls.len(), 1);
12205 assert!(matches!(
12206 &single_stmt(&ast).kind,
12207 StmtKind::ClassDecl { .. }
12208 ));
12209 }
12210
12211 #[test]
12212 fn parameterless_index_signature_is_reported_from_its_member_start() {
12213 for (source, expected) in [
12214 ("interface P { readonly []: number }", "readonly []"),
12215 ("let p: { readonly []: number } = {};", "readonly []"),
12216 ("let q: { []: number } = {};", "[]"),
12217 ] {
12218 let (_ast, diags) = parse_str(source);
12219 let diag = diags
12220 .iter()
12221 .find(|d| d.message.contains("must declare exactly one parameter"))
12222 .unwrap_or_else(|| panic!("no diagnostic for {source}"));
12223 let reported = &source[diag.span.start as usize..diag.span.end as usize];
12224 assert_eq!(reported, expected, "{source}");
12225 }
12226 }
12227
12228 #[test]
12229 fn unterminated_object_type_expects_closing_brace() {
12230 let (_ast, diags) = parse_str("let p: { a: number\n");
12231 assert!(
12232 diags.iter().any(|d| d.message == "expected `}`"),
12233 "{diags:?}"
12234 );
12235 }
12236
12237 #[test]
12238 fn conflicting_visibility_modifiers_diagnose() {
12239 let (_ast, diags) = parse_str("class C { public private x: number; }");
12240 assert!(
12241 diags
12242 .iter()
12243 .any(|d| d.message.contains("at most one visibility modifier")),
12244 "got: {diags:?}",
12245 );
12246 }
12247
12248 #[test]
12249 fn duplicate_readonly_modifier_diagnoses() {
12250 let (_ast, diags) = parse_str("class C { readonly readonly x: number; }");
12251 assert!(
12252 diags
12253 .iter()
12254 .any(|d| d.message.contains("duplicate `readonly`")),
12255 "got: {diags:?}",
12256 );
12257 }
12258
12259 #[test]
12260 fn field_named_like_a_modifier_is_allowed() {
12261 let (ast, diags) = parse_str("class C { private: number; }");
12262 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12263 let crate::ClassMember::Field {
12264 name, modifiers, ..
12265 } = &class_members(&ast)[0]
12266 else {
12267 panic!("expected a field");
12268 };
12269 assert_eq!(name.name, "private");
12270 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12271 }
12272
12273 #[test]
12274 fn method_named_get_is_allowed() {
12275 let (ast, diags) = parse_str("class C { get(): number { return 1; } }");
12276 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12277 let crate::ClassMember::Method { name, .. } = &class_members(&ast)[0] else {
12278 panic!("expected a method");
12279 };
12280 assert_eq!(name.name, "get");
12281 }
12282
12283 #[test]
12284 fn static_method_parses() {
12285 let (ast, diags) = parse_str("class C { static f(): number { return 1; } }");
12286 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12287 let crate::ClassMember::Method {
12288 name, modifiers, ..
12289 } = &class_members(&ast)[0]
12290 else {
12291 panic!("expected a method");
12292 };
12293 assert_eq!(name.name, "f");
12294 assert!(modifiers.static_span.is_some());
12295 }
12296
12297 #[test]
12298 fn static_readonly_field_parses() {
12299 let (ast, diags) = parse_str("class C { static readonly x: number = 1; }");
12300 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12301 let crate::ClassMember::Field {
12302 name,
12303 modifiers,
12304 initializer,
12305 ..
12306 } = &class_members(&ast)[0]
12307 else {
12308 panic!("expected a field");
12309 };
12310 assert_eq!(name.name, "x");
12311 assert!(modifiers.static_span.is_some());
12312 assert!(modifiers.readonly.is_some());
12313 assert!(initializer.is_some());
12314 }
12315
12316 #[test]
12317 fn private_static_method_parses() {
12318 let (ast, diags) = parse_str("class C { private static f(): void {} }");
12319 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12320 let crate::ClassMember::Method { modifiers, .. } = &class_members(&ast)[0] else {
12321 panic!("expected a method");
12322 };
12323 assert_eq!(modifiers.visibility, crate::Visibility::Private);
12324 assert!(modifiers.static_span.is_some());
12325 }
12326
12327 #[test]
12328 fn method_named_static_is_allowed() {
12329 let (ast, diags) = parse_str("class C { static(): number { return 1; } }");
12330 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12331 let crate::ClassMember::Method {
12332 name, modifiers, ..
12333 } = &class_members(&ast)[0]
12334 else {
12335 panic!("expected a method");
12336 };
12337 assert_eq!(name.name, "static");
12338 assert!(modifiers.static_span.is_none());
12339 }
12340
12341 #[test]
12342 fn visibility_after_static_diagnoses() {
12343 let (_ast, diags) = parse_str("class C { static private f(): void {} }");
12344 assert!(
12345 diags
12346 .iter()
12347 .any(|d| d.message.contains("`private` must come before `static`")),
12348 "got: {diags:?}",
12349 );
12350 }
12351
12352 #[test]
12353 fn duplicate_static_modifier_diagnoses() {
12354 let (_ast, diags) = parse_str("class C { static static f(): void {} }");
12355 assert!(
12356 diags
12357 .iter()
12358 .any(|d| d.message.contains("duplicate `static` modifier")),
12359 "got: {diags:?}",
12360 );
12361 }
12362
12363 #[test]
12364 fn static_accessor_is_rejected() {
12365 let (_ast, diags) = parse_str("class C { static get x(): number { return 1; } }");
12366 assert!(
12367 diags
12368 .iter()
12369 .any(|d| d.message.contains("static accessors are not supported")),
12370 "got: {diags:?}",
12371 );
12372 }
12373
12374 #[test]
12375 fn static_constructor_is_rejected() {
12376 let (_ast, diags) = parse_str("class C { static constructor() {} }");
12377 assert!(
12378 diags
12379 .iter()
12380 .any(|d| d.message.contains("a constructor cannot be `static`")),
12381 "got: {diags:?}",
12382 );
12383 }
12384
12385 #[test]
12386 fn optional_static_field_is_rejected() {
12387 let (_ast, diags) = parse_str("class C { static x?: number; }");
12388 assert!(
12389 diags
12390 .iter()
12391 .any(|d| d.message.contains("a static field cannot be optional")),
12392 "got: {diags:?}",
12393 );
12394 }
12395
12396 #[test]
12397 fn protected_member_is_rejected() {
12398 let (_ast, diags) = parse_str("class C { protected x: number; }");
12399 assert!(
12400 diags
12401 .iter()
12402 .any(|d| d.message.contains("`protected` is not supported")),
12403 "got: {diags:?}",
12404 );
12405 }
12406
12407 #[test]
12408 fn abstract_member_is_rejected() {
12409 let (_ast, diags) = parse_str("class C { abstract run(): void; }");
12410 assert!(
12411 diags
12412 .iter()
12413 .any(|d| d.message.contains("abstract classes are not supported")),
12414 "got: {diags:?}",
12415 );
12416 }
12417
12418 #[test]
12419 fn getter_parses_as_accessor() {
12420 let (ast, diags) = parse_str("class C { get value(): number { return 1; } }");
12421 assert!(diags.is_empty(), "got: {diags:?}");
12422 let crate::ClassMember::Accessor { kind, name, .. } = &class_members(&ast)[0] else {
12423 panic!("expected an accessor member");
12424 };
12425 assert_eq!(*kind, crate::AccessorKind::Get);
12426 assert_eq!(name.name, "value");
12427 }
12428
12429 #[test]
12430 fn getter_without_return_type_is_rejected() {
12431 let (ast, diags) = parse_str("class C { get value() { return 1; } }");
12432 assert_eq!(diags.len(), 1, "got: {diags:?}");
12433 assert_eq!(diags[0].message, "expected `:` and return type");
12434 assert_eq!(diags[0].help, vec!["get name(): T { … }".to_string()]);
12435 assert!(
12436 matches!(
12437 &class_members(&ast)[0],
12438 crate::ClassMember::Accessor {
12439 return_type: None,
12440 ..
12441 }
12442 ),
12443 "the getter is still recovered as a member"
12444 );
12445 }
12446
12447 #[test]
12448 fn setter_parses_as_accessor() {
12449 let (ast, diags) = parse_str("class C { set value(v: number) {} }");
12450 assert!(diags.is_empty(), "got: {diags:?}");
12451 let crate::ClassMember::Accessor { kind, param, .. } = &class_members(&ast)[0] else {
12452 panic!("expected an accessor member");
12453 };
12454 assert_eq!(*kind, crate::AccessorKind::Set);
12455 assert!(param.is_some(), "setter should have a parameter");
12456 }
12457
12458 #[test]
12459 fn param_property_modifier_outside_constructor_is_rejected() {
12460 let (_ast, diags) = parse_str("function f(public x: number): void {}");
12461 assert!(
12462 diags.iter().any(|d| d
12463 .message
12464 .contains("parameter properties are only allowed in a constructor")),
12465 "got: {diags:?}",
12466 );
12467 }
12468
12469 #[test]
12470 fn constructor_return_type_is_rejected() {
12471 let (_ast, diags) = parse_str("class C { constructor(): void {} }");
12472 assert!(
12473 diags.iter().any(|d| d
12474 .message
12475 .contains("constructor cannot declare a return type")),
12476 "got: {diags:?}",
12477 );
12478 }
12479
12480 #[test]
12481 fn field_initializer_without_type_is_rejected() {
12482 let (_ast, diags) = parse_str("class C { x = 1; }");
12483 assert!(
12484 diags
12485 .iter()
12486 .any(|d| d.message.contains("class fields require a type annotation")),
12487 "got: {diags:?}",
12488 );
12489 }
12490
12491 #[test]
12492 fn this_outside_method_is_left_to_typechecker() {
12493 let (_ast, diags) = parse_str("function f(): string { return this.name; }");
12494 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12495 }
12496
12497 #[test]
12498 fn super_outside_method_is_rejected() {
12499 let (_ast, diags) = parse_str("function f(): void { super(1); }");
12500 assert!(
12501 diags.iter().any(|d| d
12502 .message
12503 .contains("`super` is only valid inside a class method or constructor body")),
12504 "got: {diags:?}",
12505 );
12506 }
12507
12508 #[test]
12509 fn this_inside_method_is_accepted_by_parser() {
12510 let (_ast, diags) = parse_str("class C { x: number; read(): number { return this.x; } }");
12511 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12512 }
12513 #[test]
12514 fn parse_void_expression_preserves_operand_and_precedence() {
12515 let (ast, diags) = parse_str("void effect() === undefined;");
12516 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12517 let expression = expr_of_single_stmt(&ast);
12518 let crate::ExprKind::Binary { lhs, rhs, .. } = expression.kind else {
12519 panic!("expected equality")
12520 };
12521 let crate::ExprKind::Void { operand } = ast.try_expr(lhs).unwrap().kind else {
12522 panic!("expected void")
12523 };
12524 assert!(matches!(
12525 ast.try_expr(operand).unwrap().kind,
12526 crate::ExprKind::Call { .. }
12527 ));
12528 assert!(matches!(
12529 ast.try_expr(rhs).unwrap().kind,
12530 crate::ExprKind::Identifier(_)
12531 ));
12532 }
12533
12534 #[test]
12535 fn parse_void_newline_and_object_operands() {
12536 for source in [
12537 "void\n0;",
12538 "void { value: 1 };",
12539 "void\n{ value: 1 };",
12540 "void /x/;",
12541 "void void 0;",
12542 ] {
12543 let (_, diags) = parse_str(source);
12544 assert!(diags.is_empty(), "{source}: {diags:?}");
12545 }
12546 let (_, diags) = parse_str("void; 0;");
12547 assert!(
12548 !diags.is_empty(),
12549 "an explicit semicolon cannot split void from its operand"
12550 );
12551 }
12552
12553 #[test]
12554 fn parse_optional_parameters_and_defaults_in_callable_forms() {
12555 for source in [
12556 "function f(x = 1): number { return x; }",
12557 "function f(x: number = 1, y: number): number { return y; }",
12558 "function f({ x } = { x: 1 }): number { return x; }",
12559 "const f = (x?: number) => x;",
12560 "const f = (x: number = effect()) => x;",
12561 "const f = function(x: number = effect()) { return x; };",
12562 "const f = { m(x: number = effect()): number { return x; } };",
12563 "const f = ([x = 1]: number[] = values) => x;",
12564 "let f: (x?: number, ...rest: string[]) => void = value;",
12565 "let f: { method?(x?: number): void } = value;",
12566 "function f(x: number = 1, y?: number): void {}",
12567 ] {
12568 let (_, diags) = parse_str(source);
12569 assert!(diags.is_empty(), "{source}: {diags:?}");
12570 }
12571 }
12572
12573 #[test]
12574 fn parse_invalid_optional_parameters() {
12575 for source in [
12576 "function f(x?: number, y: number): void {}",
12577 "const f = (x?: number, y: number) => y;",
12578 "let f: (x?: number, y: number) => void = value;",
12579 "function f(...x?: number[]): void {}",
12580 "function f(x?: number = 1): void {}",
12581 "const f = (x?: number = 1) => x;",
12582 "function f({x}?: T): void {}",
12583 ] {
12584 let (_, diags) = parse_str(source);
12585 assert!(!diags.is_empty(), "expected diagnostic for {source}");
12586 }
12587 }
12588
12589 #[test]
12590 fn parse_optional_tuple_forms_and_order() {
12591 for source in [
12592 "let x: [number, string?] = value;",
12593 "let x: [x?: number, y?: string] = value;",
12594 "let x: readonly [(number | null)?, string?] = value;",
12595 ] {
12596 let (_, diags) = parse_str(source);
12597 assert!(diags.is_empty(), "{source}: {diags:?}");
12598 }
12599 for source in [
12600 "let x: [number?, string] = value;",
12601 "let x: [x: number?] = value;",
12602 ] {
12603 let (_, diags) = parse_str(source);
12604 assert!(!diags.is_empty(), "expected diagnostic for {source}");
12605 }
12606 }
12607
12608 #[test]
12609 fn parse_array_pattern_defaults_preserve_holes() {
12610 let (ast, diags) = parse_str("const [, a = effect(), b] = values;");
12611 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12612 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
12613 panic!("expected pattern")
12614 };
12615 let crate::Binding::Array {
12616 elems, defaults, ..
12617 } = binding
12618 else {
12619 panic!("expected array")
12620 };
12621 assert_eq!(elems.len(), 3);
12622 assert_eq!(defaults.len(), elems.len());
12623 assert!(elems[0].is_none());
12624 assert!(defaults[0].is_none());
12625 assert!(defaults[1].is_some());
12626 assert!(defaults[2].is_none());
12627 }
12628
12629 #[test]
12630 fn parse_optional_class_method_forms() {
12631 let (ast, diags) =
12632 parse_str("class C { declared?(x?: number): void; implemented?(): void\n{} }");
12633 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12634 let members = class_members(&ast);
12635 assert!(matches!(
12636 &members[0],
12637 crate::ClassMember::Field {
12638 optional: true,
12639 initializer: None,
12640 ty: crate::TypeAnnotation {
12641 kind: crate::TypeAnnotationKind::Function { .. },
12642 ..
12643 },
12644 ..
12645 }
12646 ));
12647 assert!(matches!(
12648 &members[1],
12649 crate::ClassMember::Method { optional: true, .. }
12650 ));
12651 }
12652 #[test]
12653 fn defaults_retain_their_callable_receiver_scope() {
12654 for source in [
12655 "class C { value: number = 1; method(value: number = this.value): number { return value; } }",
12656 "class C { constructor(value: number = this.value) {} value: number = 1; }",
12657 "const f = function(this: { value: number }, value: number = this.value): number { return value; };",
12658 ] {
12659 let (ast, diagnostics) = parse_str(source);
12660 assert!(diagnostics.is_empty(), "{source}: {diagnostics:?}");
12661 assert!(
12662 ast.source_expressions()
12663 .iter()
12664 .any(|expr| matches!(expr.kind, ExprKind::This))
12665 );
12666 assert!(
12667 !ast.source_expressions()
12668 .iter()
12669 .any(|expr| matches!(expr.kind, ExprKind::ThisOutsideReceiver))
12670 );
12671 }
12672 }
12673}