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 =
339 self.expect_binding_identifier("expected identifier after `let`/`const`")?;
340 Some(self.ident_from_token(&name_tok))
341 } else {
342 None
343 };
344
345 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
346 self.advance();
347 Some(self.parse_type_annotation()?)
348 } else {
349 None
350 };
351
352 let implicit = !is_const
353 && ty.is_some()
354 && !matches!(self.peek().kind, TokenKind::Equals)
355 && matches!(self.peek().kind, TokenKind::Semicolon);
356 let value = if let (true, Some(name)) = (implicit, &name) {
357 let value = parse_arena_result(
359 self.ast.try_push_expr(Expr {
360 kind: ExprKind::Identifier(Ident {
361 name: "undefined".to_string(),
362 span: name.span,
363 }),
364 span: name.span,
365 }),
366 &mut self.fatal,
367 )?;
368 self.ast.implicit_initializers.insert(value);
369 self.ast.synthetic_undefined.insert(value);
370 value
371 } else {
372 if !matches!(self.peek().kind, TokenKind::Equals) {
373 let (msg, help) = if is_const {
374 (
375 "`const` declaration requires an initializer",
376 vec!["const x: T = expr;".to_string()],
377 )
378 } else {
379 (
380 "`let` declaration requires an initializer",
381 vec![
382 "let x: T = expr;".to_string(),
383 "or declare one that starts empty: `let x: T | undefined;`".to_string(),
384 ],
385 )
386 };
387 self.error_at_peek_with_help(msg, help);
388 return None;
389 }
390 self.advance();
391 self.parse_expression()?
392 };
393
394 if !self.at_statement_end() {
395 self.error_at_peek("expected `;` after declaration");
396 return None;
397 }
398 let end = self.finish_statement();
399
400 let span = self.span(kw.span.start, end);
401 let kind = match (binding, name) {
402 (Some(binding), _) => {
403 if is_const {
404 StmtKind::ConstPattern {
405 binding,
406 ty,
407 value,
408 doc,
409 }
410 } else {
411 StmtKind::LetPattern {
412 binding,
413 ty,
414 value,
415 doc,
416 }
417 }
418 }
419 (None, Some(name)) => {
420 if is_const {
421 StmtKind::Const {
422 name,
423 ty,
424 value,
425 doc,
426 }
427 } else {
428 StmtKind::Let {
429 name,
430 ty,
431 value,
432 doc,
433 }
434 }
435 }
436 (None, None) => return self.invariant_failure("either binding or name must be Some"),
437 };
438 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
439 }
440
441 fn parse_binding(&mut self) -> Option<Binding> {
443 match self.peek().kind {
444 TokenKind::LeftBrace => self.parse_object_binding(),
445 TokenKind::LeftBracket => self.parse_array_binding(),
446 _ => {
447 self.error_at_peek("expected `{` or `[` to start a destructuring pattern");
448 None
449 }
450 }
451 }
452
453 fn parse_object_binding(&mut self) -> Option<Binding> {
454 let open = self.advance();
455 let mut fields: Vec<ObjectPatternField> = Vec::new();
456 let mut rest: Option<Ident> = None;
457
458 if matches!(self.peek().kind, TokenKind::RightBrace) {
459 self.error_at_peek("empty object destructuring pattern");
460 return None;
461 }
462
463 loop {
464 if matches!(self.peek().kind, TokenKind::DotDotDot) {
465 let dots = self.advance();
466 let name_tok = self.expect_binding_identifier("expected identifier after `...`")?;
467 let name = self.ident_from_token(&name_tok);
468 if !matches!(self.peek().kind, TokenKind::RightBrace) {
469 self.error_at_peek_with_help(
470 "rest element must be the last element in a destructuring pattern",
471 vec!["move `...` after every other binding".to_string()],
472 );
473 return None;
474 }
475 rest = Some(Ident {
476 name: name.name,
477 span: self.span(dots.span.start, name.span.end),
478 });
479 break;
480 }
481
482 let source_tok = self.expect_property_name("expected field name in object pattern")?;
483 let source = self.ident_from_token(&source_tok);
484 let field_span_start = source.span.start;
485
486 let (local, field_end) = if matches!(self.peek().kind, TokenKind::Colon) {
487 self.advance();
488 match self.peek().kind {
489 TokenKind::LeftBrace | TokenKind::LeftBracket => {
490 self.error_at_peek_with_help(
491 "nested destructuring is not supported",
492 vec![
493 "destructure once and access nested fields explicitly: const { a } = obj; const inner = a.b;"
494 .to_string(),
495 ],
496 );
497 return None;
498 }
499 _ => {
500 let local_tok = self.expect_binding_identifier(
501 "expected identifier after `:` in pattern",
502 )?;
503 let local = self.ident_from_token(&local_tok);
504 let end = local.span.end;
505 (local, end)
506 }
507 }
508 } else {
509 if !matches!(source_tok.kind, TokenKind::Identifier) {
510 self.error_at_peek("expected `:` after keyword field name in object pattern");
511 return None;
512 }
513 self.reject_strict_mode_reserved_word(&source_tok);
514 self.reject_eval_or_arguments(source_tok.span);
515 let end = source.span.end;
516 (source.clone(), end)
517 };
518
519 let default = if matches!(self.peek().kind, TokenKind::Equals) {
520 self.advance();
521 Some(self.parse_expression()?)
522 } else {
523 None
524 };
525 let field_end = match default {
526 Some(value) => {
527 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?
528 .span
529 .end
530 }
531 None => field_end,
532 };
533
534 fields.push(ObjectPatternField {
535 source,
536 local,
537 default,
538 span: self.span(field_span_start, field_end),
539 });
540
541 match self.peek().kind {
542 TokenKind::Comma => {
543 self.advance();
544 if matches!(self.peek().kind, TokenKind::RightBrace) {
545 break;
546 }
547 }
548 TokenKind::RightBrace => break,
549 _ => {
550 self.error_at_peek("expected `,` or `}` in object pattern");
551 return None;
552 }
553 }
554 }
555
556 if !matches!(self.peek().kind, TokenKind::RightBrace) {
557 self.error_at_peek("expected `}` to close object pattern");
558 return None;
559 }
560 let close = self.advance();
561
562 Some(Binding::Object {
563 fields,
564 rest,
565 span: self.span(open.span.start, close.span.end),
566 })
567 }
568
569 fn parse_array_binding(&mut self) -> Option<Binding> {
570 let open = self.advance();
571 let mut elems: Vec<Option<Ident>> = Vec::new();
572 let mut defaults = Vec::new();
573 let mut rest: Option<Ident> = None;
574
575 if matches!(self.peek().kind, TokenKind::RightBracket) {
576 self.error_at_peek("empty array destructuring pattern");
577 return None;
578 }
579
580 loop {
581 match self.peek().kind {
582 TokenKind::Comma => {
583 elems.push(None);
584 defaults.push(None);
585 self.advance();
586 if matches!(self.peek().kind, TokenKind::RightBracket) {
587 break;
588 }
589 continue;
590 }
591 TokenKind::RightBracket => break,
592 TokenKind::DotDotDot => {
593 let dots = self.advance();
594 let name_tok =
595 self.expect_binding_identifier("expected identifier after `...`")?;
596 let name = self.ident_from_token(&name_tok);
597 if !matches!(self.peek().kind, TokenKind::RightBracket) {
598 self.error_at_peek_with_help(
599 "rest element must be the last element in a destructuring pattern",
600 vec!["move `...` after every other binding".to_string()],
601 );
602 return None;
603 }
604 rest = Some(Ident {
605 name: name.name,
606 span: self.span(dots.span.start, name.span.end),
607 });
608 break;
609 }
610 TokenKind::LeftBrace | TokenKind::LeftBracket => {
611 self.error_at_peek_with_help(
612 "nested destructuring is not supported",
613 vec![
614 "destructure once and access nested elements explicitly: const [a] = arr; const inner = a[0];"
615 .to_string(),
616 ],
617 );
618 return None;
619 }
620 _ => {
621 let name_tok =
622 self.expect_binding_identifier("expected identifier in array pattern")?;
623 let name = self.ident_from_token(&name_tok);
624
625 let default = if matches!(self.peek().kind, TokenKind::Equals) {
626 self.advance();
627 Some(self.parse_expression()?)
628 } else {
629 None
630 };
631 defaults.push(default);
632 elems.push(Some(name));
633
634 match self.peek().kind {
635 TokenKind::Comma => {
636 self.advance();
637 if matches!(self.peek().kind, TokenKind::RightBracket) {
638 break;
639 }
640 }
641 TokenKind::RightBracket => break,
642 _ => {
643 self.error_at_peek("expected `,` or `]` in array pattern");
644 return None;
645 }
646 }
647 }
648 }
649 }
650
651 if !matches!(self.peek().kind, TokenKind::RightBracket) {
652 self.error_at_peek("expected `]` to close array pattern");
653 return None;
654 }
655 let close = self.advance();
656
657 Some(Binding::Array {
658 elems,
659 defaults,
660 rest,
661 span: self.span(open.span.start, close.span.end),
662 })
663 }
664
665 fn parse_function_decl(&mut self) -> Option<StmtId> {
666 let doc = self.take_leading_doc();
667 let kw = self.advance();
668
669 let name_tok = self.expect_binding_identifier("expected function name")?;
670 let name = self.ident_from_token(&name_tok);
671
672 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
673 self.parse_generic_param_list()?
674 } else {
675 Vec::new()
676 };
677
678 if !matches!(self.peek().kind, TokenKind::LeftParen) {
679 self.error_at_peek("expected `(` after function name");
680 return None;
681 }
682 self.advance();
683
684 let params = self.parse_param_list(false)?;
685 self.advance(); if !matches!(self.peek().kind, TokenKind::Colon) {
688 self.error_at_peek_with_help(
689 "expected `:` and return type",
690 vec!["function name(): T { … }".to_string()],
691 );
692 return None;
693 }
694 self.advance();
695 let (return_type, type_predicate) =
696 self.parse_predicate_or_return_type(TypePos::Anywhere)?;
697
698 let body = self.parse_outer_this_boundary(Self::parse_block)?;
701 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
702 .span
703 .end;
704
705 parse_arena_result(
706 self.ast.try_push_stmt(Stmt {
707 kind: StmtKind::Function {
708 name,
709 generics,
710 params,
711 return_type,
712 type_predicate,
713 body,
714 doc,
715 },
716 span: self.span(kw.span.start, body_end),
717 }),
718 &mut self.fatal,
719 )
720 }
721
722 fn parse_class_decl(&mut self) -> Option<StmtId> {
723 let doc = self.take_leading_doc();
724 let kw = self.advance();
725 if self.block_depth > 0 {
728 self.error_at_with_help(
729 kw.span,
730 "a `class` must be declared at the top level of the module",
731 vec!["move this `class` outside any block or function body".to_string()],
732 );
733 }
734 let name = self.parse_type_decl_name("a", "class")?;
735
736 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
737 self.parse_generic_param_list()?
738 } else {
739 Vec::new()
740 };
741
742 let extends = if matches!(self.peek().kind, TokenKind::Extends) {
743 self.advance();
744 Some(self.parse_type_annotation()?)
745 } else {
746 None
747 };
748
749 let mut implements: Vec<crate::TypeAnnotation> = Vec::new();
750 if matches!(self.peek().kind, TokenKind::Implements) {
751 self.advance();
752 loop {
753 implements.push(self.parse_type_annotation()?);
754 if matches!(self.peek().kind, TokenKind::Comma) {
755 self.advance();
756 continue;
757 }
758 break;
759 }
760 }
761
762 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
763 self.error_at_peek("expected `{` after class header");
764 return None;
765 }
766 self.advance();
767
768 let mut members: Vec<crate::ClassMember> = Vec::new();
769 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
770 if matches!(self.peek().kind, TokenKind::Semicolon) {
772 self.advance();
773 continue;
774 }
775 members.push(self.parse_class_member()?);
776 }
777
778 if !matches!(self.peek().kind, TokenKind::RightBrace) {
779 self.error_at_peek("expected `}` to close class");
780 return None;
781 }
782 let close = self.advance();
783
784 parse_arena_result(
785 self.ast.try_push_stmt(Stmt {
786 kind: StmtKind::ClassDecl {
787 name,
788 generics,
789 extends,
790 implements,
791 members,
792 doc,
793 },
794 span: self.span(kw.span.start, close.span.end),
795 }),
796 &mut self.fatal,
797 )
798 }
799
800 fn parse_class_member(&mut self) -> Option<crate::ClassMember> {
801 let doc = self.take_leading_doc();
802 let member_start = self.peek().span.start;
803
804 if self.peek_identifier_text_is("protected") {
806 let tok = self.advance();
807 self.error_at_with_help(
808 tok.span,
809 "`protected` is not supported",
810 vec!["use `private` or `public`".to_string()],
811 );
812 return None;
813 }
814 if self.peek_identifier_text_is("abstract") {
815 let tok = self.advance();
816 self.error_at_with_help(
817 tok.span,
818 "abstract classes are not supported",
819 vec!["provide a concrete implementation instead of `abstract`".to_string()],
820 );
821 return None;
822 }
823
824 let modifiers = self.parse_class_modifiers();
825
826 for (kw, kind) in [
829 ("get", crate::AccessorKind::Get),
830 ("set", crate::AccessorKind::Set),
831 ] {
832 if self.peek_identifier_text_is(kw)
833 && is_property_name(&self.peek_at(1).kind)
834 && matches!(self.peek_at(2).kind, TokenKind::LeftParen)
835 {
836 if let Some(span) = modifiers.readonly {
837 self.reject_readonly_modifier(span);
838 }
839 if let Some(span) = modifiers.static_span {
840 self.error_at_with_help(
841 span,
842 "static accessors are not supported",
843 vec!["use a static method: `static name(): T { … }`".to_string()],
844 );
845 return None;
846 }
847 return self.parse_accessor(member_start, modifiers, kind, doc);
848 }
849 }
850
851 if self.peek_identifier_text_is("constructor")
853 && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
854 {
855 if let Some(span) = modifiers.static_span {
856 self.error_at_with_help(
857 span,
858 "a constructor cannot be `static`",
859 vec![
860 "remove `static` — the constructor already belongs to the class, not \
861 instances"
862 .to_string(),
863 ],
864 );
865 return None;
866 }
867 if let Some(span) = modifiers.readonly {
868 self.error_at_with_help(
869 span,
870 "a constructor cannot be `readonly`",
871 vec!["remove `readonly`; it applies to fields".to_string()],
872 );
873 return None;
874 }
875 return self.parse_constructor(member_start, modifiers.visibility, doc);
876 }
877
878 let name = self.expect_property_ident("expected class member name")?;
879 let optional_span = if matches!(self.peek().kind, TokenKind::Question) {
880 Some(self.advance().span)
881 } else {
882 None
883 };
884 let optional = optional_span.is_some();
885
886 if matches!(self.peek().kind, TokenKind::LessThan | TokenKind::LeftParen) {
888 if let Some(span) = modifiers.readonly {
889 self.reject_readonly_modifier(span);
890 }
891 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
892 self.parse_generic_param_list()?
893 } else {
894 Vec::new()
895 };
896 if !matches!(self.peek().kind, TokenKind::LeftParen) {
897 self.error_at_peek("expected `(` to start the method parameter list");
898 return None;
899 }
900 self.advance();
901 let params = self.parse_class_member_params(false)?;
902 self.advance(); if !matches!(self.peek().kind, TokenKind::Colon) {
904 self.error_at_peek_with_help(
905 "expected `:` and return type",
906 vec!["method name(): T { … }".to_string()],
907 );
908 return None;
909 }
910 self.advance();
911 let return_type = self.parse_type_annotation()?;
912 if self.at_inserted_semicolon() && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
913 {
914 self.advance();
915 }
916 if optional
917 && matches!(
918 self.peek().kind,
919 TokenKind::Semicolon | TokenKind::RightBrace
920 )
921 {
922 if !generics.is_empty() {
923 self.error_at(
924 name.span,
925 "generic optional method declarations are not supported",
926 );
927 return None;
928 }
929 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
930 self.advance().span.end
931 } else {
932 return_type.span.end
933 };
934 let ty = self.method_declaration_type(
935 params,
936 return_type,
937 self.span(name.span.start, end),
938 )?;
939 return Some(crate::ClassMember::Field {
940 name,
941 modifiers,
942 optional: true,
943 method_signature: true,
944 ty,
945 initializer: None,
946 span: self.span(member_start, end),
947 doc,
948 });
949 }
950 let body = self.parse_class_member_body()?;
951 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
952 .span
953 .end;
954 return Some(crate::ClassMember::Method {
955 name,
956 optional,
957 modifiers,
958 generics,
959 params,
960 return_type,
961 body,
962 span: self.span(member_start, body_end),
963 doc,
964 });
965 }
966
967 if let Some(span) = optional_span
969 && modifiers.static_span.is_some()
970 {
971 self.error_at_with_help(
972 span,
973 "a static field cannot be optional",
974 vec!["a static field must be initialized; give it a value with `= …`".to_string()],
975 );
976 return None;
977 }
978 if !matches!(self.peek().kind, TokenKind::Colon) {
979 if matches!(self.peek().kind, TokenKind::Equals) {
980 self.error_at_peek_with_help(
981 "class fields require a type annotation",
982 vec!["add `: T` before the initializer, e.g. `name: string = …`".to_string()],
983 );
984 } else {
985 self.error_at_peek("expected `:` and a type for the class field");
986 }
987 return None;
988 }
989 self.advance();
990 let ty = self.parse_type_annotation()?;
991 let initializer = if matches!(self.peek().kind, TokenKind::Equals) {
992 self.advance();
993 self.class_member_body_depth += 1;
996 let init = self.parse_expression();
997 self.class_member_body_depth -= 1;
998 Some(init?)
999 } else {
1000 None
1001 };
1002 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1003 self.error_at_peek("expected `;` after class field");
1004 return None;
1005 }
1006 let semi = self.advance();
1007 Some(crate::ClassMember::Field {
1008 name,
1009 modifiers,
1010 optional,
1011 method_signature: false,
1012 ty,
1013 initializer,
1014 span: self.span(member_start, semi.span.end),
1015 doc,
1016 })
1017 }
1018
1019 fn parse_constructor(
1020 &mut self,
1021 member_start: u32,
1022 visibility: crate::Visibility,
1023 doc: Option<crate::DocComment>,
1024 ) -> Option<crate::ClassMember> {
1025 self.advance(); self.advance(); let params = self.parse_class_member_params(true)?;
1028 self.advance(); if matches!(self.peek().kind, TokenKind::Colon) {
1030 let colon = self.advance();
1031 let _ = self.parse_type_annotation();
1032 self.error_at_with_help(
1033 colon.span,
1034 "a constructor cannot declare a return type",
1035 vec![
1036 "drop the `: T` — a constructor always returns the class instance".to_string(),
1037 ],
1038 );
1039 }
1040 let body = self.parse_class_member_body()?;
1041 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1042 .span
1043 .end;
1044 Some(crate::ClassMember::Constructor {
1045 visibility,
1046 params,
1047 body,
1048 span: self.span(member_start, body_end),
1049 doc,
1050 })
1051 }
1052
1053 fn parse_accessor(
1056 &mut self,
1057 member_start: u32,
1058 modifiers: crate::ClassModifiers,
1059 kind: crate::AccessorKind,
1060 doc: Option<crate::DocComment>,
1061 ) -> Option<crate::ClassMember> {
1062 self.advance(); let name = self.expect_property_ident("expected accessor name")?;
1064 self.advance(); let mut params = self.parse_class_member_params(false)?;
1066 self.advance(); let param = match kind {
1068 crate::AccessorKind::Get => {
1069 if !params.is_empty() {
1070 self.error_at(name.span, "a getter cannot declare parameters");
1071 }
1072 None
1073 }
1074 crate::AccessorKind::Set => {
1075 if params.len() != 1 {
1076 self.error_at(name.span, "a setter must declare exactly one parameter");
1077 return None;
1078 }
1079 Some(Box::new(params.remove(0)))
1080 }
1081 };
1082 let return_type = if matches!(self.peek().kind, TokenKind::Colon) {
1083 self.advance();
1084 Some(self.parse_type_annotation()?)
1085 } else {
1086 if kind == crate::AccessorKind::Get {
1090 self.error_at_peek_with_help(
1091 "expected `:` and return type",
1092 vec!["get name(): T { … }".to_string()],
1093 );
1094 }
1095 None
1096 };
1097 let body = self.parse_class_member_body()?;
1098 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1099 .span
1100 .end;
1101 Some(crate::ClassMember::Accessor {
1102 name,
1103 modifiers,
1104 kind,
1105 param,
1106 return_type,
1107 body,
1108 span: self.span(member_start, body_end),
1109 doc,
1110 })
1111 }
1112
1113 fn parse_class_modifiers(&mut self) -> crate::ClassModifiers {
1119 let mut visibility = crate::Visibility::Public;
1120 let mut visibility_span: Option<Span> = None;
1121 let mut readonly: Option<Span> = None;
1122 let mut static_span: Option<Span> = None;
1123 loop {
1124 if self.peek_word_is_class_modifier("public")
1125 || self.peek_word_is_class_modifier("private")
1126 {
1127 let (kind, word) = if self.peek_identifier_text_is("private") {
1128 (crate::Visibility::Private, "private")
1129 } else {
1130 (crate::Visibility::Public, "public")
1131 };
1132 let tok = self.advance();
1133 if visibility_span.is_some() {
1134 self.error_at_with_help(
1135 tok.span,
1136 "a class member may have at most one visibility modifier",
1137 vec!["keep a single `public` or `private`".to_string()],
1138 );
1139 continue;
1140 }
1141 let preceding = static_span
1144 .map(|_| "static")
1145 .or(readonly.map(|_| "readonly"));
1146 if let Some(preceding) = preceding {
1147 self.error_at_with_help(
1148 tok.span,
1149 format!("`{word}` must come before `{preceding}`"),
1150 vec![format!("write `{word} {preceding} <name>`")],
1151 );
1152 }
1153 visibility = kind;
1154 visibility_span = Some(tok.span);
1155 continue;
1156 }
1157 if self.peek_word_is_class_modifier("static") {
1158 let tok = self.advance();
1159 if static_span.is_some() {
1160 self.error_at(tok.span, "duplicate `static` modifier");
1161 } else {
1162 static_span = Some(tok.span);
1163 }
1164 continue;
1165 }
1166 if self.peek_word_is_class_modifier("readonly") {
1167 let tok = self.advance();
1168 if readonly.is_some() {
1169 self.error_at(tok.span, "duplicate `readonly` modifier");
1170 } else {
1171 readonly = Some(tok.span);
1172 }
1173 continue;
1174 }
1175 break;
1176 }
1177 crate::ClassModifiers {
1178 visibility,
1179 visibility_span,
1180 readonly,
1181 static_span,
1182 }
1183 }
1184
1185 fn peek_word_is_class_modifier(&self, word: &str) -> bool {
1191 if !self.peek_identifier_text_is(word) {
1192 return false;
1193 }
1194 if word != "static" && self.line_break_after_peek() {
1195 return false;
1196 }
1197 let next = &self.peek_at(1).kind;
1198 is_property_name(next) || matches!(next, TokenKind::StringLiteral(_))
1199 }
1200
1201 fn parse_class_member_params(
1202 &mut self,
1203 allow_param_properties: bool,
1204 ) -> Option<Vec<ParamDecl>> {
1205 let saved = self.class_member_body_depth;
1206 self.class_member_body_depth = 1;
1207 let params = self.parse_param_list(allow_param_properties);
1208 self.class_member_body_depth = saved;
1209 params
1210 }
1211
1212 fn parse_class_member_body(&mut self) -> Option<StmtId> {
1213 self.class_member_body_depth += 1;
1214 let body = self.parse_block();
1215 self.class_member_body_depth -= 1;
1216 body
1217 }
1218
1219 fn parse_interface_decl(&mut self) -> Option<StmtId> {
1220 let doc = self.take_leading_doc();
1221 let kw = self.advance();
1222 let name = self.parse_type_decl_name("an", "interface")?;
1223
1224 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1225 self.parse_generic_param_list()?
1226 } else {
1227 Vec::new()
1228 };
1229
1230 let mut extends = Vec::new();
1231 if matches!(self.peek().kind, TokenKind::Extends) {
1232 self.advance();
1233 loop {
1234 extends.push(self.parse_type_annotation()?);
1235 if !matches!(self.peek().kind, TokenKind::Comma) {
1236 break;
1237 }
1238 self.advance();
1239 }
1240 }
1241 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1242 self.error_at_peek("expected `{` after interface name");
1243 return None;
1244 }
1245 self.advance();
1246
1247 let mut members: Vec<crate::InterfaceMember> = Vec::new();
1248 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
1249 && !self.peek_starts_declaration()
1250 {
1251 let member_doc = self.take_leading_doc();
1255 if matches!(self.peek().kind, TokenKind::LeftParen) {
1256 let open = self.advance();
1257 let params = self.parse_param_list(false)?;
1258 self.advance();
1259 if !matches!(self.peek().kind, TokenKind::Colon) {
1260 self.error_at_peek("expected `:` and return type");
1261 return None;
1262 }
1263 self.advance();
1264 let return_type = self.parse_type_annotation()?;
1265 let end = self.finish_interface_member(return_type.span.end)?;
1266 let span = self.span(open.span.start, end);
1267 let sentinel = Ident {
1268 name: "@call".to_string(),
1269 span: open.span,
1270 };
1271 members.push(crate::InterfaceMember::Method {
1272 name: sentinel,
1273 optional: false,
1274 generics: Vec::new(),
1275 params,
1276 return_type,
1277 span,
1278 doc: member_doc,
1279 });
1280 continue;
1281 }
1282 if matches!(self.peek().kind, TokenKind::LessThan) {
1283 self.error_at_peek("generic call signatures are not yet supported");
1284 return None;
1285 }
1286 let member_start = self.peek().span.start;
1287 let readonly = self.eat_readonly_property_modifier();
1288 if self.peek_is_parameterless_index_signature() {
1289 self.skip_parameterless_index_signature(member_start)?;
1290 self.finish_interface_member(self.prev_token_end())?;
1291 continue;
1292 }
1293 if matches!(self.peek().kind, TokenKind::LeftBracket) {
1294 let signature = self.parse_index_signature(readonly)?;
1295 self.finish_interface_member(signature.span.end)?;
1296 members.push(crate::InterfaceMember::IndexSignature(signature));
1297 continue;
1298 }
1299 let member_name = self.expect_property_ident("expected interface member name")?;
1300 let optional = matches!(self.peek().kind, TokenKind::Question);
1301 if optional {
1302 self.advance();
1303 }
1304 if matches!(self.peek().kind, TokenKind::Colon) {
1305 self.advance();
1306 let ty = self.parse_type_annotation()?;
1307 let end = self.finish_interface_member(ty.span.end)?;
1308 let span = self.span(member_name.span.start, end);
1309 members.push(crate::InterfaceMember::Property {
1310 name: member_name,
1311 ty,
1312 optional,
1313 readonly,
1314 span,
1315 doc: member_doc,
1316 });
1317 continue;
1318 }
1319 if readonly {
1320 self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
1321 }
1322 let m_generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1323 self.parse_generic_param_list()?
1324 } else {
1325 Vec::new()
1326 };
1327 if !matches!(self.peek().kind, TokenKind::LeftParen) {
1328 self.error_at_peek(
1329 "expected `(` to start a method signature or `:` to start a property",
1330 );
1331 return None;
1332 }
1333 self.advance();
1334 let params = self.parse_param_list(false)?;
1335 self.advance();
1336 if !matches!(self.peek().kind, TokenKind::Colon) {
1337 self.error_at_peek("expected `:` and return type");
1338 return None;
1339 }
1340 self.advance();
1341 let return_type = self.parse_type_annotation()?;
1342 let end = self.finish_interface_member(return_type.span.end)?;
1343 let span = self.span(member_name.span.start, end);
1344 if optional {
1345 if !m_generics.is_empty() {
1346 self.error_at(
1347 member_name.span,
1348 "generic optional method declarations are not supported",
1349 );
1350 return None;
1351 }
1352 let ty = self.method_declaration_type(params, return_type, span)?;
1353 members.push(crate::InterfaceMember::Property {
1354 name: member_name,
1355 ty,
1356 optional: true,
1357 readonly,
1358 span,
1359 doc: member_doc,
1360 });
1361 } else {
1362 members.push(crate::InterfaceMember::Method {
1363 name: member_name,
1364 optional,
1365 generics: m_generics,
1366 params,
1367 return_type,
1368 span,
1369 doc: member_doc,
1370 });
1371 }
1372 }
1373
1374 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1375 self.error_at_peek("expected `}` to close interface");
1376 return None;
1377 }
1378 let close = self.advance();
1379
1380 parse_arena_result(
1381 self.ast.try_push_stmt(Stmt {
1382 kind: StmtKind::InterfaceDecl {
1383 name,
1384 generics,
1385 extends,
1386 members,
1387 doc,
1388 },
1389 span: self.span(kw.span.start, close.span.end),
1390 }),
1391 &mut self.fatal,
1392 )
1393 }
1394
1395 fn method_declaration_type(
1396 &mut self,
1397 params: Vec<ParamDecl>,
1398 return_type: TypeAnnotation,
1399 span: Span,
1400 ) -> Option<TypeAnnotation> {
1401 let mut fields = Vec::new();
1402 for param in params {
1403 if param.pattern.is_some() || param.default.is_some() {
1404 self.error_at(
1405 param.name.span,
1406 "a method declaration cannot contain a parameter pattern or default value",
1407 );
1408 return None;
1409 }
1410 let Some(ty) = param.ty else {
1411 return self.invariant_failure("method parameter annotation was not retained");
1412 };
1413 fields.push(TypeAnnotationField {
1414 name: param.name,
1415 ty,
1416 optional: param.optional,
1417 readonly: false,
1418 rest: param.rest,
1419 method: false,
1420 });
1421 }
1422 Some(TypeAnnotation {
1423 kind: TypeAnnotationKind::Function {
1424 params: fields,
1425 return_type: Box::new(return_type),
1426 },
1427 span,
1428 })
1429 }
1430
1431 fn finish_interface_member(&mut self, body_end: u32) -> Option<u32> {
1432 self.finish_type_member(body_end, "expected `;`, `,`, or `}` after interface member")
1433 }
1434
1435 fn finish_type_member(&mut self, body_end: u32, message: &str) -> Option<u32> {
1441 match self.peek().kind {
1442 TokenKind::Semicolon | TokenKind::Comma => Some(self.advance().span.end),
1443 TokenKind::RightBrace => Some(body_end),
1444 _ if self.line_break_before_peek() => Some(body_end),
1445 _ => {
1446 self.error_at_peek(message);
1447 None
1448 }
1449 }
1450 }
1451
1452 fn parse_type_alias_decl(&mut self) -> Option<StmtId> {
1453 let doc = self.take_leading_doc();
1454 let kw = self.advance();
1455 let name = self.parse_type_decl_name("a", "type alias")?;
1456
1457 let generics: Vec<Ident> = if matches!(self.peek().kind, TokenKind::LessThan) {
1458 self.parse_generic_param_list()?
1459 } else {
1460 Vec::new()
1461 };
1462
1463 if !matches!(self.peek().kind, TokenKind::Equals) {
1464 self.error_at_peek("expected `=` after type alias name");
1465 return None;
1466 }
1467 self.advance();
1468
1469 let ty = self.parse_type_annotation()?;
1470
1471 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1474 self.advance().span.end
1475 } else if self.line_break_before_peek() {
1476 self.prev_token_end()
1477 } else {
1478 self.error_at_peek("expected `;` after type alias body");
1479 return None;
1480 };
1481
1482 parse_arena_result(
1483 self.ast.try_push_stmt(Stmt {
1484 kind: StmtKind::TypeAliasDecl {
1485 name,
1486 generics,
1487 ty,
1488 doc,
1489 },
1490 span: self.span(kw.span.start, end),
1491 }),
1492 &mut self.fatal,
1493 )
1494 }
1495
1496 fn parse_enum_decl(&mut self) -> Option<StmtId> {
1498 #[derive(Copy, Clone)]
1499 enum Kind {
1500 Number,
1501 String,
1502 }
1503 let doc = self.take_leading_doc();
1504 let kw = self.advance();
1505 let name = self.parse_type_decl_name("an", "enum")?;
1506
1507 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1508 self.error_at_peek("expected `{` after enum name");
1509 return None;
1510 }
1511 self.advance();
1512
1513 let mut members: Vec<EnumMember> = Vec::new();
1514 let mut first_explicit: Option<(Kind, Span)> = None;
1515 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1516 if matches!(self.peek().kind, TokenKind::Semicolon) {
1518 self.advance();
1519 continue;
1520 }
1521 let member_doc = self.take_leading_doc();
1522 let member_name_tok = self.expect_identifier_name("expected enum member name")?;
1523 let member_name = self.ident_from_token(&member_name_tok);
1524
1525 let value = if matches!(self.peek().kind, TokenKind::Equals) {
1526 self.advance();
1527 let init = self.parse_enum_initializer()?;
1528 let (this_kind, this_span) = match &init {
1529 EnumInitializer::Number { span, .. } => (Kind::Number, *span),
1530 EnumInitializer::String { span, .. } => (Kind::String, *span),
1531 };
1532 match first_explicit {
1533 None => first_explicit = Some((this_kind, this_span)),
1534 Some((prev, _)) => {
1535 let mismatched = matches!(
1536 (prev, this_kind),
1537 (Kind::Number, Kind::String) | (Kind::String, Kind::Number)
1538 );
1539 if mismatched {
1540 self.error_at(
1541 this_span,
1542 "mixed numeric and string enum members are not allowed",
1543 );
1544 }
1545 }
1546 }
1547 Some(init)
1548 } else {
1549 None
1550 };
1551
1552 let end_span = value
1553 .as_ref()
1554 .map_or(member_name.span, super::ast::EnumInitializer::span);
1555 let member_span = self.span(member_name.span.start, end_span.end);
1556 members.push(EnumMember {
1557 name: member_name,
1558 value,
1559 span: member_span,
1560 doc: member_doc,
1561 });
1562
1563 match self.peek().kind {
1564 TokenKind::Comma | TokenKind::Semicolon => {
1567 self.advance();
1568 }
1569 TokenKind::RightBrace | TokenKind::Eof => {}
1570 _ => {
1571 self.error_at_peek("expected `,` or `}` after enum member");
1572 return None;
1573 }
1574 }
1575 }
1576
1577 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1578 self.error_at_peek("expected `}` to close enum");
1579 return None;
1580 }
1581 let close = self.advance();
1582
1583 parse_arena_result(
1584 self.ast.try_push_stmt(Stmt {
1585 kind: StmtKind::EnumDecl { name, members, doc },
1586 span: self.span(kw.span.start, close.span.end),
1587 }),
1588 &mut self.fatal,
1589 )
1590 }
1591
1592 fn parse_export(&mut self) -> Option<StmtId> {
1599 let export_tok = self.advance();
1600 let export_span = export_tok.span;
1601
1602 let nested = self.block_depth > 0;
1603 if nested {
1604 self.error_at_with_help(
1605 export_span,
1606 "`export` statements must appear at the top of the file",
1607 vec!["move this `export` to the top level of the module".to_string()],
1608 );
1609 }
1612
1613 if self.peek_identifier_text_is("type")
1616 && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1617 {
1618 self.advance();
1619 }
1620
1621 let declares = matches!(
1622 self.peek().kind,
1623 TokenKind::Function
1624 | TokenKind::Class
1625 | TokenKind::Const
1626 | TokenKind::Let
1627 | TokenKind::Interface
1628 | TokenKind::Enum
1629 ) || self.at_type_alias_head();
1630 if declares {
1631 if export_tok.leading_doc.is_some() && self.peek().leading_doc.is_none() {
1634 if let Some(token) = self.tokens.get_mut(self.pos) {
1635 token.leading_doc = export_tok.leading_doc;
1636 } else {
1637 return self.invariant_failure("export cursor is outside token stream");
1638 }
1639 }
1640 let id = self.parse_statement()?;
1641 if nested {
1642 return None;
1643 }
1644 self.ast.exported_decls.push(ExportedDecl {
1645 stmt: id,
1646 export_span,
1647 });
1648 return Some(id);
1649 }
1650
1651 match self.peek().kind {
1652 TokenKind::LeftBrace => {
1653 let doc = export_tok
1656 .leading_doc
1657 .as_ref()
1658 .and_then(|raw| self.parse_doc(raw))
1659 .or_else(|| self.take_leading_doc());
1660 self.parse_export_from(export_span, nested, doc)
1661 }
1662 TokenKind::Default => {
1663 self.error_at_peek_with_help(
1664 "`export default` is not supported",
1665 vec![
1666 "default exports are out of scope; drop `default` — a named \
1667 `export` marks the declaration visible to sibling modules"
1668 .to_string(),
1669 ],
1670 );
1671 None
1672 }
1673 _ => {
1674 self.error_at_peek_with_help(
1675 "expected a declaration after `export`",
1676 vec![
1677 "`export` may precede `function`, `class`, `const`, `let`, `type`, \
1678 `interface`, or `enum`, or take the form `export { … }`"
1679 .to_string(),
1680 ],
1681 );
1682 None
1683 }
1684 }
1685 }
1686
1687 fn parse_export_from(
1690 &mut self,
1691 export_span: Span,
1692 nested: bool,
1693 doc: Option<crate::DocComment>,
1694 ) -> Option<StmtId> {
1695 let (specs, open_span) = self.parse_specifier_list(SpecifierList::Export)?;
1696 if specs.is_empty() {
1697 self.error_at_with_help(
1698 open_span,
1699 "empty export specifier list",
1700 vec!["name what you re-export: `export { name } from \"./util\";`".to_string()],
1701 );
1702 return None;
1703 }
1704
1705 let source = if self.peek_identifier_text_is("from") {
1706 self.advance();
1707 let module_tok = self.peek().clone();
1708 let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1709 s.clone()
1710 } else {
1711 self.error_at_peek("expected module specifier string after `from`");
1712 return None;
1713 };
1714 self.advance();
1715 Some((module, module_tok.span))
1716 } else {
1717 None
1718 };
1719
1720 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1721 self.error_at_peek("expected `;` after export statement");
1722 return None;
1723 }
1724 let semi = self.advance();
1725
1726 if nested {
1727 return None;
1728 }
1729
1730 parse_arena_result(
1731 self.ast.try_push_stmt(Stmt {
1732 kind: StmtKind::ExportFrom { specs, source, doc },
1733 span: self.span(export_span.start, semi.span.end),
1734 }),
1735 &mut self.fatal,
1736 )
1737 }
1738
1739 fn parse_import_decl(&mut self) -> Option<StmtId> {
1740 let doc = self.take_leading_doc();
1741 let kw = self.advance();
1742 let kw_span = kw.span;
1743
1744 let nested = self.block_depth > 0;
1745 if nested {
1746 self.error_at_with_help(
1747 kw_span,
1748 "`import` statements must appear at the top of the file",
1749 vec!["move this `import` outside any block / function body".to_string()],
1750 );
1751 }
1753
1754 let kind = self.parse_import_kind()?;
1755
1756 if !self.peek_identifier_text_is("from") {
1757 self.error_at_peek("expected `from` after import specifier list");
1758 return None;
1759 }
1760 self.advance();
1761
1762 let module_tok = self.peek().clone();
1763 let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1764 s.clone()
1765 } else {
1766 self.error_at_peek("expected module specifier string after `from`");
1767 return None;
1768 };
1769 let module_span = module_tok.span;
1770 self.advance();
1771
1772 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1773 self.error_at_peek("expected `;` after import statement");
1774 return None;
1775 }
1776 let semi = self.advance();
1777
1778 if nested {
1779 return None;
1780 }
1781
1782 parse_arena_result(
1783 self.ast.try_push_stmt(Stmt {
1784 kind: StmtKind::Import {
1785 module,
1786 module_span,
1787 kind,
1788 doc,
1789 },
1790 span: self.span(kw_span.start, semi.span.end),
1791 }),
1792 &mut self.fatal,
1793 )
1794 }
1795
1796 fn parse_import_kind(&mut self) -> Option<ImportKind> {
1797 if self.peek_identifier_text_is("type")
1802 && match self.peek_at(1).kind {
1803 TokenKind::LeftBrace | TokenKind::Star => true,
1804 TokenKind::Identifier => {
1805 !(self.peek_at_is_word(1, "from")
1806 && matches!(self.peek_at(2).kind, TokenKind::StringLiteral(_)))
1807 }
1808 _ => false,
1809 }
1810 {
1811 self.advance();
1812 }
1813
1814 match self.peek().kind {
1815 TokenKind::LeftBrace => self.parse_named_imports(),
1816 TokenKind::Identifier => {
1817 let local_tok = self.advance();
1818 let local_name = self.ident_from_token(&local_tok);
1819 if matches!(self.peek().kind, TokenKind::Comma) {
1820 self.error_at_peek_with_help(
1821 "combining default and named imports is not supported",
1822 vec!["use two separate `import` statements".to_string()],
1823 );
1824 return None;
1825 }
1826 Some(ImportKind::Namespace { local_name })
1827 }
1828 TokenKind::Star => {
1831 self.advance();
1832 if !self.peek_identifier_text_is("as") {
1833 self.error_at_peek_with_help(
1834 "expected `as <name>` after `import *`",
1835 vec![
1836 "name the namespace binding: \
1837 `import * as ns from \"pkg\";` (or use the \
1838 short form: `import ns from \"pkg\";`)"
1839 .to_string(),
1840 ],
1841 );
1842 return None;
1843 }
1844 self.advance();
1845 let local_tok = self.expect_identifier("expected namespace name after `as`")?;
1846 let local_name = self.ident_from_token(&local_tok);
1847 Some(ImportKind::Namespace { local_name })
1848 }
1849 TokenKind::StringLiteral(_) => {
1850 self.error_at_peek_with_help(
1851 "side-effect-only imports are not supported",
1852 vec![
1853 "name what you import: `import { name } from \"pkg\";` \
1854 or `import ns from \"pkg\";`"
1855 .to_string(),
1856 ],
1857 );
1858 None
1859 }
1860 _ => {
1861 self.error_at_peek("expected `{` or namespace name after `import`");
1862 None
1863 }
1864 }
1865 }
1866
1867 fn parse_named_imports(&mut self) -> Option<ImportKind> {
1868 let (specs, open_span) = self.parse_specifier_list(SpecifierList::Import)?;
1869 if specs.is_empty() {
1870 self.error_at_with_help(
1871 open_span,
1872 "empty import specifier list",
1873 vec!["name what you import: `import { name } from \"pkg\";`".to_string()],
1874 );
1875 return None;
1876 }
1877 Some(ImportKind::Named(specs))
1878 }
1879
1880 fn parse_specifier_list(
1886 &mut self,
1887 list: SpecifierList,
1888 ) -> Option<(Vec<ImportSpecifier>, Span)> {
1889 let what = list.noun();
1890 let open = self.advance();
1891 let mut specs: Vec<ImportSpecifier> = Vec::new();
1892 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1893 if self.peek_identifier_text_is("type")
1896 && matches!(self.peek_at(1).kind, TokenKind::Identifier)
1897 && !self.peek_at_is_word(1, "as")
1898 {
1899 self.advance();
1900 }
1901 let imported_tok =
1902 self.expect_identifier_name(&format!("expected {what} specifier name"))?;
1903 let imported_name = self.ident_from_token(&imported_tok);
1904
1905 let binds_local = list == SpecifierList::Import;
1906 let local_name = if self.peek_identifier_text_is("as") {
1907 self.advance();
1908 let local_tok = self.expect_identifier_name("expected local name after `as`")?;
1909 if binds_local {
1910 self.reject_strict_mode_reserved_word(&local_tok);
1911 }
1912 self.ident_from_token(&local_tok)
1913 } else {
1914 if binds_local {
1915 self.reject_strict_mode_reserved_word(&imported_tok);
1916 }
1917 imported_name.clone()
1918 };
1919
1920 specs.push(ImportSpecifier {
1921 imported_name,
1922 local_name,
1923 });
1924
1925 match self.peek().kind {
1926 TokenKind::Comma => {
1927 self.advance();
1928 }
1929 TokenKind::RightBrace | TokenKind::Eof => {}
1930 _ => {
1931 self.error_at_peek(format!("expected `,` or `}}` after {what} specifier"));
1932 return None;
1933 }
1934 }
1935 }
1936
1937 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1938 self.error_at_peek(format!("expected `}}` to close {what} specifier list"));
1939 return None;
1940 }
1941 self.advance();
1942
1943 Some((specs, open.span))
1944 }
1945
1946 fn parse_enum_initializer(&mut self) -> Option<EnumInitializer> {
1947 let neg = if matches!(self.peek().kind, TokenKind::Minus) {
1948 Some(self.advance())
1949 } else {
1950 None
1951 };
1952 let tok = self.advance();
1953 match &tok.kind {
1954 TokenKind::NumberLiteral(n) => {
1955 let value = if neg.is_some() { -n } else { *n };
1956 let value = if value == 0.0 { 0.0 } else { value };
1958 let start = neg.as_ref().map_or(tok.span.start, |t| t.span.start);
1959 Some(EnumInitializer::Number {
1960 value,
1961 span: self.span(start, tok.span.end),
1962 })
1963 }
1964 TokenKind::StringLiteral(s) => {
1965 if let Some(neg_tok) = &neg {
1966 self.error_at(
1967 self.span(neg_tok.span.start, tok.span.end),
1968 "cannot negate a string enum initializer",
1969 );
1970 return None;
1971 }
1972 Some(EnumInitializer::String {
1973 value: s.clone(),
1974 span: tok.span,
1975 })
1976 }
1977 _ => {
1978 let span = neg.map_or(tok.span, |t| self.span(t.span.start, tok.span.end));
1979 self.error_at(
1980 span,
1981 "expected a number or string literal for enum initializer",
1982 );
1983 None
1984 }
1985 }
1986 }
1987
1988 fn is_contextual_type_operator(&self, index: usize, operator: &str) -> bool {
1996 let Some(tok) = self.tokens.get(index) else {
1997 return false;
1998 };
1999 if !self.token_is_word(tok, operator) {
2000 return false;
2001 }
2002 matches!(
2003 self.tokens.get(index + 1).map(|t| &t.kind),
2004 Some(
2005 TokenKind::Identifier
2006 | TokenKind::Void
2007 | TokenKind::LeftBrace
2008 | TokenKind::LeftParen
2009 | TokenKind::LeftBracket
2010 | TokenKind::StringLiteral(_)
2011 | TokenKind::NumberLiteral(_)
2012 | TokenKind::BooleanLiteral(_)
2013 | TokenKind::NullLiteral
2014 )
2015 )
2016 }
2017
2018 fn parse_type_argument_list(&mut self) -> Option<(Vec<TypeAnnotation>, u32)> {
2019 let lt = self.advance();
2020 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2021 self.error_at(lt.span, "empty generic argument list `<>` is not allowed");
2022 return None;
2023 }
2024 let mut args = Vec::new();
2025 loop {
2026 args.push(self.parse_type_annotation()?);
2027 match self.peek().kind {
2028 TokenKind::Comma => {
2029 self.advance();
2030 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2031 let gt = self.advance();
2032 return Some((args, gt.span.end));
2033 }
2034 }
2035 TokenKind::GreaterThan => {
2036 let gt = self.advance();
2037 return Some((args, gt.span.end));
2038 }
2039 _ => {
2040 self.error_at(
2041 lt.span,
2042 "expected `,` or `>` to close generic argument list",
2043 );
2044 return None;
2045 }
2046 }
2047 }
2048 }
2049
2050 fn parse_type_decl_name(&mut self, article: &str, noun: &str) -> Option<Ident> {
2054 let name_tok = self.expect_identifier(&format!("expected {noun} name"))?;
2055 let name = self.ident_from_token(&name_tok);
2056 if BUILT_IN_TYPE_NAMES.contains(&name.name.as_str()) {
2057 self.error_at_with_help(
2058 name.span,
2059 format!(
2060 "`{}` is a built-in type and can't be used as {article} {noun} name",
2061 name.name
2062 ),
2063 vec![format!("rename the {noun}")],
2064 );
2065 }
2066 Some(name)
2067 }
2068
2069 fn parse_generic_param_list(&mut self) -> Option<Vec<Ident>> {
2070 let lt = self.advance();
2071 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2072 self.error_at(lt.span, "empty generic parameter list `<>` is not allowed");
2073 return None;
2074 }
2075 let mut generics = Vec::new();
2076 loop {
2077 let name_tok = self.expect_identifier("expected generic parameter name")?;
2078 let name = self.ident_from_token(&name_tok);
2079 if generics.iter().any(|g: &Ident| g.name == name.name) {
2083 self.error_at_with_help(
2084 name.span,
2085 format!("duplicate type parameter `{}`", name.name),
2086 vec!["give each type parameter its own name".to_string()],
2087 );
2088 } else {
2089 generics.push(name);
2090 }
2091 match self.peek().kind {
2092 TokenKind::Comma => {
2093 self.advance();
2094 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2095 self.advance();
2096 return Some(generics);
2097 }
2098 }
2099 TokenKind::GreaterThan => {
2100 self.advance();
2101 return Some(generics);
2102 }
2103 _ => {
2104 self.error_at_peek("expected `,` or `>`");
2105 return None;
2106 }
2107 }
2108 }
2109 }
2110
2111 fn parse_param_list(&mut self, allow_param_properties: bool) -> Option<Vec<ParamDecl>> {
2112 if matches!(self.peek().kind, TokenKind::RightParen) {
2113 return Some(Vec::new());
2114 }
2115 let mut params = Vec::new();
2116 loop {
2117 let param = self.parse_param(allow_param_properties)?;
2118 let is_rest = param.rest;
2119 params.push(param);
2120 match self.peek().kind {
2121 TokenKind::Comma => {
2122 if is_rest {
2123 let comma = self.advance();
2124 self.error_at(comma.span, "rest parameter must be the last parameter");
2125 return None;
2126 }
2127 self.advance();
2128 if matches!(self.peek().kind, TokenKind::RightParen) {
2129 break;
2130 }
2131 }
2132 TokenKind::RightParen => break,
2133 _ => {
2134 self.error_at_peek("expected `,` or `)`");
2135 return None;
2136 }
2137 }
2138 }
2139 self.check_parameter_order(params.iter().map(ParamOrder::of_param));
2140 Some(params)
2141 }
2142
2143 fn parse_param(&mut self, allow_param_properties: bool) -> Option<ParamDecl> {
2144 let modifiers = if self.peek_word_is_class_modifier("public")
2148 || self.peek_word_is_class_modifier("private")
2149 || self.peek_word_is_class_modifier("readonly")
2150 {
2151 let span = self.peek().span;
2152 let parsed = self.parse_class_modifiers();
2153 if allow_param_properties {
2154 Some(parsed)
2155 } else {
2156 self.error_at_with_help(
2157 span,
2158 "parameter properties are only allowed in a constructor",
2159 vec![
2160 "move the `public`/`private`/`readonly` modifier to a constructor parameter"
2161 .to_string(),
2162 ],
2163 );
2164 None
2165 }
2166 } else {
2167 None
2168 };
2169
2170 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
2171 self.advance();
2172 true
2173 } else {
2174 false
2175 };
2176 if modifiers.is_some() && rest {
2177 self.error_at_peek("a parameter property cannot be a rest parameter");
2178 return None;
2179 }
2180
2181 let (name, pattern) = match self.peek().kind {
2184 TokenKind::LeftBrace | TokenKind::LeftBracket => {
2185 if rest {
2186 self.error_at_peek("rest parameter cannot be destructured");
2187 return None;
2188 }
2189 if modifiers.is_some() {
2190 self.error_at_peek("a parameter property cannot be destructured");
2191 return None;
2192 }
2193 let binding = self.parse_binding()?;
2194 let placeholder = Ident {
2195 name: String::new(),
2196 span: binding.span(),
2197 };
2198 (placeholder, Some(binding))
2199 }
2200 _ => {
2201 let name_tok = self.expect_binding_identifier(if rest {
2202 "expected identifier after `...`"
2203 } else {
2204 "expected parameter name"
2205 })?;
2206 let name = self.ident_from_token(&name_tok);
2207 (name, None)
2208 }
2209 };
2210
2211 let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
2212 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2213 self.advance();
2214 Some(self.parse_type_annotation()?)
2215 } else if !rest && matches!(self.peek().kind, TokenKind::Equals) {
2216 None
2217 } else {
2218 if rest {
2219 self.error_at_peek_with_help(
2220 "rest parameter requires a type annotation",
2221 vec!["function name(...args: T[]): R { … }".to_string()],
2222 );
2223 } else {
2224 self.error_at_peek_with_help(
2225 "parameter requires a type annotation",
2226 vec!["add a type (`x: T`) or a default initializer (`x = value`)".to_string()],
2227 );
2228 }
2229 return None;
2230 };
2231 let default = self.parse_parameter_default(rest, optional)?;
2232 Some(ParamDecl {
2233 name,
2234 ty,
2235 default,
2236 optional,
2237 pattern,
2238 rest,
2239 modifiers,
2240 })
2241 }
2242
2243 fn parse_parameter_optional(&mut self, rest: bool, pattern: bool) -> Option<bool> {
2244 if !matches!(self.peek().kind, TokenKind::Question) {
2245 return Some(false);
2246 }
2247 let question = self.advance();
2248 if rest || pattern {
2249 self.error_at(
2250 question.span,
2251 if rest {
2252 "a rest parameter cannot be optional"
2253 } else {
2254 "a destructured parameter cannot be optional; use a default value"
2255 },
2256 );
2257 return None;
2258 }
2259 Some(true)
2260 }
2261
2262 fn parse_parameter_default(&mut self, rest: bool, optional: bool) -> Option<Option<ExprId>> {
2265 if !matches!(self.peek().kind, TokenKind::Equals) {
2266 return Some(None);
2267 }
2268 if rest {
2269 self.error_rest_parameter_default();
2270 return None;
2271 }
2272 if optional {
2273 self.error_at_peek_with_help(
2274 "an optional parameter cannot have a default value",
2275 vec!["drop the `?`: a parameter with a default is already optional".to_string()],
2276 );
2277 }
2278 self.advance();
2279 Some(Some(self.parse_expression()?))
2280 }
2281
2282 fn error_rest_parameter_default(&mut self) {
2284 self.error_at_peek_with_help(
2285 "rest parameter cannot have a default value",
2286 vec!["omit the `= …`; an unspecified rest defaults to `[]`".to_string()],
2287 );
2288 }
2289
2290 fn check_parameter_order<'p>(&mut self, params: impl IntoIterator<Item = ParamOrder<'p>>) {
2294 let mut follows_optional = false;
2295 for param in params {
2296 if param.optional {
2297 follows_optional = true;
2298 continue;
2299 }
2300 if !follows_optional || !param.required {
2301 continue;
2302 }
2303 let label = match param.name {
2304 Some(name) => format!("parameter `{name}`"),
2305 None => "destructured parameter".to_string(),
2306 };
2307 self.error_at_with_help(
2308 param.span,
2309 format!("required {label} cannot follow an optional parameter"),
2310 vec!["make it optional too (`x?: T`), give it a default (`x: T = …`), or move it before the optional parameters".to_string()],
2311 );
2312 return;
2313 }
2314 }
2315
2316 fn eat_asi_semicolon_before(&mut self, next: TokenKind) {
2321 if matches!(self.peek().kind, TokenKind::Semicolon) && self.peek_at(1).kind == next {
2322 self.advance();
2323 }
2324 }
2325
2326 fn parse_block(&mut self) -> Option<StmtId> {
2327 self.eat_asi_semicolon_before(TokenKind::LeftBrace);
2328 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2329 self.error_at_peek("expected `{`");
2330 return None;
2331 }
2332 let open = self.advance();
2333 self.block_depth += 1;
2334
2335 let mut stmts = Vec::new();
2336 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2337 if matches!(self.peek().kind, TokenKind::Semicolon) {
2338 self.advance();
2339 continue;
2340 }
2341 let pos_before = self.pos;
2342 if let Some(id) = self.parse_statement() {
2343 stmts.push(id);
2344 } else {
2345 self.recover();
2346 if self.pos == pos_before && !self.is_at_eof() {
2347 self.advance();
2348 }
2349 }
2350 }
2351
2352 self.block_depth -= 1;
2353 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2354 self.error_at_peek("expected `}`");
2355 return None;
2356 }
2357 let close = self.advance();
2358
2359 let span = self.span(open.span.start, close.span.end);
2360 parse_arena_result(
2361 self.ast.try_push_stmt(Stmt {
2362 kind: StmtKind::Block(stmts),
2363 span,
2364 }),
2365 &mut self.fatal,
2366 )
2367 }
2368
2369 fn parse_control_body(&mut self) -> Option<StmtId> {
2374 if matches!(self.peek().kind, TokenKind::LeftBrace) {
2375 return self.parse_block();
2376 }
2377 let keyword = match self.peek().kind {
2382 TokenKind::Let => Some(("a", "let")),
2383 TokenKind::Const => Some(("a", "const")),
2384 TokenKind::Function => Some(("a", "function")),
2385 TokenKind::Interface => Some(("an", "interface")),
2387 TokenKind::Enum => Some(("an", "enum")),
2388 _ if self.peek_identifier_text_is("type") => Some(("a", "type")),
2389 _ => None,
2390 };
2391 let declares = match self.peek_at(1).kind {
2395 TokenKind::Identifier => true,
2396 TokenKind::LeftBracket | TokenKind::LeftBrace => !matches!(keyword, Some((_, "type"))),
2397 _ => false,
2398 };
2399 if let Some((article, keyword)) = keyword
2400 && declares
2401 {
2402 self.error_at_peek_with_help(
2403 format!(
2404 "{article} `{keyword}` declaration can't be the body of a statement without braces"
2405 ),
2406 vec![format!("wrap it in braces: `{{ {keyword} … }}`")],
2407 );
2408 }
2409 self.block_depth += 1;
2410 let stmt = self.parse_statement();
2411 self.block_depth -= 1;
2412 let stmt = stmt?;
2413 let span = parse_arena_result(self.ast.try_stmt(stmt), &mut self.fatal)?.span;
2414 parse_arena_result(
2415 self.ast.try_push_stmt(Stmt {
2416 kind: StmtKind::Block(vec![stmt]),
2417 span,
2418 }),
2419 &mut self.fatal,
2420 )
2421 }
2422
2423 fn parse_loop_body(&mut self) -> Option<StmtId> {
2426 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2427 return self.parse_control_body();
2428 }
2429 self.empty_body_at_semicolon()
2430 }
2431
2432 fn parse_branch_body(&mut self, keyword: &str) -> Option<StmtId> {
2436 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2437 return self.parse_control_body();
2438 }
2439 self.error_at_peek_with_help(
2440 format!("`{keyword}` has an empty body"),
2441 vec![format!("remove the `;` after `{keyword}`")],
2442 );
2443 self.empty_body_at_semicolon()
2444 }
2445
2446 fn empty_body_at_semicolon(&mut self) -> Option<StmtId> {
2448 let semi = self.advance();
2449 parse_arena_result(
2450 self.ast.try_push_stmt(Stmt {
2451 kind: StmtKind::Block(vec![]),
2452 span: semi.span,
2453 }),
2454 &mut self.fatal,
2455 )
2456 }
2457
2458 fn parse_if(&mut self) -> Option<StmtId> {
2459 self.with_recursion_limit(Self::parse_if_inner)
2460 }
2461
2462 fn parse_if_inner(&mut self) -> Option<StmtId> {
2463 let kw = self.advance();
2464 let condition = self.parse_paren_condition()?;
2465 let then_block = self.parse_branch_body("if")?;
2466
2467 let else_block = if matches!(self.peek().kind, TokenKind::Else) {
2468 self.advance();
2469 if matches!(self.peek().kind, TokenKind::If) {
2470 Some(self.parse_if()?)
2471 } else {
2472 Some(self.parse_branch_body("else")?)
2473 }
2474 } else {
2475 None
2476 };
2477
2478 let end = match else_block {
2479 Some(id) => {
2480 parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
2481 .span
2482 .end
2483 }
2484 None => {
2485 parse_arena_result(self.ast.try_stmt(then_block), &mut self.fatal)?
2486 .span
2487 .end
2488 }
2489 };
2490
2491 parse_arena_result(
2492 self.ast.try_push_stmt(Stmt {
2493 kind: StmtKind::If {
2494 condition,
2495 then_block,
2496 else_block,
2497 },
2498 span: self.span(kw.span.start, end),
2499 }),
2500 &mut self.fatal,
2501 )
2502 }
2503
2504 fn parse_while(&mut self) -> Option<StmtId> {
2505 let kw = self.advance();
2506 let condition = self.parse_paren_condition()?;
2507 let body = self.parse_loop_body()?;
2508 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2509 .span
2510 .end;
2511
2512 parse_arena_result(
2513 self.ast.try_push_stmt(Stmt {
2514 kind: StmtKind::While { condition, body },
2515 span: self.span(kw.span.start, body_end),
2516 }),
2517 &mut self.fatal,
2518 )
2519 }
2520
2521 fn parse_for(&mut self) -> Option<StmtId> {
2523 let kw = self.advance();
2524 if !matches!(self.peek().kind, TokenKind::LeftParen) {
2525 self.error_at_peek("expected `(` after `for`");
2526 return None;
2527 }
2528 self.advance();
2529
2530 if self.scan_for_of_shape() {
2531 self.parse_for_of_tail(kw)
2532 } else {
2533 self.parse_c_for_tail(kw)
2534 }
2535 }
2536
2537 fn scan_for_of_shape(&self) -> bool {
2539 if !matches!(self.peek().kind, TokenKind::Let | TokenKind::Const) {
2540 return false;
2541 }
2542 let (start, mut depth) = match self.peek_at(1).kind {
2544 TokenKind::Identifier => match self.peek_at(2).kind {
2545 TokenKind::Identifier if self.peek_at_is_word(2, "of") => return true,
2546 TokenKind::Colon => (3, 0i32),
2547 _ => return false,
2548 },
2549 TokenKind::LeftBracket | TokenKind::LeftBrace => (2, 1i32),
2550 _ => return false,
2551 };
2552 let mut i = start;
2553 loop {
2554 match self.peek_at(i).kind {
2555 TokenKind::Eof => return false,
2556 TokenKind::LessThan
2557 | TokenKind::LeftParen
2558 | TokenKind::LeftBracket
2559 | TokenKind::LeftBrace => {
2560 depth += 1;
2561 }
2562 TokenKind::GreaterThan
2563 | TokenKind::RightParen
2564 | TokenKind::RightBracket
2565 | TokenKind::RightBrace => {
2566 if depth == 0 {
2567 return false;
2568 }
2569 depth -= 1;
2570 }
2571 TokenKind::Identifier if depth == 0 && self.peek_at_is_word(i, "of") => {
2574 return true;
2575 }
2576 TokenKind::Equals | TokenKind::Semicolon if depth == 0 => return false,
2577 _ => {}
2578 }
2579 i += 1;
2580 }
2581 }
2582
2583 fn parse_c_for_tail(&mut self, kw: Token) -> Option<StmtId> {
2585 let init = if matches!(self.peek().kind, TokenKind::Semicolon) {
2586 self.advance();
2587 None
2588 } else {
2589 let init_id = match self.peek().kind {
2590 TokenKind::Let => self.parse_let_or_const(false)?,
2591 TokenKind::Const => self.parse_let_or_const(true)?,
2592 _ => self.parse_expression_statement()?,
2593 };
2594 Some(init_id)
2595 };
2596 let for_stmt = self.finish_c_for(kw, init);
2597 if for_stmt.is_none()
2598 && let Some(init) = init
2599 {
2600 self.discard_detached_pattern(init);
2601 }
2602 for_stmt
2603 }
2604
2605 fn finish_c_for(&mut self, kw: Token, init: Option<StmtId>) -> Option<StmtId> {
2606 let condition = if matches!(self.peek().kind, TokenKind::Semicolon) {
2607 None
2608 } else {
2609 Some(self.parse_expression()?)
2610 };
2611 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2612 self.error_at_peek("expected `;` after `for` condition");
2613 return None;
2614 }
2615 self.advance();
2616
2617 let update = if matches!(self.peek().kind, TokenKind::RightParen) {
2618 None
2619 } else {
2620 Some(self.parse_for_update_stmt()?)
2621 };
2622 if !matches!(self.peek().kind, TokenKind::RightParen) {
2623 self.error_at_peek("expected `)` after `for` update");
2624 return None;
2625 }
2626 self.advance();
2627
2628 let body = self.parse_loop_body()?;
2629 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2630 .span
2631 .end;
2632 parse_arena_result(
2633 self.ast.try_push_stmt(Stmt {
2634 kind: StmtKind::For {
2635 init,
2636 condition,
2637 update,
2638 body,
2639 },
2640 span: self.span(kw.span.start, body_end),
2641 }),
2642 &mut self.fatal,
2643 )
2644 }
2645
2646 fn discard_detached_pattern(&mut self, init: StmtId) {
2649 let Some(stmt) = parse_arena_result(self.ast.try_stmt_mut(init), &mut self.fatal) else {
2650 return;
2651 };
2652 if matches!(
2653 stmt.kind,
2654 StmtKind::LetPattern { .. } | StmtKind::ConstPattern { .. }
2655 ) {
2656 stmt.kind = StmtKind::Block(Vec::new());
2657 }
2658 }
2659
2660 fn parse_for_update_stmt(&mut self) -> Option<StmtId> {
2661 let expr_id = self.parse_expression()?;
2662 let span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
2663 let kind = self.statement_kind_for(expr_id)?;
2664 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
2665 }
2666
2667 fn parse_for_of_tail(&mut self, kw: Token) -> Option<StmtId> {
2668 let binding_tok = self.advance();
2669 let binding_kind = match binding_tok.kind {
2670 TokenKind::Let => crate::BindingKind::Let,
2671 TokenKind::Const => crate::BindingKind::Const,
2672 _ => return self.invariant_failure("scan_for_of_shape gated this"),
2673 };
2674 let (name, binding) = match self.peek().kind {
2675 TokenKind::LeftBracket => {
2676 let b = self.parse_binding()?;
2677 (None, Some(b))
2678 }
2679 TokenKind::LeftBrace => {
2680 let b = self.parse_binding()?;
2681 self.error_at(
2682 b.span(),
2683 "object destructuring is not supported in `for-of`; use array destructuring or unpack inside the loop body",
2684 );
2685 return None;
2686 }
2687 _ => {
2688 let name_tok = self.expect_binding_identifier(
2689 "expected identifier after `let`/`const` in `for-of`",
2690 )?;
2691 (Some(self.ident_from_token(&name_tok)), None)
2692 }
2693 };
2694 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2695 self.advance();
2696 Some(self.parse_type_annotation()?)
2697 } else {
2698 None
2699 };
2700 if !self.peek_identifier_text_is("of") {
2701 self.error_at_peek("expected `of` in `for-of`");
2702 return None;
2703 }
2704 self.advance();
2705
2706 let iter = self.parse_expression()?;
2707 if !matches!(self.peek().kind, TokenKind::RightParen) {
2708 self.error_at_peek("expected `)` after `for-of` iterable");
2709 return None;
2710 }
2711 self.advance();
2712
2713 let body = self.parse_loop_body()?;
2714 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2715 .span
2716 .end;
2717 let kind = match (binding, name) {
2718 (Some(binding), _) => StmtKind::ForOfPattern {
2719 binding_kind,
2720 binding,
2721 ty,
2722 iter,
2723 body,
2724 },
2725 (None, Some(name)) => StmtKind::ForOf {
2726 binding_kind,
2727 name,
2728 ty,
2729 iter,
2730 body,
2731 },
2732 (None, None) => return self.invariant_failure("either binding or name must be Some"),
2733 };
2734 parse_arena_result(
2735 self.ast.try_push_stmt(Stmt {
2736 kind,
2737 span: self.span(kw.span.start, body_end),
2738 }),
2739 &mut self.fatal,
2740 )
2741 }
2742
2743 fn parse_do_while(&mut self) -> Option<StmtId> {
2744 let kw = self.advance();
2745 let body = self.parse_loop_body()?;
2746 self.eat_asi_semicolon_before(TokenKind::While);
2747 if !matches!(self.peek().kind, TokenKind::While) {
2748 self.error_at_peek("expected `while` after `do` body");
2749 return None;
2750 }
2751 self.advance();
2752 let condition = self.parse_paren_condition()?;
2753 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
2757 self.advance().span.end
2758 } else {
2759 parse_arena_result(self.ast.try_expr(condition), &mut self.fatal)?
2760 .span
2761 .end
2762 };
2763 parse_arena_result(
2764 self.ast.try_push_stmt(Stmt {
2765 kind: StmtKind::DoWhile { body, condition },
2766 span: self.span(kw.span.start, end),
2767 }),
2768 &mut self.fatal,
2769 )
2770 }
2771
2772 fn parse_switch(&mut self) -> Option<StmtId> {
2774 let kw = self.advance();
2775 if !matches!(self.peek().kind, TokenKind::LeftParen) {
2776 self.error_at_peek("expected `(` after `switch`");
2777 return None;
2778 }
2779 self.advance();
2780 let discriminant = self.parse_expression()?;
2781 if !matches!(self.peek().kind, TokenKind::RightParen) {
2782 self.error_at_peek("expected `)` after `switch` discriminant");
2783 return None;
2784 }
2785 self.advance();
2786
2787 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2788 self.error_at_peek("expected `{` to start `switch` body");
2789 return None;
2790 }
2791 let open = self.advance();
2792 self.block_depth += 1;
2793
2794 let mut cases: Vec<SwitchCase> = Vec::new();
2795 let mut default: Option<SwitchDefault> = None;
2796 let mut pending_values: Vec<ExprId> = Vec::new();
2797 let mut pending_label_start: Option<u32> = None;
2798 let mut pending_default: Option<Span> = None;
2799
2800 loop {
2801 match self.peek().kind {
2802 TokenKind::Eof => break,
2803 TokenKind::Semicolon => {
2805 self.advance();
2806 }
2807 TokenKind::RightBrace => {
2808 if pending_label_start.is_some() {
2810 self.error_at_peek(
2811 "expected case body before `}` — case labels must be followed by statements",
2812 );
2813 return None;
2814 }
2815 break;
2816 }
2817 TokenKind::Case => {
2818 let case_kw = self.advance();
2819 if pending_label_start.is_none() {
2820 pending_label_start = Some(case_kw.span.start);
2821 }
2822 let value = self.parse_expression()?;
2823 if !matches!(self.peek().kind, TokenKind::Colon) {
2824 self.error_at_peek("expected `:` after `case` label");
2825 return None;
2826 }
2827 self.advance();
2828 pending_values.push(value);
2829 }
2830 TokenKind::Default => {
2831 let kw_tok = self.advance();
2832 if default.is_some() {
2833 self.error_at(kw_tok.span, "duplicate `default` clause in `switch`");
2834 return None;
2835 }
2836 if !matches!(self.peek().kind, TokenKind::Colon) {
2837 self.error_at_peek("expected `:` after `default`");
2838 return None;
2839 }
2840 self.advance();
2841 if pending_label_start.is_none() {
2842 pending_label_start = Some(kw_tok.span.start);
2843 }
2844 pending_default = Some(kw_tok.span);
2845 }
2846 _ => {
2847 if pending_label_start.is_none() {
2848 self.error_at_peek(
2849 "expected `case` or `default` at the start of a `switch` body",
2850 );
2851 return None;
2852 }
2853 let Some(label_start) = pending_label_start else {
2854 return self.invariant_failure("switch arm has no label");
2855 };
2856 let body = self.parse_switch_arm_body(label_start)?;
2857 let arm_span =
2858 parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
2859 if !pending_values.is_empty() {
2860 let first = parse_arena_result(
2861 self.ast.try_expr(pending_values[0]),
2862 &mut self.fatal,
2863 )?
2864 .span
2865 .start;
2866 let case_span = self.span(first, arm_span.end);
2867 cases.push(SwitchCase {
2868 values: std::mem::take(&mut pending_values),
2869 body,
2870 span: case_span,
2871 });
2872 }
2873 if let Some(def_kw_span) = pending_default.take() {
2874 default = Some(SwitchDefault {
2875 body,
2876 span: self.span(def_kw_span.start, arm_span.end),
2877 });
2878 }
2879 pending_label_start = None;
2880 }
2881 }
2882 }
2883
2884 self.block_depth -= 1;
2885 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2886 self.error_at_peek("expected `}` to close `switch` body");
2887 return None;
2888 }
2889 let close = self.advance();
2890
2891 let _ = open;
2892 parse_arena_result(
2893 self.ast.try_push_stmt(Stmt {
2894 kind: StmtKind::Switch {
2895 discriminant,
2896 cases,
2897 default,
2898 },
2899 span: self.span(kw.span.start, close.span.end),
2900 }),
2901 &mut self.fatal,
2902 )
2903 }
2904
2905 fn parse_switch_arm_body(&mut self, body_start: u32) -> Option<StmtId> {
2907 let mut stmts: Vec<StmtId> = Vec::new();
2908 let mut end_offset = body_start;
2909 loop {
2910 if matches!(
2911 self.peek().kind,
2912 TokenKind::Case | TokenKind::Default | TokenKind::RightBrace | TokenKind::Eof
2913 ) {
2914 break;
2915 }
2916 if matches!(self.peek().kind, TokenKind::Semicolon) {
2917 end_offset = self.peek().span.end;
2918 self.advance();
2919 continue;
2920 }
2921 let pos_before = self.pos;
2922 if let Some(id) = self.parse_statement() {
2923 end_offset = parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
2924 .span
2925 .end;
2926 stmts.push(id);
2927 } else {
2928 self.recover();
2929 if self.pos == pos_before && !self.is_at_eof() {
2930 self.advance();
2931 }
2932 end_offset = end_offset.max(self.prev_token_end());
2935 }
2936 }
2937 let span = self.span(body_start, end_offset);
2938 parse_arena_result(
2939 self.ast.try_push_stmt(Stmt {
2940 kind: StmtKind::Block(stmts),
2941 span,
2942 }),
2943 &mut self.fatal,
2944 )
2945 }
2946
2947 fn parse_break(&mut self) -> Option<StmtId> {
2948 let kw = self.advance();
2949 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2950 self.error_at_peek("expected `;` after `break`");
2951 return None;
2952 }
2953 let semi = self.advance();
2954 parse_arena_result(
2955 self.ast.try_push_stmt(Stmt {
2956 kind: StmtKind::Break,
2957 span: self.span(kw.span.start, semi.span.end),
2958 }),
2959 &mut self.fatal,
2960 )
2961 }
2962
2963 fn parse_continue(&mut self) -> Option<StmtId> {
2964 let kw = self.advance();
2965 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2966 self.error_at_peek("expected `;` after `continue`");
2967 return None;
2968 }
2969 let semi = self.advance();
2970 parse_arena_result(
2971 self.ast.try_push_stmt(Stmt {
2972 kind: StmtKind::Continue,
2973 span: self.span(kw.span.start, semi.span.end),
2974 }),
2975 &mut self.fatal,
2976 )
2977 }
2978
2979 fn parse_return(&mut self) -> Option<StmtId> {
2980 let kw = self.advance();
2981
2982 let value = if matches!(self.peek().kind, TokenKind::Semicolon) {
2983 None
2984 } else {
2985 Some(self.parse_expression()?)
2986 };
2987
2988 if !self.at_statement_end() {
2989 self.error_at_peek("expected `;` after return");
2990 return None;
2991 }
2992 let end = self.finish_statement();
2993
2994 parse_arena_result(
2995 self.ast.try_push_stmt(Stmt {
2996 kind: StmtKind::Return(value),
2997 span: self.span(kw.span.start, end),
2998 }),
2999 &mut self.fatal,
3000 )
3001 }
3002
3003 fn parse_throw(&mut self) -> Option<StmtId> {
3004 let kw = self.advance();
3005
3006 if matches!(self.peek().kind, TokenKind::Semicolon) {
3007 self.error_at_peek_with_help(
3008 "expected expression after `throw`",
3009 vec!["throw new Error(\"message\");".to_string()],
3010 );
3011 return None;
3012 }
3013
3014 let value = self.parse_expression()?;
3015
3016 if !self.at_statement_end() {
3017 self.error_at_peek("expected `;` after `throw`");
3018 return None;
3019 }
3020 let end = self.finish_statement();
3021
3022 parse_arena_result(
3023 self.ast.try_push_stmt(Stmt {
3024 kind: StmtKind::Throw { value },
3025 span: self.span(kw.span.start, end),
3026 }),
3027 &mut self.fatal,
3028 )
3029 }
3030
3031 fn parse_try(&mut self) -> Option<StmtId> {
3032 let kw = self.advance();
3033
3034 let body = self.parse_block()?;
3035
3036 let mut catches: Vec<CatchClause> = Vec::new();
3037 let mut finally: Option<StmtId> = None;
3038 let mut tail_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3039 .span
3040 .end;
3041
3042 loop {
3043 match self.peek().kind {
3044 TokenKind::Catch => {
3045 if finally.is_some() {
3046 self.error_at_peek("`catch` clause must appear before `finally`");
3047 return None;
3048 }
3049 let clause = self.parse_catch_clause()?;
3050 tail_end = clause.span.end;
3051 catches.push(clause);
3052 }
3053 TokenKind::Finally => {
3054 if finally.is_some() {
3055 self.error_at_peek("duplicate `finally` clause");
3056 return None;
3057 }
3058 self.advance();
3059 let block = self.parse_block()?;
3060 tail_end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
3061 .span
3062 .end;
3063 finally = Some(block);
3064 }
3065 _ => break,
3066 }
3067 }
3068
3069 if catches.is_empty() && finally.is_none() {
3070 self.error_at(
3071 self.span(kw.span.start, tail_end),
3072 "try requires at least one of `catch` or `finally`",
3073 );
3074 return None;
3075 }
3076
3077 parse_arena_result(
3078 self.ast.try_push_stmt(Stmt {
3079 kind: StmtKind::Try {
3080 body,
3081 catches,
3082 finally,
3083 },
3084 span: self.span(kw.span.start, tail_end),
3085 }),
3086 &mut self.fatal,
3087 )
3088 }
3089
3090 fn parse_catch_clause(&mut self) -> Option<CatchClause> {
3091 let kw = self.advance();
3092
3093 let (binding, ty) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
3094 (
3096 Ident {
3097 name: "#catch".into(),
3098 span: kw.span,
3099 },
3100 None,
3101 )
3102 } else {
3103 self.parse_catch_binding()?
3104 };
3105
3106 let body = self.parse_block()?;
3107 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3108 .span
3109 .end;
3110
3111 Some(CatchClause {
3112 binding,
3113 ty,
3114 body,
3115 span: self.span(kw.span.start, body_end),
3116 })
3117 }
3118
3119 fn parse_catch_binding(&mut self) -> Option<(Ident, Option<TypeAnnotation>)> {
3120 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3121 self.error_at_peek("expected `(` after `catch`");
3122 return None;
3123 }
3124 self.advance();
3125 let name_tok = self.expect_binding_identifier("expected catch binding name")?;
3126 let binding = self.ident_from_token(&name_tok);
3127 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3128 self.advance();
3129 if self.reject_any_catch_annotation() {
3130 None
3132 } else {
3133 Some(self.parse_type_annotation()?)
3134 }
3135 } else {
3136 None
3137 };
3138 if !matches!(self.peek().kind, TokenKind::RightParen) {
3139 self.error_at_peek("expected `)` after catch binding");
3140 return None;
3141 }
3142 self.advance();
3143 Some((binding, ty))
3144 }
3145
3146 fn reject_any_catch_annotation(&mut self) -> bool {
3149 let is_bare_any = self.peek_identifier_text_is("any")
3150 && matches!(self.peek_at(1).kind, TokenKind::RightParen);
3151 if !is_bare_any {
3152 return false;
3153 }
3154 let any_tok = self.advance();
3155 self.error_at_with_help(
3157 any_tok.span,
3158 "`any` is not supported",
3159 vec![
3160 "write `catch (e)` to catch every thrown error, or name an error class, \
3161 as in `catch (e: RangeError)`, to catch only that type"
3162 .to_string(),
3163 ],
3164 );
3165 true
3166 }
3167
3168 fn parse_paren_condition(&mut self) -> Option<ExprId> {
3169 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3170 self.error_at_peek("expected `(`");
3171 return None;
3172 }
3173 self.advance();
3174 let expr = self.parse_expression()?;
3175 if !matches!(self.peek().kind, TokenKind::RightParen) {
3176 self.error_at_peek("expected `)`");
3177 return None;
3178 }
3179 self.advance();
3180 Some(expr)
3181 }
3182
3183 fn parse_predicate_or_return_type(
3184 &mut self,
3185 type_pos: TypePos,
3186 ) -> Option<(
3187 Option<TypeAnnotation>,
3188 Option<crate::TypePredicateAnnotation>,
3189 )> {
3190 if matches!(self.peek().kind, TokenKind::Identifier) && self.peek_at_is_word(1, "is") {
3191 let param_tok = self.advance();
3192 let param = self.ident_from_token(¶m_tok);
3193 self.advance();
3194 let asserted = self.parse_type_annotation_in(type_pos)?;
3195 let span = self.span(param.span.start, asserted.span.end);
3196 return Some((
3197 None,
3198 Some(crate::TypePredicateAnnotation {
3199 param,
3200 asserted,
3201 span,
3202 }),
3203 ));
3204 }
3205 Some((Some(self.parse_type_annotation_in(type_pos)?), None))
3206 }
3207
3208 fn parse_type_annotation(&mut self) -> Option<TypeAnnotation> {
3209 self.parse_type_annotation_in(TypePos::Anywhere)
3210 }
3211
3212 fn parse_type_annotation_in(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3213 let leading_pipe = matches!(self.peek().kind, TokenKind::Pipe);
3215 if leading_pipe {
3216 self.advance();
3217 }
3218 let first = self.parse_type_array(type_pos)?;
3219 if !matches!(self.peek().kind, TokenKind::Pipe) {
3220 return Some(first);
3221 }
3222 let start = first.span.start;
3223 let mut end = first.span.end;
3224 let mut members = vec![first];
3225 while matches!(self.peek().kind, TokenKind::Pipe) {
3226 self.advance();
3227 let member = self.parse_type_array(type_pos)?;
3228 end = member.span.end;
3229 members.push(member);
3230 }
3231 Some(TypeAnnotation {
3232 kind: TypeAnnotationKind::Union(members),
3233 span: self.span(start, end),
3234 })
3235 }
3236
3237 fn parse_type_array(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3238 self.with_recursion_limit(|parser| parser.parse_type_array_inner(type_pos))
3239 }
3240
3241 fn parse_type_array_inner(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3242 if self.is_contextual_type_operator(self.pos, "keyof") {
3246 let kw = self.advance();
3247 let operand = self.parse_type_array(type_pos)?;
3248 let span = self.span(kw.span.start, operand.span.end);
3249 return Some(TypeAnnotation {
3250 kind: TypeAnnotationKind::KeyOf(Box::new(operand)),
3251 span,
3252 });
3253 }
3254 if self.is_contextual_type_operator(self.pos, "readonly") {
3255 return self.parse_readonly_type(type_pos);
3256 }
3257
3258 let mut ty = match self.peek().kind {
3259 TokenKind::Typeof => {
3265 let kw = self.advance();
3266 let first = self.expect_identifier("expected a value name after `typeof`")?;
3267 let mut path = vec![self.type_name(first.span)?];
3268 let mut end = first.span.end;
3269 while matches!(self.peek().kind, TokenKind::Dot) {
3270 self.advance();
3271 let seg = self.expect_identifier_name("expected a property name after `.`")?;
3272 end = seg.span.end;
3273 path.push(self.type_name(seg.span)?);
3274 }
3275 TypeAnnotation {
3276 kind: TypeAnnotationKind::TypeOf { path },
3277 span: self.span(kw.span.start, end),
3278 }
3279 }
3280 TokenKind::Identifier | TokenKind::Void => {
3281 let leading_is_identifier = matches!(self.peek().kind, TokenKind::Identifier);
3283 let tok = self.advance();
3284 let first_span = tok.span;
3285 let mut path_end = first_span.end;
3286 let first_name = self.type_name(first_span)?;
3287 let mut path = vec![first_name.clone()];
3288 if leading_is_identifier {
3289 while matches!(self.peek().kind, TokenKind::Dot) {
3290 self.advance();
3291 if !matches!(self.peek().kind, TokenKind::Identifier) {
3292 self.error_at_peek("expected identifier after `.` in type name");
3293 return None;
3294 }
3295 let seg = self.advance();
3296 path_end = seg.span.end;
3297 path.push(self.type_name(seg.span)?);
3298 }
3299 }
3300 let (args, end) = if matches!(self.peek().kind, TokenKind::LessThan)
3302 && !self.line_break_before_peek()
3303 {
3304 self.parse_type_argument_list()?
3305 } else {
3306 (Vec::new(), path_end)
3307 };
3308 let outer_span = self.span(first_span.start, end);
3309 let kind = if path.len() == 1 {
3310 if first_name.name == "any" {
3311 self.error_at_with_help(
3312 first_span,
3313 "`any` is not supported",
3314 vec![
3315 "annotate a concrete type instead — an object shape \
3316 like `{ id: string }`, a named `interface`, or \
3317 `unknown` (a safe dynamic type you narrow before use)"
3318 .to_string(),
3319 ],
3320 );
3321 }
3322 TypeAnnotationKind::Name {
3323 name: first_name,
3324 args,
3325 }
3326 } else {
3327 TypeAnnotationKind::Qualified { path, args }
3328 };
3329 TypeAnnotation {
3330 kind,
3331 span: outer_span,
3332 }
3333 }
3334 TokenKind::NullLiteral => {
3336 let tok = self.advance();
3337 TypeAnnotation {
3338 kind: TypeAnnotationKind::Name {
3339 name: self.type_name(tok.span)?,
3340 args: Vec::new(),
3341 },
3342 span: tok.span,
3343 }
3344 }
3345 TokenKind::StringLiteral(_) => {
3346 let tok = self.advance();
3347 let span = tok.span;
3348 let TokenKind::StringLiteral(s) = tok.kind else {
3349 unreachable!("advance preserves the matched StringLiteral variant");
3351 };
3352 TypeAnnotation {
3353 kind: TypeAnnotationKind::StringLiteral(s),
3354 span,
3355 }
3356 }
3357 TokenKind::NumberLiteral(_) => {
3358 let tok = self.advance();
3359 let span = tok.span;
3360 let TokenKind::NumberLiteral(v) = tok.kind else {
3361 unreachable!("advance preserves the matched NumberLiteral variant");
3363 };
3364 let canonical = if v == 0.0 { 0.0 } else { v };
3366 TypeAnnotation {
3367 kind: TypeAnnotationKind::NumberLiteral(crate::types::LiteralF64(canonical)),
3368 span,
3369 }
3370 }
3371 TokenKind::BigIntLiteral(_) => {
3372 let tok = self.advance();
3373 let TokenKind::BigIntLiteral(digits) = tok.kind else {
3374 unreachable!("advance preserves the matched BigIntLiteral variant");
3376 };
3377 TypeAnnotation {
3378 kind: TypeAnnotationKind::BigIntLiteral(digits),
3379 span: tok.span,
3380 }
3381 }
3382 TokenKind::Minus
3383 if matches!(
3384 self.peek_at(1).kind,
3385 TokenKind::NumberLiteral(_) | TokenKind::BigIntLiteral(_)
3386 ) =>
3387 {
3388 let minus = self.advance().span;
3389 let tok = self.advance();
3390 let span = self.span(minus.start, tok.span.end);
3391 let kind =
3392 match tok.kind {
3393 TokenKind::NumberLiteral(v) => TypeAnnotationKind::NumberLiteral(
3394 crate::types::LiteralF64(if v == 0.0 { 0.0 } else { -v }),
3395 ),
3396 TokenKind::BigIntLiteral(digits) => TypeAnnotationKind::BigIntLiteral(
3397 crate::types::negate_bigint_digits(&digits),
3398 ),
3399 _ => unreachable!("guarded to a numeric literal after `-`"),
3401 };
3402 TypeAnnotation { kind, span }
3403 }
3404 TokenKind::BooleanLiteral(value) => {
3405 let span = self.advance().span;
3406 TypeAnnotation {
3407 kind: TypeAnnotationKind::BooleanLiteral(value),
3408 span,
3409 }
3410 }
3411 TokenKind::LeftBrace => self.parse_object_type_annotation()?,
3412 TokenKind::LeftParen if self.paren_at_opens_function_type(self.pos, type_pos) => {
3413 self.parse_function_type_annotation()?
3414 }
3415 TokenKind::LeftParen => self.parse_grouped_or_function_type()?,
3416 TokenKind::LeftBracket => self.parse_tuple_type_annotation()?,
3418 _ => {
3419 self.error_at_peek("expected type");
3420 return None;
3421 }
3422 };
3423 let mut array_suffixes = 0usize;
3426 while matches!(self.peek().kind, TokenKind::LeftBracket) && !self.line_break_before_peek() {
3429 array_suffixes += 1;
3430 if self.recursion_depth.saturating_add(array_suffixes) >= MAX_PARSE_DEPTH {
3431 self.stop_at_recursion_limit();
3432 return None;
3433 }
3434 let open = self.advance();
3435 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3436 self.error_at(open.span, "expected `]` to close array type");
3437 return None;
3438 }
3439 let close = self.advance();
3440 let span = self.span(ty.span.start, close.span.end);
3441 ty = TypeAnnotation {
3442 kind: TypeAnnotationKind::Array(Box::new(ty)),
3443 span,
3444 };
3445 }
3446 Some(ty)
3447 }
3448
3449 fn parse_readonly_type(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3453 let kw = self.advance();
3454 let opens_group = matches!(self.peek().kind, TokenKind::LeftParen);
3455 let operand = self.parse_type_array(type_pos)?;
3456 let grouped = opens_group
3459 && matches!(
3460 self.pos
3461 .checked_sub(1)
3462 .and_then(|index| self.tokens.get(index))
3463 .map(|token| &token.kind),
3464 Some(TokenKind::RightParen)
3465 );
3466 if grouped
3467 || !matches!(
3468 operand.kind,
3469 TypeAnnotationKind::Array(_) | TypeAnnotationKind::Tuple(_)
3470 )
3471 {
3472 self.error_at_with_help(
3473 kw.span,
3474 "`readonly` only applies to array and tuple types",
3475 vec![
3476 "write `readonly T[]` or `readonly [A, B]`; to protect an object's \
3477 properties, mark each one `readonly` instead"
3478 .to_string(),
3479 ],
3480 );
3481 return Some(operand);
3482 }
3483 let span = self.span(kw.span.start, operand.span.end);
3484 Some(TypeAnnotation {
3485 kind: TypeAnnotationKind::Readonly(Box::new(operand)),
3486 span,
3487 })
3488 }
3489
3490 fn paren_at_opens_function_type(&self, index: usize, type_pos: TypePos) -> bool {
3501 let after = |offset: usize| self.tokens.get(index + offset).map(|t| &t.kind);
3502 match (after(1), after(2)) {
3503 (Some(TokenKind::RightParen | TokenKind::DotDotDot), _) => true,
3505 (Some(TokenKind::Identifier), Some(TokenKind::Colon | TokenKind::Question)) => true,
3507 (Some(TokenKind::Identifier), Some(TokenKind::Comma | TokenKind::RightParen)) => {
3516 type_pos == TypePos::Anywhere
3517 && self.index_after_matching_paren(index).is_some_and(|i| {
3518 matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow))
3519 })
3520 }
3521 _ => false,
3523 }
3524 }
3525
3526 fn parse_grouped_or_function_type(&mut self) -> Option<TypeAnnotation> {
3531 let open = self.pos;
3532 let diagnostics_before = self.diagnostics.len();
3533 if let Some(ty) = self.parse_grouped_type_annotation() {
3534 return Some(ty);
3535 }
3536 let arrow_follows = self
3537 .index_after_matching_paren(open)
3538 .is_some_and(|i| matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)));
3539 if !arrow_follows {
3540 return None;
3541 }
3542 self.pos = open;
3543 self.diagnostics.truncate(diagnostics_before);
3544 self.parse_function_type_annotation()
3545 }
3546
3547 fn parse_grouped_type_annotation(&mut self) -> Option<TypeAnnotation> {
3550 let open = self.advance();
3551 let inner = self.parse_type_annotation()?;
3552 if !matches!(self.peek().kind, TokenKind::RightParen) {
3553 self.error_at_peek("expected `)` to close a parenthesized type");
3554 return None;
3555 }
3556 let close = self.advance();
3557 Some(TypeAnnotation {
3558 span: self.span(open.span.start, close.span.end),
3559 ..inner
3560 })
3561 }
3562
3563 fn parse_function_type_annotation(&mut self) -> Option<TypeAnnotation> {
3565 let open = self.advance();
3566 let params = self.parse_function_type_params()?;
3567 if !matches!(self.peek().kind, TokenKind::Arrow) {
3568 self.error_at_peek("expected `=>` in function-type annotation");
3569 return None;
3570 }
3571 self.advance();
3572 let return_type = self.parse_type_annotation()?;
3573 let end = return_type.span.end;
3574 Some(TypeAnnotation {
3575 kind: TypeAnnotationKind::Function {
3576 params,
3577 return_type: Box::new(return_type),
3578 },
3579 span: self.span(open.span.start, end),
3580 })
3581 }
3582
3583 fn parse_function_type_params(&mut self) -> Option<Vec<TypeAnnotationField>> {
3587 let mut params: Vec<TypeAnnotationField> = Vec::new();
3588 if !matches!(self.peek().kind, TokenKind::RightParen) {
3589 loop {
3590 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
3591 self.advance();
3592 true
3593 } else {
3594 false
3595 };
3596 let name_tok = self.expect_binding_identifier(if rest {
3597 "expected identifier after `...` in function-type annotation"
3598 } else {
3599 "expected parameter name in function-type annotation"
3600 })?;
3601 let name = self.ident_from_token(&name_tok);
3602 let optional = self.parse_parameter_optional(rest, false)?;
3603 if !matches!(self.peek().kind, TokenKind::Colon) {
3604 if rest {
3605 self.error_at_peek_with_help(
3606 "rest parameter requires a type annotation",
3607 vec!["(...args: T[]) => R".to_string()],
3608 );
3609 } else {
3610 self.error_at_peek(
3611 "expected `:` after parameter name (function-type params \
3612 require named annotations in v1)",
3613 );
3614 }
3615 return None;
3616 }
3617 self.advance();
3618 let ty = self.parse_type_annotation()?;
3619 if rest && !is_rest_array_annotation(&ty) {
3620 self.error_at(ty.span, "rest parameter type must be an array");
3621 return None;
3622 }
3623 if matches!(self.peek().kind, TokenKind::Equals) {
3624 if rest {
3625 self.error_rest_parameter_default();
3626 } else {
3627 self.error_at_peek(
3628 "default values in function-type annotations are not yet supported",
3629 );
3630 }
3631 return None;
3632 }
3633 params.push(TypeAnnotationField {
3634 name,
3635 ty,
3636 optional,
3637 readonly: false,
3638 rest,
3639 method: false,
3640 });
3641 match self.peek().kind {
3642 TokenKind::Comma => {
3643 if rest {
3644 let comma = self.advance();
3645 self.error_at(comma.span, "rest parameter must be the last parameter");
3646 return None;
3647 }
3648 self.advance();
3649 if matches!(self.peek().kind, TokenKind::RightParen) {
3650 break;
3651 }
3652 }
3653 TokenKind::RightParen => break,
3654 _ => {
3655 self.error_at_peek("expected `,` or `)`");
3656 return None;
3657 }
3658 }
3659 }
3660 self.check_parameter_order(params.iter().map(ParamOrder::of_field));
3661 }
3662 if !matches!(self.peek().kind, TokenKind::RightParen) {
3663 self.error_at_peek("expected `)`");
3664 return None;
3665 }
3666 self.advance();
3667 Some(params)
3668 }
3669
3670 fn parse_tuple_type_annotation(&mut self) -> Option<TypeAnnotation> {
3671 let open = self.advance();
3672 if matches!(self.peek().kind, TokenKind::RightBracket) {
3673 let close = self.peek().span;
3674 self.error_at(
3675 self.span(open.span.start, close.end),
3676 "tuple types must have at least one element",
3677 );
3678 return None;
3679 }
3680 let mut elements: Vec<TypeAnnotation> = Vec::new();
3681 loop {
3682 if matches!(self.peek().kind, TokenKind::DotDotDot) {
3683 self.error_at_with_help(
3684 self.peek().span,
3685 "rest elements in tuple types are not supported",
3686 vec!["use an array type `T[]` for a list of varying length".to_string()],
3687 );
3688 return None;
3689 }
3690 let label = self.skip_tuple_element_label();
3693 let mut ty = self.parse_type_annotation()?;
3694 let suffix_optional = matches!(self.peek().kind, TokenKind::Question);
3695 if suffix_optional && label != TupleElementLabel::None {
3696 self.error_at_peek_with_help(
3697 "put the optional marker after the tuple element label",
3698 vec!["write `name?: T` instead of `name: T?`".to_string()],
3699 );
3700 return None;
3701 }
3702 let optional = label == TupleElementLabel::Optional || suffix_optional;
3703 if optional {
3704 let end = if suffix_optional {
3705 self.advance().span.end
3706 } else {
3707 ty.span.end
3708 };
3709 let span = self.span(ty.span.start, end);
3710 ty = TypeAnnotation {
3711 kind: TypeAnnotationKind::Optional(Box::new(ty)),
3712 span,
3713 };
3714 } else if elements
3715 .iter()
3716 .any(|element| matches!(element.kind, TypeAnnotationKind::Optional(_)))
3717 {
3718 self.error_at(
3719 ty.span,
3720 "a required tuple element cannot follow an optional element",
3721 );
3722 return None;
3723 }
3724 elements.push(ty);
3725 match self.peek().kind {
3726 TokenKind::Comma => {
3727 self.advance();
3728 if matches!(self.peek().kind, TokenKind::RightBracket) {
3729 break;
3730 }
3731 }
3732 TokenKind::RightBracket => break,
3733 _ => {
3734 self.error_at_peek("expected `,` or `]`");
3735 return None;
3736 }
3737 }
3738 }
3739 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3740 self.error_at_peek("expected `]`");
3741 return None;
3742 }
3743 let close = self.advance();
3744 Some(TypeAnnotation {
3745 kind: TypeAnnotationKind::Tuple(elements),
3746 span: self.span(open.span.start, close.span.end),
3747 })
3748 }
3749
3750 fn skip_tuple_element_label(&mut self) -> TupleElementLabel {
3752 if !is_property_name(&self.peek().kind) {
3753 return TupleElementLabel::None;
3754 }
3755 match self.peek_at(1).kind {
3756 TokenKind::Colon => {
3757 self.advance();
3758 self.advance();
3759 TupleElementLabel::Required
3760 }
3761 TokenKind::Question if matches!(self.peek_at(2).kind, TokenKind::Colon) => {
3762 self.advance();
3763 self.advance();
3764 self.advance();
3765 TupleElementLabel::Optional
3766 }
3767 _ => TupleElementLabel::None,
3768 }
3769 }
3770
3771 fn peek_is_parameterless_index_signature(&self) -> bool {
3774 matches!(self.peek().kind, TokenKind::LeftBracket)
3775 && matches!(self.peek_at(1).kind, TokenKind::RightBracket)
3776 }
3777
3778 fn skip_parameterless_index_signature(&mut self, member_start: u32) -> Option<()> {
3782 self.advance();
3783 let close = self.advance();
3784 self.error_at_with_help(
3785 self.span(member_start, close.span.end),
3786 "an index signature must declare exactly one parameter",
3787 vec!["write `[key: string]: V`".to_string()],
3788 );
3789 if matches!(self.peek().kind, TokenKind::Colon) {
3790 self.advance();
3791 self.parse_type_annotation()?;
3792 }
3793 Some(())
3794 }
3795
3796 fn parse_index_signature(&mut self, readonly: bool) -> Option<crate::IndexSignatureAnnotation> {
3797 let open = self.advance();
3798 let parameter = self.expect_property_ident("expected index parameter name")?;
3799 self.reject_eval_or_arguments(parameter.span);
3800 if !matches!(self.peek().kind, TokenKind::Colon) {
3801 self.error_at_peek("expected `:` after index parameter name");
3802 return None;
3803 }
3804 self.advance();
3805 if !self.peek_identifier_text_is("string") {
3806 self.error_at_peek("index signatures require string keys; use `[key: string]: V`");
3807 return None;
3808 }
3809 self.advance();
3810 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3811 self.error_at_peek("expected `]` after string index key type");
3812 return None;
3813 }
3814 self.advance();
3815 if !matches!(self.peek().kind, TokenKind::Colon) {
3816 self.error_at_peek("expected `:` before index value type");
3817 return None;
3818 }
3819 self.advance();
3820 let value = self.parse_type_annotation()?;
3821 Some(crate::IndexSignatureAnnotation {
3822 span: self.span(open.span.start, value.span.end),
3823 value,
3824 readonly,
3825 })
3826 }
3827
3828 fn parse_object_type_annotation(&mut self) -> Option<TypeAnnotation> {
3829 let open = self.advance();
3830 let mut fields: Vec<TypeAnnotationField> = Vec::new();
3831 let mut index = None;
3832 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
3833 && !self.peek_starts_declaration()
3834 {
3835 let member_start = self.peek().span.start;
3836 let readonly = self.eat_readonly_property_modifier();
3837 if self.peek_is_parameterless_index_signature() {
3838 self.skip_parameterless_index_signature(member_start)?;
3839 } else if self.peek_is_construct_signature() {
3840 self.reject_construct_signature()?;
3841 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
3842 let signature = self.parse_index_signature(readonly)?;
3843 if index.is_some() {
3844 self.error_at_with_help(
3845 signature.span,
3846 "duplicate string index signature",
3847 vec![],
3848 );
3849 }
3850 index = Some(Box::new(signature));
3851 } else {
3852 let field = self.parse_object_type_field(readonly, member_start)?;
3853 if let Some(existing) = fields.iter().find(|f| f.name.name == field.name.name) {
3854 self.diagnostics.push(Diagnostic {
3855 severity: Severity::Error,
3856 span: field.name.span,
3857 message: format!("duplicate field `{}` in object type", field.name.name),
3858 help: vec![],
3859 notes: vec![(existing.name.span, "first defined here".into())],
3860 });
3861 }
3862 fields.push(field);
3863 }
3864 self.finish_type_member(self.prev_token_end(), "expected `;`, `,`, or `}`")?;
3865 }
3866 if !matches!(self.peek().kind, TokenKind::RightBrace) {
3867 self.error_at_peek("expected `}`");
3868 return None;
3869 }
3870 let close = self.advance();
3871 Some(TypeAnnotation {
3872 kind: TypeAnnotationKind::Object { index, fields },
3873 span: self.span(open.span.start, close.span.end),
3874 })
3875 }
3876
3877 fn peek_starts_declaration(&self) -> bool {
3883 if matches!(self.peek().kind, TokenKind::Export)
3884 && matches!(
3885 self.peek_at(1).kind,
3886 TokenKind::Default | TokenKind::LeftBrace | TokenKind::Star
3887 )
3888 {
3889 return true;
3890 }
3891 let mut offset = 0;
3892 while self.peek_at_is_declaration_modifier(offset) {
3893 offset += 1;
3894 }
3895 let declaration_keyword = matches!(
3896 self.peek_at(offset).kind,
3897 TokenKind::Function
3898 | TokenKind::Class
3899 | TokenKind::Let
3900 | TokenKind::Const
3901 | TokenKind::Enum
3902 | TokenKind::Interface
3903 ) || self.peek_at_is_word(offset, "type");
3904 declaration_keyword && matches!(self.peek_at(offset + 1).kind, TokenKind::Identifier)
3905 }
3906
3907 fn peek_at_is_declaration_modifier(&self, offset: usize) -> bool {
3909 matches!(self.peek_at(offset).kind, TokenKind::Export)
3910 || ["declare", "async", "abstract"]
3911 .iter()
3912 .any(|word| self.peek_at_is_word(offset, word))
3913 }
3914
3915 fn peek_is_construct_signature(&self) -> bool {
3920 matches!(self.peek().kind, TokenKind::New)
3921 && matches!(
3922 self.peek_at(1).kind,
3923 TokenKind::LeftParen | TokenKind::LessThan
3924 )
3925 }
3926
3927 fn reject_construct_signature(&mut self) -> Option<()> {
3931 let keyword = self.advance();
3932 self.error_at_with_help(
3933 keyword.span,
3934 "construct signatures are not supported in object types",
3935 vec![
3936 "declare an `interface` with the `new (…)` signature, or pass a factory \
3937 function such as `() => T`"
3938 .to_string(),
3939 ],
3940 );
3941 if matches!(self.peek().kind, TokenKind::LessThan) {
3942 self.parse_generic_param_list()?;
3943 }
3944 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3945 self.error_at_peek("expected `(` after construct signature type parameters");
3946 return None;
3947 }
3948 self.parse_object_type_method_signature(keyword.span.start)?;
3949 Some(())
3950 }
3951
3952 fn parse_object_type_field(
3954 &mut self,
3955 readonly: bool,
3956 member_start: u32,
3957 ) -> Option<TypeAnnotationField> {
3958 let name = self.expect_property_ident("expected field name in object type")?;
3959 let optional = matches!(self.peek().kind, TokenKind::Question);
3961 if optional {
3962 self.advance();
3963 }
3964 let method = matches!(self.peek().kind, TokenKind::LeftParen);
3965 let ty = if method {
3966 if readonly {
3967 self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
3968 }
3969 self.parse_object_type_method_signature(name.span.start)?
3970 } else {
3971 if !matches!(self.peek().kind, TokenKind::Colon) {
3972 self.error_at_peek("expected `:` after field name");
3973 return None;
3974 }
3975 self.advance();
3976 self.parse_type_annotation()?
3977 };
3978 Some(TypeAnnotationField {
3979 name,
3980 ty,
3981 optional,
3982 readonly,
3983 rest: false,
3984 method,
3985 })
3986 }
3987
3988 fn parse_object_type_method_signature(&mut self, start: u32) -> Option<TypeAnnotation> {
3993 self.advance();
3994 let params = self.parse_function_type_params()?;
3995 if !matches!(self.peek().kind, TokenKind::Colon) {
3996 self.error_at_peek("expected `:` and return type after method parameters");
3997 return None;
3998 }
3999 self.advance();
4000 let return_type = self.parse_type_annotation()?;
4001 let end = return_type.span.end;
4002 Some(TypeAnnotation {
4003 kind: TypeAnnotationKind::Function {
4004 params,
4005 return_type: Box::new(return_type),
4006 },
4007 span: self.span(start, end),
4008 })
4009 }
4010
4011 fn expect_identifier(&mut self, message: &str) -> Option<Token> {
4014 let tok = self.expect_identifier_name(message)?;
4015 self.reject_strict_mode_reserved_word(&tok);
4016 Some(tok)
4017 }
4018
4019 fn expect_binding_identifier(&mut self, message: &str) -> Option<Token> {
4023 let tok = self.expect_identifier(message)?;
4024 self.reject_eval_or_arguments(tok.span);
4025 Some(tok)
4026 }
4027
4028 fn expect_identifier_name(&mut self, message: &str) -> Option<Token> {
4031 if matches!(self.peek().kind, TokenKind::Identifier) {
4032 return Some(self.advance());
4033 }
4034 if is_reserved_identifier_word(&self.peek().kind) {
4035 let tok = self.advance();
4036 let keyword = &self.source[tok.span.start as usize..tok.span.end as usize];
4037 self.error_at_with_help(
4038 tok.span,
4039 format!("`{keyword}` is a reserved keyword and can't be used as a name"),
4040 vec![format!(
4041 "rename it, for example: `{}`",
4042 reserved_keyword_rename_example(keyword)
4043 )],
4044 );
4045 return None;
4046 }
4047 self.error_at_peek(message.to_string());
4048 None
4049 }
4050
4051 fn reject_strict_mode_reserved_word(&mut self, tok: &Token) {
4054 let word = &self.source[tok.span.start as usize..tok.span.end as usize];
4055 if !STRICT_MODE_RESERVED_WORDS.contains(&word) {
4056 return;
4057 }
4058 self.error_at_with_help(
4059 tok.span,
4060 format!("`{word}` is a reserved word in strict mode and can't be used as a name"),
4061 vec![format!(
4062 "rename it, for example: `{}`",
4063 reserved_keyword_rename_example(word)
4064 )],
4065 );
4066 }
4067
4068 fn reject_eval_or_arguments(&mut self, span: Span) {
4071 let word = &self.source[span.start as usize..span.end as usize];
4072 if !matches!(word, "eval" | "arguments") {
4073 return;
4074 }
4075 self.error_at_with_help(
4076 span,
4077 format!("`{word}` can't name a binding in strict mode"),
4078 vec![format!(
4079 "rename it, for example: `{}`",
4080 reserved_keyword_rename_example(word)
4081 )],
4082 );
4083 }
4084
4085 fn ident_from_token(&self, tok: &Token) -> Ident {
4086 Ident {
4087 name: self.source[tok.span.start as usize..tok.span.end as usize].to_string(),
4088 span: tok.span,
4089 }
4090 }
4091
4092 fn property_ident_from_token(&self, tok: &Token) -> Ident {
4093 if let TokenKind::StringLiteral(s) = &tok.kind {
4094 Ident {
4095 name: s.clone(),
4096 span: tok.span,
4097 }
4098 } else {
4099 self.ident_from_token(tok)
4100 }
4101 }
4102
4103 fn expect_property_name(&mut self, message: &str) -> Option<Token> {
4104 if !is_property_name(&self.peek().kind) {
4105 self.error_at_peek(message.to_string());
4106 return None;
4107 }
4108 Some(self.advance())
4109 }
4110
4111 fn expect_property_ident(&mut self, message: &str) -> Option<Ident> {
4112 if !is_property_name(&self.peek().kind)
4113 && !matches!(self.peek().kind, TokenKind::StringLiteral(_))
4114 {
4115 self.error_at_peek(message.to_string());
4116 return None;
4117 }
4118 let tok = self.advance();
4119 Some(self.property_ident_from_token(&tok))
4120 }
4121
4122 fn readonly_modifier_span(&self, member_start: u32) -> Span {
4125 self.span(member_start, member_start + "readonly".len() as u32)
4126 }
4127
4128 fn reject_readonly_modifier(&mut self, span: Span) {
4131 self.error_at_with_help(
4132 span,
4133 "`readonly` can only modify a property or index signature",
4134 vec!["remove `readonly`".to_string()],
4135 );
4136 }
4137
4138 fn eat_readonly_property_modifier(&mut self) -> bool {
4139 if self.peek_identifier_text_is("readonly")
4140 && !self.line_break_after_peek()
4141 && (self.peek_starts_property_after_readonly()
4142 || matches!(self.peek_at(1).kind, TokenKind::LeftBracket))
4143 {
4144 self.advance();
4145 return true;
4146 }
4147 false
4148 }
4149
4150 fn peek_starts_property_after_readonly(&self) -> bool {
4151 let name = &self.peek_at(1).kind;
4152 let after_name = &self.peek_at(2).kind;
4153 let after_optional = &self.peek_at(3).kind;
4154 let starts_member_type =
4157 |kind: &TokenKind| matches!(kind, TokenKind::Colon | TokenKind::LeftParen);
4158 (is_property_name(name) || matches!(name, TokenKind::StringLiteral(_)))
4159 && (starts_member_type(after_name)
4160 || matches!(after_name, TokenKind::Question) && starts_member_type(after_optional))
4161 }
4162
4163 fn peek_identifier_text_is(&self, expected: &str) -> bool {
4164 self.token_is_word(self.peek(), expected)
4165 }
4166
4167 fn peek_at_is_word(&self, offset: usize, expected: &str) -> bool {
4168 self.token_is_word(self.peek_at(offset), expected)
4169 }
4170
4171 fn token_is_word(&self, tok: &Token, expected: &str) -> bool {
4172 matches!(tok.kind, TokenKind::Identifier)
4173 && &self.source[tok.span.start as usize..tok.span.end as usize] == expected
4174 }
4175
4176 fn at_delete_operator(&self) -> bool {
4183 if !self.peek_identifier_text_is("delete") {
4184 return false;
4185 }
4186 if !matches!(
4187 self.peek_at(1).kind,
4188 TokenKind::Identifier | TokenKind::This
4189 ) {
4190 return false;
4191 }
4192 let next_is_cast_keyword =
4193 self.peek_at_is_word(1, "as") || self.peek_at_is_word(1, "satisfies");
4194 !next_is_cast_keyword || cannot_start_type(&self.peek_at(2).kind)
4195 }
4196
4197 fn at_type_alias_head(&self) -> bool {
4201 self.peek_identifier_text_is("type")
4202 && matches!(self.peek_at(1).kind, TokenKind::Identifier)
4203 }
4204
4205 fn parse_expression(&mut self) -> Option<ExprId> {
4208 self.with_recursion_limit(Self::parse_expression_inner)
4209 }
4210
4211 fn parse_expression_inner(&mut self) -> Option<ExprId> {
4212 let written = self.parse_conditional()?;
4213 let shift_assignment = self.peek_shift().filter(|shift| shift.assignment);
4214 if !is_assign_lookahead(&self.peek().kind) && shift_assignment.is_none() {
4215 return Some(written);
4216 }
4217 let target_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4218 let target = self.assignment_target(written)?;
4219 let op_tok = self.advance();
4220 let mut op_span = op_tok.span;
4221 let op = if let Some(ShiftToken { op, tokens, .. }) = shift_assignment {
4222 for _ in 1..tokens {
4223 op_span.end = self.advance().span.end;
4224 }
4225 Some(op)
4226 } else {
4227 compound_op_for_token(&op_tok.kind)
4228 };
4229 let value = self.parse_expression()?;
4230 let value_span = parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
4231 parse_arena_result(
4232 self.ast.try_push_expr(Expr {
4233 kind: ExprKind::Assign {
4234 target,
4235 op,
4236 op_span,
4237 value,
4238 },
4239 span: self.span(target_span.start, value_span.end),
4240 }),
4241 &mut self.fatal,
4242 )
4243 }
4244
4245 fn assignment_target(&mut self, written: ExprId) -> Option<ExprId> {
4248 let mut target = written;
4249 while let ExprKind::Paren(inner) =
4250 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind
4251 {
4252 target = inner;
4253 }
4254 if matches!(
4255 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind,
4256 ExprKind::Identifier(_) | ExprKind::FieldAccess { .. } | ExprKind::IndexAccess { .. }
4257 ) {
4258 return Some(target);
4259 }
4260 let error_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4261 self.error_at_with_help(
4262 error_span,
4263 "invalid assignment target",
4264 vec![
4265 "assign to a variable, a field (`o.f = …`), or an element (`a[i] = …`)".to_string(),
4266 ],
4267 );
4268 None
4269 }
4270
4271 fn parse_conditional(&mut self) -> Option<ExprId> {
4272 if self.is_arrow_start() {
4273 return self.parse_arrow();
4274 }
4275 let cond = self.parse_binary(0)?;
4276 if !matches!(self.peek().kind, TokenKind::Question) {
4278 return Some(cond);
4279 }
4280 self.advance();
4281 let then_ = self.parse_expression()?;
4282 if !matches!(self.peek().kind, TokenKind::Colon) {
4283 self.error_at_peek_with_help(
4284 "expected `:` to complete ternary expression",
4285 vec!["const x = cond ? then : else;".to_string()],
4286 );
4287 return None;
4288 }
4289 self.advance();
4290 let else_ = self.parse_expression()?;
4291 let cond_span = parse_arena_result(self.ast.try_expr(cond), &mut self.fatal)?.span;
4292 let else_span = parse_arena_result(self.ast.try_expr(else_), &mut self.fatal)?.span;
4293 parse_arena_result(
4294 self.ast.try_push_expr(Expr {
4295 kind: ExprKind::Ternary { cond, then_, else_ },
4296 span: self.span(cond_span.start, else_span.end),
4297 }),
4298 &mut self.fatal,
4299 )
4300 }
4301
4302 fn is_arrow_start(&self) -> bool {
4303 match self.peek().kind {
4304 TokenKind::Identifier => matches!(self.peek_at(1).kind, TokenKind::Arrow),
4305 TokenKind::LeftParen => self.find_arrow_after_matching_paren(),
4306 _ => false,
4307 }
4308 }
4309
4310 fn index_after_matching_paren(&self, index: usize) -> Option<usize> {
4313 if !matches!(
4314 self.tokens.get(index).map(|t| &t.kind),
4315 Some(TokenKind::LeftParen)
4316 ) {
4317 return None;
4318 }
4319 self.scan_past_balanced(
4320 index,
4321 |k| matches!(k, TokenKind::LeftParen),
4322 |k| matches!(k, TokenKind::RightParen),
4323 )
4324 }
4325
4326 fn find_arrow_after_matching_paren(&self) -> bool {
4327 let Some(i) = self.index_after_matching_paren(self.pos) else {
4328 return false;
4329 };
4330 let after_type = if matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Colon)) {
4331 match self.scan_past_type_annotation(i + 1, TypePos::ArrowReturn) {
4332 Some(p) => p,
4333 None => return false,
4334 }
4335 } else {
4336 i
4337 };
4338 matches!(
4339 self.tokens.get(after_type).map(|t| &t.kind),
4340 Some(TokenKind::Arrow),
4341 )
4342 }
4343
4344 fn scan_past_type_annotation(&self, start: usize, type_pos: TypePos) -> Option<usize> {
4350 self.scan_past_type_annotation_at_depth(start, type_pos, 0)
4351 }
4352
4353 fn scan_past_type_annotation_at_depth(
4354 &self,
4355 start: usize,
4356 type_pos: TypePos,
4357 depth: usize,
4358 ) -> Option<usize> {
4359 if depth >= MAX_PARSE_DEPTH {
4360 return None;
4361 }
4362 if matches!(
4363 self.tokens.get(start).map(|t| &t.kind),
4364 Some(TokenKind::Identifier)
4365 ) && self
4366 .tokens
4367 .get(start + 1)
4368 .is_some_and(|t| self.token_is_word(t, "is"))
4369 {
4370 return self.scan_past_type_annotation_at_depth(start + 2, type_pos, depth + 1);
4371 }
4372 let start = if matches!(
4374 self.tokens.get(start).map(|t| &t.kind),
4375 Some(TokenKind::Pipe)
4376 ) {
4377 start + 1
4378 } else {
4379 start
4380 };
4381 let mut i = self.scan_past_single_type_member(start, type_pos, depth + 1)?;
4382 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Pipe)) {
4383 i = self.scan_past_single_type_member(i + 1, type_pos, depth + 1)?;
4384 }
4385 Some(i)
4386 }
4387
4388 fn scan_past_single_type_member(
4389 &self,
4390 start: usize,
4391 type_pos: TypePos,
4392 depth: usize,
4393 ) -> Option<usize> {
4394 if depth >= MAX_PARSE_DEPTH {
4395 return None;
4396 }
4397 if self.is_contextual_type_operator(start, "keyof")
4398 || self.is_contextual_type_operator(start, "readonly")
4399 {
4400 return self.scan_past_single_type_member(start + 1, type_pos, depth + 1);
4401 }
4402 let mut i = start;
4403 match self.tokens.get(i)?.kind {
4404 TokenKind::Identifier | TokenKind::Void => {
4405 i += 1;
4406 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Dot)) {
4407 if !matches!(
4408 self.tokens.get(i + 1).map(|t| &t.kind),
4409 Some(TokenKind::Identifier)
4410 ) {
4411 return None;
4412 }
4413 i += 2;
4414 }
4415 if matches!(
4416 self.tokens.get(i).map(|t| &t.kind),
4417 Some(TokenKind::LessThan)
4418 ) {
4419 i = self.scan_past_balanced(
4420 i,
4421 |k| matches!(k, TokenKind::LessThan),
4422 |k| matches!(k, TokenKind::GreaterThan),
4423 )?;
4424 }
4425 }
4426 TokenKind::NullLiteral
4427 | TokenKind::StringLiteral(_)
4428 | TokenKind::NumberLiteral(_)
4429 | TokenKind::BooleanLiteral(_) => {
4430 i += 1;
4431 }
4432 TokenKind::LeftBrace => {
4433 i = self.scan_past_balanced(
4434 i,
4435 |k| matches!(k, TokenKind::LeftBrace),
4436 |k| matches!(k, TokenKind::RightBrace),
4437 )?;
4438 }
4439 TokenKind::LeftBracket => {
4440 i = self.scan_past_balanced(
4441 i,
4442 |k| matches!(k, TokenKind::LeftBracket),
4443 |k| matches!(k, TokenKind::RightBracket),
4444 )?;
4445 }
4446 TokenKind::LeftParen => {
4451 let is_function_type = self.paren_at_opens_function_type(i, type_pos);
4452 i = self.scan_past_balanced(
4453 i,
4454 |k| matches!(k, TokenKind::LeftParen),
4455 |k| matches!(k, TokenKind::RightParen),
4456 )?;
4457 if is_function_type {
4458 if !matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)) {
4459 return None;
4460 }
4461 return self.scan_past_type_annotation_at_depth(
4462 i + 1,
4463 TypePos::Anywhere,
4464 depth + 1,
4465 );
4466 }
4467 }
4468 _ => return None,
4469 }
4470 while matches!(
4471 self.tokens.get(i).map(|t| &t.kind),
4472 Some(TokenKind::LeftBracket)
4473 ) {
4474 i += 1;
4475 if !matches!(
4476 self.tokens.get(i).map(|t| &t.kind),
4477 Some(TokenKind::RightBracket)
4478 ) {
4479 return None;
4480 }
4481 i += 1;
4482 }
4483 Some(i)
4484 }
4485
4486 fn scan_past_balanced(
4489 &self,
4490 start: usize,
4491 is_open: fn(&TokenKind) -> bool,
4492 is_close: fn(&TokenKind) -> bool,
4493 ) -> Option<usize> {
4494 let mut depth: i32 = 1;
4495 let mut i = start + 1;
4496 while i < self.tokens.len() && depth > 0 {
4497 let token = self.tokens.get(i)?;
4498 let kind = &token.kind;
4499 if is_open(kind) {
4500 depth += 1;
4501 } else if is_close(kind) {
4502 depth -= 1;
4503 } else if matches!(kind, TokenKind::Eof) {
4504 return None;
4505 }
4506 i += 1;
4507 }
4508 if depth == 0 { Some(i) } else { None }
4509 }
4510
4511 fn parse_arrow(&mut self) -> Option<ExprId> {
4512 let start_span = self.peek().span;
4513
4514 let params = match self.peek().kind {
4515 TokenKind::Identifier => {
4516 let name_tok = self.advance();
4517 self.reject_eval_or_arguments(name_tok.span);
4518 let name = self.ident_from_token(&name_tok);
4519 vec![ParamDecl {
4520 name,
4521 ty: None,
4522 default: None,
4523 optional: false,
4524 pattern: None,
4525 rest: false,
4526 modifiers: None,
4527 }]
4528 }
4529 TokenKind::LeftParen => {
4530 self.advance();
4531 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4532 Vec::new()
4533 } else {
4534 self.parse_arrow_param_list()?
4535 };
4536 if !matches!(self.peek().kind, TokenKind::RightParen) {
4537 self.error_at_peek("expected `)`");
4538 return None;
4539 }
4540 self.advance();
4541 params
4542 }
4543 _ => return self.invariant_failure("is_arrow_start guarded the entry"),
4544 };
4545
4546 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4547 self.advance();
4548 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4549 } else {
4550 (None, None)
4551 };
4552
4553 if !matches!(self.peek().kind, TokenKind::Arrow) {
4554 self.error_at_peek("expected `=>` in arrow function");
4555 return None;
4556 }
4557 self.advance();
4558
4559 let (body, end_pos) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
4560 let block = self.parse_block()?;
4561 let span = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?.span;
4562 (ArrowBody::Block(block), span.end)
4563 } else {
4564 let expr = self.parse_expression()?;
4565 let span = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?.span;
4566 (ArrowBody::Expr(expr), span.end)
4567 };
4568
4569 parse_arena_result(
4570 self.ast.try_push_expr(Expr {
4571 kind: ExprKind::Arrow {
4572 params,
4573 return_type,
4574 type_predicate,
4575 body,
4576 },
4577 span: self.span(start_span.start, end_pos),
4578 }),
4579 &mut self.fatal,
4580 )
4581 }
4582
4583 fn parse_function_expression(&mut self) -> Option<ExprId> {
4586 let kw = self.advance();
4587
4588 if matches!(self.peek().kind, TokenKind::LessThan) {
4589 self.error_at_peek("generic function expressions are not supported");
4590 return None;
4591 }
4592
4593 let self_name = if matches!(self.peek().kind, TokenKind::Identifier) {
4594 let name_tok = self.advance();
4595 self.reject_eval_or_arguments(name_tok.span);
4596 Some(self.ident_from_token(&name_tok))
4597 } else {
4598 None
4599 };
4600
4601 if !matches!(self.peek().kind, TokenKind::LeftParen) {
4602 self.error_at_peek("expected `(` after `function`");
4603 return None;
4604 }
4605 self.advance();
4606
4607 let this_type = if matches!(self.peek().kind, TokenKind::This) {
4608 self.advance();
4609 if !matches!(self.peek().kind, TokenKind::Colon) {
4610 self.error_at_peek("expected a type annotation after `this`");
4611 return None;
4612 }
4613 self.advance();
4614 let ty = self.parse_type_annotation()?;
4615 if matches!(self.peek().kind, TokenKind::Comma) {
4616 self.advance();
4617 } else if !matches!(self.peek().kind, TokenKind::RightParen) {
4618 self.error_at_peek("expected `,` or `)` after the `this` parameter");
4619 return None;
4620 }
4621 Some(ty)
4622 } else {
4623 None
4624 };
4625 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4626 Vec::new()
4627 } else {
4628 let saved_class = self.class_member_body_depth;
4629 let saved_function = self.function_expression_body_depth;
4630 self.class_member_body_depth = 0;
4631 self.function_expression_body_depth = 1;
4632 let params = self.parse_arrow_param_list();
4633 self.class_member_body_depth = saved_class;
4634 self.function_expression_body_depth = saved_function;
4635 params?
4636 };
4637 if !matches!(self.peek().kind, TokenKind::RightParen) {
4638 self.error_at_peek("expected `)`");
4639 return None;
4640 }
4641 self.advance();
4642
4643 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4644 self.advance();
4645 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4646 } else {
4647 (None, None)
4648 };
4649
4650 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
4651 self.error_at_peek("expected `{` to open the function body");
4652 return None;
4653 }
4654 let saved = self.class_member_body_depth;
4655 self.class_member_body_depth = 0;
4656 self.function_expression_body_depth += 1;
4657 let block = self.parse_block();
4658 self.function_expression_body_depth -= 1;
4659 self.class_member_body_depth = saved;
4660 let block = block?;
4661 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
4662 .span
4663 .end;
4664
4665 let function = parse_arena_result(
4666 self.ast.try_push_expr(Expr {
4667 kind: ExprKind::Arrow {
4668 params,
4669 return_type,
4670 type_predicate,
4671 body: ArrowBody::Block(block),
4672 },
4673 span: self.span(kw.span.start, end),
4674 }),
4675 &mut self.fatal,
4676 )?;
4677 parse_arena_result(
4678 self.ast.try_push_expr(Expr {
4679 kind: ExprKind::FunctionExpression {
4680 name: self_name,
4681 this_type,
4682 function,
4683 },
4684 span: self.span(kw.span.start, end),
4685 }),
4686 &mut self.fatal,
4687 )
4688 }
4689
4690 fn parse_outer_this_boundary<T>(
4693 &mut self,
4694 parse: impl FnOnce(&mut Self) -> Option<T>,
4695 ) -> Option<T> {
4696 let saved = self.class_member_body_depth;
4697 self.class_member_body_depth = 0;
4698 let saved_function = self.function_expression_body_depth;
4699 self.function_expression_body_depth = 0;
4700 let parsed = parse(self);
4701 self.class_member_body_depth = saved;
4702 self.function_expression_body_depth = saved_function;
4703 parsed
4704 }
4705
4706 fn parse_arrow_param_list(&mut self) -> Option<Vec<ParamDecl>> {
4707 let mut params = Vec::new();
4708 loop {
4709 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
4710 self.advance();
4711 true
4712 } else {
4713 false
4714 };
4715 let (name, pattern) = match self.peek().kind {
4718 TokenKind::LeftBrace | TokenKind::LeftBracket => {
4719 if rest {
4720 self.error_at_peek("rest parameter cannot be destructured");
4721 return None;
4722 }
4723 let binding = self.parse_binding()?;
4724 let placeholder = Ident {
4725 name: String::new(),
4726 span: binding.span(),
4727 };
4728 (placeholder, Some(binding))
4729 }
4730 _ => {
4731 let name_tok = self.expect_binding_identifier(if rest {
4732 "expected identifier after `...`"
4733 } else {
4734 "expected parameter name"
4735 })?;
4736 let name = self.ident_from_token(&name_tok);
4737 (name, None)
4738 }
4739 };
4740 let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
4741 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
4742 self.advance();
4743 Some(self.parse_type_annotation()?)
4744 } else {
4745 None
4748 };
4749 let default = self.parse_parameter_default(rest, optional)?;
4750 params.push(ParamDecl {
4751 name,
4752 ty,
4753 default,
4754 optional,
4755 pattern,
4756 rest,
4757 modifiers: None,
4758 });
4759 match self.peek().kind {
4760 TokenKind::Comma => {
4761 if rest {
4762 let comma = self.advance();
4763 self.error_at(comma.span, "rest parameter must be the last parameter");
4764 return None;
4765 }
4766 self.advance();
4767 if matches!(self.peek().kind, TokenKind::RightParen) {
4768 break;
4769 }
4770 }
4771 TokenKind::RightParen => break,
4772 _ => {
4773 self.error_at_peek("expected `,` or `)`");
4774 return None;
4775 }
4776 }
4777 }
4778 self.check_parameter_order(params.iter().map(ParamOrder::of_param));
4779 Some(params)
4780 }
4781
4782 fn parse_binary(&mut self, min_prec: u8) -> Option<ExprId> {
4783 self.with_recursion_limit(|parser| parser.parse_binary_inner(min_prec))
4784 }
4785
4786 fn peek_shift(&self) -> Option<ShiftToken> {
4789 let first = self.peek();
4790 let second = self.peek_at(1);
4791 if first.span.end != second.span.start {
4792 return None;
4793 }
4794 match (&first.kind, &second.kind) {
4795 (TokenKind::LessThan, TokenKind::LessThan) => {
4796 Some(ShiftToken::new(BinOp::Shl, 2, false))
4797 }
4798 (TokenKind::LessThan, TokenKind::LessEquals) => {
4799 Some(ShiftToken::new(BinOp::Shl, 2, true))
4800 }
4801 (TokenKind::GreaterThan, TokenKind::GreaterEquals) => {
4802 Some(ShiftToken::new(BinOp::Shr, 2, true))
4803 }
4804 (TokenKind::GreaterThan, TokenKind::GreaterThan) => {
4805 let third = self.peek_at(2);
4806 if second.span.end == third.span.start {
4807 match third.kind {
4808 TokenKind::GreaterThan => {
4809 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, false));
4810 }
4811 TokenKind::GreaterEquals => {
4812 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, true));
4813 }
4814 _ => {}
4815 }
4816 }
4817 Some(ShiftToken::new(BinOp::Shr, 2, false))
4818 }
4819 _ => None,
4820 }
4821 }
4822
4823 fn peek_binary(&self) -> Option<BinaryToken> {
4824 if let Some(shift) = self.peek_shift() {
4825 return (!shift.assignment).then_some(BinaryToken {
4826 op: shift.op,
4827 precedence: SHIFT_PRECEDENCE,
4828 tokens: shift.tokens,
4829 });
4830 }
4831 peek_binop(&self.peek().kind).map(|(op, precedence)| BinaryToken {
4832 op,
4833 precedence,
4834 tokens: 1,
4835 })
4836 }
4837
4838 fn parse_binary_inner(&mut self, min_prec: u8) -> Option<ExprId> {
4839 let mut lhs = self.parse_cast()?;
4840 let mut last_op: Option<BinOp> = None;
4844 let mut reported_mixing = false;
4845 while let Some(BinaryToken {
4846 op,
4847 precedence: prec,
4848 tokens: count,
4849 }) = self.peek_binary()
4850 {
4851 if prec < min_prec {
4852 break;
4853 }
4854 if !reported_mixing
4855 && let Some(last) = last_op
4856 && mixed_logical(last, op)
4857 {
4858 reported_mixing = true;
4859 let op_span = self.peek().span;
4860 self.error_at_with_help(
4864 op_span,
4865 "mixing `??` with `||` / `&&` requires parentheses",
4866 vec![mixed_logical_help(last, op)],
4867 );
4868 }
4869 if op == BinOp::Pow
4870 && matches!(
4871 parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.kind,
4872 ExprKind::Unary { .. }
4873 | ExprKind::Typeof { .. }
4874 | ExprKind::Delete { .. }
4875 | ExprKind::Void { .. }
4876 )
4877 {
4878 let error_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
4879 self.error_at_with_help(
4880 error_span,
4881 "an unparenthesized unary expression cannot be the left operand of `**`",
4882 vec!["choose the grouping explicitly: `(-x) ** 2` or `-(x ** 2)`".into()],
4883 );
4884 return None;
4885 }
4886 let loose = matches!(self.peek().kind, TokenKind::EqEq | TokenKind::BangEq);
4887 for _ in 0..count {
4888 self.advance();
4889 }
4890 let rhs = self.parse_binary(right_operand_min_prec(op, prec))?;
4891 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
4892 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
4893 if loose {
4894 self.reject_loose_nullish_comparison(op, lhs, rhs)?;
4895 }
4896 lhs = parse_arena_result(
4897 self.ast.try_push_expr(Expr {
4898 kind: ExprKind::Binary { op, lhs, rhs },
4899 span: self.span(lhs_span.start, rhs_span.end),
4900 }),
4901 &mut self.fatal,
4902 )?;
4903 last_op = Some(op);
4904 }
4905 Some(lhs)
4906 }
4907
4908 fn parse_cast(&mut self) -> Option<ExprId> {
4910 let mut expr = self.parse_unary()?;
4911 while matches!(self.peek().kind, TokenKind::Instanceof)
4912 || self.peek_identifier_text_is("as")
4913 {
4914 let is_instanceof = matches!(self.peek().kind, TokenKind::Instanceof);
4915 self.advance();
4916 let ty = self.parse_type_annotation()?;
4917 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
4918 .span
4919 .start;
4920 let end = ty.span.end;
4921 let kind = if is_instanceof {
4922 ExprKind::InstanceOf { value: expr, ty }
4923 } else {
4924 self.last_as_type_end = Some(end);
4925 ExprKind::As { expr, ty }
4926 };
4927 expr = parse_arena_result(
4928 self.ast.try_push_expr(Expr {
4929 kind,
4930 span: self.span(start, end),
4931 }),
4932 &mut self.fatal,
4933 )?;
4934 }
4935 Some(expr)
4936 }
4937
4938 fn parse_unary(&mut self) -> Option<ExprId> {
4939 self.with_recursion_limit(Self::parse_unary_inner)
4940 }
4941
4942 fn parse_unary_inner(&mut self) -> Option<ExprId> {
4943 if matches!(self.peek().kind, TokenKind::Typeof) {
4946 return self.parse_prefix_expr(|operand| ExprKind::Typeof { operand });
4947 }
4948 if matches!(self.peek().kind, TokenKind::Void) {
4949 return self.parse_prefix_expr(|operand| ExprKind::Void { operand });
4950 }
4951 if self.at_delete_operator() {
4952 return self.parse_prefix_expr(|operand| ExprKind::Delete { operand });
4953 }
4954 if matches!(self.peek().kind, TokenKind::New) {
4955 let op_tok = self.advance();
4956 let mut callee = self.parse_atom()?;
4957 while matches!(self.peek().kind, TokenKind::Dot) {
4958 self.advance();
4959 if !matches!(self.peek().kind, TokenKind::Identifier) {
4960 self.error_at_peek("expected identifier after `.` in constructor name");
4961 return None;
4962 }
4963 let ident_tok = self.advance();
4964 let name = self.ident_from_token(&ident_tok);
4965 let receiver_span =
4966 parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
4967 let combined_span = self.span(receiver_span.start, ident_tok.span.end);
4968 callee = parse_arena_result(
4969 self.ast.try_push_expr(Expr {
4970 kind: ExprKind::FieldAccess {
4971 receiver: callee,
4972 name,
4973 },
4974 span: combined_span,
4975 }),
4976 &mut self.fatal,
4977 )?;
4978 }
4979 let type_args = if matches!(self.peek().kind, TokenKind::LessThan) {
4980 let saved_pos = self.pos;
4981 let saved_diag_len = self.diagnostics.len();
4982 if let Some(args) = self.try_parse_type_args_only() {
4983 Some(args)
4984 } else {
4985 self.pos = saved_pos;
4986 self.diagnostics.truncate(saved_diag_len);
4987 None
4988 }
4989 } else {
4990 None
4991 };
4992 if !matches!(self.peek().kind, TokenKind::LeftParen) {
4993 self.error_at_peek("expected `(` after constructor name in `new` expression");
4994 return None;
4995 }
4996 self.advance();
4997 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
4998 Vec::new()
4999 } else {
5000 self.parse_call_args()?
5001 };
5002 if !matches!(self.peek().kind, TokenKind::RightParen) {
5003 self.error_at_peek("expected `)`");
5004 return None;
5005 }
5006 let close = self.advance();
5007 let new_expr = parse_arena_result(
5008 self.ast.try_push_expr(Expr {
5009 kind: ExprKind::New {
5010 callee,
5011 type_args,
5012 args,
5013 },
5014 span: self.span(op_tok.span.start, close.span.end),
5015 }),
5016 &mut self.fatal,
5017 )?;
5018 return self.parse_postfix_from(new_expr);
5021 }
5022 let op = match self.peek().kind {
5023 TokenKind::Bang => UnOp::Not,
5024 TokenKind::Tilde => UnOp::BitNot,
5025 TokenKind::Minus => UnOp::Neg,
5026 TokenKind::Plus => UnOp::Pos,
5027 _ => return self.parse_postfix(),
5028 };
5029 self.parse_prefix_expr(|operand| ExprKind::Unary { op, operand })
5030 }
5031
5032 fn parse_prefix_expr(&mut self, kind: impl FnOnce(ExprId) -> ExprKind) -> Option<ExprId> {
5034 let op_tok = self.advance();
5035 if matches!(op_tok.kind, TokenKind::Void) && self.at_inserted_semicolon() {
5038 self.advance();
5039 }
5040 let operand = self.parse_unary()?;
5041 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5042 parse_arena_result(
5043 self.ast.try_push_expr(Expr {
5044 kind: kind(operand),
5045 span: self.span(op_tok.span.start, operand_span.end),
5046 }),
5047 &mut self.fatal,
5048 )
5049 }
5050
5051 fn parse_postfix(&mut self) -> Option<ExprId> {
5052 let expr = self.parse_atom()?;
5053 self.parse_postfix_from(expr)
5054 }
5055
5056 fn parse_postfix_from(&mut self, mut expr: ExprId) -> Option<ExprId> {
5057 let mut chain_parts: Option<(ExprId, Vec<crate::ChainPart>)> = None;
5061 loop {
5062 match self.peek().kind {
5063 TokenKind::LeftParen => {
5064 if let Some((_, parts)) = &mut chain_parts {
5065 let part = self.parse_chain_call_tail(false)?;
5066 parts.push(part);
5067 } else {
5068 expr = self.parse_call_tail(expr, None)?;
5069 }
5070 }
5071 TokenKind::Dot => {
5072 if let Some((_, parts)) = &mut chain_parts {
5073 let part = self.parse_chain_field_tail(false)?;
5074 parts.push(part);
5075 } else {
5076 expr = self.parse_field_access_tail(expr)?;
5077 }
5078 }
5079 TokenKind::LeftBracket => {
5080 if let Some((_, parts)) = &mut chain_parts {
5081 let part = self.parse_chain_index_tail(false)?;
5082 parts.push(part);
5083 } else {
5084 expr = self.parse_index_access_tail(expr)?;
5085 }
5086 }
5087 TokenKind::QuestionDot => {
5088 if chain_parts.is_none() {
5089 chain_parts = Some((expr, Vec::new()));
5090 }
5091 self.advance();
5092 let part = match self.peek().kind {
5093 TokenKind::LeftParen => self.parse_chain_call_tail(true)?,
5094 TokenKind::LeftBracket => self.parse_chain_index_tail(true)?,
5095 _ if is_property_name(&self.peek().kind) => {
5096 self.parse_chain_field_tail(true)?
5097 }
5098 _ => {
5099 self.error_at_peek(
5100 "expected field name, `(` for call, or `[` for index after `?.`",
5101 );
5102 return None;
5103 }
5104 };
5105 if let Some((_, parts)) = &mut chain_parts {
5106 parts.push(part);
5107 }
5108 }
5109 TokenKind::LessThan => {
5113 if chain_parts.is_some() {
5114 break;
5116 }
5117 let saved_pos = self.pos;
5118 let saved_diag_len = self.diagnostics.len();
5119 if let Some(call_id) = self.try_parse_generic_call_tail(expr) {
5120 expr = call_id;
5121 } else {
5122 self.pos = saved_pos;
5123 self.diagnostics.truncate(saved_diag_len);
5124 break;
5125 }
5126 }
5127 TokenKind::Bang => {
5128 if let Some((_, parts)) = &mut chain_parts {
5129 let tok = self.advance();
5130 parts.push(crate::ChainPart::NonNull { span: tok.span });
5131 continue;
5132 }
5133 let tok = self.advance();
5134 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5135 .span
5136 .start;
5137 expr = parse_arena_result(
5138 self.ast.try_push_expr(Expr {
5139 kind: ExprKind::PostfixUnary {
5140 op: PostfixOp::NonNullAssert,
5141 operand: expr,
5142 },
5143 span: self.span(start, tok.span.end),
5144 }),
5145 &mut self.fatal,
5146 )?;
5147 }
5148 TokenKind::PlusPlus | TokenKind::MinusMinus => {
5150 if chain_parts.is_some() {
5151 break;
5152 }
5153 let tok = self.advance();
5154 let op = if matches!(tok.kind, TokenKind::PlusPlus) {
5155 PostfixOp::Inc
5156 } else {
5157 PostfixOp::Dec
5158 };
5159 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5160 .span
5161 .start;
5162 expr = parse_arena_result(
5163 self.ast.try_push_expr(Expr {
5164 kind: ExprKind::PostfixUnary { op, operand: expr },
5165 span: self.span(start, tok.span.end),
5166 }),
5167 &mut self.fatal,
5168 )?;
5169 break;
5170 }
5171 _ => break,
5172 }
5173 }
5174 if let Some((base, parts)) = chain_parts {
5175 let base_span = parse_arena_result(self.ast.try_expr(base), &mut self.fatal)?.span;
5176 let end = parts.last().map_or(base_span.end, |p| p.span().end);
5177 Some(parse_arena_result(
5178 self.ast.try_push_expr(Expr {
5179 kind: ExprKind::OptionalChain { base, parts },
5180 span: self.span(base_span.start, end),
5181 }),
5182 &mut self.fatal,
5183 )?)
5184 } else {
5185 Some(expr)
5186 }
5187 }
5188
5189 fn parse_chain_field_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5191 let start_pos = self.peek().span.start;
5192 if !optional {
5193 self.advance();
5194 }
5195 let name_tok = if is_property_name(&self.peek().kind) {
5196 self.advance()
5197 } else {
5198 let what = if optional { "?." } else { "." };
5199 self.error_at_peek(format!("expected field name after `{what}`"));
5200 return None;
5201 };
5202 let name = self.ident_from_token(&name_tok);
5203 let end = name.span.end;
5204 Some(crate::ChainPart::Field {
5205 name,
5206 optional,
5207 span: self.span(start_pos, end),
5208 })
5209 }
5210
5211 fn parse_chain_index_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5212 let open = self.advance();
5213 let idx = self.parse_expression()?;
5214 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5215 self.error_at_peek("expected `]`");
5216 return None;
5217 }
5218 let close = self.advance();
5219 Some(crate::ChainPart::Index {
5220 idx,
5221 optional,
5222 span: self.span(open.span.start, close.span.end),
5223 })
5224 }
5225
5226 fn parse_chain_call_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5227 let open = self.advance();
5228 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5229 Vec::new()
5230 } else {
5231 self.parse_call_args()?
5232 };
5233 if !matches!(self.peek().kind, TokenKind::RightParen) {
5234 self.error_at_peek("expected `)`");
5235 return None;
5236 }
5237 let close = self.advance();
5238 Some(crate::ChainPart::Call {
5239 args,
5240 type_args: None,
5241 optional,
5242 span: self.span(open.span.start, close.span.end),
5243 })
5244 }
5245
5246 fn parse_field_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5247 self.advance();
5248 let name_tok = if is_property_name(&self.peek().kind) {
5250 self.advance()
5251 } else {
5252 self.error_at_peek("expected field name after `.`");
5253 return None;
5254 };
5255 let name = self.ident_from_token(&name_tok);
5256 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5257 parse_arena_result(
5258 self.ast.try_push_expr(Expr {
5259 kind: ExprKind::FieldAccess {
5260 receiver,
5261 name: name.clone(),
5262 },
5263 span: self.span(receiver_span.start, name.span.end),
5264 }),
5265 &mut self.fatal,
5266 )
5267 }
5268
5269 fn parse_index_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5270 self.advance();
5271 let index = self.parse_expression()?;
5272 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5273 self.error_at_peek("expected `]`");
5274 return None;
5275 }
5276 let close = self.advance();
5277 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5278 parse_arena_result(
5279 self.ast.try_push_expr(Expr {
5280 kind: ExprKind::IndexAccess { receiver, index },
5281 span: self.span(receiver_span.start, close.span.end),
5282 }),
5283 &mut self.fatal,
5284 )
5285 }
5286
5287 fn parse_call_tail(
5288 &mut self,
5289 callee: ExprId,
5290 type_args: Option<Vec<TypeAnnotation>>,
5291 ) -> Option<ExprId> {
5292 self.advance();
5293 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5294 Vec::new()
5295 } else {
5296 self.parse_call_args()?
5297 };
5298 if !matches!(self.peek().kind, TokenKind::RightParen) {
5299 self.error_at_peek("expected `)`");
5300 return None;
5301 }
5302 let close = self.advance();
5303
5304 let callee_span = parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5305 parse_arena_result(
5306 self.ast.try_push_expr(Expr {
5307 kind: ExprKind::Call {
5308 callee,
5309 type_args,
5310 args,
5311 },
5312 span: self.span(callee_span.start, close.span.end),
5313 }),
5314 &mut self.fatal,
5315 )
5316 }
5317
5318 fn try_parse_generic_call_tail(&mut self, callee: ExprId) -> Option<ExprId> {
5320 let type_args = self.try_parse_type_args_only()?;
5321 if !matches!(self.peek().kind, TokenKind::LeftParen) {
5322 return None;
5323 }
5324 self.parse_call_tail(callee, Some(type_args))
5325 }
5326
5327 fn try_parse_type_args_only(&mut self) -> Option<Vec<TypeAnnotation>> {
5328 if !matches!(self.peek().kind, TokenKind::LessThan) {
5329 return self.invariant_failure("type argument parser expected `<`");
5330 }
5331 self.advance();
5332
5333 if matches!(self.peek().kind, TokenKind::GreaterThan) {
5334 return None;
5335 }
5336
5337 let mut type_args = Vec::new();
5338 loop {
5339 let ta = self.parse_type_annotation()?;
5340 type_args.push(ta);
5341 match self.peek().kind {
5342 TokenKind::Comma => {
5343 self.advance();
5344 if matches!(self.peek().kind, TokenKind::GreaterThan) {
5345 self.advance();
5346 break;
5347 }
5348 }
5349 TokenKind::GreaterThan => {
5350 self.advance();
5351 break;
5352 }
5353 _ => return None,
5354 }
5355 }
5356 Some(type_args)
5357 }
5358
5359 fn parse_call_args(&mut self) -> Option<Vec<ExprId>> {
5360 let mut args = Vec::new();
5361 loop {
5362 let arg = self.parse_expression()?;
5363 args.push(arg);
5364 match self.peek().kind {
5365 TokenKind::Comma => {
5366 self.advance();
5367 if matches!(self.peek().kind, TokenKind::RightParen) {
5368 return Some(args);
5369 }
5370 }
5371 TokenKind::RightParen => return Some(args),
5372 _ => {
5373 self.error_at_peek("expected `,` or `)`");
5374 return None;
5375 }
5376 }
5377 }
5378 }
5379
5380 fn parse_angle_cast(&mut self) -> Option<ExprId> {
5387 let open = self.advance();
5388 let ty = self.parse_type_annotation()?;
5389 if !matches!(self.peek().kind, TokenKind::GreaterThan) {
5390 self.error_at_peek("expected `>` to close a type assertion");
5391 return None;
5392 }
5393 self.advance();
5394 let expr = self.parse_unary()?;
5395 let end = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5396 .span
5397 .end;
5398 parse_arena_result(
5399 self.ast.try_push_expr(Expr {
5400 kind: ExprKind::As { expr, ty },
5401 span: self.span(open.span.start, end),
5402 }),
5403 &mut self.fatal,
5404 )
5405 }
5406
5407 fn parse_atom(&mut self) -> Option<ExprId> {
5408 match self.peek().kind {
5409 TokenKind::LessThan => return self.parse_angle_cast(),
5414 TokenKind::LeftParen => return self.parse_paren(),
5415 TokenKind::LeftBrace => return self.parse_object_literal(),
5418 TokenKind::LeftBracket => return self.parse_array_literal(),
5419 TokenKind::TemplateHead(_) => return self.parse_template_literal(),
5420 TokenKind::This => return self.parse_this_or_super(true),
5421 TokenKind::Super => return self.parse_this_or_super(false),
5422 TokenKind::Function => return self.parse_function_expression(),
5423 _ => {}
5424 }
5425 if !matches!(
5426 self.peek().kind,
5427 TokenKind::NumberLiteral(_)
5428 | TokenKind::BigIntLiteral(_)
5429 | TokenKind::StringLiteral(_)
5430 | TokenKind::TemplateNoSubstitution(_)
5431 | TokenKind::BooleanLiteral(_)
5432 | TokenKind::NullLiteral
5433 | TokenKind::Identifier
5434 | TokenKind::RegexLiteral { .. }
5435 ) {
5436 self.error_at_peek("expected expression");
5437 return None;
5438 }
5439 let tok = self.advance();
5440 let span = tok.span;
5441 let kind = match tok.kind {
5442 TokenKind::NumberLiteral(v) => ExprKind::Number(v),
5443 TokenKind::BigIntLiteral(s) => ExprKind::BigInt(s),
5444 TokenKind::StringLiteral(s) => ExprKind::String(s),
5445 TokenKind::TemplateNoSubstitution(s) => ExprKind::String(s),
5446 TokenKind::BooleanLiteral(b) => ExprKind::Boolean(b),
5447 TokenKind::NullLiteral => ExprKind::Null,
5448 TokenKind::Identifier => ExprKind::Identifier(Ident {
5449 name: self.source[span.start as usize..span.end as usize].to_string(),
5450 span,
5451 }),
5452 TokenKind::RegexLiteral { source, flags } => ExprKind::Regex { source, flags },
5453 _ => unreachable!("advance preserves the accepted expression token"),
5455 };
5456 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5457 }
5458
5459 fn parse_this_or_super(&mut self, is_this: bool) -> Option<ExprId> {
5462 let tok = self.advance();
5463 let span = tok.span;
5464 if !is_this && self.class_member_body_depth == 0 {
5465 self.error_at_with_help(
5466 span,
5467 "`super` is only valid inside a class method or constructor body",
5468 vec![
5469 "call `super(...)` in a subclass constructor or `super.method(...)` \
5470 in a subclass method"
5471 .to_string(),
5472 ],
5473 );
5474 }
5475 let kind = if is_this
5476 && self.class_member_body_depth == 0
5477 && self.function_expression_body_depth == 0
5478 {
5479 ExprKind::ThisOutsideReceiver
5480 } else if is_this {
5481 ExprKind::This
5482 } else {
5483 ExprKind::Super
5484 };
5485 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5486 }
5487
5488 fn parse_template_literal(&mut self) -> Option<ExprId> {
5491 let head_tok = self.advance();
5492 let TokenKind::TemplateHead(head) = head_tok.kind else {
5493 unreachable!("template dispatch requires TemplateHead");
5494 };
5495 let start = head_tok.span.start;
5496 let mut parts: Vec<String> = vec![head];
5497 let mut exprs: Vec<ExprId> = Vec::new();
5498 let mut substitution_spans: Vec<Span> = Vec::new();
5499 let mut substitution_start = head_tok.span.end.saturating_sub(2);
5502 loop {
5503 let expr = self.parse_expression()?;
5504 exprs.push(expr);
5505 let closing = self.peek().span.start;
5506 let substitution_span = self.span(substitution_start, closing.saturating_add(1));
5507 match self.peek().kind.clone() {
5508 TokenKind::TemplateMiddle(s) => {
5509 let tok = self.advance();
5510 substitution_spans.push(substitution_span);
5511 substitution_start = tok.span.end.saturating_sub(2);
5512 parts.push(s);
5513 }
5514 TokenKind::TemplateTail(s) => {
5515 let tok = self.advance();
5516 substitution_spans.push(substitution_span);
5517 parts.push(s);
5518 return parse_arena_result(
5519 self.ast.try_push_expr(Expr {
5520 kind: ExprKind::TemplateLiteral {
5521 parts,
5522 exprs,
5523 substitution_spans,
5524 },
5525 span: self.span(start, tok.span.end),
5526 }),
5527 &mut self.fatal,
5528 );
5529 }
5530 _ => {
5531 self.error_at_peek("expected `}` to close template interpolation");
5532 return None;
5533 }
5534 }
5535 }
5536 }
5537
5538 fn parse_object_literal(&mut self) -> Option<ExprId> {
5539 let open = self.advance();
5540
5541 let mut members: Vec<ObjectLiteralMember> = Vec::new();
5542 if !matches!(self.peek().kind, TokenKind::RightBrace) {
5543 loop {
5544 if matches!(self.peek().kind, TokenKind::DotDotDot) {
5545 let dots = self.advance();
5546 let value = self.parse_expression()?;
5547 let value_span =
5548 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5549 members.push(ObjectLiteralMember::Spread {
5550 value,
5551 span: self.span(dots.span.start, value_span.end),
5552 });
5553 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
5554 self.advance();
5555 let key = self.parse_expression()?;
5556 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5557 self.error_at_peek("expected `]` after computed property key");
5558 return None;
5559 }
5560 self.advance();
5561 if !matches!(self.peek().kind, TokenKind::Colon) {
5562 self.error_at_peek("expected `:` after computed property key; computed methods are not supported");
5563 return None;
5564 }
5565 self.advance();
5566 let value = self.parse_expression()?;
5567 members.push(ObjectLiteralMember::Computed { key, value });
5568 } else {
5569 let field = self.parse_object_literal_field()?;
5570 if let Some(existing) = members.iter().find_map(|m| match m {
5573 ObjectLiteralMember::Field(f) if f.name.name == field.name.name => Some(f),
5574 _ => None,
5575 }) {
5576 self.diagnostics.push(Diagnostic {
5577 severity: Severity::Error,
5578 span: field.name.span,
5579 message: format!(
5580 "duplicate field `{}` in object literal",
5581 field.name.name
5582 ),
5583 help: vec![],
5584 notes: vec![(existing.name.span, "first defined here".into())],
5585 });
5586 }
5588 members.push(ObjectLiteralMember::Field(field));
5589 }
5590 if self.at_inserted_semicolon()
5591 && matches!(self.peek_at(1).kind, TokenKind::RightBrace)
5592 {
5593 self.advance();
5594 }
5595 match self.peek().kind {
5596 TokenKind::Comma => {
5597 self.advance();
5598 if matches!(self.peek().kind, TokenKind::RightBrace) {
5599 break;
5600 }
5601 }
5602 TokenKind::RightBrace => break,
5603 _ => {
5604 self.error_at_peek("expected `,` or `}`");
5605 return None;
5606 }
5607 }
5608 }
5609 }
5610
5611 if !matches!(self.peek().kind, TokenKind::RightBrace) {
5612 self.error_at_peek("expected `}`");
5613 return None;
5614 }
5615 let close = self.advance();
5616
5617 parse_arena_result(
5618 self.ast.try_push_expr(Expr {
5619 kind: ExprKind::ObjectLiteral { members },
5620 span: self.span(open.span.start, close.span.end),
5621 }),
5622 &mut self.fatal,
5623 )
5624 }
5625
5626 fn parse_object_literal_field(&mut self) -> Option<ObjectLiteralField> {
5627 let (name, shorthandable) = match self.peek().kind.clone() {
5628 TokenKind::Identifier => {
5629 let tok = self.advance();
5630 (self.ident_from_token(&tok), true)
5631 }
5632 kind if is_property_name(&kind) => {
5633 let tok = self.advance();
5634 (self.ident_from_token(&tok), false)
5635 }
5636 TokenKind::StringLiteral(_) => {
5637 let tok = self.advance();
5638 (self.property_ident_from_token(&tok), false)
5639 }
5640 _ => {
5641 self.error_at_peek("expected field name");
5642 return None;
5643 }
5644 };
5645
5646 if matches!(self.peek().kind, TokenKind::LeftParen) {
5652 let value = self.parse_method_shorthand_body(name.span)?;
5653 return Some(ObjectLiteralField { name, value });
5654 }
5655
5656 if !matches!(self.peek().kind, TokenKind::Colon) {
5657 if shorthandable {
5660 let value = parse_arena_result(
5661 self.ast.try_push_expr(Expr {
5662 kind: ExprKind::Identifier(name.clone()),
5663 span: name.span,
5664 }),
5665 &mut self.fatal,
5666 )?;
5667 return Some(ObjectLiteralField { name, value });
5668 }
5669 self.error_at_peek("expected `:` after field name");
5670 return None;
5671 }
5672 self.advance();
5673
5674 let value = self.parse_expression()?;
5675 Some(ObjectLiteralField { name, value })
5676 }
5677
5678 fn parse_method_shorthand_body(&mut self, name_span: Span) -> Option<ExprId> {
5681 self.advance();
5682
5683 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
5684 Vec::new()
5685 } else {
5686 self.parse_arrow_param_list()?
5687 };
5688 if !matches!(self.peek().kind, TokenKind::RightParen) {
5689 self.error_at_peek("expected `)`");
5690 return None;
5691 }
5692 self.advance();
5693
5694 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
5695 self.advance();
5696 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
5697 } else {
5698 (None, None)
5699 };
5700
5701 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
5702 self.error_at_peek("expected `{` to open the method body");
5703 return None;
5704 }
5705 let block = self.parse_outer_this_boundary(Self::parse_block)?;
5706 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
5707 .span
5708 .end;
5709
5710 parse_arena_result(
5711 self.ast.try_push_expr(Expr {
5712 kind: ExprKind::Arrow {
5713 params,
5714 return_type,
5715 type_predicate,
5716 body: ArrowBody::Block(block),
5717 },
5718 span: self.span(name_span.start, end),
5719 }),
5720 &mut self.fatal,
5721 )
5722 }
5723
5724 fn parse_array_literal(&mut self) -> Option<ExprId> {
5725 let open = self.advance();
5726
5727 let mut elements: Vec<ArrayLiteralElement> = Vec::new();
5728 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5729 loop {
5730 let element = if matches!(self.peek().kind, TokenKind::DotDotDot) {
5731 let dots = self.advance();
5732 let value = self.parse_expression()?;
5733 let value_span =
5734 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5735 ArrayLiteralElement::Spread {
5736 value,
5737 span: self.span(dots.span.start, value_span.end),
5738 }
5739 } else {
5740 ArrayLiteralElement::Value(self.parse_expression()?)
5741 };
5742 elements.push(element);
5743 match self.peek().kind {
5744 TokenKind::Comma => {
5745 self.advance();
5746 if matches!(self.peek().kind, TokenKind::RightBracket) {
5747 break;
5748 }
5749 }
5750 TokenKind::RightBracket => break,
5751 _ => {
5752 self.error_at_peek("expected `,` or `]`");
5753 return None;
5754 }
5755 }
5756 }
5757 }
5758
5759 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5760 self.error_at_peek("expected `]`");
5761 return None;
5762 }
5763 let close = self.advance();
5764
5765 parse_arena_result(
5766 self.ast.try_push_expr(Expr {
5767 kind: ExprKind::ArrayLiteral { elements },
5768 span: self.span(open.span.start, close.span.end),
5769 }),
5770 &mut self.fatal,
5771 )
5772 }
5773
5774 fn parse_paren(&mut self) -> Option<ExprId> {
5775 let open = self.advance();
5776 let inner = self.parse_expression()?;
5777 if !matches!(self.peek().kind, TokenKind::RightParen) {
5778 self.error_at_peek("expected `)`");
5779 return None;
5780 }
5781 let close = self.advance();
5782 let span = self.span(open.span.start, close.span.end);
5783 parse_arena_result(
5784 self.ast.try_push_expr(Expr {
5785 kind: ExprKind::Paren(inner),
5786 span,
5787 }),
5788 &mut self.fatal,
5789 )
5790 }
5791
5792 fn with_recursion_limit<T>(&mut self, parse: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
5793 if self.fatal.is_some() {
5794 return None;
5795 }
5796 if self.recursion_limit_span.is_some() || self.recursion_depth >= MAX_PARSE_DEPTH {
5797 self.stop_at_recursion_limit();
5798 return None;
5799 }
5800 self.recursion_depth += 1;
5801 let result = parse(self);
5802 self.recursion_depth -= 1;
5803 result
5804 }
5805
5806 fn stop_at_recursion_limit(&mut self) {
5807 self.recursion_limit_span.get_or_insert(self.peek().span);
5808 self.pos = self.tokens.len().saturating_sub(1);
5810 }
5811
5812 fn recover(&mut self) {
5813 while !self.is_at_eof() {
5814 match self.peek().kind {
5815 TokenKind::Semicolon => {
5816 self.advance();
5817 return;
5818 }
5819 TokenKind::RightBrace
5820 | TokenKind::Let
5821 | TokenKind::Const
5822 | TokenKind::Function
5823 | TokenKind::Export
5824 | TokenKind::If
5825 | TokenKind::While
5826 | TokenKind::Return
5827 | TokenKind::Try
5828 | TokenKind::Throw => return,
5829 _ => {
5830 self.advance();
5831 }
5832 }
5833 }
5834 }
5835
5836 fn peek(&self) -> &Token {
5837 self.tokens.get(self.pos).unwrap_or(&self.eof)
5838 }
5839
5840 fn peek_at(&self, offset: usize) -> &Token {
5841 self.pos
5842 .checked_add(offset)
5843 .and_then(|index| self.tokens.get(index))
5844 .unwrap_or(&self.eof)
5845 }
5846
5847 fn at_inserted_semicolon(&self) -> bool {
5849 let token = self.peek();
5850 matches!(token.kind, TokenKind::Semicolon) && token.span.start == token.span.end
5851 }
5852
5853 fn advance(&mut self) -> Token {
5854 let tok = self.peek().clone();
5855 if self.pos < self.tokens.len().saturating_sub(1) {
5856 self.pos += 1;
5857 }
5858 tok
5859 }
5860
5861 fn is_at_eof(&self) -> bool {
5862 self.fatal.is_some() || matches!(self.peek().kind, TokenKind::Eof)
5863 }
5864
5865 fn line_break_before_peek(&self) -> bool {
5867 self.source_has_line_break(self.prev_token_end(), self.peek().span.start)
5868 }
5869
5870 fn line_break_after_peek(&self) -> bool {
5872 self.source_has_line_break(self.peek().span.end, self.peek_at(1).span.start)
5873 }
5874
5875 fn source_has_line_break(&self, start: u32, end: u32) -> bool {
5878 self.source
5879 .get(start as usize..end as usize)
5880 .is_some_and(|gap| gap.contains(['\n', '\r']))
5881 }
5882
5883 fn at_statement_end(&self) -> bool {
5888 matches!(self.peek().kind, TokenKind::Semicolon)
5889 || (self.last_as_type_end == Some(self.prev_token_end())
5890 && self.line_break_before_peek())
5891 }
5892
5893 fn finish_statement(&mut self) -> u32 {
5896 if matches!(self.peek().kind, TokenKind::Semicolon) {
5897 return self.advance().span.end;
5898 }
5899 self.prev_token_end()
5900 }
5901
5902 fn prev_token_end(&self) -> u32 {
5905 self.tokens
5906 .get(self.pos.saturating_sub(1))
5907 .unwrap_or(&self.eof)
5908 .span
5909 .end
5910 }
5911
5912 fn take_leading_doc(&mut self) -> Option<crate::DocComment> {
5913 let parsed = self
5914 .peek()
5915 .leading_doc
5916 .as_ref()
5917 .map(crate::parse_doc_comment)
5918 .transpose();
5919 match parsed {
5920 Ok(doc) => doc,
5921 Err(error) => {
5922 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
5923 None
5924 }
5925 }
5926 }
5927
5928 fn parse_doc(&mut self, raw: &crate::RawDoc) -> Option<crate::DocComment> {
5929 match crate::parse_doc_comment(raw) {
5930 Ok(doc) => Some(doc),
5931 Err(error) => {
5932 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
5933 None
5934 }
5935 }
5936 }
5937
5938 fn error_at(&mut self, span: Span, message: impl Into<String>) {
5939 self.diagnostics.push(Diagnostic {
5940 severity: Severity::Error,
5941 span,
5942 message: message.into(),
5943 help: vec![],
5944 notes: vec![],
5945 });
5946 }
5947
5948 fn reject_loose_nullish_comparison(
5951 &mut self,
5952 op: BinOp,
5953 lhs: ExprId,
5954 rhs: ExprId,
5955 ) -> Option<()> {
5956 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5957 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
5958 let (nullish, operand, nullish_first) =
5959 match (self.nullish_operand(lhs)?, self.nullish_operand(rhs)?) {
5960 (Some(left), Some(right)) => {
5961 self.reject_nullish_against_nullish(op, lhs_span, rhs_span, [left, right]);
5962 return Some(());
5963 }
5964 (None, Some(nullish)) => (nullish, lhs, false),
5965 (Some(nullish), None) => (nullish, rhs, true),
5966 (None, None) => return Some(()),
5967 };
5968 let comparison = LooseComparison {
5969 nullish,
5970 nullish_first,
5971 negated: op == BinOp::NotEq,
5972 };
5973 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5974 let x = self.single_line_source(operand_span).unwrap_or("x");
5975 let strict = self.strict_suggestion(&comparison, x);
5976 let either = self.either_suggestion(&comparison, operand, x)?;
5977 let literal = comparison.nullish.literal.text();
5978 let written = self
5980 .single_line_source(self.span(lhs_span.start, rhs_span.end))
5981 .map_or_else(
5982 || {
5983 let written_op = if comparison.negated { "!=" } else { "==" };
5984 comparison.ordered(x, written_op, literal)
5985 },
5986 str::to_string,
5987 );
5988 self.error_at_with_help(
5989 self.span(lhs_span.start, rhs_span.end),
5990 format!(
5991 "`{written}` does not also match `{}`: `==` compares like `===`",
5992 comparison.nullish.literal.other().text()
5993 ),
5994 vec![
5995 either,
5996 format!("to test for `{literal}` alone, write `{strict}`"),
5997 ],
5998 );
5999 Some(())
6000 }
6001
6002 fn reject_nullish_against_nullish(
6006 &mut self,
6007 op: BinOp,
6008 lhs: Span,
6009 rhs: Span,
6010 sides: [NullishOperand; 2],
6011 ) {
6012 let span = self.span(lhs.start, rhs.end);
6013 let subject = self.single_line_source(span).map_or_else(
6016 || "this comparison".to_string(),
6017 |written| format!("`{written}`"),
6018 );
6019 let value = if op == BinOp::NotEq { "false" } else { "true" };
6020 let effects: Vec<String> = sides
6021 .iter()
6022 .filter_map(|side| side.effect)
6023 .map(|effect| {
6024 self.single_line_source(effect.operand).map_or_else(
6025 || "the `void` operand's expression".to_string(),
6026 |statement| format!("`{statement};`"),
6027 )
6028 })
6029 .collect();
6030 let help = if effects.is_empty() {
6031 format!("in TypeScript this is always `{value}`; write `{value}`")
6032 } else {
6033 format!(
6034 "in TypeScript this is always `{value}`; run {} {}, then use `{value}`",
6035 effects.join(" and "),
6036 if effects.len() > 1 {
6037 "each as its own statement"
6038 } else {
6039 "as its own statement"
6040 }
6041 )
6042 };
6043 self.error_at_with_help(
6044 span,
6045 format!("{subject} compares two nullish values: `==` compares like `===`"),
6046 vec![help],
6047 );
6048 }
6049
6050 fn strict_suggestion(&self, comparison: &LooseComparison, x: &str) -> String {
6053 let eq = comparison.strict_op();
6054 match comparison.nullish.effect {
6055 Some(effect) => {
6056 let void = self.single_line_source(effect.void).unwrap_or("void expr");
6057 comparison.ordered(x, eq, void)
6058 }
6059 None => format!("{x} {eq} {}", comparison.nullish.literal.text()),
6060 }
6061 }
6062
6063 fn either_suggestion(
6067 &mut self,
6068 comparison: &LooseComparison,
6069 operand: ExprId,
6070 x: &str,
6071 ) -> Option<String> {
6072 let mut steps = Vec::new();
6073 let tested = if self.repeats_without_effect(operand)? {
6074 x
6075 } else {
6076 steps.push(format!("bind `{x}` to a `const` (`const value = {x};`)"));
6077 "value"
6078 };
6079 if let Some(effect) = comparison.nullish.effect {
6080 let statement = self.single_line_source(effect.operand).unwrap_or("expr");
6081 let step = format!("run `{statement};` as its own statement");
6082 if comparison.nullish_first {
6083 steps.insert(0, step);
6084 } else {
6085 steps.push(step);
6086 }
6087 }
6088 let eq = comparison.strict_op();
6089 let joiner = if comparison.negated { "&&" } else { "||" };
6090 let both = format!("{tested} {eq} null {joiner} {tested} {eq} undefined");
6091 Some(if steps.is_empty() {
6092 format!("to test for either, write `{both}`")
6093 } else {
6094 format!(
6095 "to test for either, first {}, then write `{both}`",
6096 steps.join(", then ")
6097 )
6098 })
6099 }
6100
6101 fn repeats_without_effect(&mut self, id: ExprId) -> Option<bool> {
6104 let unwrapped = self.unparenthesized(id)?;
6105 let expr = parse_arena_result(self.ast.try_expr(unwrapped), &mut self.fatal)?;
6106 if is_literal(&expr.kind) {
6107 return Some(true);
6108 }
6109 if self
6110 .nullish_operand(unwrapped)?
6111 .is_some_and(|nullish| nullish.effect.is_none())
6112 {
6113 return Some(true);
6114 }
6115 self.is_reference_path(id)
6116 }
6117
6118 fn nullish_operand(&mut self, id: ExprId) -> Option<Option<NullishOperand>> {
6120 let id = self.unparenthesized(id)?;
6121 let expr = parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?;
6122 let span = expr.span;
6123 Some(match &expr.kind {
6124 ExprKind::Null => Some(NullishOperand {
6125 literal: Nullish::Null,
6126 effect: None,
6127 }),
6128 ExprKind::Identifier(ident) if ident.name == "undefined" => Some(NullishOperand {
6129 literal: Nullish::Undefined,
6130 effect: None,
6131 }),
6132 ExprKind::Void { operand } => {
6133 let operand_id = self.unparenthesized(*operand)?;
6134 let operand =
6135 parse_arena_result(self.ast.try_expr(operand_id), &mut self.fatal)?.clone();
6136 let effect_free =
6138 is_literal(&operand.kind) || self.is_reference_path(operand_id)?;
6139 let effect = (!effect_free).then_some(VoidEffect {
6140 void: span,
6141 operand: operand.span,
6142 });
6143 Some(NullishOperand {
6144 literal: Nullish::Undefined,
6145 effect,
6146 })
6147 }
6148 _ => None,
6149 })
6150 }
6151
6152 fn is_reference_path(&mut self, id: ExprId) -> Option<bool> {
6155 let mut id = id;
6156 loop {
6157 match &parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6158 ExprKind::Identifier(_) | ExprKind::This => return Some(true),
6159 ExprKind::FieldAccess { receiver, .. } => id = *receiver,
6160 ExprKind::Paren(inner) => id = *inner,
6161 _ => return Some(false),
6162 }
6163 }
6164 }
6165
6166 fn unparenthesized(&mut self, id: ExprId) -> Option<ExprId> {
6167 let mut id = id;
6168 loop {
6169 match parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6170 ExprKind::Paren(inner) => id = inner,
6171 _ => return Some(id),
6172 }
6173 }
6174 }
6175
6176 fn single_line_source(&self, span: Span) -> Option<&'a str> {
6177 self.source
6178 .get(span.start as usize..span.end as usize)
6179 .filter(|text| !text.contains('\n'))
6180 }
6181
6182 fn error_at_with_help(&mut self, span: Span, message: impl Into<String>, help: Vec<String>) {
6183 self.diagnostics.push(Diagnostic {
6184 severity: Severity::Error,
6185 span,
6186 message: message.into(),
6187 help,
6188 notes: vec![],
6189 });
6190 }
6191
6192 fn invariant_failure<T>(&mut self, message: &str) -> Option<T> {
6193 self.fatal.get_or_insert_with(|| CompilerFailure::Internal {
6194 stage: CompilerStage::Parse,
6195 span: None,
6196 message: message.into(),
6197 });
6198 self.pos = self.tokens.len();
6199 None
6200 }
6201
6202 fn error_at_peek(&mut self, message: impl Into<String>) {
6203 let span = self.peek().span;
6204 self.error_at(span, message);
6205 }
6206
6207 fn error_at_peek_with_help(&mut self, message: impl Into<String>, help: Vec<String>) {
6208 let span = self.peek().span;
6209 self.error_at_with_help(span, message, help);
6210 }
6211
6212 fn error_count(&self) -> usize {
6213 self.diagnostics
6214 .iter()
6215 .filter(|d| d.severity == Severity::Error)
6216 .count()
6217 }
6218}
6219
6220fn is_property_name(kind: &TokenKind) -> bool {
6221 matches!(
6222 kind,
6223 TokenKind::Identifier
6224 | TokenKind::BooleanLiteral(_)
6225 | TokenKind::NullLiteral
6226 | TokenKind::Let
6227 | TokenKind::Const
6228 | TokenKind::Function
6229 | TokenKind::If
6230 | TokenKind::Else
6231 | TokenKind::While
6232 | TokenKind::Do
6233 | TokenKind::For
6234 | TokenKind::Break
6235 | TokenKind::Continue
6236 | TokenKind::Return
6237 | TokenKind::Switch
6238 | TokenKind::Case
6239 | TokenKind::Default
6240 | TokenKind::Void
6241 | TokenKind::Interface
6242 | TokenKind::Enum
6243 | TokenKind::In
6244 | TokenKind::Typeof
6245 | TokenKind::Import
6246 | TokenKind::Export
6247 | TokenKind::New
6248 | TokenKind::Try
6249 | TokenKind::Catch
6250 | TokenKind::Finally
6251 | TokenKind::Throw
6252 | TokenKind::Class
6253 | TokenKind::Extends
6254 | TokenKind::Implements
6255 | TokenKind::Super
6256 | TokenKind::This
6257 )
6258}
6259
6260fn is_reserved_identifier_word(kind: &TokenKind) -> bool {
6261 matches!(
6262 kind,
6263 TokenKind::BooleanLiteral(_)
6264 | TokenKind::NullLiteral
6265 | TokenKind::Let
6266 | TokenKind::Const
6267 | TokenKind::Function
6268 | TokenKind::If
6269 | TokenKind::Else
6270 | TokenKind::While
6271 | TokenKind::Do
6272 | TokenKind::For
6273 | TokenKind::Break
6274 | TokenKind::Continue
6275 | TokenKind::Return
6276 | TokenKind::Switch
6277 | TokenKind::Case
6278 | TokenKind::Default
6279 | TokenKind::Export
6280 | TokenKind::Void
6281 | TokenKind::Interface
6282 | TokenKind::Enum
6283 | TokenKind::In
6284 | TokenKind::Typeof
6285 | TokenKind::Import
6286 | TokenKind::New
6287 | TokenKind::Try
6288 | TokenKind::Catch
6289 | TokenKind::Finally
6290 | TokenKind::Throw
6291 | TokenKind::Class
6292 | TokenKind::Extends
6293 | TokenKind::Implements
6294 | TokenKind::Super
6295 | TokenKind::This
6296 )
6297}
6298
6299#[derive(Clone, Copy, PartialEq, Eq)]
6303enum SpecifierList {
6304 Import,
6305 Export,
6306}
6307
6308impl SpecifierList {
6309 fn noun(self) -> &'static str {
6310 match self {
6311 SpecifierList::Import => "import",
6312 SpecifierList::Export => "export",
6313 }
6314 }
6315}
6316
6317const STRICT_MODE_RESERVED_WORDS: [&str; 6] = [
6321 "package",
6322 "private",
6323 "protected",
6324 "public",
6325 "static",
6326 "yield",
6327];
6328
6329fn reserved_keyword_rename_example(keyword: &str) -> &'static str {
6330 match keyword {
6331 "default" => "defaultValue",
6332 _ => "valueName",
6333 }
6334}
6335
6336fn mixed_logical(prev: BinOp, next: BinOp) -> bool {
6337 let prev_logical = matches!(prev, BinOp::Or | BinOp::And);
6338 let next_logical = matches!(next, BinOp::Or | BinOp::And);
6339 let prev_nullish = matches!(prev, BinOp::NullishCoalesce);
6340 let next_nullish = matches!(next, BinOp::NullishCoalesce);
6341 (prev_logical && next_nullish) || (prev_nullish && next_logical)
6342}
6343
6344fn mixed_logical_help(prev: BinOp, next: BinOp) -> String {
6347 let symbol = |op: BinOp| if op == BinOp::And { "&&" } else { "||" };
6348 let (first, second) = if prev == BinOp::NullishCoalesce {
6349 ("??", symbol(next))
6350 } else {
6351 (symbol(prev), "??")
6352 };
6353 format!(
6354 "add parentheses to choose the grouping: `(a {first} b) {second} c` or `a {first} (b {second} c)`"
6355 )
6356}
6357
6358const LOGICAL_AND_PREC: u8 = 2;
6359
6360#[derive(Clone, Copy)]
6362struct NullishOperand {
6363 literal: Nullish,
6364 effect: Option<VoidEffect>,
6366}
6367
6368#[derive(Clone, Copy, PartialEq, Eq)]
6369enum Nullish {
6370 Null,
6371 Undefined,
6372}
6373
6374impl Nullish {
6375 fn text(self) -> &'static str {
6376 match self {
6377 Nullish::Null => "null",
6378 Nullish::Undefined => "undefined",
6379 }
6380 }
6381
6382 fn other(self) -> Self {
6384 match self {
6385 Nullish::Null => Nullish::Undefined,
6386 Nullish::Undefined => Nullish::Null,
6387 }
6388 }
6389}
6390
6391struct LooseComparison {
6393 nullish: NullishOperand,
6394 nullish_first: bool,
6396 negated: bool,
6398}
6399
6400impl LooseComparison {
6401 fn strict_op(&self) -> &'static str {
6402 if self.negated { "!==" } else { "===" }
6403 }
6404
6405 fn ordered(&self, x: &str, op: &str, nullish: &str) -> String {
6407 if self.nullish_first {
6408 format!("{nullish} {op} {x}")
6409 } else {
6410 format!("{x} {op} {nullish}")
6411 }
6412 }
6413}
6414
6415#[derive(Clone, Copy)]
6416struct VoidEffect {
6417 void: Span,
6419 operand: Span,
6421}
6422
6423fn is_literal(kind: &ExprKind) -> bool {
6424 matches!(
6425 kind,
6426 ExprKind::Number(_)
6427 | ExprKind::BigInt(_)
6428 | ExprKind::String(_)
6429 | ExprKind::Boolean(_)
6430 | ExprKind::Null
6431 )
6432}
6433
6434struct ParamOrder<'p> {
6436 optional: bool,
6437 required: bool,
6439 name: Option<&'p str>,
6441 span: Span,
6442}
6443
6444impl<'p> ParamOrder<'p> {
6445 fn of_param(param: &'p ParamDecl) -> Self {
6446 let (name, span) = match ¶m.pattern {
6447 Some(binding) => (None, binding.span()),
6448 None => (Some(param.name.name.as_str()), param.name.span),
6449 };
6450 Self {
6451 optional: param.optional,
6452 required: !param.is_omittable() && !param.rest,
6453 name,
6454 span,
6455 }
6456 }
6457
6458 fn of_field(field: &'p TypeAnnotationField) -> Self {
6459 Self {
6460 optional: field.optional,
6461 required: !field.optional && !field.rest,
6462 name: Some(field.name.name.as_str()),
6463 span: field.name.span,
6464 }
6465 }
6466}
6467
6468struct BinaryToken {
6470 op: BinOp,
6471 precedence: u8,
6472 tokens: usize,
6473}
6474
6475struct ShiftToken {
6477 op: BinOp,
6478 tokens: usize,
6479 assignment: bool,
6481}
6482
6483impl ShiftToken {
6484 fn new(op: BinOp, tokens: usize, assignment: bool) -> Self {
6485 Self {
6486 op,
6487 tokens,
6488 assignment,
6489 }
6490 }
6491}
6492
6493const SHIFT_PRECEDENCE: u8 = 8;
6495
6496#[derive(Clone, Copy, PartialEq)]
6497enum TupleElementLabel {
6498 None,
6499 Required,
6501 Optional,
6503}
6504
6505fn peek_binop(kind: &TokenKind) -> Option<(BinOp, u8)> {
6506 Some(match kind {
6507 TokenKind::QuestionQuestion => (BinOp::NullishCoalesce, 0),
6508 TokenKind::PipePipe => (BinOp::Or, 1),
6509 TokenKind::AmpAmp => (BinOp::And, LOGICAL_AND_PREC),
6510 TokenKind::Pipe => (BinOp::BitOr, 3),
6511 TokenKind::Caret => (BinOp::BitXor, 4),
6512 TokenKind::Amp => (BinOp::BitAnd, 5),
6513 TokenKind::EqEqEq | TokenKind::EqEq => (BinOp::Eq, 6),
6514 TokenKind::BangEqEq | TokenKind::BangEq => (BinOp::NotEq, 6),
6515 TokenKind::LessThan => (BinOp::Lt, 7),
6516 TokenKind::GreaterThan => (BinOp::Gt, 7),
6517 TokenKind::LessEquals => (BinOp::Le, 7),
6518 TokenKind::GreaterEquals => (BinOp::Ge, 7),
6519 TokenKind::In => (BinOp::In, 7),
6520 TokenKind::Plus => (BinOp::Add, 9),
6523 TokenKind::Minus => (BinOp::Sub, 9),
6524 TokenKind::Star => (BinOp::Mul, 10),
6525 TokenKind::Slash => (BinOp::Div, 10),
6526 TokenKind::Percent => (BinOp::Rem, 10),
6527 TokenKind::StarStar => (BinOp::Pow, 11),
6529 _ => return None,
6530 })
6531}
6532
6533fn cannot_start_type(kind: &TokenKind) -> bool {
6537 matches!(
6538 kind,
6539 TokenKind::Dot
6540 | TokenKind::QuestionDot
6541 | TokenKind::LeftBracket
6542 | TokenKind::Bang
6543 | TokenKind::Semicolon
6544 | TokenKind::RightParen
6545 | TokenKind::RightBrace
6546 | TokenKind::Comma
6547 | TokenKind::Eof
6548 )
6549}
6550
6551fn right_operand_min_prec(op: BinOp, prec: u8) -> u8 {
6555 match op {
6556 BinOp::Pow => prec,
6557 BinOp::NullishCoalesce => LOGICAL_AND_PREC + 1,
6558 _ => prec + 1,
6559 }
6560}
6561
6562fn compound_op_for_token(kind: &TokenKind) -> Option<BinOp> {
6563 Some(match kind {
6564 TokenKind::PlusEquals => BinOp::Add,
6565 TokenKind::MinusEquals => BinOp::Sub,
6566 TokenKind::StarEquals => BinOp::Mul,
6567 TokenKind::SlashEquals => BinOp::Div,
6568 TokenKind::PercentEquals => BinOp::Rem,
6569 TokenKind::StarStarEquals => BinOp::Pow,
6570 TokenKind::AmpEquals => BinOp::BitAnd,
6571 TokenKind::PipeEquals => BinOp::BitOr,
6572 TokenKind::CaretEquals => BinOp::BitXor,
6573 _ => return None,
6574 })
6575}
6576
6577fn is_assign_lookahead(kind: &TokenKind) -> bool {
6578 matches!(kind, TokenKind::Equals) || compound_op_for_token(kind).is_some()
6579}
6580
6581fn parse_arena_result<T>(
6584 result: Result<T, crate::arena::ArenaError>,
6585 fatal: &mut Option<CompilerFailure>,
6586) -> Option<T> {
6587 match result {
6588 Ok(value) => Some(value),
6589 Err(error) => {
6590 fatal.get_or_insert_with(|| error.into_compiler_failure(CompilerStage::Parse));
6591 None
6592 }
6593 }
6594}
6595
6596fn is_rest_array_annotation(ty: &crate::TypeAnnotation) -> bool {
6598 use crate::ast::TypeAnnotationKind;
6599 match &ty.kind {
6600 TypeAnnotationKind::Array(_) => true,
6601 TypeAnnotationKind::Readonly(inner) => matches!(inner.kind, TypeAnnotationKind::Array(_)),
6602 _ => false,
6603 }
6604}
6605
6606#[cfg(test)]
6607mod tests {
6608 use super::{MAX_ERRORS, MAX_PARSE_DEPTH, Parser, parse};
6609 use crate::source::Sources;
6610 use crate::{Asi, Ast, ExprKind, FileId, ImportKind, StmtKind, Token, TokenKind, diagnostics};
6611
6612 const F: FileId = FileId(0);
6613
6614 #[test]
6615 fn malformed_direct_token_streams_return_typed_failures() {
6616 use crate::compiler_error::CompilerFailure;
6617 for tokens in [
6618 Vec::new(),
6619 vec![Token::new(
6620 TokenKind::Identifier,
6621 crate::Span::new(F, 0, 2).unwrap(),
6622 )],
6623 vec![
6624 Token::new(
6625 TokenKind::Identifier,
6626 crate::Span {
6627 file: F,
6628 start: 1,
6629 end: 2,
6630 },
6631 ),
6632 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
6633 ],
6634 vec![
6635 Token::new(TokenKind::Eof, crate::Span::at(F)),
6636 Token::new(TokenKind::Eof, crate::Span::at(F)),
6637 ],
6638 vec![
6639 Token::new(
6640 TokenKind::Identifier,
6641 crate::Span {
6642 file: F,
6643 start: 2,
6644 end: 0,
6645 },
6646 ),
6647 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
6648 ],
6649 vec![Token::new(TokenKind::Eof, crate::Span::at(FileId(9)))],
6650 ] {
6651 let error = super::parse_checked("é", tokens, F).expect_err("invalid token metadata");
6652 assert!(matches!(
6653 error.fatal,
6654 Some(CompilerFailure::Internal { .. })
6655 ));
6656 }
6657 }
6658
6659 #[test]
6660 fn malformed_documentation_metadata_returns_a_fatal_error() {
6661 let source = "/** hello */ function main(): void {}";
6662 let valid = tokens_of(source);
6663 for doc in [
6664 crate::RawDoc {
6665 text: "/** hi */".into(),
6666 span: crate::Span {
6667 file: F,
6668 start: u32::MAX,
6669 end: u32::MAX,
6670 },
6671 },
6672 crate::RawDoc {
6673 text: "/** different */".into(),
6674 span: crate::Span::new(F, 0, 12).unwrap(),
6675 },
6676 crate::RawDoc {
6677 text: "/** hello */".into(),
6678 span: crate::Span::new(FileId(9), 0, 12).unwrap(),
6679 },
6680 ] {
6681 let mut tokens = valid.clone();
6682 tokens[0].leading_doc = Some(doc);
6683 let error = super::parse_checked(source, tokens, F).expect_err("invalid doc metadata");
6684 assert!(error.to_string().contains("documentation metadata"));
6685 }
6686 assert!(super::parse_checked(source, valid, F).is_ok());
6687 }
6688
6689 #[test]
6690 fn invalid_parser_dispatch_survives_diagnostic_rollback() {
6691 let mut parser = parser_from_source("let x = 1;");
6692 assert!(parser.try_parse_type_args_only().is_none());
6693 parser.diagnostics.clear();
6694 assert!(parser.fatal.is_some());
6695 assert!(parser.parse_expression().is_none());
6696 }
6697
6698 #[test]
6699 fn invalid_arena_read_stops_parser_and_survives_diagnostic_rollback() {
6700 use crate::{
6701 ExprId,
6702 compiler_error::{CompilerFailure, CompilerStage},
6703 };
6704 let mut parser = parser_from_source("let x = 1;");
6705 assert!(parser.statement_kind_for(ExprId(u32::MAX)).is_none());
6706 assert!(matches!(
6707 parser.fatal,
6708 Some(CompilerFailure::Internal {
6709 stage: CompilerStage::Parse,
6710 span: None,
6711 ..
6712 })
6713 ));
6714 parser.diagnostics.clear();
6715 let position = parser.pos;
6716 assert!(parser.parse_statement().is_none());
6717 parser.recover();
6718 assert_eq!(parser.pos, position);
6719 assert!(parser.ast.expr_ids().unwrap().next().is_none());
6720 }
6721
6722 fn tokens_of(source: &str) -> Vec<Token> {
6723 let mut asi = Asi::new(source, crate::FileId(0));
6724 let mut tokens = Vec::new();
6725 loop {
6726 let tok = asi.next_token();
6727 let is_eof = matches!(tok.kind, TokenKind::Eof);
6728 tokens.push(tok);
6729 if is_eof {
6730 break;
6731 }
6732 }
6733 let diags = asi.into_diagnostics();
6734 assert!(diags.is_empty(), "unexpected lexer diagnostics: {diags:?}");
6735 tokens
6736 }
6737
6738 fn parse_str(source: &str) -> (Ast, Vec<crate::Diagnostic>) {
6739 parse(source, tokens_of(source), crate::FileId(0))
6740 }
6741
6742 fn parser_from_source(source: &str) -> Parser<'_> {
6743 Parser {
6744 source,
6745 tokens: tokens_of(source),
6746 file: F,
6747 pos: 0,
6748 ast: Ast::new(),
6749 diagnostics: Vec::new(),
6750 block_depth: 0,
6751 class_member_body_depth: 0,
6752 function_expression_body_depth: 0,
6753 recursion_depth: 0,
6754 recursion_limit_span: None,
6755 last_as_type_end: None,
6756 eof: Token::new(
6757 TokenKind::Eof,
6758 crate::Span::new(F, source.len() as u32, source.len() as u32).unwrap(),
6759 ),
6760 fatal: None,
6761 }
6762 }
6763
6764 #[test]
6765 fn recursion_budget_accepts_boundary_and_restores_depth() {
6766 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH - 1));
6767 let mut parser = parser_from_source(&source);
6768 assert!(parser.parse_unary().is_some());
6769 assert_eq!(parser.recursion_depth, 0);
6770 assert!(parser.recursion_limit_span.is_none());
6771
6772 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH));
6773 let mut parser = parser_from_source(&source);
6774 assert!(parser.parse_unary().is_none());
6775 assert_eq!(parser.recursion_depth, 0);
6776 assert!(parser.recursion_limit_span.is_some());
6777 }
6778
6779 #[test]
6780 fn parse_empty_input() {
6781 let (ast, diags) = parse_str("");
6782 assert!(diags.is_empty());
6783 assert!(ast.top_level.is_empty());
6784 }
6785
6786 #[test]
6787 fn any_type_is_rejected() {
6788 for src in [
6791 "function main(): void { const x: any = 1; }",
6792 "function main(): void { const x = 1 as any; }",
6793 "function main(): void { const x: any[] = []; }",
6794 ] {
6795 let (_ast, diags) = parse_str(src);
6796 assert!(
6797 diags
6798 .iter()
6799 .any(|d| d.message.contains("`any` is not supported")),
6800 "expected an `any` rejection for {src:?}, got: {diags:?}",
6801 );
6802 assert!(
6803 diags
6804 .iter()
6805 .any(|d| d.help.iter().any(|h| h.contains("`unknown`"))),
6806 "expected the fix-shape help for {src:?}, got: {diags:?}",
6807 );
6808 }
6809 }
6810
6811 #[test]
6812 fn any_catch_binding_help_suggests_catch_forms() {
6813 let (_ast, diags) = parse_str("function main(): void { try { } catch (e: any) { } }");
6816 let any_diags: Vec<_> = diags
6817 .iter()
6818 .filter(|d| d.message.contains("`any` is not supported"))
6819 .collect();
6820 assert_eq!(any_diags.len(), 1, "got: {diags:?}");
6821 assert!(
6822 any_diags[0].help.iter().any(|h| h.contains("`catch (e)`")),
6823 "got: {diags:?}"
6824 );
6825 assert!(
6826 !any_diags[0].help.iter().any(|h| h.contains("`unknown`")),
6827 "got: {diags:?}"
6828 );
6829 }
6830
6831 #[test]
6832 fn arrow_return_type_scanner_matches_type_grammar() {
6833 for ty in [
6838 "number",
6839 "void",
6840 "null",
6841 "ns.Foo",
6842 "Map<string, number>",
6843 "ns.Foo<string>[]",
6844 "number[]",
6845 "[number, number]",
6846 "[number, [string, boolean]]",
6847 "[number, string][]",
6848 "{ a: number }",
6849 "{ a: number }[]",
6850 "\"on\" | \"off\"",
6851 "1 | 2",
6852 "Point | null",
6853 "| number | string",
6854 "(n: number) => number",
6855 "(n: number) => [number, number]",
6856 "readonly number[]",
6857 "readonly number[][]",
6858 "readonly [number, string]",
6859 "readonly number[] | null",
6860 "[first: number, second: string]",
6861 "[new: number, string]",
6862 "keyof { a: number }",
6863 ] {
6864 let annotation = format!("function f(): void {{ const a: {ty} = x; }}");
6865 let (_ast, diags) = parse_str(&annotation);
6866 assert!(
6867 diags.is_empty(),
6868 "annotation grammar rejected {ty:?}: {diags:?}",
6869 );
6870
6871 let arrow = format!("function f(): void {{ const g = (): {ty} => x; }}");
6872 let (_ast, diags) = parse_str(&arrow);
6873 assert!(
6874 diags.is_empty(),
6875 "arrow lookahead rejected return type {ty:?}: {diags:?}",
6876 );
6877 }
6878
6879 let (_ast, diags) =
6881 parse_str("function f(): void { const g = (v: unknown): v is string => x; }");
6882 assert!(
6883 diags.is_empty(),
6884 "arrow lookahead rejected predicate return: {diags:?}",
6885 );
6886 }
6887
6888 #[test]
6889 fn bare_semicolons_are_silent_empty_statements() {
6890 let (ast, diags) = parse_str(";");
6891 assert!(diags.is_empty());
6892 assert!(ast.top_level.is_empty());
6893 }
6894
6895 #[test]
6896 fn parse_single_non_expression_token_errors() {
6897 let (ast, diags) = parse_str("}");
6898 assert_eq!(diags.len(), 1);
6899 assert_eq!(diags[0].message, "expected expression");
6900 assert!(ast.top_level.is_empty());
6901 }
6902
6903 #[test]
6904 fn recovery_across_many_bad_starts() {
6905 let (ast, diags) = parse_str("} } } x;");
6906 assert_eq!(diags.len(), 3);
6907 for d in &diags {
6908 assert_eq!(d.message, "expected expression");
6909 }
6910 assert_eq!(ast.top_level.len(), 1);
6911 }
6912
6913 #[test]
6914 fn recovery_consumes_semicolon() {
6915 let mut p = parser_from_source("foo ;");
6916 p.error_at_peek("test");
6917 p.recover();
6918 assert!(matches!(
6919 p.peek().kind,
6920 TokenKind::Semicolon | TokenKind::Eof
6921 ));
6922 assert!(matches!(p.peek().kind, TokenKind::Eof));
6923 }
6924
6925 #[test]
6926 fn recovery_stops_at_right_brace() {
6927 let mut p = parser_from_source("foo }");
6928 p.error_at_peek("test");
6929 p.recover();
6930 assert!(matches!(p.peek().kind, TokenKind::RightBrace));
6931 }
6932
6933 #[test]
6934 fn recovery_stops_at_let_keyword() {
6935 let mut p = parser_from_source("foo let x");
6936 p.error_at_peek("test");
6937 p.recover();
6938 assert!(matches!(p.peek().kind, TokenKind::Let));
6939 }
6940
6941 #[test]
6942 fn recovery_stops_at_function_keyword() {
6943 let mut p = parser_from_source("foo function bar");
6944 p.error_at_peek("test");
6945 p.recover();
6946 assert!(matches!(p.peek().kind, TokenKind::Function));
6947 }
6948
6949 #[test]
6950 fn error_cap_at_20() {
6951 let source = "} ".repeat(25);
6952 let (_, diags) = parse_str(&source);
6953 assert_eq!(diags.len(), MAX_ERRORS);
6954 }
6955
6956 #[test]
6957 fn driver_makes_progress_past_unmatched_brace() {
6958 let (ast, diags) = parse_str("} x;");
6959 assert_eq!(diags.len(), 1);
6960 assert_eq!(diags[0].message, "expected expression");
6961 assert_eq!(ast.top_level.len(), 1);
6962 }
6963
6964 #[test]
6965 fn snapshot_multi_error_render() {
6966 let source = "foo bar;\nbaz qux;\nhello world;";
6967 let (_, diags) = parse_str(source);
6968 let (sources, _) = Sources::single("script.subm", source).unwrap();
6969 let rendered: String = diags
6970 .iter()
6971 .map(|d| diagnostics::render(d, &sources))
6972 .collect::<Vec<_>>()
6973 .join("\n");
6974 insta::assert_snapshot!(rendered);
6975 }
6976
6977 fn single_stmt(ast: &Ast) -> &crate::Stmt {
6978 assert_eq!(ast.top_level.len(), 1);
6979 ast.try_stmt(ast.top_level[0]).unwrap()
6980 }
6981
6982 fn expr_of_single_stmt(ast: &Ast) -> &crate::Expr {
6983 let stmt = single_stmt(ast);
6984 match stmt.kind {
6985 crate::StmtKind::Expr(id) => ast.try_expr(id).unwrap(),
6986 _ => panic!("expected expression statement, got {:?}", stmt.kind),
6987 }
6988 }
6989
6990 #[test]
6991 fn parse_number_literal() {
6992 let (ast, diags) = parse_str("42;");
6993 assert!(diags.is_empty());
6994 let expr = expr_of_single_stmt(&ast);
6995 assert_eq!(expr.kind, crate::ExprKind::Number(42.0));
6996 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
6997 }
6998
6999 #[test]
7000 fn parse_string_literal() {
7001 let (ast, diags) = parse_str(r#""s";"#);
7002 assert!(diags.is_empty());
7003 let expr = expr_of_single_stmt(&ast);
7004 assert_eq!(expr.kind, crate::ExprKind::String("s".to_string()));
7005 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7006 }
7007
7008 #[test]
7009 fn parse_boolean_true() {
7010 let (ast, diags) = parse_str("true;");
7011 assert!(diags.is_empty());
7012 let expr = expr_of_single_stmt(&ast);
7013 assert_eq!(expr.kind, crate::ExprKind::Boolean(true));
7014 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7015 }
7016
7017 #[test]
7018 fn parse_boolean_false() {
7019 let (ast, diags) = parse_str("false;");
7020 assert!(diags.is_empty());
7021 let expr = expr_of_single_stmt(&ast);
7022 assert_eq!(expr.kind, crate::ExprKind::Boolean(false));
7023 assert_eq!(expr.span, crate::Span::new(F, 0, 5).unwrap());
7024 }
7025
7026 #[test]
7027 fn parse_null_literal() {
7028 let (ast, diags) = parse_str("null;");
7029 assert!(diags.is_empty());
7030 let expr = expr_of_single_stmt(&ast);
7031 assert_eq!(expr.kind, crate::ExprKind::Null);
7032 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7033 }
7034
7035 #[test]
7036 fn parse_identifier() {
7037 let (ast, diags) = parse_str("x;");
7038 assert!(diags.is_empty());
7039 let expr = expr_of_single_stmt(&ast);
7040 assert!(matches!(expr.kind, crate::ExprKind::Identifier(_)));
7041 assert_eq!(expr.span, crate::Span::new(F, 0, 1).unwrap());
7042 }
7043
7044 #[test]
7045 fn reserved_keywords_remain_invalid_declaration_names() {
7046 let (_ast, diags) = parse_str("function default(): void {}");
7047 assert_eq!(diags.len(), 1, "{diags:#?}");
7048 assert_eq!(
7049 diags[0].message,
7050 "`default` is a reserved keyword and can't be used as a name"
7051 );
7052 assert!(
7053 diags[0]
7054 .help
7055 .iter()
7056 .any(|h| h == "rename it, for example: `defaultValue`"),
7057 "expected rename help, got: {diags:?}"
7058 );
7059 }
7060
7061 #[test]
7062 fn eval_and_arguments_cannot_name_bindings() {
7063 let bindings = [
7064 ("let arguments = 1;", "arguments"),
7065 ("const [eval] = [1];", "eval"),
7066 ("const { a: eval } = { a: 1 };", "eval"),
7067 ("const { eval } = { eval: 1 };", "eval"),
7068 ("const [...arguments] = [1];", "arguments"),
7069 ("function eval(): void {}", "eval"),
7070 ("function f(arguments: number): void {}", "arguments"),
7071 ("const f = arguments => arguments;", "arguments"),
7072 ("const f = (...eval: number[]) => 1;", "eval"),
7073 ("const f = function eval(): void {};", "eval"),
7074 ("for (const eval of [1]) {}", "eval"),
7075 ("try {} catch (arguments) {}", "arguments"),
7076 ("type F = (eval: number) => void;", "eval"),
7077 ("type G = (...arguments: number[]) => void;", "arguments"),
7078 ("type H = { [eval: string]: number };", "eval"),
7079 ];
7080 for (source, word) in bindings {
7081 let (_ast, diags) = parse_str(source);
7082 assert!(
7083 diags
7084 .iter()
7085 .any(|d| d.message == format!("`{word}` can't name a binding in strict mode")),
7086 "{source}: {diags:#?}"
7087 );
7088 }
7089 }
7090
7091 #[test]
7092 fn eval_and_arguments_still_name_types_and_properties() {
7093 for source in [
7094 "class eval {}",
7095 "interface arguments { eval: number }",
7096 "type eval = number;",
7097 "enum arguments { eval }",
7098 "const o = { eval: 1, arguments: 2 };",
7099 "import { eval } from \"./m\";",
7100 ] {
7101 let (_ast, diags) = parse_str(source);
7102 assert!(diags.is_empty(), "{source}: {diags:#?}");
7103 }
7104 }
7105
7106 #[test]
7107 fn reserved_keywords_remain_invalid_parameter_names() {
7108 let (_ast, diags) = parse_str("function f(default: string): void {}");
7109 assert_eq!(diags.len(), 1, "{diags:#?}");
7110 assert_eq!(
7111 diags[0].message,
7112 "`default` is a reserved keyword and can't be used as a name"
7113 );
7114 assert!(
7115 diags[0]
7116 .help
7117 .iter()
7118 .any(|h| h == "rename it, for example: `defaultValue`"),
7119 "expected rename help, got: {diags:?}"
7120 );
7121 }
7122
7123 #[test]
7124 fn contextual_keywords_are_valid_names() {
7125 for src in [
7126 "const type = \"x\";",
7127 "const from = 1;",
7128 "let of = 2;",
7129 "const as = 3;",
7130 "const is = 4;",
7131 "function f(from: string): void {}",
7132 ] {
7133 let (_ast, diags) = parse_str(src);
7134 assert!(
7135 diags.is_empty(),
7136 "unexpected diagnostics for {src:?}: {diags:#?}"
7137 );
7138 }
7139 }
7140
7141 #[test]
7142 fn type_alias_dispatch_table() {
7143 let (ast, diags) = parse_str("type X = number;");
7144 assert!(diags.is_empty(), "{diags:#?}");
7145 assert!(matches!(
7146 ast.try_stmt(ast.top_level[0]).unwrap().kind,
7147 crate::StmtKind::TypeAliasDecl { .. }
7148 ));
7149
7150 for src in ["type = 5;", "type;", "type(1);", "type < 3;", "type.foo;"] {
7152 let (ast, diags) = parse_str(src);
7153 assert!(
7154 diags.is_empty(),
7155 "unexpected diagnostics for {src:?}: {diags:#?}"
7156 );
7157 assert!(
7158 !matches!(
7159 ast.try_stmt(ast.top_level[0]).unwrap().kind,
7160 crate::StmtKind::TypeAliasDecl { .. }
7161 ),
7162 "{src:?} must not parse as a type alias"
7163 );
7164 }
7165
7166 let (_ast, diags) = parse_str("type X;");
7167 assert!(
7168 diags
7169 .iter()
7170 .any(|d| d.message == "expected `=` after type alias name"),
7171 "bare `type X` should commit to the alias diagnostic, got: {diags:#?}"
7172 );
7173 }
7174
7175 #[test]
7176 fn export_type_alias_dispatch() {
7177 let (ast, diags) = parse_str("export type X = number;");
7178 assert!(diags.is_empty(), "{diags:#?}");
7179 assert_eq!(ast.exported_decls.len(), 1);
7180 assert!(matches!(
7181 ast.try_stmt(ast.exported_decls[0].stmt).unwrap().kind,
7182 crate::StmtKind::TypeAliasDecl { .. }
7183 ));
7184
7185 let (_ast, diags) = parse_str("export type = 5;");
7186 assert!(
7187 diags
7188 .iter()
7189 .any(|d| d.message == "expected a declaration after `export`"),
7190 "`export type = 5` must not become an exported assignment, got: {diags:#?}"
7191 );
7192 }
7193
7194 #[test]
7195 fn parse_paren_simple() {
7196 let (ast, diags) = parse_str("(x);");
7197 assert!(diags.is_empty());
7198 let expr = expr_of_single_stmt(&ast);
7199 let crate::ExprKind::Paren(inner_id) = expr.kind else {
7200 panic!("expected Paren, got {:?}", expr.kind);
7201 };
7202 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7203 let inner = ast.try_expr(inner_id).unwrap();
7204 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7205 assert_eq!(inner.span, crate::Span::new(F, 1, 2).unwrap());
7206 }
7207
7208 #[test]
7209 fn parse_paren_nested() {
7210 let (ast, diags) = parse_str("((x));");
7211 assert!(diags.is_empty());
7212 let outer = expr_of_single_stmt(&ast);
7213 let crate::ExprKind::Paren(mid_id) = outer.kind else {
7214 panic!("expected outer Paren");
7215 };
7216 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7217 let mid = ast.try_expr(mid_id).unwrap();
7218 let crate::ExprKind::Paren(inner_id) = mid.kind else {
7219 panic!("expected inner Paren");
7220 };
7221 assert_eq!(mid.span, crate::Span::new(F, 1, 4).unwrap());
7222 let inner = ast.try_expr(inner_id).unwrap();
7223 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7224 assert_eq!(inner.span, crate::Span::new(F, 2, 3).unwrap());
7225 }
7226
7227 #[test]
7228 fn parse_sequence_of_atoms() {
7229 let (ast, diags) = parse_str(r#"42; "hi"; true; null; x; (x);"#);
7230 assert!(diags.is_empty());
7231 assert_eq!(ast.top_level.len(), 6);
7232 insta::assert_debug_snapshot!(ast);
7233 }
7234
7235 #[test]
7236 fn missing_semicolon_after_expression() {
7237 let (ast, diags) = parse_str("42 43");
7238 assert_eq!(diags.len(), 1);
7239 assert_eq!(diags[0].message, "expected `;` after expression");
7240 assert!(ast.top_level.is_empty());
7241 }
7242
7243 #[test]
7244 fn empty_paren_diagnoses() {
7245 let (ast, diags) = parse_str("();");
7246 assert_eq!(diags.len(), 1);
7247 assert_eq!(diags[0].message, "expected expression");
7248 assert!(ast.top_level.is_empty());
7249 }
7250
7251 #[test]
7252 fn unterminated_paren_diagnoses() {
7253 let (ast, diags) = parse_str("(x;");
7254 assert_eq!(diags.len(), 1);
7255 assert_eq!(diags[0].message, "expected `)`");
7256 assert!(ast.top_level.is_empty());
7257 }
7258
7259 fn binary(e: &crate::Expr) -> (crate::BinOp, crate::ExprId, crate::ExprId) {
7260 match e.kind {
7261 crate::ExprKind::Binary { op, lhs, rhs } => (op, lhs, rhs),
7262 _ => panic!("expected Binary, got {:?}", e.kind),
7263 }
7264 }
7265
7266 #[test]
7267 fn parse_binary_add() {
7268 let (ast, diags) = parse_str("a + b;");
7269 assert!(diags.is_empty());
7270 let outer = expr_of_single_stmt(&ast);
7271 let (op, lhs, rhs) = binary(outer);
7272 assert_eq!(op, crate::BinOp::Add);
7273 assert!(matches!(
7274 ast.try_expr(lhs).unwrap().kind,
7275 crate::ExprKind::Identifier(_)
7276 ));
7277 assert!(matches!(
7278 ast.try_expr(rhs).unwrap().kind,
7279 crate::ExprKind::Identifier(_)
7280 ));
7281 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7282 }
7283
7284 #[test]
7285 fn bitwise_precedence_and_shift_adjacency() {
7286 let (ast, diags) = parse_str("a | b ^ c & d == e >> f + g;");
7287 assert!(diags.is_empty(), "{diags:?}");
7288 let (op, _, mut rhs) = binary(expr_of_single_stmt(&ast));
7289 assert_eq!(op, crate::BinOp::BitOr);
7290 for expected in [
7291 crate::BinOp::BitXor,
7292 crate::BinOp::BitAnd,
7293 crate::BinOp::Eq,
7294 crate::BinOp::Shr,
7295 crate::BinOp::Add,
7296 ] {
7297 let (op, _, next) = binary(ast.try_expr(rhs).unwrap());
7298 assert_eq!(op, expected);
7299 rhs = next;
7300 }
7301 for source in ["a > > b;", "a >/*gap*/> b;", "a < < b;"] {
7302 let (_, diags) = parse_str(source);
7303 assert!(!diags.is_empty(), "{source}");
7304 }
7305 }
7306
7307 #[test]
7308 fn bitwise_shift_assignment_span() {
7309 let (ast, diags) = parse_str("a >>>= b;");
7310 assert!(diags.is_empty(), "{diags:?}");
7311 let crate::StmtKind::CompoundAssign { op, op_span, .. } = single_stmt(&ast).kind else {
7312 panic!("expected assignment");
7313 };
7314 assert_eq!(op, crate::BinOp::UnsignedShr);
7315 assert_eq!(op_span, crate::Span::new(F, 2, 6).unwrap());
7316 }
7317
7318 #[test]
7319 fn precedence_mul_binds_tighter_than_add() {
7320 let (ast, diags) = parse_str("a + b * c;");
7321 assert!(diags.is_empty());
7322 let outer = expr_of_single_stmt(&ast);
7323 let (op, lhs, rhs) = binary(outer);
7324 assert_eq!(op, crate::BinOp::Add);
7325 assert!(matches!(
7326 ast.try_expr(lhs).unwrap().kind,
7327 crate::ExprKind::Identifier(_)
7328 ));
7329 let (rop, rlhs, rrhs) = binary(ast.try_expr(rhs).unwrap());
7330 assert_eq!(rop, crate::BinOp::Mul);
7331 assert!(matches!(
7332 ast.try_expr(rlhs).unwrap().kind,
7333 crate::ExprKind::Identifier(_)
7334 ));
7335 assert!(matches!(
7336 ast.try_expr(rrhs).unwrap().kind,
7337 crate::ExprKind::Identifier(_)
7338 ));
7339 }
7340
7341 #[test]
7342 fn precedence_mul_left_of_add() {
7343 let (ast, diags) = parse_str("a * b + c;");
7344 assert!(diags.is_empty());
7345 let outer = expr_of_single_stmt(&ast);
7346 let (op, lhs, rhs) = binary(outer);
7347 assert_eq!(op, crate::BinOp::Add);
7348 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7349 assert_eq!(lop, crate::BinOp::Mul);
7350 assert!(matches!(
7351 ast.try_expr(rhs).unwrap().kind,
7352 crate::ExprKind::Identifier(_)
7353 ));
7354 }
7355
7356 #[test]
7357 fn left_associative_same_precedence() {
7358 let (ast, diags) = parse_str("a - b - c;");
7359 assert!(diags.is_empty());
7360 let outer = expr_of_single_stmt(&ast);
7361 let (op, lhs, rhs) = binary(outer);
7362 assert_eq!(op, crate::BinOp::Sub);
7363 let (lop, llhs, lrhs) = binary(ast.try_expr(lhs).unwrap());
7364 assert_eq!(lop, crate::BinOp::Sub);
7365 assert!(matches!(
7366 ast.try_expr(llhs).unwrap().kind,
7367 crate::ExprKind::Identifier(_)
7368 ));
7369 assert!(matches!(
7370 ast.try_expr(lrhs).unwrap().kind,
7371 crate::ExprKind::Identifier(_)
7372 ));
7373 assert!(matches!(
7374 ast.try_expr(rhs).unwrap().kind,
7375 crate::ExprKind::Identifier(_)
7376 ));
7377 }
7378
7379 #[test]
7380 fn equality_inside_logical() {
7381 let (ast, diags) = parse_str("a === b && c === d;");
7382 assert!(diags.is_empty());
7383 let outer = expr_of_single_stmt(&ast);
7384 let (op, lhs, rhs) = binary(outer);
7385 assert_eq!(op, crate::BinOp::And);
7386 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7387 assert_eq!(lop, crate::BinOp::Eq);
7388 let (rop, ..) = binary(ast.try_expr(rhs).unwrap());
7389 assert_eq!(rop, crate::BinOp::Eq);
7390 }
7391
7392 #[test]
7393 fn double_equal_is_same_variant_as_triple() {
7394 let (ast, _) = parse_str("a == b;");
7395 let outer = expr_of_single_stmt(&ast);
7396 let (op, ..) = binary(outer);
7397 assert_eq!(op, crate::BinOp::Eq);
7398 }
7399
7400 #[test]
7401 fn bang_eq_is_not_eq() {
7402 let (ast, _) = parse_str("a != b;");
7403 let outer = expr_of_single_stmt(&ast);
7404 let (op, ..) = binary(outer);
7405 assert_eq!(op, crate::BinOp::NotEq);
7406 }
7407
7408 #[test]
7409 fn comparison_has_higher_prec_than_logical() {
7410 let (ast, _) = parse_str("a < b && c;");
7411 let outer = expr_of_single_stmt(&ast);
7412 let (op, lhs, _) = binary(outer);
7413 assert_eq!(op, crate::BinOp::And);
7414 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7415 assert_eq!(lop, crate::BinOp::Lt);
7416 }
7417
7418 #[test]
7419 fn full_precedence_chain_snapshot() {
7420 let (ast, diags) = parse_str("a || b && c === d < e + f * g;");
7421 assert!(diags.is_empty());
7422 insta::assert_debug_snapshot!(ast);
7423 }
7424
7425 #[test]
7426 fn paren_overrides_precedence() {
7427 let (ast, _) = parse_str("(a + b) * c;");
7428 let outer = expr_of_single_stmt(&ast);
7429 let (op, lhs, rhs) = binary(outer);
7430 assert_eq!(op, crate::BinOp::Mul);
7431 let crate::ExprKind::Paren(inner_id) = ast.try_expr(lhs).unwrap().kind else {
7432 panic!("expected Paren on lhs");
7433 };
7434 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7435 assert_eq!(inner_op, crate::BinOp::Add);
7436 assert!(matches!(
7437 ast.try_expr(rhs).unwrap().kind,
7438 crate::ExprKind::Identifier(_)
7439 ));
7440 }
7441
7442 #[test]
7443 fn binary_missing_rhs_diagnoses() {
7444 let (ast, diags) = parse_str("a +;");
7445 assert_eq!(diags.len(), 1);
7446 assert_eq!(diags[0].message, "expected expression");
7447 assert!(ast.top_level.is_empty());
7448 }
7449
7450 fn unary(e: &crate::Expr) -> (crate::UnOp, crate::ExprId) {
7451 match e.kind {
7452 crate::ExprKind::Unary { op, operand } => (op, operand),
7453 _ => panic!("expected Unary, got {:?}", e.kind),
7454 }
7455 }
7456
7457 #[test]
7458 fn parse_unary_not() {
7459 let (ast, _) = parse_str("!x;");
7460 let outer = expr_of_single_stmt(&ast);
7461 let (op, operand) = unary(outer);
7462 assert_eq!(op, crate::UnOp::Not);
7463 assert!(matches!(
7464 ast.try_expr(operand).unwrap().kind,
7465 crate::ExprKind::Identifier(_)
7466 ));
7467 assert_eq!(outer.span, crate::Span::new(F, 0, 2).unwrap());
7468 }
7469
7470 #[test]
7471 fn parse_unary_neg() {
7472 let (ast, _) = parse_str("-x;");
7473 let (op, _) = unary(expr_of_single_stmt(&ast));
7474 assert_eq!(op, crate::UnOp::Neg);
7475 }
7476
7477 #[test]
7478 fn parse_unary_pos() {
7479 let (ast, _) = parse_str("+x;");
7480 let (op, _) = unary(expr_of_single_stmt(&ast));
7481 assert_eq!(op, crate::UnOp::Pos);
7482 }
7483
7484 #[test]
7485 fn repeated_unary_not() {
7486 let (ast, _) = parse_str("!!x;");
7487 let outer = expr_of_single_stmt(&ast);
7488 let (op, operand) = unary(outer);
7489 assert_eq!(op, crate::UnOp::Not);
7490 let (inner_op, inner_operand) = unary(ast.try_expr(operand).unwrap());
7491 assert_eq!(inner_op, crate::UnOp::Not);
7492 assert!(matches!(
7493 ast.try_expr(inner_operand).unwrap().kind,
7494 crate::ExprKind::Identifier(_)
7495 ));
7496 }
7497
7498 #[test]
7499 fn unary_binds_tighter_than_binary() {
7500 let (ast, _) = parse_str("-x + 1;");
7501 let outer = expr_of_single_stmt(&ast);
7502 let (op, lhs, rhs) = binary(outer);
7503 assert_eq!(op, crate::BinOp::Add);
7504 let (unop, _) = unary(ast.try_expr(lhs).unwrap());
7505 assert_eq!(unop, crate::UnOp::Neg);
7506 assert_eq!(
7507 ast.try_expr(rhs).unwrap().kind,
7508 crate::ExprKind::Number(1.0)
7509 );
7510 }
7511
7512 #[test]
7513 fn unary_over_paren() {
7514 let (ast, _) = parse_str("-(x + 1);");
7515 let outer = expr_of_single_stmt(&ast);
7516 let (op, operand) = unary(outer);
7517 assert_eq!(op, crate::UnOp::Neg);
7518 let crate::ExprKind::Paren(inner_id) = ast.try_expr(operand).unwrap().kind else {
7519 panic!("expected Paren");
7520 };
7521 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7522 assert_eq!(inner_op, crate::BinOp::Add);
7523 }
7524
7525 #[test]
7526 fn parse_is_as_expression_rejected() {
7527 let (_, diags) = parse_str("x is number;");
7528 assert!(!diags.is_empty(), "expected parse error for `x is T`");
7529 }
7530
7531 fn as_cast(e: &crate::Expr) -> (crate::ExprId, &crate::TypeAnnotation) {
7532 match &e.kind {
7533 crate::ExprKind::As { expr, ty } => (*expr, ty),
7534 other => panic!("expected ExprKind::As, got {other:?}"),
7535 }
7536 }
7537
7538 #[test]
7539 fn parse_instanceof_class() {
7540 let (ast, diags) = parse_str("x instanceof Foo;");
7541 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7542 let outer = expr_of_single_stmt(&ast);
7543 let crate::ExprKind::InstanceOf { value, ty } = &outer.kind else {
7544 panic!("expected ExprKind::InstanceOf, got {:?}", outer.kind);
7545 };
7546 assert!(matches!(
7547 ast.try_expr(*value).unwrap().kind,
7548 crate::ExprKind::Identifier(_)
7549 ));
7550 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7551 }
7552
7553 #[test]
7554 fn parse_instanceof_binds_tighter_than_logical_and() {
7555 let (ast, diags) = parse_str("a instanceof B && c;");
7557 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7558 let outer = expr_of_single_stmt(&ast);
7559 let (op, lhs, rhs) = binary(outer);
7560 assert_eq!(op, crate::BinOp::And);
7561 assert!(matches!(
7562 ast.try_expr(lhs).unwrap().kind,
7563 crate::ExprKind::InstanceOf { .. }
7564 ));
7565 assert!(matches!(
7566 ast.try_expr(rhs).unwrap().kind,
7567 crate::ExprKind::Identifier(_)
7568 ));
7569 }
7570
7571 #[test]
7574 fn parse_angle_cast_lowers_to_the_as_node() {
7575 let (ast, diags) = parse_str("<number>x;");
7576 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7577 let outer = expr_of_single_stmt(&ast);
7578 let (inner, ty) = as_cast(outer);
7579 assert!(matches!(
7580 ast.try_expr(inner).unwrap().kind,
7581 crate::ExprKind::Identifier(_)
7582 ));
7583 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7584 }
7585
7586 #[test]
7590 fn a_less_than_comparison_is_not_read_as_a_cast() {
7591 let (ast, diags) = parse_str("a < b;");
7592 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7593 let e = expr_of_single_stmt(&ast);
7594 assert!(matches!(e.kind, crate::ExprKind::Binary { .. }));
7595 }
7596
7597 #[test]
7598 fn parse_typeof_in_type_position() {
7599 let (_ast, diags) = parse_str("let a: typeof b = 1;");
7600 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7601 }
7602
7603 #[test]
7604 fn parse_typeof_with_a_dotted_path() {
7605 let (_ast, diags) = parse_str("let a: typeof b.c.d = 1;");
7606 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7607 }
7608
7609 #[test]
7610 fn parse_as_primitive() {
7611 let (ast, diags) = parse_str("x as number;");
7612 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7613 let outer = expr_of_single_stmt(&ast);
7614 let (inner, ty) = as_cast(outer);
7615 assert!(matches!(
7616 ast.try_expr(inner).unwrap().kind,
7617 crate::ExprKind::Identifier(_)
7618 ));
7619 match &ty.kind {
7620 crate::TypeAnnotationKind::Name { args, .. } => {
7621 assert!(args.is_empty());
7622 }
7623 other => panic!("expected Name target, got {other:?}"),
7624 }
7625 assert_eq!(outer.span, crate::Span::new(F, 0, 11).unwrap());
7626 }
7627
7628 #[test]
7629 fn parse_as_object_literal_type() {
7630 let (ast, diags) = parse_str("x as { a: number };");
7631 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7632 let outer = expr_of_single_stmt(&ast);
7633 let (_, ty) = as_cast(outer);
7634 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Object { .. }));
7635 }
7636
7637 #[test]
7638 fn parse_as_array_short() {
7639 let (ast, diags) = parse_str("x as string[];");
7640 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7641 let outer = expr_of_single_stmt(&ast);
7642 let (_, ty) = as_cast(outer);
7643 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
7644 }
7645
7646 #[test]
7647 fn parse_as_array_generic() {
7648 let (ast, diags) = parse_str("x as T[];");
7649 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7650 let outer = expr_of_single_stmt(&ast);
7651 let (_, ty) = as_cast(outer);
7652 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
7653 }
7654
7655 #[test]
7656 fn parse_as_generic_instantiation() {
7657 let (ast, diags) = parse_str("x as Foo<Bar>;");
7658 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7659 let outer = expr_of_single_stmt(&ast);
7660 let (_, ty) = as_cast(outer);
7661 match &ty.kind {
7662 crate::TypeAnnotationKind::Name { args, .. } => {
7663 assert_eq!(args.len(), 1, "Foo<Bar> has one type arg");
7664 }
7665 other => panic!("expected Name with type args, got {other:?}"),
7666 }
7667 }
7668
7669 #[test]
7670 fn parse_as_precedence_higher_than_additive() {
7671 let (ast, diags) = parse_str("x as number + 1;");
7672 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7673 let outer = expr_of_single_stmt(&ast);
7674 let (op, lhs, rhs) = binary(outer);
7675 assert_eq!(op, crate::BinOp::Add);
7676 assert!(matches!(
7677 ast.try_expr(lhs).unwrap().kind,
7678 crate::ExprKind::As { .. }
7679 ));
7680 assert!(matches!(
7681 ast.try_expr(rhs).unwrap().kind,
7682 crate::ExprKind::Number(_)
7683 ));
7684 }
7685
7686 #[test]
7687 fn parse_as_precedence_lower_than_unary() {
7688 let (ast, diags) = parse_str("!x as boolean;");
7689 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7690 let outer = expr_of_single_stmt(&ast);
7691 let (inner, _) = as_cast(outer);
7692 assert!(matches!(
7693 ast.try_expr(inner).unwrap().kind,
7694 crate::ExprKind::Unary {
7695 op: crate::UnOp::Not,
7696 ..
7697 }
7698 ));
7699 }
7700
7701 #[test]
7702 fn parse_as_left_associative_chain() {
7703 let (ast, diags) = parse_str("x as A as B;");
7704 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7705 let outer = expr_of_single_stmt(&ast);
7706 let (inner, _outer_ty) = as_cast(outer);
7707 let (innermost, _inner_ty) = as_cast(ast.try_expr(inner).unwrap());
7708 assert!(matches!(
7709 ast.try_expr(innermost).unwrap().kind,
7710 crate::ExprKind::Identifier(_)
7711 ));
7712 }
7713
7714 #[test]
7715 fn parse_as_inside_call_arg() {
7716 let (ast, diags) = parse_str("f(x as T);");
7717 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7718 let outer = expr_of_single_stmt(&ast);
7719 match &outer.kind {
7720 crate::ExprKind::Call { args, .. } => {
7721 assert_eq!(args.len(), 1);
7722 assert!(matches!(
7723 ast.try_expr(args[0]).unwrap().kind,
7724 crate::ExprKind::As { .. }
7725 ));
7726 }
7727 other => panic!("expected Call, got {other:?}"),
7728 }
7729 }
7730
7731 #[test]
7732 fn parse_as_after_field_access() {
7733 let (ast, diags) = parse_str("obj.field as T;");
7734 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7735 let outer = expr_of_single_stmt(&ast);
7736 let (inner, _) = as_cast(outer);
7737 assert!(matches!(
7738 ast.try_expr(inner).unwrap().kind,
7739 crate::ExprKind::FieldAccess { .. }
7740 ));
7741 }
7742
7743 #[test]
7744 fn parse_type_guard_return_annotation_on_function() {
7745 let src = "function isCircle(s: Shape): s is Circle { return true; }";
7746 let (ast, diags) = parse_str(src);
7747 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7748 let stmt = single_stmt(&ast);
7749 match stmt.kind {
7750 crate::StmtKind::Function {
7751 ref return_type,
7752 ref type_predicate,
7753 ..
7754 } => {
7755 assert!(
7756 return_type.is_none(),
7757 "predicate form leaves return_type empty",
7758 );
7759 let tp = type_predicate.as_ref().expect("predicate parsed");
7760 assert_eq!(tp.param.name, "s");
7761 assert!(matches!(
7762 tp.asserted.kind,
7763 crate::TypeAnnotationKind::Name { .. }
7764 ));
7765 assert_eq!(tp.span, crate::Span::new(F, 29, 40).unwrap());
7766 }
7767 _ => panic!("expected Function"),
7768 }
7769 }
7770
7771 #[test]
7772 fn parse_type_guard_return_annotation_on_arrow() {
7773 let src = "const f = (s: Shape): s is Circle => true;";
7774 let (ast, diags) = parse_str(src);
7775 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7776 let value = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
7777 crate::StmtKind::Const { value, .. } => *value,
7778 _ => panic!("expected const"),
7779 };
7780 match &ast.try_expr(value).unwrap().kind {
7781 crate::ExprKind::Arrow {
7782 return_type,
7783 type_predicate,
7784 ..
7785 } => {
7786 assert!(return_type.is_none());
7787 let tp = type_predicate.as_ref().expect("predicate parsed");
7788 assert_eq!(tp.param.name, "s");
7789 }
7790 other => panic!("expected Arrow, got {other:?}"),
7791 }
7792 }
7793
7794 #[test]
7795 fn parse_plain_return_type_unaffected_by_predicate_parser() {
7796 let src = "function f(s: Shape): boolean { return true; }";
7797 let (ast, diags) = parse_str(src);
7798 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7799 let stmt = single_stmt(&ast);
7800 match stmt.kind {
7801 crate::StmtKind::Function {
7802 ref return_type,
7803 ref type_predicate,
7804 ..
7805 } => {
7806 assert!(return_type.is_some(), "plain form has return_type");
7807 assert!(type_predicate.is_none(), "plain form has no type_predicate",);
7808 }
7809 _ => panic!("expected Function"),
7810 }
7811 }
7812
7813 #[test]
7814 fn parse_type_guard_rejects_non_return_position() {
7815 let (_, diags) = parse_str("let x: y is Foo = null;");
7816 assert!(
7817 !diags.is_empty(),
7818 "expected parse error for `is` outside return-type position",
7819 );
7820 }
7821
7822 #[test]
7823 fn parse_typeof_prefix() {
7824 let (ast, diags) = parse_str("typeof x;");
7825 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7826 let outer = expr_of_single_stmt(&ast);
7827 let crate::ExprKind::Typeof { operand } = outer.kind else {
7828 panic!("expected Typeof, got {:?}", outer.kind);
7829 };
7830 assert!(matches!(
7831 ast.try_expr(operand).unwrap().kind,
7832 crate::ExprKind::Identifier(_)
7833 ));
7834 assert_eq!(outer.span, crate::Span::new(F, 0, 8).unwrap());
7835 }
7836
7837 #[test]
7838 fn parse_typeof_in_equality() {
7839 let (ast, diags) = parse_str(r#"typeof x === "number";"#);
7840 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7841 let outer = expr_of_single_stmt(&ast);
7842 let (op, lhs, rhs) = binary(outer);
7843 assert_eq!(op, crate::BinOp::Eq);
7844 assert!(matches!(
7845 ast.try_expr(lhs).unwrap().kind,
7846 crate::ExprKind::Typeof { .. }
7847 ));
7848 assert!(matches!(
7849 ast.try_expr(rhs).unwrap().kind,
7850 crate::ExprKind::String(ref s) if s == "number"
7851 ));
7852 }
7853
7854 #[test]
7855 fn parse_let_without_type() {
7856 let (ast, diags) = parse_str("let x = 1;");
7857 assert!(diags.is_empty());
7858 let stmt = single_stmt(&ast);
7859 assert_eq!(stmt.span, crate::Span::new(F, 0, 10).unwrap());
7860 match stmt.kind {
7861 crate::StmtKind::Let {
7862 ref name,
7863 ref ty,
7864 value,
7865 ..
7866 } => {
7867 assert_eq!(name.name, "x");
7868 assert_eq!(name.span, crate::Span::new(F, 4, 5).unwrap());
7869 assert!(ty.is_none());
7870 assert_eq!(
7871 ast.try_expr(value).unwrap().kind,
7872 crate::ExprKind::Number(1.0)
7873 );
7874 }
7875 _ => panic!("expected Let"),
7876 }
7877 }
7878
7879 #[test]
7880 fn parse_const_with_type_and_string() {
7881 let (ast, diags) = parse_str(r#"const y: string = "hi";"#);
7882 assert!(diags.is_empty());
7883 let stmt = single_stmt(&ast);
7884 match stmt.kind {
7885 crate::StmtKind::Const {
7886 ref name,
7887 ref ty,
7888 value,
7889 ..
7890 } => {
7891 assert_eq!(name.name, "y");
7892 assert_eq!(name.span, crate::Span::new(F, 6, 7).unwrap());
7893 let ty = ty.as_ref().expect("expected type annotation");
7894 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7895 assert_eq!(ty.span, crate::Span::new(F, 9, 15).unwrap());
7896 assert_eq!(
7897 ast.try_expr(value).unwrap().kind,
7898 crate::ExprKind::String("hi".to_string())
7899 );
7900 }
7901 _ => panic!("expected Const"),
7902 }
7903 }
7904
7905 #[test]
7906 fn parse_let_with_type_and_binary_initializer() {
7907 let (ast, diags) = parse_str("let z: number = 1 + 2;");
7908 assert!(diags.is_empty());
7909 let stmt = single_stmt(&ast);
7910 match stmt.kind {
7911 crate::StmtKind::Let { ref ty, value, .. } => {
7912 assert!(ty.is_some());
7913 let (op, ..) = binary(ast.try_expr(value).unwrap());
7914 assert_eq!(op, crate::BinOp::Add);
7915 }
7916 _ => panic!("expected Let"),
7917 }
7918 }
7919
7920 #[test]
7921 fn void_type_annotation_is_accepted_syntactically() {
7922 let (ast, diags) = parse_str("let v: void = null;");
7923 assert!(diags.is_empty());
7924 let stmt = single_stmt(&ast);
7925 match stmt.kind {
7926 crate::StmtKind::Let { ref ty, .. } => {
7927 let ty = ty.as_ref().unwrap();
7928 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7929 assert_eq!(ty.span, crate::Span::new(F, 7, 11).unwrap());
7930 }
7931 _ => panic!("expected Let"),
7932 }
7933 }
7934
7935 #[test]
7936 fn const_missing_initializer_diagnoses() {
7937 let (ast, diags) = parse_str("const x;");
7938 assert_eq!(diags.len(), 1);
7939 assert_eq!(
7940 diags[0].message,
7941 "`const` declaration requires an initializer"
7942 );
7943 assert!(ast.top_level.is_empty());
7944 }
7945
7946 #[test]
7947 fn parse_const_object_shorthand_pattern() {
7948 let (ast, diags) = parse_str("const { a, b } = obj;");
7949 assert!(diags.is_empty(), "unexpected: {diags:?}");
7950 let stmt = single_stmt(&ast);
7951 match stmt.kind {
7952 crate::StmtKind::ConstPattern {
7953 binding:
7954 crate::Binding::Object {
7955 ref fields,
7956 ref rest,
7957 ..
7958 },
7959 ..
7960 } => {
7961 assert_eq!(fields.len(), 2);
7962 assert_eq!(fields[0].source.name, "a");
7963 assert_eq!(fields[0].local.name, "a");
7964 assert_eq!(fields[1].source.name, "b");
7965 assert_eq!(fields[1].local.name, "b");
7966 assert!(rest.is_none());
7967 }
7968 _ => panic!("expected ConstPattern Object, got {:?}", stmt.kind),
7969 }
7970 }
7971
7972 #[test]
7973 fn parse_const_object_pattern_keyword_source_key() {
7974 let (ast, diags) = parse_str("const { type: statusType, default: fallback } = obj;");
7975 assert!(diags.is_empty(), "unexpected: {diags:?}");
7976 let stmt = single_stmt(&ast);
7977 let crate::StmtKind::ConstPattern {
7978 binding: crate::Binding::Object { ref fields, .. },
7979 ..
7980 } = stmt.kind
7981 else {
7982 panic!("expected object const pattern");
7983 };
7984
7985 let names: Vec<(&str, &str)> = fields
7986 .iter()
7987 .map(|f| (f.source.name.as_str(), f.local.name.as_str()))
7988 .collect();
7989 assert_eq!(names, vec![("type", "statusType"), ("default", "fallback")]);
7990 }
7991
7992 #[test]
7993 fn parse_const_object_pattern_keyword_shorthand_rejected() {
7994 let (_ast, diags) = parse_str("const { default } = obj;");
7995 assert!(
7996 diags
7997 .iter()
7998 .any(|d| d.message == "expected `:` after keyword field name in object pattern"),
7999 "expected keyword-shorthand diagnostic, got: {diags:?}"
8000 );
8001 }
8002
8003 #[test]
8004 fn parse_const_object_renamed_pattern() {
8005 let (ast, diags) = parse_str("const { a: x, b: y } = obj;");
8006 assert!(diags.is_empty(), "unexpected: {diags:?}");
8007 match single_stmt(&ast).kind {
8008 crate::StmtKind::ConstPattern {
8009 binding: crate::Binding::Object { ref fields, .. },
8010 ..
8011 } => {
8012 assert_eq!(fields[0].source.name, "a");
8013 assert_eq!(fields[0].local.name, "x");
8014 assert_eq!(fields[1].source.name, "b");
8015 assert_eq!(fields[1].local.name, "y");
8016 }
8017 ref other => panic!("expected ConstPattern Object, got {other:?}"),
8018 }
8019 }
8020
8021 #[test]
8022 fn parse_const_object_with_rest() {
8023 let (ast, diags) = parse_str("const { a, ...rest } = obj;");
8024 assert!(diags.is_empty(), "unexpected: {diags:?}");
8025 match single_stmt(&ast).kind {
8026 crate::StmtKind::ConstPattern {
8027 binding:
8028 crate::Binding::Object {
8029 ref fields,
8030 ref rest,
8031 ..
8032 },
8033 ..
8034 } => {
8035 assert_eq!(fields.len(), 1);
8036 assert_eq!(fields[0].source.name, "a");
8037 let r = rest.as_ref().expect("expected rest");
8038 assert_eq!(r.name, "rest");
8039 }
8040 ref other => panic!("expected ConstPattern Object, got {other:?}"),
8041 }
8042 }
8043
8044 #[test]
8045 fn parse_const_array_pattern() {
8046 let (ast, diags) = parse_str("const [x, y] = arr;");
8047 assert!(diags.is_empty(), "unexpected: {diags:?}");
8048 match single_stmt(&ast).kind {
8049 crate::StmtKind::ConstPattern {
8050 binding:
8051 crate::Binding::Array {
8052 ref elems,
8053 ref rest,
8054 ..
8055 },
8056 ..
8057 } => {
8058 assert_eq!(elems.len(), 2);
8059 assert_eq!(elems[0].as_ref().unwrap().name, "x");
8060 assert_eq!(elems[1].as_ref().unwrap().name, "y");
8061 assert!(rest.is_none());
8062 }
8063 ref other => panic!("expected ConstPattern Array, got {other:?}"),
8064 }
8065 }
8066
8067 #[test]
8068 fn parse_const_array_with_hole_and_rest() {
8069 let (ast, diags) = parse_str("const [, x, ...rest] = arr;");
8070 assert!(diags.is_empty(), "unexpected: {diags:?}");
8071 match single_stmt(&ast).kind {
8072 crate::StmtKind::ConstPattern {
8073 binding:
8074 crate::Binding::Array {
8075 ref elems,
8076 ref rest,
8077 ..
8078 },
8079 ..
8080 } => {
8081 assert_eq!(elems.len(), 2);
8082 assert!(elems[0].is_none(), "first slot should be a hole");
8083 assert_eq!(elems[1].as_ref().unwrap().name, "x");
8084 assert_eq!(rest.as_ref().unwrap().name, "rest");
8085 }
8086 ref other => panic!("expected ConstPattern Array, got {other:?}"),
8087 }
8088 }
8089
8090 #[test]
8091 fn parse_let_object_pattern() {
8092 let (ast, diags) = parse_str("let { a, b } = obj;");
8093 assert!(diags.is_empty(), "unexpected: {diags:?}");
8094 assert!(matches!(
8095 single_stmt(&ast).kind,
8096 crate::StmtKind::LetPattern {
8097 binding: crate::Binding::Object { .. },
8098 ..
8099 }
8100 ));
8101 }
8102
8103 #[test]
8104 fn parse_function_param_object_pattern() {
8105 let src = "function f({ a, b }: T): void { return; }";
8106 let (ast, diags) = parse_str(src);
8107 assert!(diags.is_empty(), "unexpected: {diags:?}");
8108 match single_stmt(&ast).kind {
8109 crate::StmtKind::Function { ref params, .. } => {
8110 assert_eq!(params.len(), 1);
8111 let p = ¶ms[0];
8112 assert!(p.pattern.is_some(), "expected pattern param");
8113 assert_eq!(p.name.name, "", "pattern placeholder uses empty name");
8114 match p.pattern.as_ref().unwrap() {
8115 crate::Binding::Object { fields, .. } => {
8116 assert_eq!(fields.len(), 2);
8117 assert_eq!(fields[0].source.name, "a");
8118 }
8119 other => panic!("expected Object binding, got {other:?}"),
8120 }
8121 }
8122 ref other => panic!("expected Function, got {other:?}"),
8123 }
8124 }
8125
8126 #[test]
8127 fn parse_arrow_param_array_pattern() {
8128 let src = "const g = ([x, y]: number[]): number => x + y;";
8129 let (_ast, diags) = parse_str(src);
8130 assert!(diags.is_empty(), "unexpected: {diags:?}");
8131 }
8132
8133 #[test]
8134 fn parse_destructure_nested_rejected() {
8135 let (_ast, diags) = parse_str("const { a: { b } } = obj;");
8136 assert!(
8137 diags
8138 .iter()
8139 .any(|d| d.message.contains("nested destructuring")),
8140 "expected nested-destructuring diagnostic, got: {diags:?}",
8141 );
8142 }
8143
8144 #[test]
8145 fn parse_destructure_array_nested_rejected() {
8146 let (_ast, diags) = parse_str("const [[a]] = arr;");
8147 assert!(
8148 diags
8149 .iter()
8150 .any(|d| d.message.contains("nested destructuring")),
8151 "expected nested-destructuring diagnostic, got: {diags:?}",
8152 );
8153 }
8154
8155 #[test]
8156 fn parse_destructure_pattern_defaults() {
8157 let (ast, diags) = parse_str("const { a = 1, b: c = 2 } = obj;");
8158 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8159 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
8160 panic!("expected pattern")
8161 };
8162 let crate::Binding::Object { fields, .. } = binding else {
8163 panic!("expected object")
8164 };
8165 assert_eq!(fields.len(), 2);
8166 assert!(fields.iter().all(|field| field.default.is_some()));
8167 assert_eq!(fields[1].local.name, "c");
8168 }
8169
8170 #[test]
8171 fn parse_destructure_param_default() {
8172 let (ast, diags) = parse_str("function f({ a = 1 }: T = obj): void { return; }");
8173 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8174 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
8175 panic!("expected function")
8176 };
8177 assert!(params[0].default.is_some());
8178 assert!(params[0].pattern.is_some());
8179 }
8180
8181 #[test]
8182 fn parse_destructure_rest_not_last_rejected() {
8183 let (_ast, diags) = parse_str("const { ...rest, a } = obj;");
8184 assert!(
8185 diags
8186 .iter()
8187 .any(|d| d.message.contains("rest element must be the last element")),
8188 "expected rest-not-last diagnostic, got: {diags:?}",
8189 );
8190 }
8191
8192 #[test]
8193 fn parse_destructure_empty_object_rejected() {
8194 let (_ast, diags) = parse_str("const { } = obj;");
8195 assert!(
8196 diags
8197 .iter()
8198 .any(|d| d.message.contains("empty object destructuring pattern")),
8199 "expected empty-pattern diagnostic, got: {diags:?}",
8200 );
8201 }
8202
8203 #[test]
8204 fn parse_ternary_simple() {
8205 let (ast, diags) = parse_str("const x: number = a ? 1 : 2;");
8206 assert!(diags.is_empty(), "unexpected: {diags:?}");
8207 let stmt = single_stmt(&ast);
8208 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8209 panic!("expected Const, got {:?}", stmt.kind);
8210 };
8211 assert!(matches!(
8212 ast.try_expr(value).unwrap().kind,
8213 crate::ExprKind::Ternary { .. }
8214 ));
8215 }
8216
8217 #[test]
8218 fn parse_ternary_nested_right_assoc() {
8219 let (ast, diags) = parse_str("const x: number = a ? 1 : b ? 2 : 3;");
8220 assert!(diags.is_empty(), "unexpected: {diags:?}");
8221 let stmt = single_stmt(&ast);
8222 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8223 panic!("expected Const");
8224 };
8225 let crate::ExprKind::Ternary { else_, .. } = ast.try_expr(value).unwrap().kind else {
8226 panic!("expected outer Ternary");
8227 };
8228 assert!(matches!(
8229 ast.try_expr(else_).unwrap().kind,
8230 crate::ExprKind::Ternary { .. }
8231 ));
8232 }
8233
8234 #[test]
8235 fn parse_ternary_missing_colon_diagnoses() {
8236 let (_, diags) = parse_str("const x: number = a ? 1;");
8237 assert!(
8238 diags
8239 .iter()
8240 .any(|d| d.message.contains("expected `:` to complete ternary")),
8241 "expected colon diagnostic, got: {diags:?}",
8242 );
8243 }
8244
8245 #[test]
8246 fn parse_nullish_coalesce_binop() {
8247 let (ast, diags) = parse_str("const x: number = a ?? 1;");
8248 assert!(diags.is_empty(), "unexpected: {diags:?}");
8249 let stmt = single_stmt(&ast);
8250 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8251 panic!("expected Const");
8252 };
8253 match ast.try_expr(value).unwrap().kind {
8254 crate::ExprKind::Binary { op, .. } => {
8255 assert_eq!(op, crate::BinOp::NullishCoalesce);
8256 }
8257 ref other => panic!("expected Binary NullishCoalesce, got {other:?}"),
8258 }
8259 }
8260
8261 #[test]
8262 fn parse_nullish_left_associative() {
8263 let (ast, diags) = parse_str("const x: number = a ?? b ?? c;");
8264 assert!(diags.is_empty(), "unexpected: {diags:?}");
8265 let stmt = single_stmt(&ast);
8266 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8267 panic!("expected Const");
8268 };
8269 let crate::ExprKind::Binary { lhs, .. } = ast.try_expr(value).unwrap().kind else {
8270 panic!("expected outer Binary");
8271 };
8272 match ast.try_expr(lhs).unwrap().kind {
8273 crate::ExprKind::Binary { op, .. } => {
8274 assert_eq!(op, crate::BinOp::NullishCoalesce);
8275 }
8276 ref other => panic!("expected inner Binary, got {other:?}"),
8277 }
8278 }
8279
8280 #[test]
8281 fn parse_pow_is_right_associative() {
8282 let (ast, diags) = parse_str("const x: number = 2 ** 3 ** 2;");
8283 assert!(diags.is_empty(), "unexpected: {diags:?}");
8284 let stmt = single_stmt(&ast);
8285 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8286 panic!("expected Const");
8287 };
8288 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8289 panic!("expected outer Binary");
8290 };
8291 assert_eq!(op, crate::BinOp::Pow);
8292 match ast.try_expr(rhs).unwrap().kind {
8293 crate::ExprKind::Binary { op: inner_op, .. } => {
8294 assert_eq!(inner_op, crate::BinOp::Pow);
8295 }
8296 ref other => panic!("expected inner Pow Binary on RHS, got {other:?}"),
8297 }
8298 }
8299
8300 #[test]
8301 fn parse_pow_higher_than_multiply() {
8302 let (ast, diags) = parse_str("const x: number = 2 * 3 ** 2;");
8303 assert!(diags.is_empty(), "unexpected: {diags:?}");
8304 let stmt = single_stmt(&ast);
8305 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8306 panic!("expected Const");
8307 };
8308 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8309 panic!("expected outer Binary");
8310 };
8311 assert_eq!(op, crate::BinOp::Mul);
8312 match ast.try_expr(rhs).unwrap().kind {
8313 crate::ExprKind::Binary { op: inner_op, .. } => {
8314 assert_eq!(inner_op, crate::BinOp::Pow);
8315 }
8316 ref other => panic!("expected Pow on the RHS of *, got {other:?}"),
8317 }
8318 }
8319
8320 #[test]
8321 fn parse_compound_assign_ident() {
8322 let (ast, diags) = parse_str("function main(): void { let x: number = 0; x += 5; }");
8323 assert!(diags.is_empty(), "unexpected: {diags:?}");
8324 let func = ast.top_level.iter().find_map(|sid| {
8325 if let crate::StmtKind::Function { body, .. } = &ast.try_stmt(*sid).unwrap().kind {
8326 Some(*body)
8327 } else {
8328 None
8329 }
8330 });
8331 let crate::StmtKind::Block(stmts) = &ast.try_stmt(func.expect("main")).unwrap().kind else {
8332 panic!("expected function body to be a block");
8333 };
8334 let target = stmts
8335 .iter()
8336 .find_map(|sid| match &ast.try_stmt(*sid).unwrap().kind {
8337 crate::StmtKind::CompoundAssign { target, op, .. } => {
8338 Some((target.name.clone(), *op))
8339 }
8340 _ => None,
8341 });
8342 let (name, op) = target.expect("expected a CompoundAssign in the body");
8343 assert_eq!(name, "x");
8344 assert_eq!(op, crate::BinOp::Add);
8345 }
8346
8347 #[test]
8348 fn parse_nullish_mixed_with_or_rejected() {
8349 let (_, diags) = parse_str("const x: number = a || b ?? c;");
8350 assert!(
8351 diags
8352 .iter()
8353 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8354 "expected mixing diagnostic, got: {diags:?}",
8355 );
8356 }
8357
8358 #[test]
8359 fn parse_nullish_mixed_with_and_rejected() {
8360 let (_, diags) = parse_str("const x: number = a && b ?? c;");
8361 assert!(
8362 diags
8363 .iter()
8364 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8365 "expected mixing diagnostic, got: {diags:?}",
8366 );
8367 }
8368
8369 #[test]
8370 fn parse_nullish_followed_by_logical_rejected() {
8371 for (source, help) in [
8372 (
8373 "const x: number = a ?? b || c;",
8374 "`(a ?? b) || c` or `a ?? (b || c)`",
8375 ),
8376 (
8377 "const x: number = a ?? b && c;",
8378 "`(a ?? b) && c` or `a ?? (b && c)`",
8379 ),
8380 (
8381 "const x: number = a ?? b ?? c || d;",
8382 "`(a ?? b) || c` or `a ?? (b || c)`",
8383 ),
8384 (
8386 "const x: number = a || b ?? c || d;",
8387 "`(a || b) ?? c` or `a || (b ?? c)`",
8388 ),
8389 ] {
8390 let (_, diags) = parse_str(source);
8391 assert_eq!(diags.len(), 1, "diags for {source:?}: {diags:?}");
8392 assert!(
8393 diags[0].message.contains("mixing `??` with `||` / `&&`"),
8394 "message for {source:?}: {diags:?}",
8395 );
8396 assert!(
8397 diags[0].help.iter().any(|h| h.contains(help)),
8398 "help for {source:?}: {diags:?}",
8399 );
8400 }
8401 }
8402
8403 #[test]
8404 fn parse_nullish_operand_keeps_tighter_operators() {
8405 let (ast, diags) = parse_str("const x: boolean = a ?? b == c;");
8407 assert!(diags.is_empty(), "unexpected: {diags:?}");
8408 let stmt = single_stmt(&ast);
8409 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8410 panic!("expected Const");
8411 };
8412 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8413 panic!("expected outer Binary");
8414 };
8415 assert_eq!(op, crate::BinOp::NullishCoalesce);
8416 assert!(matches!(
8417 ast.try_expr(rhs).unwrap().kind,
8418 crate::ExprKind::Binary {
8419 op: crate::BinOp::Eq,
8420 ..
8421 }
8422 ));
8423 }
8424
8425 #[test]
8426 fn parse_nullish_with_parens_ok() {
8427 let (_, diags) = parse_str("const x: number = (a || b) ?? c;");
8428 assert!(diags.is_empty(), "unexpected: {diags:?}");
8429 }
8430
8431 #[test]
8432 fn parse_optional_field_chain() {
8433 let (ast, diags) = parse_str("const x: number = a?.b;");
8434 assert!(diags.is_empty(), "unexpected: {diags:?}");
8435 let stmt = single_stmt(&ast);
8436 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8437 panic!("expected Const");
8438 };
8439 match ast.try_expr(value).unwrap().kind {
8440 crate::ExprKind::OptionalChain { ref parts, .. } => {
8441 assert_eq!(parts.len(), 1);
8442 match &parts[0] {
8443 crate::ChainPart::Field { name, optional, .. } => {
8444 assert_eq!(name.name, "b");
8445 assert!(*optional);
8446 }
8447 other => panic!("expected Field, got {other:?}"),
8448 }
8449 }
8450 ref other => panic!("expected OptionalChain, got {other:?}"),
8451 }
8452 }
8453
8454 #[test]
8455 fn parse_optional_chain_continues_after_first_dot() {
8456 let (ast, diags) = parse_str("const x: number = a?.b.c;");
8457 assert!(diags.is_empty(), "unexpected: {diags:?}");
8458 let stmt = single_stmt(&ast);
8459 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8460 panic!("expected Const");
8461 };
8462 match ast.try_expr(value).unwrap().kind {
8463 crate::ExprKind::OptionalChain { ref parts, .. } => {
8464 assert_eq!(parts.len(), 2);
8465 if let crate::ChainPart::Field { optional, .. } = &parts[0] {
8466 assert!(*optional);
8467 }
8468 if let crate::ChainPart::Field { optional, .. } = &parts[1] {
8469 assert!(!*optional);
8470 }
8471 }
8472 ref other => panic!("expected OptionalChain, got {other:?}"),
8473 }
8474 }
8475
8476 #[test]
8477 fn parse_optional_chain_multiple_short_circuits() {
8478 let (ast, diags) = parse_str("const x: number = a?.b?.c;");
8479 assert!(diags.is_empty(), "unexpected: {diags:?}");
8480 let stmt = single_stmt(&ast);
8481 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8482 panic!("expected Const");
8483 };
8484 let crate::ExprKind::OptionalChain { parts, .. } = &ast.try_expr(value).unwrap().kind
8485 else {
8486 panic!("expected OptionalChain");
8487 };
8488 assert_eq!(parts.len(), 2);
8489 for p in parts {
8490 if let crate::ChainPart::Field { optional, .. } = p {
8491 assert!(*optional, "both parts should be optional");
8492 }
8493 }
8494 }
8495
8496 #[test]
8497 fn parse_optional_call_and_index() {
8498 let (_, diags) = parse_str("const x: number = f?.();");
8499 assert!(diags.is_empty(), "unexpected: {diags:?}");
8500 let (_, diags) = parse_str("const y: number = arr?.[0];");
8501 assert!(diags.is_empty(), "unexpected: {diags:?}");
8502 }
8503
8504 #[test]
8505 fn parse_optional_chain_terminates_at_questionmark_for_ternary() {
8506 let (ast, diags) = parse_str("const x: number = a?.b ? c : d;");
8507 assert!(diags.is_empty(), "unexpected: {diags:?}");
8508 let stmt = single_stmt(&ast);
8509 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8510 panic!("expected Const");
8511 };
8512 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8513 panic!(
8514 "expected top-level Ternary, got {:?}",
8515 ast.try_expr(value).unwrap().kind
8516 );
8517 };
8518 assert!(matches!(
8519 ast.try_expr(cond).unwrap().kind,
8520 crate::ExprKind::OptionalChain { .. }
8521 ));
8522 }
8523
8524 #[test]
8525 fn parse_nullish_below_ternary() {
8526 let (ast, diags) = parse_str("const x: number = a ?? b ? c : d;");
8527 assert!(diags.is_empty(), "unexpected: {diags:?}");
8528 let stmt = single_stmt(&ast);
8529 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8530 panic!("expected Const");
8531 };
8532 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8533 panic!("expected top-level Ternary");
8534 };
8535 match ast.try_expr(cond).unwrap().kind {
8536 crate::ExprKind::Binary { op, .. } => {
8537 assert_eq!(op, crate::BinOp::NullishCoalesce);
8538 }
8539 ref other => panic!("expected Binary ??, got {other:?}"),
8540 }
8541 }
8542
8543 #[test]
8544 fn let_missing_initializer_diagnoses() {
8545 let (ast, diags) = parse_str("let x;");
8546 assert_eq!(diags.len(), 1);
8547 assert_eq!(
8548 diags[0].message,
8549 "`let` declaration requires an initializer"
8550 );
8551 assert!(ast.top_level.is_empty());
8552 }
8553
8554 #[test]
8555 fn missing_identifier_after_let_diagnoses() {
8556 let (ast, diags) = parse_str("let = 1;");
8557 assert_eq!(diags.len(), 1);
8558 assert_eq!(diags[0].message, "expected identifier after `let`/`const`");
8559 assert!(ast.top_level.is_empty());
8560 }
8561
8562 #[test]
8563 fn missing_type_after_colon_diagnoses() {
8564 let (ast, diags) = parse_str("let x: = 1;");
8565 assert_eq!(diags.len(), 1);
8566 assert_eq!(diags[0].message, "expected type");
8567 assert!(ast.top_level.is_empty());
8568 }
8569
8570 #[test]
8571 fn missing_semicolon_after_declaration_diagnoses() {
8572 let (ast, diags) = parse_str("let x = 1 y;");
8573 assert_eq!(diags.len(), 1);
8574 assert_eq!(diags[0].message, "expected `;` after declaration");
8575 assert!(ast.top_level.is_empty());
8576 }
8577
8578 #[test]
8579 fn expression_statements_still_parse() {
8580 let (ast, diags) = parse_str("42;");
8581 assert!(diags.is_empty());
8582 assert_eq!(ast.top_level.len(), 1);
8583 }
8584
8585 #[test]
8586 fn snapshot_mixed_declarations() {
8587 let (ast, diags) =
8588 parse_str(r#"let x = 1; const y: string = "hi"; let z: number = 1 + 2;"#);
8589 assert!(diags.is_empty());
8590 assert_eq!(ast.top_level.len(), 3);
8591 insta::assert_debug_snapshot!(ast);
8592 }
8593
8594 #[test]
8595 fn parse_function_typical() {
8596 let (ast, diags) = parse_str("function f(a: number, b: string): boolean { }");
8597 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8598 let stmt = single_stmt(&ast);
8599 match stmt.kind {
8600 crate::StmtKind::Function {
8601 ref name,
8602 ref generics,
8603 ref params,
8604 ref return_type,
8605 body,
8606 ..
8607 } => {
8608 assert_eq!(name.name, "f");
8609 assert_eq!(name.span, crate::Span::new(F, 9, 10).unwrap());
8610 assert!(generics.is_empty());
8611 assert_eq!(params.len(), 2);
8612 assert_eq!(params[0].name.name, "a");
8613 assert_eq!(params[0].name.span, crate::Span::new(F, 11, 12).unwrap());
8614 let p0_ty = params[0]
8615 .ty
8616 .as_ref()
8617 .expect("function-decl param requires annotation");
8618 assert!(matches!(p0_ty.kind, crate::TypeAnnotationKind::Name { .. }));
8619 assert_eq!(p0_ty.span, crate::Span::new(F, 14, 20).unwrap()); assert_eq!(params[1].name.name, "b");
8621 assert_eq!(params[1].name.span, crate::Span::new(F, 22, 23).unwrap());
8622 let p1_ty = params[1]
8623 .ty
8624 .as_ref()
8625 .expect("function-decl param requires annotation");
8626 assert_eq!(p1_ty.span, crate::Span::new(F, 25, 31).unwrap()); let return_type = return_type.as_ref().expect("plain return type");
8628 assert!(matches!(
8629 return_type.kind,
8630 crate::TypeAnnotationKind::Name { .. }
8631 ));
8632 assert_eq!(return_type.span, crate::Span::new(F, 34, 41).unwrap()); let block = ast.try_stmt(body).unwrap();
8634 assert!(matches!(block.kind, crate::StmtKind::Block(ref v) if v.is_empty()));
8635 }
8636 _ => panic!("expected Function"),
8637 }
8638 }
8639
8640 #[test]
8641 fn parse_function_with_doc_comment() {
8642 let (ast, diags) = parse_str(
8643 "/**\n * Sum two numbers.\n * @param a First.\n * @param b Second.\n */\nfunction add(a: number, b: number): number { return a + b; }",
8644 );
8645 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8646 let stmt = single_stmt(&ast);
8647 match &stmt.kind {
8648 crate::StmtKind::Function { doc, .. } => {
8649 let d = doc.as_ref().expect("doc attached");
8650 assert_eq!(d.summary, "Sum two numbers.");
8651 assert_eq!(d.params.len(), 2);
8652 assert_eq!(d.params[0].name, "a");
8653 assert_eq!(d.params[1].name, "b");
8654 }
8655 _ => panic!("expected Function"),
8656 }
8657 }
8658
8659 #[test]
8660 fn parse_interface_member_docs() {
8661 let (ast, diags) = parse_str(
8662 "interface Foo {\n /** First method. */ first(): void;\n /** A field. */ field: number;\n}",
8663 );
8664 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8665 let stmt = single_stmt(&ast);
8666 match &stmt.kind {
8667 crate::StmtKind::InterfaceDecl { members, .. } => {
8668 assert_eq!(members.len(), 2);
8669 match &members[0] {
8670 crate::InterfaceMember::Method { doc, .. } => {
8671 assert_eq!(doc.as_ref().unwrap().summary, "First method.");
8672 }
8673 _ => panic!("expected Method first"),
8674 }
8675 match &members[1] {
8676 crate::InterfaceMember::Property { doc, .. } => {
8677 assert_eq!(doc.as_ref().unwrap().summary, "A field.");
8678 }
8679 _ => panic!("expected Property second"),
8680 }
8681 }
8682 _ => panic!("expected InterfaceDecl"),
8683 }
8684 }
8685
8686 #[test]
8687 fn parse_numeric_enum_all_implicit() {
8688 let (ast, diags) = parse_str("enum Direction { Up, Down, Left, Right }");
8689 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8690 let stmt = single_stmt(&ast);
8691 match &stmt.kind {
8692 crate::StmtKind::EnumDecl { name, members, doc } => {
8693 assert_eq!(name.name, "Direction");
8694 assert!(doc.is_none());
8695 assert_eq!(members.len(), 4);
8696 for (m, expected) in members.iter().zip(["Up", "Down", "Left", "Right"]) {
8697 assert_eq!(m.name.name, expected);
8698 assert!(m.value.is_none(), "expected no initializer on {expected}");
8699 }
8700 }
8701 _ => panic!("expected EnumDecl"),
8702 }
8703 }
8704
8705 #[test]
8706 fn parse_string_enum_all_explicit() {
8707 let (ast, diags) =
8708 parse_str("enum Status { Active = \"active\", Inactive = \"inactive\" }");
8709 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8710 let stmt = single_stmt(&ast);
8711 match &stmt.kind {
8712 crate::StmtKind::EnumDecl { name, members, .. } => {
8713 assert_eq!(name.name, "Status");
8714 assert_eq!(members.len(), 2);
8715 match &members[0].value {
8716 Some(crate::EnumInitializer::String { value, .. }) => {
8717 assert_eq!(value, "active");
8718 }
8719 other => panic!("expected String init, got {other:?}"),
8720 }
8721 match &members[1].value {
8722 Some(crate::EnumInitializer::String { value, .. }) => {
8723 assert_eq!(value, "inactive");
8724 }
8725 other => panic!("expected String init, got {other:?}"),
8726 }
8727 }
8728 _ => panic!("expected EnumDecl"),
8729 }
8730 }
8731
8732 #[test]
8733 fn parse_numeric_enum_with_negative_init() {
8734 let (ast, diags) = parse_str("enum D { Up = 1, Down = -1 }");
8735 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8736 let stmt = single_stmt(&ast);
8737 match &stmt.kind {
8738 crate::StmtKind::EnumDecl { members, .. } => {
8739 assert_eq!(members.len(), 2);
8740 match &members[0].value {
8741 Some(crate::EnumInitializer::Number { value, .. }) => {
8742 assert_eq!(*value, 1.0);
8743 }
8744 other => panic!("expected Number init, got {other:?}"),
8745 }
8746 match &members[1].value {
8747 Some(crate::EnumInitializer::Number { value, .. }) => {
8748 assert_eq!(*value, -1.0);
8749 }
8750 other => panic!("expected Number init, got {other:?}"),
8751 }
8752 }
8753 _ => panic!("expected EnumDecl"),
8754 }
8755 }
8756
8757 #[test]
8758 fn parse_enum_mixed_kinds_is_parse_error() {
8759 let (_ast, diags) = parse_str("enum Mix { A = 1, B = \"two\" }");
8760 assert!(
8761 diags
8762 .iter()
8763 .any(|d| d.message.contains("mixed numeric and string")),
8764 "expected mixed-kind diagnostic, got: {diags:?}"
8765 );
8766 }
8767
8768 #[test]
8769 fn parse_enum_member_docs() {
8770 let (ast, diags) = parse_str(
8771 "enum D {\n /** The first one. */ A = 1,\n /** The second one. */ B = 2,\n}",
8772 );
8773 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8774 let stmt = single_stmt(&ast);
8775 match &stmt.kind {
8776 crate::StmtKind::EnumDecl { members, .. } => {
8777 assert_eq!(members.len(), 2);
8778 assert_eq!(
8779 members[0].doc.as_ref().expect("first doc").summary,
8780 "The first one."
8781 );
8782 assert_eq!(
8783 members[1].doc.as_ref().expect("second doc").summary,
8784 "The second one."
8785 );
8786 }
8787 _ => panic!("expected EnumDecl"),
8788 }
8789 }
8790
8791 #[test]
8792 fn parse_enum_trailing_comma_ok() {
8793 let (ast, diags) = parse_str("enum D { A, B, }");
8794 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8795 let stmt = single_stmt(&ast);
8796 match &stmt.kind {
8797 crate::StmtKind::EnumDecl { members, .. } => assert_eq!(members.len(), 2),
8798 _ => panic!("expected EnumDecl"),
8799 }
8800 }
8801
8802 #[test]
8803 fn parse_function_empty_params() {
8804 let (ast, diags) = parse_str("function noop(): void { }");
8805 assert!(diags.is_empty());
8806 let stmt = single_stmt(&ast);
8807 match stmt.kind {
8808 crate::StmtKind::Function { ref params, .. } => assert!(params.is_empty()),
8809 _ => panic!("expected Function"),
8810 }
8811 }
8812
8813 #[test]
8814 fn parse_generic_function_single_param() {
8815 let (ast, diags) = parse_str("function identity<T>(x: T): T { return x; }");
8816 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8817 let stmt = single_stmt(&ast);
8818 match stmt.kind {
8819 crate::StmtKind::Function {
8820 ref name,
8821 ref generics,
8822 ref params,
8823 ..
8824 } => {
8825 assert_eq!(name.name, "identity");
8826 assert_eq!(generics.len(), 1);
8827 assert_eq!(generics[0].name, "T");
8828 assert_eq!(params.len(), 1);
8829 assert_eq!(params[0].name.name, "x");
8830 }
8831 _ => panic!("expected Function"),
8832 }
8833 }
8834
8835 #[test]
8836 fn parse_generic_function_multiple_params() {
8837 let (ast, diags) = parse_str("function pair<T, U>(a: T, b: U): T { return a; }");
8838 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8839 let stmt = single_stmt(&ast);
8840 match stmt.kind {
8841 crate::StmtKind::Function { ref generics, .. } => {
8842 assert_eq!(generics.len(), 2);
8843 assert_eq!(generics[0].name, "T");
8844 assert_eq!(generics[1].name, "U");
8845 }
8846 _ => panic!("expected Function"),
8847 }
8848 }
8849
8850 #[test]
8851 fn parse_non_generic_function_has_empty_generics() {
8852 let (ast, diags) = parse_str("function noop(): void { }");
8853 assert!(diags.is_empty());
8854 let stmt = single_stmt(&ast);
8855 match stmt.kind {
8856 crate::StmtKind::Function { ref generics, .. } => assert!(generics.is_empty()),
8857 _ => panic!("expected Function"),
8858 }
8859 }
8860
8861 #[test]
8862 fn parse_empty_generic_list_diagnoses() {
8863 let (_ast, diags) = parse_str("function f<>(): void { }");
8864 assert!(
8865 diags
8866 .iter()
8867 .any(|d| d.message.contains("empty generic parameter list")),
8868 "expected empty-generic-list diagnostic, got: {diags:?}",
8869 );
8870 }
8871
8872 #[test]
8873 fn parse_trailing_comma_in_generic_list_ok() {
8874 let (ast, diags) = parse_str("function f<T,>(): void { }");
8875 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8876 match single_stmt(&ast).kind {
8877 crate::StmtKind::Function { ref generics, .. } => assert_eq!(generics.len(), 1),
8878 _ => panic!("expected Function"),
8879 }
8880 }
8881
8882 #[test]
8883 fn parse_generic_call_single_type_arg() {
8884 let (ast, diags) = parse_str("identity<number>(42);");
8885 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8886 let stmt = single_stmt(&ast);
8887 let crate::StmtKind::Expr(call_id) = stmt.kind else {
8888 panic!("expected Expr stmt");
8889 };
8890 match &ast.try_expr(call_id).unwrap().kind {
8891 crate::ExprKind::Call {
8892 type_args, args, ..
8893 } => {
8894 let ta = type_args.as_ref().expect("type_args set for generic call");
8895 assert_eq!(ta.len(), 1);
8896 assert_eq!(args.len(), 1);
8897 }
8898 other => panic!("expected Call, got {other:?}"),
8899 }
8900 }
8901
8902 #[test]
8903 fn parse_generic_call_trailing_comma_ok() {
8904 let (ast, diags) = parse_str("identity<number,>(42);");
8905 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8906 let stmt = single_stmt(&ast);
8907 let crate::StmtKind::Expr(call_id) = stmt.kind else {
8908 panic!("expected Expr stmt");
8909 };
8910 match &ast.try_expr(call_id).unwrap().kind {
8911 crate::ExprKind::Call {
8912 type_args, args, ..
8913 } => {
8914 assert_eq!(type_args.as_ref().expect("type_args set").len(), 1);
8915 assert_eq!(args.len(), 1);
8916 }
8917 other => panic!("expected Call, got {other:?}"),
8918 }
8919 }
8920
8921 #[test]
8922 fn parse_generic_call_multi_type_args() {
8923 let (ast, diags) = parse_str("pair<number, string>(1, \"x\");");
8924 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8925 let stmt = single_stmt(&ast);
8926 let crate::StmtKind::Expr(call_id) = stmt.kind else {
8927 panic!("expected Expr stmt");
8928 };
8929 match &ast.try_expr(call_id).unwrap().kind {
8930 crate::ExprKind::Call { type_args, .. } => {
8931 let ta = type_args.as_ref().expect("type_args set for generic call");
8932 assert_eq!(ta.len(), 2);
8933 }
8934 other => panic!("expected Call, got {other:?}"),
8935 }
8936 }
8937
8938 #[test]
8939 fn parse_comparison_chain_unaffected_by_generic_lookahead() {
8940 let (ast, diags) = parse_str("let z = a < b > c;");
8941 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8942 assert!(
8943 !ast_contains_call_with_type_args(&ast),
8944 "should not have parsed any generic call",
8945 );
8946 }
8947
8948 #[test]
8949 fn parse_lt_with_paren_rhs_is_comparison_not_generic() {
8950 let (ast, diags) = parse_str("let z = a < (b);");
8951 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8952 assert!(!ast_contains_call_with_type_args(&ast));
8953 }
8954
8955 #[test]
8956 fn parse_generic_call_no_args() {
8957 let (ast, diags) = parse_str("none<number>();");
8958 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8959 let stmt = single_stmt(&ast);
8960 let crate::StmtKind::Expr(call_id) = stmt.kind else {
8961 panic!("expected Expr stmt");
8962 };
8963 match &ast.try_expr(call_id).unwrap().kind {
8964 crate::ExprKind::Call {
8965 type_args, args, ..
8966 } => {
8967 assert_eq!(type_args.as_ref().unwrap().len(), 1);
8968 assert!(args.is_empty());
8969 }
8970 other => panic!("expected Call, got {other:?}"),
8971 }
8972 }
8973
8974 fn ast_contains_call_with_type_args(ast: &crate::Ast) -> bool {
8975 for id in 0..ast.top_level.len() {
8976 let stmt = ast.try_stmt(ast.top_level[id]).unwrap();
8977 if walk_stmt_for_typed_call(ast, stmt) {
8978 return true;
8979 }
8980 }
8981 false
8982 }
8983
8984 fn walk_stmt_for_typed_call(ast: &crate::Ast, stmt: &crate::Stmt) -> bool {
8985 match &stmt.kind {
8986 crate::StmtKind::Let { value, .. } | crate::StmtKind::Const { value, .. } => {
8987 walk_expr_for_typed_call(ast, *value)
8988 }
8989 crate::StmtKind::Expr(e) => walk_expr_for_typed_call(ast, *e),
8990 crate::StmtKind::Block(stmts) => stmts
8991 .iter()
8992 .any(|sid| walk_stmt_for_typed_call(ast, ast.try_stmt(*sid).unwrap())),
8993 _ => false,
8994 }
8995 }
8996
8997 fn walk_expr_for_typed_call(ast: &crate::Ast, id: crate::ExprId) -> bool {
8998 match &ast.try_expr(id).unwrap().kind {
8999 crate::ExprKind::Call {
9000 type_args: Some(_), ..
9001 } => true,
9002 crate::ExprKind::Call { callee, args, .. } => {
9003 walk_expr_for_typed_call(ast, *callee)
9004 || args.iter().any(|a| walk_expr_for_typed_call(ast, *a))
9005 }
9006 crate::ExprKind::Binary { lhs, rhs, .. } => {
9007 walk_expr_for_typed_call(ast, *lhs) || walk_expr_for_typed_call(ast, *rhs)
9008 }
9009 crate::ExprKind::Unary { operand, .. } => walk_expr_for_typed_call(ast, *operand),
9010 crate::ExprKind::Paren(e) => walk_expr_for_typed_call(ast, *e),
9011 crate::ExprKind::FieldAccess { receiver, .. } => {
9012 walk_expr_for_typed_call(ast, *receiver)
9013 }
9014 crate::ExprKind::IndexAccess { receiver, index } => {
9015 walk_expr_for_typed_call(ast, *receiver) || walk_expr_for_typed_call(ast, *index)
9016 }
9017 _ => false,
9018 }
9019 }
9020
9021 #[test]
9022 fn parse_function_body_with_statements() {
9023 let (ast, diags) = parse_str("function g(x: number): number { let y = x; y; }");
9024 assert!(diags.is_empty());
9025 let stmt = single_stmt(&ast);
9026 match stmt.kind {
9027 crate::StmtKind::Function { body, .. } => {
9028 let block = ast.try_stmt(body).unwrap();
9029 match block.kind {
9030 crate::StmtKind::Block(ref v) => assert_eq!(v.len(), 2),
9031 _ => panic!("expected Block"),
9032 }
9033 }
9034 _ => panic!("expected Function"),
9035 }
9036 }
9037
9038 #[test]
9039 fn function_missing_return_type_diagnoses() {
9040 let (ast, diags) = parse_str("function f() { }");
9041 assert!(!diags.is_empty());
9042 assert_eq!(diags[0].message, "expected `:` and return type");
9043 assert!(ast.top_level.is_empty());
9044 }
9045
9046 #[test]
9047 fn function_missing_param_type_diagnoses() {
9048 let (ast, diags) = parse_str("function f(a): void { }");
9049 assert!(!diags.is_empty());
9050 assert_eq!(diags[0].message, "parameter requires a type annotation");
9051 assert!(ast.top_level.is_empty());
9052 }
9053
9054 #[test]
9055 fn function_missing_name_diagnoses() {
9056 let (ast, diags) = parse_str("function (x: number): void { }");
9057 assert!(!diags.is_empty());
9058 assert_eq!(diags[0].message, "expected function name");
9059 assert!(ast.top_level.is_empty());
9060 }
9061
9062 #[test]
9063 fn function_trailing_param_comma_ok() {
9064 let (ast, diags) = parse_str("function f(a: number,): void { }");
9065 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9066 match single_stmt(&ast).kind {
9067 crate::StmtKind::Function { ref params, .. } => assert_eq!(params.len(), 1),
9068 _ => panic!("expected Function"),
9069 }
9070 }
9071
9072 #[test]
9073 fn function_missing_open_brace_diagnoses() {
9074 let (ast, diags) = parse_str("function f(): void");
9075 assert_eq!(diags.len(), 1);
9076 assert_eq!(diags[0].message, "expected `{`");
9077 assert!(ast.top_level.is_empty());
9078 }
9079
9080 #[test]
9081 fn snapshot_function_declaration() {
9082 let (ast, diags) =
9083 parse_str("function add(a: number, b: number): number { let sum = a + b; sum; }");
9084 assert!(diags.is_empty());
9085 insta::assert_debug_snapshot!(ast);
9086 }
9087
9088 #[test]
9089 fn rest_param_basic_parses() {
9090 let (_, diags) = parse_str("function sum(...nums: number[]): number { return 0; }");
9091 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9092 }
9093
9094 #[test]
9095 fn rest_param_with_fixed_prefix_parses() {
9096 let (_, diags) =
9097 parse_str("function tag(label: string, ...vals: number[]): string { return label; }");
9098 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9099 }
9100
9101 #[test]
9102 fn rest_param_in_arrow_parses() {
9103 let (_, diags) = parse_str("const f = (...n: number[]) => n.length;");
9104 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9105 }
9106
9107 #[test]
9108 fn rest_param_in_function_type_annotation_parses() {
9109 let (_, diags) =
9110 parse_str("let f: (a: number, ...rest: string[]) => void = (a, ...r) => {};");
9111 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9112 }
9113
9114 #[test]
9115 fn rest_param_not_last_rejected() {
9116 let (_, diags) = parse_str("function f(...xs: number[], y: number): void {}");
9117 assert!(
9118 diags.iter().any(|d| d
9119 .message
9120 .contains("rest parameter must be the last parameter")),
9121 "expected 'must be last' diagnostic, got {diags:?}",
9122 );
9123 }
9124
9125 #[test]
9126 fn rest_param_with_default_rejected() {
9127 let (_, diags) = parse_str("function f(...xs: number[] = []): void {}");
9128 assert!(
9129 diags.iter().any(|d| d
9130 .message
9131 .contains("rest parameter cannot have a default value")),
9132 "expected default-rejection diagnostic, got {diags:?}",
9133 );
9134 }
9135
9136 #[test]
9137 fn rest_param_without_annotation_rejected() {
9138 let (_, diags) = parse_str("function f(...xs): void {}");
9139 assert!(
9140 diags.iter().any(|d| d
9141 .message
9142 .contains("rest parameter requires a type annotation")),
9143 "expected missing-annotation diagnostic, got {diags:?}",
9144 );
9145 }
9146
9147 #[test]
9148 fn rest_param_destructured_rejected() {
9149 let (_, diags) = parse_str("function f(...{a, b}): void {}");
9150 assert!(
9151 diags
9152 .iter()
9153 .any(|d| d.message.contains("rest parameter cannot be destructured")),
9154 "expected destructured-rejection diagnostic, got {diags:?}",
9155 );
9156 }
9157
9158 #[test]
9159 fn rest_param_in_function_type_with_non_array_rejected() {
9160 let (_, diags) = parse_str("let f: (...xs: number) => void = () => {};");
9161 assert!(
9162 diags
9163 .iter()
9164 .any(|d| d.message.contains("rest parameter type must be an array")),
9165 "expected non-array-type diagnostic, got {diags:?}",
9166 );
9167 }
9168
9169 fn call(e: &crate::Expr) -> (crate::ExprId, &[crate::ExprId]) {
9170 match e.kind {
9171 crate::ExprKind::Call {
9172 callee, ref args, ..
9173 } => (callee, args.as_slice()),
9174 _ => panic!("expected Call, got {:?}", e.kind),
9175 }
9176 }
9177
9178 #[test]
9179 fn parse_call_no_args() {
9180 let (ast, diags) = parse_str("f();");
9181 assert!(diags.is_empty());
9182 let outer = expr_of_single_stmt(&ast);
9183 let (callee, args) = call(outer);
9184 assert!(matches!(
9185 ast.try_expr(callee).unwrap().kind,
9186 crate::ExprKind::Identifier(_)
9187 ));
9188 assert!(args.is_empty());
9189 assert_eq!(outer.span, crate::Span::new(F, 0, 3).unwrap());
9190 }
9191
9192 #[test]
9193 fn parse_call_one_arg() {
9194 let (ast, diags) = parse_str("f(1);");
9195 assert!(diags.is_empty());
9196 let outer = expr_of_single_stmt(&ast);
9197 let (callee, args) = call(outer);
9198 assert!(matches!(
9199 ast.try_expr(callee).unwrap().kind,
9200 crate::ExprKind::Identifier(_)
9201 ));
9202 assert_eq!(args.len(), 1);
9203 assert_eq!(
9204 ast.try_expr(args[0]).unwrap().kind,
9205 crate::ExprKind::Number(1.0)
9206 );
9207 }
9208
9209 #[test]
9210 fn parse_call_three_args() {
9211 let (ast, diags) = parse_str("f(1, 2, 3);");
9212 assert!(diags.is_empty());
9213 let outer = expr_of_single_stmt(&ast);
9214 let (_callee, args) = call(outer);
9215 assert_eq!(args.len(), 3);
9216 assert_eq!(
9217 ast.try_expr(args[0]).unwrap().kind,
9218 crate::ExprKind::Number(1.0)
9219 );
9220 assert_eq!(
9221 ast.try_expr(args[1]).unwrap().kind,
9222 crate::ExprKind::Number(2.0)
9223 );
9224 assert_eq!(
9225 ast.try_expr(args[2]).unwrap().kind,
9226 crate::ExprKind::Number(3.0)
9227 );
9228 }
9229
9230 #[test]
9231 fn parse_call_nested() {
9232 let (ast, diags) = parse_str("f(g(x));");
9233 assert!(diags.is_empty());
9234 let outer = expr_of_single_stmt(&ast);
9235 let (_callee, args) = call(outer);
9236 assert_eq!(args.len(), 1);
9237 let (_inner_callee, inner_args) = call(ast.try_expr(args[0]).unwrap());
9238 assert_eq!(inner_args.len(), 1);
9239 assert!(matches!(
9240 ast.try_expr(inner_args[0]).unwrap().kind,
9241 crate::ExprKind::Identifier(_)
9242 ));
9243 }
9244
9245 #[test]
9246 fn parse_curried_call() {
9247 let (ast, diags) = parse_str("f()(x);");
9248 assert!(diags.is_empty());
9249 let outer = expr_of_single_stmt(&ast);
9250 let (callee, args) = call(outer);
9251 assert_eq!(args.len(), 1);
9252 let (_inner_callee, inner_args) = call(ast.try_expr(callee).unwrap());
9253 assert!(inner_args.is_empty());
9254 }
9255
9256 #[test]
9257 fn unary_binds_looser_than_call() {
9258 let (ast, diags) = parse_str("-f();");
9259 assert!(diags.is_empty());
9260 let outer = expr_of_single_stmt(&ast);
9261 let (op, operand) = unary(outer);
9262 assert_eq!(op, crate::UnOp::Neg);
9263 let (_callee, args) = call(ast.try_expr(operand).unwrap());
9264 assert!(args.is_empty());
9265 }
9266
9267 #[test]
9268 fn call_inside_binary() {
9269 let (ast, diags) = parse_str("f() + 1;");
9270 assert!(diags.is_empty());
9271 let outer = expr_of_single_stmt(&ast);
9272 let (op, lhs, rhs) = binary(outer);
9273 assert_eq!(op, crate::BinOp::Add);
9274 let (_callee, args) = call(ast.try_expr(lhs).unwrap());
9275 assert!(args.is_empty());
9276 assert_eq!(
9277 ast.try_expr(rhs).unwrap().kind,
9278 crate::ExprKind::Number(1.0)
9279 );
9280 }
9281
9282 #[test]
9283 fn call_with_complex_args() {
9284 let (ast, diags) = parse_str("f(1 + 2, g(x));");
9285 assert!(diags.is_empty());
9286 let outer = expr_of_single_stmt(&ast);
9287 let (_callee, args) = call(outer);
9288 assert_eq!(args.len(), 2);
9289 let (op, ..) = binary(ast.try_expr(args[0]).unwrap());
9290 assert_eq!(op, crate::BinOp::Add);
9291 let (_, inner_args) = call(ast.try_expr(args[1]).unwrap());
9292 assert_eq!(inner_args.len(), 1);
9293 }
9294
9295 #[test]
9296 fn call_trailing_comma_ok() {
9297 let (ast, diags) = parse_str("f(1, 2,);");
9298 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9299 let (_callee, args) = call(expr_of_single_stmt(&ast));
9300 assert_eq!(args.len(), 2);
9301 }
9302
9303 #[test]
9304 fn unclosed_call_diagnoses() {
9305 let (ast, diags) = parse_str("f(1, 2");
9306 assert!(!diags.is_empty());
9307 assert!(diags[0].message == "expected `,` or `)`" || diags[0].message == "expected `)`");
9308 assert!(ast.top_level.is_empty());
9309 }
9310
9311 #[test]
9312 fn call_expression_statement() {
9313 let (ast, diags) = parse_str(r#"greet("world");"#);
9314 assert!(diags.is_empty());
9315 let outer = expr_of_single_stmt(&ast);
9316 let (callee, args) = call(outer);
9317 assert!(matches!(
9318 ast.try_expr(callee).unwrap().kind,
9319 crate::ExprKind::Identifier(_)
9320 ));
9321 assert_eq!(args.len(), 1);
9322 assert_eq!(
9323 ast.try_expr(args[0]).unwrap().kind,
9324 crate::ExprKind::String("world".to_string())
9325 );
9326 }
9327
9328 #[test]
9329 fn snapshot_multi_call_program() {
9330 let (ast, diags) = parse_str("f(); g(1); h(f(2), 3);");
9331 assert!(diags.is_empty());
9332 assert_eq!(ast.top_level.len(), 3);
9333 insta::assert_debug_snapshot!(ast);
9334 }
9335
9336 #[test]
9337 fn parse_if_simple() {
9338 let (ast, diags) = parse_str("if (a) { }");
9339 assert!(diags.is_empty());
9340 let stmt = single_stmt(&ast);
9341 match stmt.kind {
9342 crate::StmtKind::If {
9343 condition,
9344 then_block,
9345 else_block,
9346 } => {
9347 assert!(matches!(
9348 ast.try_expr(condition).unwrap().kind,
9349 crate::ExprKind::Identifier(_)
9350 ));
9351 assert!(matches!(
9352 ast.try_stmt(then_block).unwrap().kind,
9353 crate::StmtKind::Block(ref v) if v.is_empty()
9354 ));
9355 assert!(else_block.is_none());
9356 }
9357 _ => panic!("expected If"),
9358 }
9359 }
9360
9361 #[test]
9362 fn parse_if_else() {
9363 let (ast, diags) = parse_str("if (a) { } else { }");
9364 assert!(diags.is_empty());
9365 let stmt = single_stmt(&ast);
9366 match stmt.kind {
9367 crate::StmtKind::If { else_block, .. } => {
9368 let else_id = else_block.expect("expected else block");
9369 assert!(matches!(
9370 ast.try_stmt(else_id).unwrap().kind,
9371 crate::StmtKind::Block(ref v) if v.is_empty()
9372 ));
9373 }
9374 _ => panic!("expected If"),
9375 }
9376 }
9377
9378 #[test]
9379 fn parse_else_if_chain() {
9380 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
9381 assert!(diags.is_empty());
9382 let stmt = single_stmt(&ast);
9383 let crate::StmtKind::If {
9384 else_block: Some(inner_id),
9385 ..
9386 } = stmt.kind
9387 else {
9388 panic!("expected outer If with else_block");
9389 };
9390 let inner = ast.try_stmt(inner_id).unwrap();
9391 let crate::StmtKind::If {
9392 else_block: Some(tail_id),
9393 ..
9394 } = inner.kind
9395 else {
9396 panic!("expected inner If for `else if`");
9397 };
9398 assert!(matches!(
9399 ast.try_stmt(tail_id).unwrap().kind,
9400 crate::StmtKind::Block(_)
9401 ));
9402 }
9403
9404 #[test]
9405 fn parse_if_with_body_statements() {
9406 let (ast, diags) = parse_str("if (a) { let x = 1; }");
9407 assert!(diags.is_empty());
9408 let stmt = single_stmt(&ast);
9409 match stmt.kind {
9410 crate::StmtKind::If { then_block, .. } => {
9411 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind
9412 else {
9413 panic!("expected Block")
9414 };
9415 assert_eq!(body.len(), 1);
9416 }
9417 _ => panic!("expected If"),
9418 }
9419 }
9420
9421 #[test]
9422 fn parse_if_braceless_body() {
9423 let (ast, diags) = parse_str("if (a) return b;");
9424 assert!(diags.is_empty(), "{diags:?}");
9425 let stmt = single_stmt(&ast);
9426 let crate::StmtKind::If {
9427 then_block,
9428 else_block,
9429 ..
9430 } = stmt.kind
9431 else {
9432 panic!("expected If");
9433 };
9434 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9435 panic!("braceless body should wrap in a Block");
9436 };
9437 assert_eq!(body.len(), 1);
9438 assert!(matches!(
9439 ast.try_stmt(body[0]).unwrap().kind,
9440 crate::StmtKind::Return(_)
9441 ));
9442 assert!(else_block.is_none());
9443 }
9444
9445 #[test]
9446 fn parse_braceless_dangling_else_binds_nearest_if() {
9447 let (ast, diags) = parse_str("if (a) if (b) x(); else y();");
9449 assert!(diags.is_empty(), "{diags:?}");
9450 let crate::StmtKind::If {
9451 then_block,
9452 else_block: outer_else,
9453 ..
9454 } = single_stmt(&ast).kind
9455 else {
9456 panic!("expected outer If");
9457 };
9458 assert!(outer_else.is_none(), "else must bind to the inner if");
9459 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9460 panic!("expected wrapped Block");
9461 };
9462 let crate::StmtKind::If {
9463 else_block: Some(_),
9464 ..
9465 } = ast.try_stmt(body[0]).unwrap().kind
9466 else {
9467 panic!("inner If should own the else");
9468 };
9469 }
9470
9471 #[test]
9472 fn parse_while_braceless_body() {
9473 let (ast, diags) = parse_str("while (a) x();");
9474 assert!(diags.is_empty(), "{diags:?}");
9475 let crate::StmtKind::While { body, .. } = single_stmt(&ast).kind else {
9476 panic!("expected While");
9477 };
9478 assert!(matches!(
9479 ast.try_stmt(body).unwrap().kind,
9480 crate::StmtKind::Block(ref v) if v.len() == 1
9481 ));
9482 }
9483
9484 #[test]
9485 fn parse_loop_with_empty_body() {
9486 for src in [
9487 "while (a());",
9488 "for (const x of xs);",
9489 "for (let i = 0; i < 3; i++);",
9490 "do ; while (a());",
9491 ] {
9492 let (ast, diags) = parse_str(src);
9493 assert!(diags.is_empty(), "{src}: {diags:?}");
9494 let body = match single_stmt(&ast).kind {
9495 crate::StmtKind::While { body, .. }
9496 | crate::StmtKind::DoWhile { body, .. }
9497 | crate::StmtKind::For { body, .. }
9498 | crate::StmtKind::ForOf { body, .. } => body,
9499 ref other => panic!("{src}: expected a loop, got {other:?}"),
9500 };
9501 assert!(
9502 matches!(ast.try_stmt(body).unwrap().kind, crate::StmtKind::Block(ref v) if v.is_empty()),
9503 "{src}"
9504 );
9505 }
9506 }
9507
9508 #[test]
9509 fn parse_if_with_empty_body_is_an_error() {
9510 let (_ast, diags) = parse_str("if (a);");
9511 assert_eq!(diags[0].message, "`if` has an empty body");
9512 }
9513
9514 #[test]
9515 fn parse_empty_branch_body_reports_once() {
9516 let (_ast, diags) = parse_str("if (a) ; else b();");
9517 assert_eq!(diags.len(), 1, "{diags:?}");
9518 assert_eq!(diags[0].message, "`if` has an empty body");
9519 let (_ast, diags) = parse_str("if (a) b(); else ;");
9520 assert_eq!(diags.len(), 1, "{diags:?}");
9521 assert_eq!(diags[0].message, "`else` has an empty body");
9522 let (_ast, diags) = parse_str("if (a) ; else if (b) ; else ;");
9524 let messages: Vec<_> = diags.iter().map(|d| d.message.as_str()).collect();
9525 assert_eq!(
9526 messages,
9527 [
9528 "`if` has an empty body",
9529 "`if` has an empty body",
9530 "`else` has an empty body"
9531 ]
9532 );
9533 }
9534
9535 #[test]
9536 fn parse_for_of_braceless_body() {
9537 let (ast, diags) = parse_str("for (const x of xs) f(x);");
9538 assert!(diags.is_empty(), "{diags:?}");
9539 let crate::StmtKind::ForOf { body, .. } = single_stmt(&ast).kind else {
9540 panic!("expected ForOf");
9541 };
9542 assert!(matches!(
9543 ast.try_stmt(body).unwrap().kind,
9544 crate::StmtKind::Block(ref v) if v.len() == 1
9545 ));
9546 }
9547
9548 #[test]
9549 fn parse_nested_if() {
9550 let (ast, diags) = parse_str("if (a) { if (b) { } }");
9551 assert!(diags.is_empty());
9552 let outer = single_stmt(&ast);
9553 let crate::StmtKind::If { then_block, .. } = outer.kind else {
9554 panic!("expected outer If");
9555 };
9556 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9557 panic!("expected Block")
9558 };
9559 assert_eq!(body.len(), 1);
9560 assert!(matches!(
9561 ast.try_stmt(body[0]).unwrap().kind,
9562 crate::StmtKind::If { .. }
9563 ));
9564 }
9565
9566 #[test]
9567 fn if_missing_open_paren_diagnoses() {
9568 let (ast, diags) = parse_str("if a { }");
9569 assert!(!diags.is_empty());
9570 assert_eq!(diags[0].message, "expected `(`");
9571 assert!(ast.top_level.is_empty());
9572 }
9573
9574 #[test]
9575 fn if_missing_close_paren_diagnoses() {
9576 let (ast, diags) = parse_str("if (a { }");
9577 assert!(!diags.is_empty());
9578 assert_eq!(diags[0].message, "expected `)`");
9579 assert!(ast.top_level.is_empty());
9580 }
9581
9582 #[test]
9583 fn if_braceless_body_parses() {
9584 let (ast, diags) = parse_str("if (a) x;");
9585 assert!(diags.is_empty(), "{diags:?}");
9586 let crate::StmtKind::If { then_block, .. } = single_stmt(&ast).kind else {
9587 panic!("expected If");
9588 };
9589 assert!(matches!(
9590 ast.try_stmt(then_block).unwrap().kind,
9591 crate::StmtKind::Block(ref v) if v.len() == 1
9592 ));
9593 }
9594
9595 #[test]
9596 fn snapshot_else_if_chain() {
9597 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
9598 assert!(diags.is_empty());
9599 insta::assert_debug_snapshot!(ast);
9600 }
9601
9602 #[test]
9603 fn parse_while_simple() {
9604 let (ast, diags) = parse_str("while (a) { }");
9605 assert!(diags.is_empty());
9606 let stmt = single_stmt(&ast);
9607 match stmt.kind {
9608 crate::StmtKind::While { condition, body } => {
9609 assert!(matches!(
9610 ast.try_expr(condition).unwrap().kind,
9611 crate::ExprKind::Identifier(_)
9612 ));
9613 assert!(matches!(
9614 ast.try_stmt(body).unwrap().kind,
9615 crate::StmtKind::Block(ref v) if v.is_empty()
9616 ));
9617 }
9618 _ => panic!("expected While"),
9619 }
9620 }
9621
9622 #[test]
9623 fn parse_while_with_body() {
9624 let (ast, diags) = parse_str("while (a) { x; y; }");
9625 assert!(diags.is_empty());
9626 let stmt = single_stmt(&ast);
9627 let crate::StmtKind::While { body, .. } = stmt.kind else {
9628 panic!("expected While");
9629 };
9630 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
9631 panic!("expected Block")
9632 };
9633 assert_eq!(stmts.len(), 2);
9634 }
9635
9636 #[test]
9637 fn while_missing_paren_diagnoses() {
9638 let (ast, diags) = parse_str("while a { }");
9639 assert!(!diags.is_empty());
9640 assert_eq!(diags[0].message, "expected `(`");
9641 assert!(ast.top_level.is_empty());
9642 }
9643
9644 #[test]
9645 fn parse_for_all_slots() {
9646 let (ast, diags) = parse_str("for (let i = 0; i < 10; i = i + 1) { x; }");
9647 assert!(diags.is_empty(), "{diags:?}");
9648 let stmt = single_stmt(&ast);
9649 let crate::StmtKind::For {
9650 init,
9651 condition,
9652 update,
9653 body,
9654 } = stmt.kind
9655 else {
9656 panic!("expected For");
9657 };
9658 assert!(init.is_some(), "init missing");
9659 assert!(condition.is_some(), "condition missing");
9660 assert!(update.is_some(), "update missing");
9661 assert!(matches!(
9662 ast.try_stmt(body).unwrap().kind,
9663 crate::StmtKind::Block(_)
9664 ));
9665 }
9666
9667 #[test]
9668 fn parse_for_empty_slots() {
9669 let (ast, diags) = parse_str("for (;;) { break; }");
9670 assert!(diags.is_empty(), "{diags:?}");
9671 let stmt = single_stmt(&ast);
9672 let crate::StmtKind::For {
9673 init,
9674 condition,
9675 update,
9676 ..
9677 } = stmt.kind
9678 else {
9679 panic!("expected For");
9680 };
9681 assert!(init.is_none() && condition.is_none() && update.is_none());
9682 }
9683
9684 #[test]
9685 fn parse_for_of_const_binding() {
9686 let (ast, diags) = parse_str("for (const x of arr) { y; }");
9687 assert!(diags.is_empty(), "{diags:?}");
9688 let stmt = single_stmt(&ast);
9689 let crate::StmtKind::ForOf {
9690 binding_kind,
9691 ref name,
9692 ref ty,
9693 ..
9694 } = stmt.kind
9695 else {
9696 panic!("expected ForOf");
9697 };
9698 assert!(matches!(binding_kind, crate::BindingKind::Const));
9699 assert_eq!(name.name, "x");
9700 assert!(ty.is_none());
9701 }
9702
9703 #[test]
9704 fn parse_for_of_let_with_annotation() {
9705 let (ast, diags) = parse_str("for (let x: number of arr) { y; }");
9706 assert!(diags.is_empty(), "{diags:?}");
9707 let stmt = single_stmt(&ast);
9708 let crate::StmtKind::ForOf {
9709 binding_kind,
9710 ref ty,
9711 ..
9712 } = stmt.kind
9713 else {
9714 panic!("expected ForOf");
9715 };
9716 assert!(matches!(binding_kind, crate::BindingKind::Let));
9717 assert!(ty.is_some());
9718 }
9719
9720 #[test]
9721 fn parse_do_while_simple() {
9722 let (ast, diags) = parse_str("do { x; } while (a);");
9723 assert!(diags.is_empty(), "{diags:?}");
9724 let stmt = single_stmt(&ast);
9725 assert!(matches!(stmt.kind, crate::StmtKind::DoWhile { .. }));
9726 }
9727
9728 #[test]
9729 fn parse_break_and_continue() {
9730 let (ast, diags) = parse_str("while (a) { break; continue; }");
9731 assert!(diags.is_empty(), "{diags:?}");
9732 let stmt = single_stmt(&ast);
9733 let crate::StmtKind::While { body, .. } = stmt.kind else {
9734 panic!("expected While");
9735 };
9736 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
9737 panic!("expected Block");
9738 };
9739 assert!(matches!(
9740 ast.try_stmt(stmts[0]).unwrap().kind,
9741 crate::StmtKind::Break
9742 ));
9743 assert!(matches!(
9744 ast.try_stmt(stmts[1]).unwrap().kind,
9745 crate::StmtKind::Continue
9746 ));
9747 }
9748
9749 #[test]
9750 fn parse_bare_return_in_function() {
9751 let (ast, diags) = parse_str("function f(): void { return; }");
9752 assert!(diags.is_empty());
9753 let f = single_stmt(&ast);
9754 let crate::StmtKind::Function { body, .. } = f.kind else {
9755 panic!("expected Function");
9756 };
9757 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
9758 panic!("expected Block")
9759 };
9760 assert_eq!(stmts.len(), 1);
9761 match ast.try_stmt(stmts[0]).unwrap().kind {
9762 crate::StmtKind::Return(None) => {}
9763 _ => panic!("expected Return(None)"),
9764 }
9765 }
9766
9767 #[test]
9768 fn parse_return_with_value() {
9769 let (ast, diags) = parse_str("function f(): number { return 1; }");
9770 assert!(diags.is_empty());
9771 let f = single_stmt(&ast);
9772 let crate::StmtKind::Function { body, .. } = f.kind else {
9773 panic!("expected Function");
9774 };
9775 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
9776 panic!("expected Block")
9777 };
9778 match ast.try_stmt(stmts[0]).unwrap().kind {
9779 crate::StmtKind::Return(Some(value)) => {
9780 assert_eq!(
9781 ast.try_expr(value).unwrap().kind,
9782 crate::ExprKind::Number(1.0)
9783 );
9784 }
9785 _ => panic!("expected Return(Some)"),
9786 }
9787 }
9788
9789 #[test]
9790 fn parse_return_with_expression() {
9791 let (ast, diags) = parse_str("function f(): number { return 1 + 2; }");
9792 assert!(diags.is_empty());
9793 let f = single_stmt(&ast);
9794 let crate::StmtKind::Function { body, .. } = f.kind else {
9795 panic!("expected Function");
9796 };
9797 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
9798 panic!("expected Block")
9799 };
9800 match ast.try_stmt(stmts[0]).unwrap().kind {
9801 crate::StmtKind::Return(Some(value)) => {
9802 let (op, ..) = binary(ast.try_expr(value).unwrap());
9803 assert_eq!(op, crate::BinOp::Add);
9804 }
9805 _ => panic!("expected Return(Some(Binary))"),
9806 }
9807 }
9808
9809 #[test]
9810 fn return_missing_semicolon_diagnoses() {
9811 let (_, diags) = parse_str("function f(): number { return x x; }");
9812 assert!(!diags.is_empty());
9813 assert_eq!(diags[0].message, "expected `;` after return");
9814 }
9815
9816 #[test]
9817 fn parse_bare_block() {
9818 let (ast, diags) = parse_str("{ x; y; }");
9819 assert!(diags.is_empty());
9820 let stmt = single_stmt(&ast);
9821 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
9822 panic!("expected Block");
9823 };
9824 assert_eq!(stmts.len(), 2);
9825 }
9826
9827 #[test]
9828 fn parse_empty_bare_block() {
9829 let (ast, diags) = parse_str("{ }");
9830 assert!(diags.is_empty());
9831 let stmt = single_stmt(&ast);
9832 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
9833 panic!("expected Block");
9834 };
9835 assert!(stmts.is_empty());
9836 }
9837
9838 #[test]
9839 fn parse_nested_bare_blocks() {
9840 let (ast, diags) = parse_str("{ { } }");
9841 assert!(diags.is_empty());
9842 let outer = single_stmt(&ast);
9843 let crate::StmtKind::Block(ref stmts) = outer.kind else {
9844 panic!("expected outer Block");
9845 };
9846 assert_eq!(stmts.len(), 1);
9847 assert!(matches!(
9848 ast.try_stmt(stmts[0]).unwrap().kind,
9849 crate::StmtKind::Block(ref v) if v.is_empty()
9850 ));
9851 }
9852
9853 #[test]
9854 fn snapshot_function_with_if_and_return() {
9855 let source = "function isPositive(n: number): boolean {\n if (n > 0) {\n return true;\n } else {\n return false;\n }\n}";
9856 let (ast, diags) = parse_str(source);
9857 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9858 insta::assert_debug_snapshot!(ast);
9859 }
9860
9861 fn object_literal(e: &crate::Expr) -> &[crate::ObjectLiteralMember] {
9862 match e.kind {
9863 crate::ExprKind::ObjectLiteral { ref members } => members.as_slice(),
9864 _ => panic!("expected ObjectLiteral, got {:?}", e.kind),
9865 }
9866 }
9867
9868 fn object_literal_field(m: &crate::ObjectLiteralMember) -> &crate::ObjectLiteralField {
9869 match m {
9870 crate::ObjectLiteralMember::Field(f) => f,
9871 crate::ObjectLiteralMember::Spread { .. }
9872 | crate::ObjectLiteralMember::Computed { .. } => {
9873 panic!("expected Field member, got Spread")
9874 }
9875 }
9876 }
9877
9878 fn array_literal(e: &crate::Expr) -> &[crate::ArrayLiteralElement] {
9879 match e.kind {
9880 crate::ExprKind::ArrayLiteral { ref elements } => elements.as_slice(),
9881 _ => panic!("expected ArrayLiteral, got {:?}", e.kind),
9882 }
9883 }
9884
9885 fn array_literal_value(el: &crate::ArrayLiteralElement) -> crate::ExprId {
9886 match el {
9887 crate::ArrayLiteralElement::Value(id) => *id,
9888 crate::ArrayLiteralElement::Spread { .. } => {
9889 panic!("expected Value element, got Spread")
9890 }
9891 }
9892 }
9893
9894 #[test]
9895 fn parse_empty_object_literal() {
9896 let (ast, diags) = parse_str("let x = {};");
9897 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9898 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9899 panic!("expected Let");
9900 };
9901 assert!(object_literal(ast.try_expr(value).unwrap()).is_empty());
9902 }
9903
9904 #[test]
9905 fn parse_object_literal_simple() {
9906 let (ast, diags) = parse_str("let p = { x: 1, y: 2 };");
9907 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9908 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9909 panic!("expected Let");
9910 };
9911 let members = object_literal(ast.try_expr(value).unwrap());
9912 assert_eq!(members.len(), 2);
9913 let f0 = object_literal_field(&members[0]);
9914 let f1 = object_literal_field(&members[1]);
9915 assert_eq!(f0.name.name, "x");
9916 assert_eq!(f1.name.name, "y");
9917 assert_eq!(
9918 ast.try_expr(f0.value).unwrap().kind,
9919 crate::ExprKind::Number(1.0)
9920 );
9921 assert_eq!(
9922 ast.try_expr(f1.value).unwrap().kind,
9923 crate::ExprKind::Number(2.0)
9924 );
9925 }
9926
9927 #[test]
9928 fn parse_object_literal_keyword_keys() {
9929 let (ast, diags) = parse_str("let p = { type: 1, default: 2, null: 3, true: 4 };");
9930 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9931 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9932 panic!("expected Let");
9933 };
9934 let names: Vec<&str> = object_literal(ast.try_expr(value).unwrap())
9935 .iter()
9936 .map(object_literal_field)
9937 .map(|f| f.name.name.as_str())
9938 .collect();
9939 assert_eq!(names, vec!["type", "default", "null", "true"]);
9940 }
9941
9942 #[test]
9943 fn parse_object_literal_string_key() {
9944 let (ast, diags) = parse_str(r#"let p = { "hello": 1 };"#);
9945 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9946 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9947 panic!("expected Let");
9948 };
9949 let members = object_literal(ast.try_expr(value).unwrap());
9950 assert_eq!(members.len(), 1);
9951 let f0 = object_literal_field(&members[0]);
9952 assert_eq!(f0.name.name, "hello");
9953 }
9954
9955 #[test]
9956 fn parse_object_literal_string_key_decodes_escape() {
9957 let (ast, diags) = parse_str(r#"let p = { "content\u002dtype": 1 };"#);
9958 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9959 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9960 panic!("expected Let");
9961 };
9962 let members = object_literal(ast.try_expr(value).unwrap());
9963 assert_eq!(members.len(), 1);
9964 let f0 = object_literal_field(&members[0]);
9965 assert_eq!(f0.name.name, "content-type");
9966 }
9967
9968 #[test]
9969 fn parse_object_literal_shorthand() {
9970 let (ast, diags) = parse_str("let p = { x, y: 2 };");
9971 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9972 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
9973 panic!("expected Let");
9974 };
9975 let members = object_literal(ast.try_expr(value).unwrap());
9976 assert_eq!(members.len(), 2);
9977 let f0 = object_literal_field(&members[0]);
9978 assert_eq!(f0.name.name, "x");
9979 assert_eq!(
9981 ast.try_expr(f0.value).unwrap().kind,
9982 crate::ExprKind::Identifier(crate::Ident {
9983 name: "x".into(),
9984 span: f0.name.span,
9985 })
9986 );
9987 let f1 = object_literal_field(&members[1]);
9988 assert_eq!(f1.name.name, "y");
9989 assert_eq!(
9990 ast.try_expr(f1.value).unwrap().kind,
9991 crate::ExprKind::Number(2.0)
9992 );
9993 }
9994
9995 #[test]
9996 fn parse_object_literal_string_key_shorthand_rejected() {
9997 let (_ast, diags) = parse_str(r#"let p = { "x" };"#);
9998 assert!(
9999 diags
10000 .iter()
10001 .any(|d| d.message.contains("expected `:` after field name")),
10002 "string-key shorthand should be rejected: {diags:?}"
10003 );
10004 }
10005
10006 #[test]
10007 fn parse_object_literal_trailing_comma() {
10008 let (ast, diags) = parse_str("let p = { x: 1, y: 2, };");
10009 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10010 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10011 panic!("expected Let");
10012 };
10013 assert_eq!(object_literal(ast.try_expr(value).unwrap()).len(), 2);
10014 }
10015
10016 #[test]
10017 fn parse_object_literal_duplicate_key_diagnoses() {
10018 let (_, diags) = parse_str("let p = { x: 1, x: 2 };");
10019 assert_eq!(diags.len(), 1);
10020 assert_eq!(diags[0].message, "duplicate field `x` in object literal");
10021 }
10022
10023 #[test]
10024 fn parse_empty_array_literal() {
10025 let (ast, diags) = parse_str("let xs = [];");
10026 assert!(diags.is_empty());
10027 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10028 panic!("expected Let");
10029 };
10030 assert!(array_literal(ast.try_expr(value).unwrap()).is_empty());
10031 }
10032
10033 #[test]
10034 fn parse_array_literal_simple() {
10035 let (ast, diags) = parse_str("let xs = [1, 2, 3];");
10036 assert!(diags.is_empty());
10037 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10038 panic!("expected Let");
10039 };
10040 let elements = array_literal(ast.try_expr(value).unwrap());
10041 assert_eq!(elements.len(), 3);
10042 assert_eq!(
10043 ast.try_expr(array_literal_value(&elements[0]))
10044 .unwrap()
10045 .kind,
10046 crate::ExprKind::Number(1.0)
10047 );
10048 assert_eq!(
10049 ast.try_expr(array_literal_value(&elements[2]))
10050 .unwrap()
10051 .kind,
10052 crate::ExprKind::Number(3.0)
10053 );
10054 }
10055
10056 #[test]
10057 fn parse_array_literal_trailing_comma() {
10058 let (ast, diags) = parse_str("let xs = [1, 2,];");
10059 assert!(diags.is_empty());
10060 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10061 panic!("expected Let");
10062 };
10063 assert_eq!(array_literal(ast.try_expr(value).unwrap()).len(), 2);
10064 }
10065
10066 #[test]
10067 fn parse_nested_array_in_object() {
10068 let (ast, diags) = parse_str("let p = { items: [1, 2] };");
10069 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10070 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10071 panic!("expected Let");
10072 };
10073 let members = object_literal(ast.try_expr(value).unwrap());
10074 let f0 = object_literal_field(&members[0]);
10075 assert_eq!(f0.name.name, "items");
10076 let inner = array_literal(ast.try_expr(f0.value).unwrap());
10077 assert_eq!(inner.len(), 2);
10078 }
10079
10080 #[test]
10081 fn parse_nested_object_in_array() {
10082 let (ast, diags) = parse_str("let xs = [{ x: 1 }, { x: 2 }];");
10083 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10084 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10085 panic!("expected Let");
10086 };
10087 let elements = array_literal(ast.try_expr(value).unwrap());
10088 assert_eq!(elements.len(), 2);
10089 assert_eq!(
10090 object_literal(ast.try_expr(array_literal_value(&elements[0])).unwrap()).len(),
10091 1
10092 );
10093 assert_eq!(
10094 object_literal(ast.try_expr(array_literal_value(&elements[1])).unwrap()).len(),
10095 1
10096 );
10097 }
10098
10099 #[test]
10100 fn brace_at_statement_start_is_block_not_object() {
10101 let (_, diags) = parse_str("{ x: 1 }");
10102 assert!(
10103 !diags.is_empty(),
10104 "expected a diagnostic for `x:` inside block"
10105 );
10106 }
10107
10108 fn field_access(e: &crate::Expr) -> (crate::ExprId, &str) {
10109 match e.kind {
10110 crate::ExprKind::FieldAccess { receiver, ref name } => (receiver, name.name.as_str()),
10111 _ => panic!("expected FieldAccess, got {:?}", e.kind),
10112 }
10113 }
10114
10115 fn index_access(e: &crate::Expr) -> (crate::ExprId, crate::ExprId) {
10116 match e.kind {
10117 crate::ExprKind::IndexAccess { receiver, index } => (receiver, index),
10118 _ => panic!("expected IndexAccess, got {:?}", e.kind),
10119 }
10120 }
10121
10122 #[test]
10123 fn parse_field_access_simple() {
10124 let (ast, diags) = parse_str("a.b;");
10125 assert!(diags.is_empty());
10126 let outer = expr_of_single_stmt(&ast);
10127 let (receiver, name) = field_access(outer);
10128 assert!(matches!(
10129 ast.try_expr(receiver).unwrap().kind,
10130 crate::ExprKind::Identifier(_)
10131 ));
10132 assert_eq!(name, "b");
10133 }
10134
10135 #[test]
10136 fn parse_field_access_keyword_name() {
10137 let (ast, diags) = parse_str("status.type.default.null;");
10138 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10139 let outer = expr_of_single_stmt(&ast);
10140 let (receiver, name) = field_access(outer);
10141 assert_eq!(name, "null");
10142 let (receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10143 assert_eq!(name, "default");
10144 let (_receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10145 assert_eq!(name, "type");
10146 }
10147
10148 #[test]
10149 fn parse_field_access_chain_left_assoc() {
10150 let (ast, diags) = parse_str("a.b.c;");
10151 assert!(diags.is_empty());
10152 let outer = expr_of_single_stmt(&ast);
10153 let (mid_id, c) = field_access(outer);
10154 assert_eq!(c, "c");
10155 let (a_id, b) = field_access(ast.try_expr(mid_id).unwrap());
10156 assert_eq!(b, "b");
10157 assert!(matches!(
10158 ast.try_expr(a_id).unwrap().kind,
10159 crate::ExprKind::Identifier(_)
10160 ));
10161 }
10162
10163 #[test]
10164 fn parse_index_access_simple() {
10165 let (ast, diags) = parse_str("a[0];");
10166 assert!(diags.is_empty());
10167 let outer = expr_of_single_stmt(&ast);
10168 let (receiver, idx) = index_access(outer);
10169 assert!(matches!(
10170 ast.try_expr(receiver).unwrap().kind,
10171 crate::ExprKind::Identifier(_)
10172 ));
10173 assert_eq!(
10174 ast.try_expr(idx).unwrap().kind,
10175 crate::ExprKind::Number(0.0)
10176 );
10177 }
10178
10179 #[test]
10180 fn parse_index_access_chain() {
10181 let (ast, diags) = parse_str("a[0][1];");
10182 assert!(diags.is_empty());
10183 let outer = expr_of_single_stmt(&ast);
10184 let (mid_id, one) = index_access(outer);
10185 assert_eq!(
10186 ast.try_expr(one).unwrap().kind,
10187 crate::ExprKind::Number(1.0)
10188 );
10189 let (a_id, zero) = index_access(ast.try_expr(mid_id).unwrap());
10190 assert_eq!(
10191 ast.try_expr(zero).unwrap().kind,
10192 crate::ExprKind::Number(0.0)
10193 );
10194 assert!(matches!(
10195 ast.try_expr(a_id).unwrap().kind,
10196 crate::ExprKind::Identifier(_)
10197 ));
10198 }
10199
10200 #[test]
10201 fn parse_mixed_access_and_call_chain() {
10202 let (ast, diags) = parse_str("a.b[c].d();");
10203 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10204 let outer = expr_of_single_stmt(&ast);
10205 let (callee, args) = call(outer);
10206 assert!(args.is_empty());
10207 let (idx_id, d_name) = field_access(ast.try_expr(callee).unwrap());
10208 assert_eq!(d_name, "d");
10209 let (b_id, c_idx) = index_access(ast.try_expr(idx_id).unwrap());
10210 assert!(matches!(
10211 ast.try_expr(c_idx).unwrap().kind,
10212 crate::ExprKind::Identifier(_)
10213 ));
10214 let (a_id, b_name) = field_access(ast.try_expr(b_id).unwrap());
10215 assert_eq!(b_name, "b");
10216 assert!(matches!(
10217 ast.try_expr(a_id).unwrap().kind,
10218 crate::ExprKind::Identifier(_)
10219 ));
10220 }
10221
10222 #[test]
10223 fn parse_call_then_field() {
10224 let (ast, diags) = parse_str("f().x;");
10225 assert!(diags.is_empty());
10226 let outer = expr_of_single_stmt(&ast);
10227 let (call_id, x_name) = field_access(outer);
10228 assert_eq!(x_name, "x");
10229 let (_callee, args) = call(ast.try_expr(call_id).unwrap());
10230 assert!(args.is_empty());
10231 }
10232
10233 #[test]
10234 fn parse_index_with_complex_expression() {
10235 let (ast, diags) = parse_str("a[i + 1];");
10236 assert!(diags.is_empty());
10237 let outer = expr_of_single_stmt(&ast);
10238 let (_, idx) = index_access(outer);
10239 let (op, ..) = binary(ast.try_expr(idx).unwrap());
10240 assert_eq!(op, crate::BinOp::Add);
10241 }
10242
10243 #[test]
10244 fn field_access_missing_name_diagnoses() {
10245 let (_, diags) = parse_str("a.;");
10246 assert!(!diags.is_empty());
10247 assert_eq!(diags[0].message, "expected field name after `.`");
10248 }
10249
10250 #[test]
10251 fn parse_postfix_increment() {
10252 let (ast, diags) = parse_str("x++;");
10253 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10254 let expr = expr_of_single_stmt(&ast);
10255 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10256 panic!("expected PostfixUnary, got {:?}", expr.kind);
10257 };
10258 assert_eq!(op, crate::PostfixOp::Inc);
10259 assert!(matches!(
10260 ast.try_expr(operand).unwrap().kind,
10261 crate::ExprKind::Identifier(_)
10262 ));
10263 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
10264 }
10265
10266 #[test]
10267 fn parse_postfix_decrement() {
10268 let (ast, diags) = parse_str("x--;");
10269 assert!(diags.is_empty());
10270 let expr = expr_of_single_stmt(&ast);
10271 let crate::ExprKind::PostfixUnary { op, .. } = expr.kind else {
10272 panic!("expected PostfixUnary");
10273 };
10274 assert_eq!(op, crate::PostfixOp::Dec);
10275 }
10276
10277 #[test]
10278 fn parse_postfix_non_null_assertion() {
10279 let (ast, diags) = parse_str("x!;");
10280 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10281 let expr = expr_of_single_stmt(&ast);
10282 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10283 panic!("expected PostfixUnary, got {:?}", expr.kind);
10284 };
10285 assert_eq!(op, crate::PostfixOp::NonNullAssert);
10286 assert!(matches!(
10287 ast.try_expr(operand).unwrap().kind,
10288 crate::ExprKind::Identifier(_)
10289 ));
10290 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
10291 }
10292
10293 #[test]
10294 fn parse_non_null_assertion_continues_chain() {
10295 let (ast, diags) = parse_str("x!.y;");
10296 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10297 let expr = expr_of_single_stmt(&ast);
10298 let crate::ExprKind::FieldAccess { receiver, name } = &expr.kind else {
10299 panic!("expected FieldAccess, got {:?}", expr.kind);
10300 };
10301 assert_eq!(name.name, "y");
10302 assert!(matches!(
10303 ast.try_expr(*receiver).unwrap().kind,
10304 crate::ExprKind::PostfixUnary {
10305 op: crate::PostfixOp::NonNullAssert,
10306 ..
10307 }
10308 ));
10309 }
10310
10311 #[test]
10312 fn parse_postfix_on_field_access() {
10313 let (ast, diags) = parse_str("o.f++;");
10314 assert!(diags.is_empty());
10315 let expr = expr_of_single_stmt(&ast);
10316 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
10317 panic!("expected PostfixUnary");
10318 };
10319 assert!(matches!(
10320 ast.try_expr(operand).unwrap().kind,
10321 crate::ExprKind::FieldAccess { .. }
10322 ));
10323 }
10324
10325 #[test]
10326 fn parse_postfix_on_index_access() {
10327 let (ast, diags) = parse_str("xs[0]++;");
10328 assert!(diags.is_empty());
10329 let expr = expr_of_single_stmt(&ast);
10330 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
10331 panic!("expected PostfixUnary");
10332 };
10333 assert!(matches!(
10334 ast.try_expr(operand).unwrap().kind,
10335 crate::ExprKind::IndexAccess { .. }
10336 ));
10337 }
10338
10339 #[test]
10340 fn parse_postfix_terminates_chain() {
10341 let (_, diags) = parse_str("x++.y;");
10342 assert!(!diags.is_empty());
10343 assert_eq!(diags[0].message, "expected `;` after expression");
10344 }
10345
10346 #[test]
10347 fn parse_postfix_in_binary_continues() {
10348 let (ast, diags) = parse_str("let y = x++ + 1;");
10349 assert!(diags.is_empty(), "unexpected: {diags:?}");
10350 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10351 panic!("expected Let");
10352 };
10353 let (op, lhs, _) = binary(ast.try_expr(value).unwrap());
10354 assert_eq!(op, crate::BinOp::Add);
10355 assert!(matches!(
10356 ast.try_expr(lhs).unwrap().kind,
10357 crate::ExprKind::PostfixUnary { .. }
10358 ));
10359 }
10360
10361 #[test]
10362 fn parse_index_assignment() {
10363 let (ast, diags) = parse_str("xs[0] = 1;");
10364 assert!(diags.is_empty(), "unexpected: {diags:?}");
10365 let stmt = single_stmt(&ast);
10366 let crate::StmtKind::AssignIndex {
10367 receiver,
10368 index,
10369 value,
10370 } = stmt.kind
10371 else {
10372 panic!("expected AssignIndex, got {:?}", stmt.kind);
10373 };
10374 assert!(matches!(
10375 ast.try_expr(receiver).unwrap().kind,
10376 crate::ExprKind::Identifier(_)
10377 ));
10378 assert_eq!(
10379 ast.try_expr(index).unwrap().kind,
10380 crate::ExprKind::Number(0.0)
10381 );
10382 assert_eq!(
10383 ast.try_expr(value).unwrap().kind,
10384 crate::ExprKind::Number(1.0)
10385 );
10386 }
10387
10388 #[test]
10389 fn parse_for_update_postfix() {
10390 let (ast, diags) = parse_str("function main(): void { for (let i = 0; i < 3; i++) {} }");
10391 assert!(diags.is_empty(), "unexpected: {diags:?}");
10392 assert_eq!(ast.top_level.len(), 1);
10393 }
10394
10395 #[test]
10396 fn parse_for_update_index_assignment() {
10397 let (ast, diags) =
10398 parse_str("function main(): void { for (let i = 0; i < 3; xs[i] = i) {} }");
10399 assert!(diags.is_empty(), "unexpected: {diags:?}");
10400 assert_eq!(ast.top_level.len(), 1);
10401 }
10402
10403 #[test]
10404 fn index_access_missing_close_bracket_diagnoses() {
10405 let (_, diags) = parse_str("a[1;");
10406 assert!(!diags.is_empty());
10407 assert_eq!(diags[0].message, "expected `]`");
10408 }
10409
10410 #[test]
10411 fn field_access_inside_object_literal_value_does_not_steal_dot() {
10412 let (ast, diags) = parse_str("let x = { a: 1 }.foo;");
10413 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10414 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10415 panic!("expected Let");
10416 };
10417 let (recv_id, name) = field_access(ast.try_expr(value).unwrap());
10418 assert_eq!(name, "foo");
10419 assert_eq!(object_literal(ast.try_expr(recv_id).unwrap()).len(), 1);
10420 }
10421
10422 fn type_of_let(stmt: &crate::Stmt) -> &crate::TypeAnnotation {
10423 match stmt.kind {
10424 crate::StmtKind::Let { ref ty, .. } => ty.as_ref().expect("expected type annotation"),
10425 _ => panic!("expected Let"),
10426 }
10427 }
10428
10429 #[test]
10430 fn parse_array_type_annotation() {
10431 let (ast, diags) = parse_str("let xs: number[] = null;");
10432 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10433 let ty = type_of_let(single_stmt(&ast));
10434 match ty.kind {
10435 crate::TypeAnnotationKind::Array(ref inner) => {
10436 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Name { .. }));
10437 }
10438 _ => panic!("expected Array, got {:?}", ty.kind),
10439 }
10440 }
10441
10442 #[test]
10443 fn parse_nested_array_type_annotation() {
10444 let (ast, diags) = parse_str("let xs: number[][] = null;");
10445 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10446 let ty = type_of_let(single_stmt(&ast));
10447 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
10448 panic!("expected outer Array");
10449 };
10450 let crate::TypeAnnotationKind::Array(ref inner2) = inner.kind else {
10451 panic!("expected inner Array");
10452 };
10453 assert!(matches!(
10454 inner2.kind,
10455 crate::TypeAnnotationKind::Name { .. }
10456 ));
10457 }
10458
10459 #[test]
10460 fn parse_object_type_annotation_simple() {
10461 let (ast, diags) = parse_str("let p: { x: number; y: number } = null;");
10462 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10463 let ty = type_of_let(single_stmt(&ast));
10464 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10465 panic!("expected Object, got {:?}", ty.kind);
10466 };
10467 assert_eq!(fields.len(), 2);
10468 assert_eq!(fields[0].name.name, "x");
10469 assert_eq!(fields[1].name.name, "y");
10470 }
10471
10472 #[test]
10473 fn readonly_is_rejected_on_methods_accessors_and_method_signatures() {
10474 let source = "class K {\n\
10475 readonly m(): number { return 1; }\n\
10476 public readonly n<T>(): number { return 1; }\n\
10477 readonly get g(): number { return 1; }\n\
10478 readonly p: number = 1;\n\
10479 }\n\
10480 type T = { readonly r(a: number): boolean; readonly f: () => void };\n\
10481 interface I { readonly s(): void; readonly q: number; }\n";
10482 let (_, diags) = parse_str(source);
10483 let lines: Vec<usize> = diags
10484 .iter()
10485 .map(|d| {
10486 assert_eq!(
10487 d.message,
10488 "`readonly` can only modify a property or index signature"
10489 );
10490 source[..d.span.start as usize].matches('\n').count() + 1
10491 })
10492 .collect();
10493 assert_eq!(lines, vec![2, 3, 4, 7, 8]);
10494 }
10495
10496 #[test]
10497 fn parse_object_type_method_members() {
10498 let (ast, diags) =
10501 parse_str("let p: { m(): number; opt?(): string; r(a: number): boolean } = null;");
10502 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10503 let ty = type_of_let(single_stmt(&ast));
10504 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10505 panic!("expected Object, got {:?}", ty.kind);
10506 };
10507 assert_eq!(fields.len(), 3);
10508 for field in fields {
10509 assert!(
10510 matches!(field.ty.kind, crate::TypeAnnotationKind::Function { .. }),
10511 "member `{}` should be function-typed, got {:?}",
10512 field.name.name,
10513 field.ty.kind,
10514 );
10515 }
10516 assert!(fields[1].optional, "`opt?()` is an optional member");
10517 let crate::TypeAnnotationKind::Function { ref params, .. } = fields[2].ty.kind else {
10518 unreachable!("checked above");
10519 };
10520 assert_eq!(params.len(), 1);
10521 assert_eq!(params[0].name.name, "a");
10522 }
10523
10524 #[test]
10525 fn parse_object_type_annotation_with_commas() {
10526 let (ast, diags) = parse_str("let p: { x: number, y: number } = null;");
10527 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10528 let ty = type_of_let(single_stmt(&ast));
10529 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10530 panic!("expected Object");
10531 };
10532 assert_eq!(fields.len(), 2);
10533 }
10534
10535 #[test]
10536 fn parse_object_type_annotation_empty() {
10537 let (ast, diags) = parse_str("let p: {} = null;");
10538 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10539 let ty = type_of_let(single_stmt(&ast));
10540 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10541 panic!("expected Object");
10542 };
10543 assert!(fields.is_empty());
10544 }
10545
10546 #[test]
10547 fn parse_object_type_with_optional_field() {
10548 let (ast, diags) = parse_str("let p: { id: number; nick?: string } = null;");
10549 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10550 let ty = type_of_let(single_stmt(&ast));
10551 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10552 panic!("expected Object");
10553 };
10554 assert_eq!(fields.len(), 2);
10555 assert_eq!(fields[0].name.name, "id");
10556 assert!(!fields[0].optional);
10557 assert_eq!(fields[1].name.name, "nick");
10558 assert!(fields[1].optional);
10559 }
10560
10561 #[test]
10562 fn parse_object_type_with_readonly_fields() {
10563 let (ast, diags) = parse_str(
10564 "let issue: { readonly id: string; readonly title?: string; body: string } = null;",
10565 );
10566 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10567 let ty = type_of_let(single_stmt(&ast));
10568 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10569 panic!("expected Object");
10570 };
10571 let fields: Vec<(&str, bool, bool)> = fields
10572 .iter()
10573 .map(|field| (field.name.name.as_str(), field.optional, field.readonly))
10574 .collect();
10575 assert_eq!(
10576 fields,
10577 vec![
10578 ("id", false, true),
10579 ("title", true, true),
10580 ("body", false, false),
10581 ]
10582 );
10583 }
10584
10585 #[test]
10586 fn parse_object_type_mixed_optional_and_required() {
10587 let (ast, diags) = parse_str("let p: { a?: number; b: string; c?: boolean } = null;");
10588 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10589 let ty = type_of_let(single_stmt(&ast));
10590 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10591 panic!("expected Object");
10592 };
10593 let opt: Vec<(&str, bool)> = fields
10594 .iter()
10595 .map(|f| (f.name.name.as_str(), f.optional))
10596 .collect();
10597 assert_eq!(opt, vec![("a", true), ("b", false), ("c", true)]);
10598 }
10599
10600 #[test]
10601 fn parse_object_type_keyword_fields() {
10602 let (ast, diags) =
10603 parse_str("let p: { type: string; default?: number; null: boolean } = null;");
10604 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10605 let ty = type_of_let(single_stmt(&ast));
10606 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10607 panic!("expected Object");
10608 };
10609 let opt: Vec<(&str, bool)> = fields
10610 .iter()
10611 .map(|f| (f.name.name.as_str(), f.optional))
10612 .collect();
10613 assert_eq!(
10614 opt,
10615 vec![("type", false), ("default", true), ("null", false)]
10616 );
10617 }
10618
10619 #[test]
10620 fn parse_object_type_quoted_fields() {
10621 let (ast, diags) =
10622 parse_str(r#"let p: { "content-type": string; "x\u002drequest-id"?: string } = null;"#);
10623 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10624 let ty = type_of_let(single_stmt(&ast));
10625 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10626 panic!("expected Object");
10627 };
10628 let opt: Vec<(&str, bool)> = fields
10629 .iter()
10630 .map(|f| (f.name.name.as_str(), f.optional))
10631 .collect();
10632 assert_eq!(opt, vec![("content-type", false), ("x-request-id", true)]);
10633 }
10634
10635 #[test]
10636 fn parse_interface_with_optional_property() {
10637 let (ast, diags) = parse_str("interface User { id: number; deletedAt?: string; }");
10638 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10639 let stmt = single_stmt(&ast);
10640 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
10641 panic!("expected interface decl");
10642 };
10643 let opt: Vec<(&str, bool)> = members
10644 .iter()
10645 .map(|m| match m {
10646 crate::InterfaceMember::Property { name, optional, .. } => {
10647 (name.name.as_str(), *optional)
10648 }
10649 _ => panic!("expected property"),
10650 })
10651 .collect();
10652 assert_eq!(opt, vec![("id", false), ("deletedAt", true)]);
10653 }
10654
10655 #[test]
10656 fn parse_interface_with_readonly_properties() {
10657 let (ast, diags) = parse_str(
10658 "interface Issue { readonly id: string; readonly title?: string; body: string; }",
10659 );
10660 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10661 let stmt = single_stmt(&ast);
10662 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
10663 panic!("expected interface decl");
10664 };
10665 let opt: Vec<(&str, bool, bool)> = members
10666 .iter()
10667 .map(|member| match member {
10668 crate::InterfaceMember::Property {
10669 name,
10670 optional,
10671 readonly,
10672 ..
10673 } => (name.name.as_str(), *optional, *readonly),
10674 _ => panic!("expected property"),
10675 })
10676 .collect();
10677 assert_eq!(
10678 opt,
10679 vec![
10680 ("id", false, true),
10681 ("title", true, true),
10682 ("body", false, false),
10683 ]
10684 );
10685 }
10686
10687 #[test]
10688 fn parse_readonly_property_name_without_modifier() {
10689 let (ast, diags) = parse_str(
10690 "interface Meta { readonly: boolean; }\nlet meta: { readonly: boolean } = null;",
10691 );
10692 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10693 let crate::StmtKind::InterfaceDecl { ref members, .. } =
10694 ast.try_stmt(ast.top_level[0]).unwrap().kind
10695 else {
10696 panic!("expected interface decl");
10697 };
10698 let crate::InterfaceMember::Property {
10699 ref name, readonly, ..
10700 } = members[0]
10701 else {
10702 panic!("expected property");
10703 };
10704 assert_eq!(name.name, "readonly");
10705 assert!(
10706 !readonly,
10707 "`readonly` here is the property name, not a modifier"
10708 );
10709
10710 let ty = type_of_let(ast.try_stmt(ast.top_level[1]).unwrap());
10711 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10712 panic!("expected Object");
10713 };
10714 assert_eq!(fields[0].name.name, "readonly");
10715 assert!(!fields[0].readonly);
10716 }
10717
10718 #[test]
10719 fn parse_interface_keyword_members() {
10720 let (ast, diags) =
10721 parse_str("interface IssueStatus { type: string; default(): string; null: boolean; }");
10722 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10723 let stmt = single_stmt(&ast);
10724 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
10725 panic!("expected interface decl");
10726 };
10727 let names: Vec<&str> = members
10728 .iter()
10729 .map(|m| match m {
10730 crate::InterfaceMember::Method { name, .. }
10731 | crate::InterfaceMember::Property { name, .. } => name.name.as_str(),
10732 crate::InterfaceMember::IndexSignature(_) => panic!("unexpected index signature"),
10733 })
10734 .collect();
10735 assert_eq!(names, vec!["type", "default", "null"]);
10736 }
10737
10738 #[test]
10739 fn parse_interface_quoted_property_members() {
10740 let (ast, diags) = parse_str(
10741 r#"interface Headers { "content-type": string; "x\u002drequest-id"?: string; }"#,
10742 );
10743 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10744 let stmt = single_stmt(&ast);
10745 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
10746 panic!("expected interface decl");
10747 };
10748 let names: Vec<(&str, bool)> = members
10749 .iter()
10750 .map(|m| match m {
10751 crate::InterfaceMember::Property { name, optional, .. } => {
10752 (name.name.as_str(), *optional)
10753 }
10754 _ => panic!("expected property"),
10755 })
10756 .collect();
10757 assert_eq!(names, vec![("content-type", false), ("x-request-id", true)]);
10758 }
10759
10760 #[test]
10761 fn parse_param_optional_marker() {
10762 let (ast, diags) = parse_str("function f(x?: number): void {}");
10763 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10764 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
10765 panic!("expected function")
10766 };
10767 assert!(params[0].optional);
10768 assert!(params[0].default.is_none());
10769 }
10770
10771 #[test]
10772 fn parse_interface_optional_method() {
10773 let (ast, diags) = parse_str("interface F { foo?(x?: number): void; }");
10774 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10775 let crate::StmtKind::InterfaceDecl { ref members, .. } = single_stmt(&ast).kind else {
10776 panic!("expected interface")
10777 };
10778 let crate::InterfaceMember::Property { optional, ty, .. } = &members[0] else {
10779 panic!("expected callable property")
10780 };
10781 assert!(*optional);
10782 let crate::TypeAnnotationKind::Function { params, .. } = &ty.kind else {
10783 panic!("expected function type")
10784 };
10785 assert!(params[0].optional);
10786 }
10787
10788 #[test]
10789 fn parse_interface_with_call_signature() {
10790 let (ast, diags) = parse_str("interface F { (s: string): number; }");
10791 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10792 let stmt = single_stmt(&ast);
10793 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
10794 panic!("expected interface decl");
10795 };
10796 assert_eq!(members.len(), 1);
10797 match &members[0] {
10798 crate::InterfaceMember::Method { name, params, .. } => {
10799 assert_eq!(name.name, "@call");
10800 assert_eq!(params.len(), 1);
10801 assert_eq!(params[0].name.name, "s");
10802 }
10803 other => panic!("expected method (call signature), got {other:?}"),
10804 }
10805 }
10806
10807 #[test]
10808 fn parse_interface_mixed_call_signature_and_method() {
10809 let (ast, diags) = parse_str("interface F { greet(): string; (x: number): number; }");
10810 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10811 let stmt = single_stmt(&ast);
10812 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
10813 panic!("expected interface decl");
10814 };
10815 let names: Vec<&str> = members
10816 .iter()
10817 .map(|m| match m {
10818 crate::InterfaceMember::Method { name, .. } => name.name.as_str(),
10819 _ => "<property>",
10820 })
10821 .collect();
10822 assert_eq!(names, vec!["greet", "@call"]);
10823 }
10824
10825 #[test]
10826 fn parse_interface_rejects_generic_call_signature() {
10827 let (_ast, diags) = parse_str("interface F { <T>(x: T): T; }");
10828 assert!(
10829 diags
10830 .iter()
10831 .any(|d| d.message.contains("generic call signatures")),
10832 "expected generic-call-signature diagnostic, got {diags:?}"
10833 );
10834 }
10835
10836 #[test]
10837 fn parse_object_type_with_array_field() {
10838 let (ast, diags) = parse_str("let p: { xs: number[] } = null;");
10839 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10840 let ty = type_of_let(single_stmt(&ast));
10841 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10842 panic!("expected Object");
10843 };
10844 assert_eq!(fields[0].name.name, "xs");
10845 assert!(matches!(
10846 fields[0].ty.kind,
10847 crate::TypeAnnotationKind::Array(_)
10848 ));
10849 }
10850
10851 #[test]
10852 fn parse_array_of_objects() {
10853 let (ast, diags) = parse_str("let xs: { x: number }[] = null;");
10854 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10855 let ty = type_of_let(single_stmt(&ast));
10856 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
10857 panic!("expected Array");
10858 };
10859 assert!(matches!(
10860 inner.kind,
10861 crate::TypeAnnotationKind::Object { .. }
10862 ));
10863 }
10864
10865 #[test]
10866 fn parse_tuple_type_annotation() {
10867 let (ast, diags) = parse_str("let p: [string, number] = null;");
10868 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10869 let ty = type_of_let(single_stmt(&ast));
10870 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
10871 panic!("expected Tuple, got {:?}", ty.kind);
10872 };
10873 assert_eq!(elems.len(), 2);
10874 assert!(matches!(
10875 elems[0].kind,
10876 crate::TypeAnnotationKind::Name { .. }
10877 ));
10878 assert!(matches!(
10879 elems[1].kind,
10880 crate::TypeAnnotationKind::Name { .. }
10881 ));
10882 }
10883
10884 #[test]
10885 fn parse_singleton_tuple_type() {
10886 let (ast, diags) = parse_str("let p: [number] = null;");
10887 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10888 let ty = type_of_let(single_stmt(&ast));
10889 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
10890 panic!("expected Tuple");
10891 };
10892 assert_eq!(elems.len(), 1);
10893 }
10894
10895 #[test]
10896 fn parse_nested_tuple_type() {
10897 let (ast, diags) = parse_str("let p: [[number, string], boolean] = null;");
10898 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10899 let ty = type_of_let(single_stmt(&ast));
10900 let crate::TypeAnnotationKind::Tuple(ref outer) = ty.kind else {
10901 panic!("expected outer Tuple");
10902 };
10903 assert_eq!(outer.len(), 2);
10904 let crate::TypeAnnotationKind::Tuple(ref inner) = outer[0].kind else {
10905 panic!("expected inner Tuple");
10906 };
10907 assert_eq!(inner.len(), 2);
10908 }
10909
10910 #[test]
10911 fn parse_array_of_tuple_type() {
10912 let (ast, diags) = parse_str("let xs: [string, number][] = null;");
10913 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10914 let ty = type_of_let(single_stmt(&ast));
10915 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
10916 panic!("expected outer Array, got {:?}", ty.kind);
10917 };
10918 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Tuple(_)));
10919 }
10920
10921 #[test]
10922 fn parse_trailing_comma_tuple_type() {
10923 let (ast, diags) = parse_str("let p: [number, string,] = null;");
10924 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10925 let ty = type_of_let(single_stmt(&ast));
10926 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
10927 panic!("expected Tuple");
10928 };
10929 assert_eq!(elems.len(), 2);
10930 }
10931
10932 #[test]
10933 fn parse_empty_tuple_type_rejected() {
10934 let (_ast, diags) = parse_str("let p: [] = null;");
10935 assert!(
10936 diags.iter().any(|d| d
10937 .message
10938 .contains("tuple types must have at least one element")),
10939 "expected empty-tuple diagnostic, got: {diags:?}"
10940 );
10941 }
10942
10943 #[test]
10944 fn parse_labeled_tuple_drops_labels() {
10945 let (ast, diags) = parse_str("let p: [first: number, string] = null;");
10946 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10947 let ty = type_of_let(single_stmt(&ast));
10948 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
10949 panic!("expected Tuple, got {:?}", ty.kind);
10950 };
10951 assert_eq!(elems.len(), 2);
10952 assert!(
10953 elems
10954 .iter()
10955 .all(|e| matches!(e.kind, crate::TypeAnnotationKind::Name { .. }))
10956 );
10957 }
10958
10959 #[test]
10960 fn parse_labeled_optional_tuple_element() {
10961 let (ast, diags) = parse_str("let p: [first: number, second?: string] = null;");
10962 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10963 let ty = type_of_let(single_stmt(&ast));
10964 let crate::TypeAnnotationKind::Tuple(elements) = &ty.kind else {
10965 panic!("expected tuple")
10966 };
10967 assert!(matches!(
10968 elements[1].kind,
10969 crate::TypeAnnotationKind::Optional(_)
10970 ));
10971 }
10972
10973 #[test]
10974 fn parse_readonly_binds_to_the_postfix_type() {
10975 let (ast, diags) = parse_str("let p: readonly number[][] | null = null;");
10976 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10977 let ty = type_of_let(single_stmt(&ast));
10978 let crate::TypeAnnotationKind::Union(ref members) = ty.kind else {
10979 panic!("expected Union, got {:?}", ty.kind);
10980 };
10981 let crate::TypeAnnotationKind::Readonly(ref operand) = members[0].kind else {
10982 panic!("expected Readonly, got {:?}", members[0].kind);
10983 };
10984 let crate::TypeAnnotationKind::Array(ref inner) = operand.kind else {
10985 panic!("expected Array, got {:?}", operand.kind);
10986 };
10987 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Array(_)));
10988 }
10989
10990 #[test]
10991 fn parse_readonly_as_a_type_name_is_not_the_operator() {
10992 let (ast, diags) = parse_str("let p: readonly = null;");
10993 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10994 let ty = type_of_let(single_stmt(&ast));
10995 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
10996 }
10997
10998 #[test]
10999 fn parse_generic_one_arg() {
11000 let (ast, diags) = parse_str("let x: Foo<number> = null;");
11001 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11002 let ty = type_of_let(single_stmt(&ast));
11003 let crate::TypeAnnotationKind::Name { ref name, ref args } = ty.kind else {
11004 panic!("expected Name, got {:?}", ty.kind);
11005 };
11006 assert_eq!(name.span, crate::Span::new(F, 7, 10).unwrap());
11007 assert_eq!(ty.span, crate::Span::new(F, 7, 18).unwrap());
11008 assert_eq!(args.len(), 1);
11009 assert!(matches!(
11010 args[0].kind,
11011 crate::TypeAnnotationKind::Name { .. }
11012 ));
11013 }
11014
11015 #[test]
11016 fn parse_generic_two_args() {
11017 let (ast, diags) = parse_str("let m: Map<string, number> = null;");
11018 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11019 let ty = type_of_let(single_stmt(&ast));
11020 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11021 panic!("expected Name");
11022 };
11023 assert_eq!(args.len(), 2);
11024 }
11025
11026 #[test]
11027 fn parse_generic_nested() {
11028 let (ast, diags) = parse_str("let b: Box<Box<T>> = null;");
11029 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11030 let ty = type_of_let(single_stmt(&ast));
11031 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11032 panic!("expected outer Name");
11033 };
11034 assert_eq!(args.len(), 1);
11035 let crate::TypeAnnotationKind::Name {
11036 args: ref inner, ..
11037 } = args[0].kind
11038 else {
11039 panic!("expected inner Name");
11040 };
11041 assert_eq!(inner.len(), 1);
11042 }
11043
11044 #[test]
11045 fn parse_generic_array_postfix() {
11046 let (ast, diags) = parse_str("let xs: Foo<number>[] = null;");
11047 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11048 let ty = type_of_let(single_stmt(&ast));
11049 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11050 panic!("expected Array");
11051 };
11052 assert!(
11053 matches!(inner.kind, crate::TypeAnnotationKind::Name { ref args, .. } if args.len() == 1)
11054 );
11055 }
11056
11057 #[test]
11058 fn parse_generic_empty_diagnoses() {
11059 let (_ast, diags) = parse_str("let x: Foo<> = null;");
11060 assert!(
11061 diags
11062 .iter()
11063 .any(|d| d.message.contains("empty generic argument list")),
11064 "expected empty-generic-args diagnostic, got: {diags:?}"
11065 );
11066 }
11067
11068 #[test]
11069 fn parse_generic_trailing_comma_ok() {
11070 let (_ast, diags) = parse_str("let x: Foo<number,> = null;");
11071 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11072 }
11073
11074 #[test]
11075 fn parse_generic_missing_close_diagnoses() {
11076 let (_ast, diags) = parse_str("let x: Foo<number = null;");
11077 assert!(
11078 diags
11079 .iter()
11080 .any(|d| d.message.contains("expected `,` or `>`")),
11081 "expected unterminated-generic diagnostic, got: {diags:?}"
11082 );
11083 }
11084
11085 #[test]
11086 fn parse_qualified_two_segments() {
11087 let (ast, diags) = parse_str("let x: Foo.Bar = null;");
11088 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11089 let ty = type_of_let(single_stmt(&ast));
11090 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11091 panic!("expected Qualified, got {:?}", ty.kind);
11092 };
11093 assert_eq!(path.len(), 2);
11094 assert_eq!(path[0].span, crate::Span::new(F, 7, 10).unwrap());
11096 assert_eq!(path[1].span, crate::Span::new(F, 11, 14).unwrap());
11097 assert!(args.is_empty());
11098 assert_eq!(ty.span, crate::Span::new(F, 7, 14).unwrap());
11099 }
11100
11101 #[test]
11102 fn parse_qualified_three_segments() {
11103 let (ast, diags) = parse_str("let x: Foo.Bar.Baz = null;");
11104 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11105 let ty = type_of_let(single_stmt(&ast));
11106 let crate::TypeAnnotationKind::Qualified { ref path, .. } = ty.kind else {
11107 panic!("expected Qualified, got {:?}", ty.kind);
11108 };
11109 assert_eq!(path.len(), 3);
11110 }
11111
11112 #[test]
11113 fn parse_qualified_with_generic_args() {
11114 let (ast, diags) = parse_str("let x: Foo.Box<number> = null;");
11115 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11116 let ty = type_of_let(single_stmt(&ast));
11117 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11118 panic!("expected Qualified, got {:?}", ty.kind);
11119 };
11120 assert_eq!(path.len(), 2);
11121 assert_eq!(args.len(), 1);
11122 assert!(matches!(
11123 args[0].kind,
11124 crate::TypeAnnotationKind::Name { .. }
11125 ));
11126 }
11127
11128 #[test]
11129 fn parse_qualified_with_array_postfix() {
11130 let (ast, diags) = parse_str("let xs: Foo.Bar[] = null;");
11131 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11132 let ty = type_of_let(single_stmt(&ast));
11133 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11134 panic!("expected Array, got {:?}", ty.kind);
11135 };
11136 assert!(matches!(
11137 inner.kind,
11138 crate::TypeAnnotationKind::Qualified { .. }
11139 ));
11140 }
11141
11142 #[test]
11143 fn parse_qualified_trailing_dot_diagnoses() {
11144 let (_ast, diags) = parse_str("let x: Foo. = null;");
11145 assert!(
11146 diags.iter().any(|d| d
11147 .message
11148 .contains("expected identifier after `.` in type name")),
11149 "expected dotted-name diagnostic, got: {diags:?}"
11150 );
11151 }
11152
11153 #[test]
11154 fn parse_qualified_void_root_rejected_dot() {
11155 let (ast, diags) = parse_str("function f(): void.Bar { }");
11156 assert!(!diags.is_empty(), "expected a diagnostic for `void.Bar`");
11157 let _ = ast;
11158 }
11159
11160 fn union_members(ty: &crate::TypeAnnotation) -> &[crate::TypeAnnotation] {
11161 match &ty.kind {
11162 crate::TypeAnnotationKind::Union(members) => members,
11163 other => panic!("expected Union, got {other:?}"),
11164 }
11165 }
11166
11167 #[test]
11168 fn parse_union_two_members() {
11169 let (ast, diags) = parse_str("let x: number | string = null;");
11170 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11171 let ty = type_of_let(single_stmt(&ast));
11172 let members = union_members(ty);
11173 assert_eq!(members.len(), 2);
11174 assert!(matches!(
11175 members[0].kind,
11176 crate::TypeAnnotationKind::Name { .. }
11177 ));
11178 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
11180 members[1].kind,
11181 crate::TypeAnnotationKind::Name { .. }
11182 ));
11183 assert_eq!(members[1].span, crate::Span::new(F, 16, 22).unwrap()); assert_eq!(ty.span, crate::Span::new(F, 7, 22).unwrap());
11185 }
11186
11187 #[test]
11188 fn parse_union_three_members() {
11189 let (ast, diags) = parse_str("let x: A | B | C = null;");
11190 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11191 let ty = type_of_let(single_stmt(&ast));
11192 let members = union_members(ty);
11193 assert_eq!(members.len(), 3);
11194 for m in members {
11195 assert!(matches!(m.kind, crate::TypeAnnotationKind::Name { .. }));
11196 }
11197 }
11198
11199 #[test]
11200 fn parse_union_lower_than_array() {
11201 let (ast, diags) = parse_str("let x: number[] | string = null;");
11202 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11203 let ty = type_of_let(single_stmt(&ast));
11204 let members = union_members(ty);
11205 assert_eq!(members.len(), 2);
11206 assert!(
11207 matches!(members[0].kind, crate::TypeAnnotationKind::Array(_)),
11208 "expected first member to be Array, got {:?}",
11209 members[0].kind
11210 );
11211 assert!(matches!(
11212 members[1].kind,
11213 crate::TypeAnnotationKind::Name { .. }
11214 ));
11215 }
11216
11217 #[test]
11218 fn parse_union_with_null_member() {
11219 let (ast, diags) = parse_str("let x: number | null = null;");
11220 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11221 let ty = type_of_let(single_stmt(&ast));
11222 let members = union_members(ty);
11223 assert_eq!(members.len(), 2);
11224 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
11226 members[1].kind,
11227 crate::TypeAnnotationKind::Name { .. }
11228 ));
11229 assert_eq!(members[1].span, crate::Span::new(F, 16, 20).unwrap()); }
11231
11232 #[test]
11233 fn parse_union_in_function_return_type() {
11234 let (ast, diags) = parse_str("function f(): number | string { }");
11235 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11236 let stmt = single_stmt(&ast);
11237 match stmt.kind {
11238 crate::StmtKind::Function {
11239 ref return_type, ..
11240 } => {
11241 let return_type = return_type.as_ref().expect("plain return type");
11242 let members = union_members(return_type);
11243 assert_eq!(members.len(), 2);
11244 }
11245 _ => panic!("expected Function"),
11246 }
11247 }
11248
11249 #[test]
11250 fn parse_union_in_function_param_type() {
11251 let (ast, diags) = parse_str("function f(x: number | string): void { }");
11252 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11253 let stmt = single_stmt(&ast);
11254 match stmt.kind {
11255 crate::StmtKind::Function { ref params, .. } => {
11256 let p_ty = params[0].ty.as_ref().expect("param type");
11257 let members = union_members(p_ty);
11258 assert_eq!(members.len(), 2);
11259 }
11260 _ => panic!("expected Function"),
11261 }
11262 }
11263
11264 #[test]
11265 fn parse_union_in_object_field_type() {
11266 let (ast, diags) = parse_str("let p: { x: number | string } = null;");
11267 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11268 let ty = type_of_let(single_stmt(&ast));
11269 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11270 panic!("expected Object, got {:?}", ty.kind);
11271 };
11272 assert_eq!(fields.len(), 1);
11273 let members = union_members(&fields[0].ty);
11274 assert_eq!(members.len(), 2);
11275 }
11276
11277 #[test]
11278 fn parse_union_leading_pipe_is_forgiven() {
11279 let (ast, diags) = parse_str("let x: | number | string = null;");
11280 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11281 let ty = type_of_let(single_stmt(&ast));
11282 let members = union_members(ty);
11283 assert_eq!(members.len(), 2);
11284 }
11285
11286 #[test]
11287 fn parse_union_leading_pipe_single_member_unwraps() {
11288 let (ast, diags) = parse_str("let x: | number = null;");
11289 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11290 let ty = type_of_let(single_stmt(&ast));
11291 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11292 }
11293
11294 #[test]
11295 fn parse_union_trailing_pipe_diagnoses() {
11296 let (_, diags) = parse_str("let x: number | = null;");
11297 assert!(!diags.is_empty());
11298 assert_eq!(diags[0].message, "expected type");
11299 }
11300
11301 #[test]
11302 fn snapshot_union_type_mix() {
11303 let (ast, diags) = parse_str("let xs: number[] | string | null = null;");
11304 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11305 insta::assert_debug_snapshot!(ast);
11306 }
11307
11308 #[test]
11309 fn parse_object_type_duplicate_field_diagnoses() {
11310 let (_, diags) = parse_str("let p: { readonly x: number; x: string } = null;");
11311 assert!(!diags.is_empty());
11312 assert!(
11313 diags
11314 .iter()
11315 .any(|d| d.message == "duplicate field `x` in object type"),
11316 "expected duplicate-field diagnostic, got: {diags:?}"
11317 );
11318 }
11319
11320 #[test]
11321 fn snapshot_object_and_array_literals() {
11322 let (ast, diags) = parse_str(r#"let mix = [{ a: 1, b: "hi" }, {}];"#);
11323 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11324 insta::assert_debug_snapshot!(ast);
11325 }
11326
11327 #[test]
11328 fn snapshot_function_with_while() {
11329 let source = "function loop(n: number): void {\n while (n > 0) {\n log(n);\n }\n}";
11330 let (ast, diags) = parse_str(source);
11331 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11332 insta::assert_debug_snapshot!(ast);
11333 }
11334
11335 fn parse_arrow_const(
11336 source: &str,
11337 ) -> (
11338 Ast,
11339 Vec<crate::ParamDecl>,
11340 Option<crate::TypeAnnotation>,
11341 crate::ArrowBody,
11342 ) {
11343 let (ast, diags) = parse_str(source);
11344 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11345 assert_eq!(ast.top_level.len(), 1);
11346 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11347 let value = match &stmt.kind {
11348 StmtKind::Const { value, .. } => *value,
11349 _ => panic!("expected const declaration"),
11350 };
11351 let value = match ast.try_expr(value).unwrap().kind {
11352 ExprKind::FunctionExpression { function, .. } => function,
11353 _ => value,
11354 };
11355 let expr = ast.try_expr(value).unwrap().clone();
11356 let (params, return_type, body) = match expr.kind {
11357 ExprKind::Arrow {
11358 params,
11359 return_type,
11360 body,
11361 ..
11362 } => (params, return_type, body),
11363 other => panic!("expected Arrow, got {other:?}"),
11364 };
11365 (ast, params, return_type, body)
11366 }
11367
11368 #[test]
11369 fn parses_anonymous_function_expression_as_arrow() {
11370 let (ast, params, return_type, body) =
11371 parse_arrow_const("const f = function (x: number): number { return x + 1; };");
11372 assert_eq!(params.len(), 1);
11373 assert_eq!(params[0].name.name, "x");
11374 assert!(return_type.is_some());
11375 assert!(matches!(body, crate::ArrowBody::Block(_)));
11376 let _ = ast;
11377 }
11378
11379 #[test]
11380 fn parses_function_expression_with_no_params() {
11381 let (_ast, params, _return_type, body) =
11382 parse_arrow_const("const f = function (): void {};");
11383 assert!(params.is_empty());
11384 assert!(matches!(body, crate::ArrowBody::Block(_)));
11385 }
11386
11387 #[test]
11389 fn parses_named_function_expression_signature() {
11390 let (_ast, params, _return_type, _body) =
11391 parse_arrow_const("const f = function named(x: number): number { return x + 1; };");
11392 assert_eq!(params.len(), 1);
11393 }
11394
11395 #[test]
11397 fn accepts_a_named_function_expression_that_calls_itself() {
11398 let (_ast, diags) =
11399 parse_str("const f = function bar(x: number): number { return bar(x); };");
11400 assert!(diags.is_empty(), "{diags:?}");
11401 }
11402
11403 #[test]
11405 fn accepts_a_named_function_expression_using_the_name_as_a_property() {
11406 let (_ast, diags) = parse_str("const f = function bar(o: O): number { return o.bar; };");
11407 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11408 }
11409
11410 #[test]
11412 fn accepts_a_named_function_expression_using_the_name_as_an_object_key() {
11413 let (_ast, diags) = parse_str(
11414 "const f = function bar(x: number): number { const o = { bar: 1 }; return o.bar + x; };",
11415 );
11416 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11417 }
11418
11419 #[test]
11421 fn accepts_a_named_function_expression_with_its_name_in_a_string() {
11422 let (_ast, diags) =
11423 parse_str("const f = function bar(x: number): string { return \"bar\"; };");
11424 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11425 }
11426
11427 #[test]
11429 fn accepts_a_self_call_inside_a_template_substitution() {
11430 let (_ast, diags) =
11431 parse_str("const f = function bar(x: number): string { return `${bar(0)}`; };");
11432 assert!(diags.is_empty(), "{diags:?}");
11433 }
11434
11435 #[test]
11436 fn rejects_a_generic_function_expression() {
11437 let (_ast, diags) = parse_str("const f = function <T>(x: T): T { return x; };");
11438 assert!(
11439 diags
11440 .iter()
11441 .any(|d| d.message.contains("generic function expressions")),
11442 "generic function expression should be rejected: {diags:?}"
11443 );
11444 }
11445
11446 #[test]
11447 fn accepts_this_inside_a_function_expression_in_a_class_method() {
11448 let (_ast, diags) = parse_str(
11449 "class C { x: number = 1; m(): number { \
11450 const f = function (this: { x: number }): number { return this.x; }; return f(); } }",
11451 );
11452 assert!(diags.is_empty(), "{diags:?}");
11453 }
11454
11455 #[test]
11456 fn retains_this_boundary_inside_method_shorthand() {
11457 let (ast, diags) = parse_str(
11458 "class C { x: number = 1; m(): number { \
11459 const o = { x: 2, g(): number { return this.x; } }; return o.g(); } }",
11460 );
11461 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11462 assert!((0..ast.exprs_len()).any(|index| matches!(
11463 ast.try_expr(crate::ExprId(index as u32)).unwrap().kind,
11464 ExprKind::ThisOutsideReceiver
11465 )));
11466 }
11467
11468 #[test]
11469 fn this_still_parses_in_an_ordinary_class_method() {
11470 let (_ast, diags) = parse_str("class C { x: number = 1; m(): number { return this.x; } }");
11471 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11472 }
11473
11474 #[test]
11476 fn function_keyword_still_parses_as_an_object_key() {
11477 let (_ast, diags) = parse_str("let o = { function: 1 };");
11478 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11479 }
11480
11481 fn object_literal_member_values(source: &str) -> (Ast, Vec<crate::ExprId>) {
11482 let (ast, diags) = parse_str(source);
11483 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11484 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11485 let value = match &stmt.kind {
11486 StmtKind::Const { value, .. } => *value,
11487 other => panic!("expected a const declaration, got {other:?}"),
11488 };
11489 let members = match &ast.try_expr(value).unwrap().kind {
11490 ExprKind::ObjectLiteral { members } => members
11491 .iter()
11492 .map(crate::ObjectLiteralMember::value)
11493 .collect::<Vec<_>>(),
11494 other => panic!("expected ObjectLiteral, got {other:?}"),
11495 };
11496 (ast, members)
11497 }
11498
11499 #[test]
11500 fn parses_object_literal_method_shorthand_as_arrow() {
11501 let (ast, members) =
11502 object_literal_member_values("const o = { m(x: number): number { return x; } };");
11503 assert_eq!(members.len(), 1);
11504 match &ast.try_expr(members[0]).unwrap().kind {
11505 ExprKind::Arrow { params, body, .. } => {
11506 assert_eq!(params.len(), 1);
11507 assert_eq!(params[0].name.name, "x");
11508 assert!(matches!(body, crate::ArrowBody::Block(_)));
11509 }
11510 other => panic!("expected Arrow, got {other:?}"),
11511 }
11512 }
11513
11514 #[test]
11516 fn parses_method_shorthand_with_a_keyword_name() {
11517 let (ast, members) =
11518 object_literal_member_values("const o = { if(x: number): number { return x; } };");
11519 assert!(matches!(
11520 ast.try_expr(members[0]).unwrap().kind,
11521 ExprKind::Arrow { .. }
11522 ));
11523 }
11524
11525 #[test]
11526 fn parses_method_shorthand_alongside_plain_and_shorthand_properties() {
11527 let (ast, members) =
11528 object_literal_member_values("const o = { a: 1, m(): void {}, b: 2 };");
11529 assert_eq!(members.len(), 3);
11530 assert!(matches!(
11531 ast.try_expr(members[1]).unwrap().kind,
11532 ExprKind::Arrow { .. }
11533 ));
11534 }
11535
11536 #[test]
11537 fn parses_bare_ident_arrow_expression_body() {
11538 let (_ast, params, return_type, body) = parse_arrow_const("const f = x => x + 1;");
11539 assert_eq!(params.len(), 1);
11540 assert_eq!(params[0].name.name, "x");
11541 assert!(params[0].ty.is_none());
11542 assert!(return_type.is_none());
11543 assert!(matches!(body, crate::ArrowBody::Expr(_)));
11544 }
11545
11546 #[test]
11547 fn parses_zero_param_arrow() {
11548 let (_ast, params, return_type, body) = parse_arrow_const("const f = () => 0;");
11549 assert!(params.is_empty());
11550 assert!(return_type.is_none());
11551 assert!(matches!(body, crate::ArrowBody::Expr(_)));
11552 }
11553
11554 #[test]
11555 fn parses_paren_unannotated_arrow() {
11556 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x) => x;");
11557 assert_eq!(params.len(), 1);
11558 assert_eq!(params[0].name.name, "x");
11559 assert!(params[0].ty.is_none());
11560 }
11561
11562 #[test]
11563 fn parses_typed_param_arrow() {
11564 let (_ast, params, return_type, body) =
11565 parse_arrow_const("const f = (x: number) => x * 2;");
11566 assert_eq!(params.len(), 1);
11567 assert!(params[0].ty.is_some());
11568 assert!(return_type.is_none());
11569 assert!(matches!(body, crate::ArrowBody::Expr(_)));
11570 }
11571
11572 #[test]
11573 fn parses_typed_param_and_return_arrow() {
11574 let (_ast, params, return_type, body) =
11575 parse_arrow_const("const f = (x: number): number => x * 2;");
11576 assert_eq!(params.len(), 1);
11577 assert!(params[0].ty.is_some());
11578 assert!(return_type.is_some());
11579 assert!(matches!(body, crate::ArrowBody::Expr(_)));
11580 }
11581
11582 #[test]
11583 fn parses_multi_param_unannotated_arrow() {
11584 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x, y) => x + y;");
11585 assert_eq!(params.len(), 2);
11586 assert_eq!(params[0].name.name, "x");
11587 assert_eq!(params[1].name.name, "y");
11588 assert!(params[0].ty.is_none());
11589 assert!(params[1].ty.is_none());
11590 }
11591
11592 #[test]
11593 fn parses_multi_param_typed_arrow() {
11594 let (_ast, params, _return_type, _body) =
11595 parse_arrow_const("const f = (x: number, y: number) => x + y;");
11596 assert_eq!(params.len(), 2);
11597 assert!(params[0].ty.is_some());
11598 assert!(params[1].ty.is_some());
11599 }
11600
11601 #[test]
11602 fn parses_block_body_arrow() {
11603 let (_ast, params, _return_type, body) =
11604 parse_arrow_const("const f = (x: number) => { return x; };");
11605 assert_eq!(params.len(), 1);
11606 assert!(matches!(body, crate::ArrowBody::Block(_)));
11607 }
11608
11609 #[test]
11610 fn rejects_typed_bare_ident_arrow() {
11611 let (_ast, diags) = parse_str("const f = x: number => x;");
11612 assert!(
11613 !diags.is_empty(),
11614 "expected diagnostics for `x: number => …`",
11615 );
11616 }
11617
11618 #[test]
11619 fn arrow_inside_call_argument_parses() {
11620 let (ast, diags) = parse_str("f((x: number) => x + 1, 2);");
11621 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11622 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11623 let call = match &stmt.kind {
11624 StmtKind::Expr(eid) => ast.try_expr(*eid).unwrap(),
11625 _ => panic!("expected expression statement"),
11626 };
11627 let args = match &call.kind {
11628 ExprKind::Call { args, .. } => args.clone(),
11629 other => panic!("expected Call, got {other:?}"),
11630 };
11631 assert_eq!(args.len(), 2);
11632 assert!(matches!(
11633 ast.try_expr(args[0]).unwrap().kind,
11634 ExprKind::Arrow { .. }
11635 ));
11636 }
11637
11638 #[test]
11639 fn function_decl_param_still_requires_annotation() {
11640 let (_ast, diags) = parse_str("function f(x): number { return x; }");
11641 assert!(
11642 diags.iter().any(|d| d.message.contains("type annotation")),
11643 "expected `parameter requires a type annotation` diagnostic, got {diags:?}",
11644 );
11645 }
11646
11647 #[test]
11648 fn paren_expression_still_parses_when_no_arrow() {
11649 let (ast, diags) = parse_str("const a = (1 + 2);");
11650 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11651 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11652 let value = match &stmt.kind {
11653 StmtKind::Const { value, .. } => *value,
11654 _ => panic!("expected const declaration"),
11655 };
11656 assert!(matches!(
11657 ast.try_expr(value).unwrap().kind,
11658 ExprKind::Paren(_)
11659 ));
11660 }
11661
11662 #[test]
11663 fn parse_named_import_single() {
11664 let (ast, diags) = parse_str("import { v4 } from \"submilli:uuid\";");
11665 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11666 assert_eq!(ast.top_level.len(), 1);
11667 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11668 let (module, kind) = match &stmt.kind {
11669 StmtKind::Import { module, kind, .. } => (module, kind),
11670 other => panic!("expected Import, got {other:?}"),
11671 };
11672 assert_eq!(module, "submilli:uuid");
11673 let specs = match kind {
11674 ImportKind::Named(specs) => specs,
11675 other => panic!("expected Named, got {other:?}"),
11676 };
11677 assert_eq!(specs.len(), 1);
11678 assert_eq!(specs[0].imported_name.name, "v4");
11679 assert_eq!(specs[0].local_name.name, "v4");
11680 }
11681
11682 #[test]
11683 fn parse_named_import_multi_and_alias() {
11684 let (ast, diags) =
11685 parse_str("import { v4, v7 as makeId, validate } from \"submilli:uuid\";");
11686 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11687 let specs = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
11688 StmtKind::Import {
11689 kind: ImportKind::Named(s),
11690 ..
11691 } => s.clone(),
11692 other => panic!("expected named import, got {other:?}"),
11693 };
11694 assert_eq!(specs.len(), 3);
11695 assert_eq!(specs[0].imported_name.name, "v4");
11696 assert_eq!(specs[0].local_name.name, "v4");
11697 assert_eq!(specs[1].imported_name.name, "v7");
11698 assert_eq!(specs[1].local_name.name, "makeId");
11699 assert_eq!(specs[2].imported_name.name, "validate");
11700 assert_eq!(specs[2].local_name.name, "validate");
11701 }
11702
11703 #[test]
11704 fn parse_namespace_import() {
11705 let (ast, diags) = parse_str("import uuid from \"submilli:uuid\";");
11706 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11707 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
11708 StmtKind::Import { kind, .. } => kind.clone(),
11709 other => panic!("expected import, got {other:?}"),
11710 };
11711 let local = match kind {
11712 ImportKind::Namespace { local_name } => local_name,
11713 other => panic!("expected Namespace, got {other:?}"),
11714 };
11715 assert_eq!(local.name, "uuid");
11716 }
11717
11718 #[test]
11719 fn parse_wildcard_namespace_import_synonym() {
11720 let (ast, diags) = parse_str("import * as uuid from \"submilli:uuid\";");
11721 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11722 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
11723 StmtKind::Import { kind, .. } => kind.clone(),
11724 other => panic!("expected import, got {other:?}"),
11725 };
11726 let local = match kind {
11727 ImportKind::Namespace { local_name } => local_name,
11728 other => panic!("expected Namespace, got {other:?}"),
11729 };
11730 assert_eq!(local.name, "uuid");
11731 }
11732
11733 #[test]
11734 fn parse_wildcard_namespace_import_without_as_rejected() {
11735 let (_ast, diags) = parse_str("import * from \"submilli:uuid\";");
11736 assert!(
11737 diags
11738 .iter()
11739 .any(|d| d.message.contains("expected `as <name>`")),
11740 "expected missing-as diagnostic, got: {diags:?}",
11741 );
11742 }
11743
11744 #[test]
11745 fn parse_combined_default_and_named_rejected() {
11746 let (_ast, diags) = parse_str("import uuid, { v4 } from \"submilli:uuid\";");
11747 assert!(
11748 diags
11749 .iter()
11750 .any(|d| d.message.contains("combining default and named")),
11751 "expected combined-import diagnostic, got: {diags:?}",
11752 );
11753 }
11754
11755 #[test]
11756 fn parse_side_effect_import_rejected() {
11757 let (_ast, diags) = parse_str("import \"submilli:uuid\";");
11758 assert!(
11759 diags
11760 .iter()
11761 .any(|d| d.message.contains("side-effect-only imports")),
11762 "expected side-effect-import diagnostic, got: {diags:?}",
11763 );
11764 }
11765
11766 #[test]
11767 fn parse_empty_specifier_list_rejected() {
11768 let (_ast, diags) = parse_str("import {} from \"submilli:uuid\";");
11769 assert!(
11770 diags
11771 .iter()
11772 .any(|d| d.message.contains("empty import specifier list")),
11773 "expected empty-specifier diagnostic, got: {diags:?}",
11774 );
11775 }
11776
11777 #[test]
11778 fn parse_import_inside_function_body_rejected() {
11779 let (_ast, diags) =
11780 parse_str("function main(): void { import { v4 } from \"submilli:uuid\"; }");
11781 assert!(
11782 diags
11783 .iter()
11784 .any(|d| d.message.contains("must appear at the top of the file")),
11785 "expected top-level diagnostic, got: {diags:?}",
11786 );
11787 }
11788
11789 #[test]
11790 fn parse_missing_from_rejected() {
11791 let (_ast, diags) = parse_str("import { v4 } \"submilli:uuid\";");
11792 assert!(
11793 diags.iter().any(|d| d.message.contains("expected `from`")),
11794 "expected missing-from diagnostic, got: {diags:?}",
11795 );
11796 }
11797
11798 #[test]
11799 fn parse_missing_module_specifier_rejected() {
11800 let (_ast, diags) = parse_str("import { v4 } from;");
11801 assert!(
11802 diags
11803 .iter()
11804 .any(|d| d.message.contains("expected module specifier")),
11805 "expected missing-module diagnostic, got: {diags:?}",
11806 );
11807 }
11808
11809 #[test]
11810 fn parse_export_function_marks_exported() {
11811 let (ast, diags) = parse_str("export function main(): void {}");
11814 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11815 let stmt = single_stmt(&ast);
11816 assert!(
11817 matches!(stmt.kind, crate::StmtKind::Function { .. }),
11818 "expected Function decl, got {:?}",
11819 stmt.kind,
11820 );
11821 assert_eq!(ast.exported_decls.len(), 1, "one exported decl");
11822 assert_eq!(ast.exported_decls[0].stmt, ast.top_level[0]);
11823 }
11824
11825 #[test]
11826 fn parse_export_const_type_interface_enum_are_marked() {
11827 for src in [
11828 "export const X: number = 1;",
11829 "export type Id = string;",
11830 "export interface P { x: number; }",
11831 "export enum E { A, B }",
11832 ] {
11833 let (ast, diags) = parse_str(src);
11834 assert!(diags.is_empty(), "unexpected diags for {src:?}: {diags:?}");
11835 assert_eq!(ast.top_level.len(), 1, "one decl for {src:?}");
11836 assert_eq!(ast.exported_decls.len(), 1, "one exported decl for {src:?}");
11837 }
11838 }
11839
11840 #[test]
11841 fn parse_export_default_is_rejected_with_focused_message() {
11842 let (_ast, diags) = parse_str("export default function main(): void {}");
11843 assert!(
11844 diags
11845 .iter()
11846 .any(|d| d.message.contains("`export default` is not supported")),
11847 "expected focused export-default diagnostic, got: {diags:?}",
11848 );
11849 assert!(
11850 !diags.iter().any(|d| d.message.contains("expected `;`")),
11851 "the spurious missing-semicolon error must be gone: {diags:?}",
11852 );
11853 }
11854
11855 #[test]
11856 fn parse_export_list_parses_as_export_from() {
11857 let (ast, diags) = parse_str("function main(): void {}\nexport { main };");
11860 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11861 assert_eq!(ast.top_level.len(), 2, "function + export-from");
11862 let export = ast.try_stmt(ast.top_level[1]).unwrap();
11863 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
11864 panic!("expected ExportFrom, got {:?}", export.kind);
11865 };
11866 assert!(source.is_none(), "bare `export {{ x }}` has no source");
11867 assert_eq!(specs.len(), 1);
11868 assert_eq!(specs[0].imported_name.name, "main");
11869 assert_eq!(specs[0].local_name.name, "main");
11870 }
11871
11872 #[test]
11873 fn parse_export_from_with_source_and_alias_parses() {
11874 let (ast, diags) = parse_str("export { foo, bar as baz } from \"./util\";");
11875 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11876 let export = ast.try_stmt(ast.top_level[0]).unwrap();
11877 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
11878 panic!("expected ExportFrom, got {:?}", export.kind);
11879 };
11880 let Some((module, _)) = source else {
11881 panic!("expected a `from` source");
11882 };
11883 assert_eq!(module, "./util");
11884 assert_eq!(specs.len(), 2);
11885 assert_eq!(specs[1].imported_name.name, "bar");
11886 assert_eq!(specs[1].local_name.name, "baz");
11887 }
11888
11889 #[test]
11890 fn parse_export_inside_function_body_rejected() {
11891 let (_ast, diags) = parse_str("function main(): void { export const X: number = 1; }");
11892 assert!(
11893 diags
11894 .iter()
11895 .any(|d| d.message.contains("must appear at the top of the file")),
11896 "expected top-level diagnostic, got: {diags:?}",
11897 );
11898 }
11899
11900 #[test]
11901 fn parse_throw_with_expression() {
11902 let (ast, diags) = parse_str(r#"throw new Error("oops");"#);
11903 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11904 let stmt = single_stmt(&ast);
11905 let crate::StmtKind::Throw { value } = stmt.kind else {
11906 panic!("expected Throw, got {:?}", stmt.kind);
11907 };
11908 assert!(matches!(
11909 ast.try_expr(value).unwrap().kind,
11910 crate::ExprKind::New { .. }
11911 ));
11912 }
11913
11914 #[test]
11915 fn parse_new_with_dotted_callee() {
11916 let (ast, diags) = parse_str("new Temporal.Duration({});");
11917 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11918 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
11919 panic!("expected expression statement");
11920 };
11921 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
11922 panic!("expected New");
11923 };
11924 let crate::ExprKind::FieldAccess { name, .. } = &ast.try_expr(*callee).unwrap().kind else {
11925 panic!("expected FieldAccess callee");
11926 };
11927 assert_eq!(name.name, "Duration");
11928 }
11929
11930 #[test]
11931 fn parse_new_with_three_level_dotted_callee() {
11932 let (ast, diags) = parse_str("new Foo.Bar.Baz();");
11933 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11934 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
11935 panic!("expected expression statement");
11936 };
11937 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
11938 panic!("expected New");
11939 };
11940 let crate::ExprKind::FieldAccess { receiver, name } = &ast.try_expr(*callee).unwrap().kind
11941 else {
11942 panic!("expected outer FieldAccess");
11943 };
11944 assert_eq!(name.name, "Baz");
11945 let crate::ExprKind::FieldAccess { name: mid_name, .. } =
11946 &ast.try_expr(*receiver).unwrap().kind
11947 else {
11948 panic!("expected inner FieldAccess");
11949 };
11950 assert_eq!(mid_name.name, "Bar");
11951 }
11952
11953 #[test]
11954 fn parse_new_with_trailing_dot_diagnoses() {
11955 let (_ast, diags) = parse_str("new Foo.;");
11956 assert!(
11957 diags.iter().any(|d| d
11958 .message
11959 .contains("expected identifier after `.` in constructor name")),
11960 "expected trailing-dot diagnostic, got: {diags:?}",
11961 );
11962 }
11963
11964 #[test]
11965 fn throw_without_expression_diagnoses() {
11966 let (_ast, diags) = parse_str("throw;");
11967 assert!(
11968 diags
11969 .iter()
11970 .any(|d| d.message == "expected expression after `throw`"),
11971 "expected bare-throw diagnostic, got: {diags:?}",
11972 );
11973 }
11974
11975 #[test]
11976 fn parse_try_catch() {
11977 let (ast, diags) = parse_str("try { } catch (e: Error) { }");
11978 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
11979 let stmt = single_stmt(&ast);
11980 let crate::StmtKind::Try {
11981 body,
11982 ref catches,
11983 ref finally,
11984 } = stmt.kind
11985 else {
11986 panic!("expected Try, got {:?}", stmt.kind);
11987 };
11988 assert!(matches!(
11989 ast.try_stmt(body).unwrap().kind,
11990 crate::StmtKind::Block(ref v) if v.is_empty()
11991 ));
11992 let [clause] = catches.as_slice() else {
11993 panic!("expected one catch clause, got {catches:?}");
11994 };
11995 assert_eq!(clause.binding.name, "e");
11996 assert!(clause.ty.is_some());
11997 assert!(finally.is_none());
11998 }
11999
12000 #[test]
12001 fn parse_try_finally() {
12002 let (ast, diags) = parse_str("try { } finally { }");
12003 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12004 let stmt = single_stmt(&ast);
12005 let crate::StmtKind::Try {
12006 ref catches,
12007 ref finally,
12008 ..
12009 } = stmt.kind
12010 else {
12011 panic!("expected Try");
12012 };
12013 assert!(catches.is_empty());
12014 let finally_id = finally.expect("finally block");
12015 assert!(matches!(
12016 ast.try_stmt(finally_id).unwrap().kind,
12017 crate::StmtKind::Block(_)
12018 ));
12019 }
12020
12021 #[test]
12022 fn parse_try_catch_finally() {
12023 let (ast, diags) = parse_str("try { } catch (e: Error) { } finally { }");
12024 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12025 let stmt = single_stmt(&ast);
12026 let crate::StmtKind::Try {
12027 ref catches,
12028 ref finally,
12029 ..
12030 } = stmt.kind
12031 else {
12032 panic!("expected Try");
12033 };
12034 assert_eq!(catches.len(), 1);
12035 assert!(finally.is_some());
12036 }
12037
12038 #[test]
12039 fn bare_try_diagnoses() {
12040 let (_ast, diags) = parse_str("try { }");
12041 assert!(
12042 diags
12043 .iter()
12044 .any(|d| d.message == "try requires at least one of `catch` or `finally`"),
12045 "expected bare-try diagnostic, got: {diags:?}",
12046 );
12047 }
12048
12049 #[test]
12050 fn parse_untyped_catch() {
12051 let (ast, diags) = parse_str("try { } catch (e) { }");
12052 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12053 let stmt = single_stmt(&ast);
12054 let crate::StmtKind::Try {
12055 ref catches,
12056 ref finally,
12057 ..
12058 } = stmt.kind
12059 else {
12060 panic!("expected Try");
12061 };
12062 let [clause] = catches.as_slice() else {
12063 panic!("expected one catch clause, got {catches:?}");
12064 };
12065 assert_eq!(clause.binding.name, "e");
12066 assert!(clause.ty.is_none());
12067 assert!(finally.is_none());
12068 }
12069
12070 #[test]
12071 fn parse_multiple_catch_clauses() {
12072 let (ast, diags) = parse_str("try { } catch (e: HttpError) { } catch (f) { }");
12073 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12074 let stmt = single_stmt(&ast);
12075 let crate::StmtKind::Try {
12076 ref catches,
12077 ref finally,
12078 ..
12079 } = stmt.kind
12080 else {
12081 panic!("expected Try");
12082 };
12083 let [first, second] = catches.as_slice() else {
12084 panic!("expected two catch clauses, got {catches:?}");
12085 };
12086 assert_eq!(first.binding.name, "e");
12087 assert!(first.ty.is_some());
12088 assert_eq!(second.binding.name, "f");
12089 assert!(second.ty.is_none());
12090 assert!(finally.is_none());
12091 }
12092
12093 #[test]
12094 fn catch_after_finally_diagnoses() {
12095 let (_ast, diags) = parse_str("try { } finally { } catch (e: Error) { }");
12096 assert!(
12097 !diags.is_empty(),
12098 "expected diagnostic for catch-after-finally"
12099 );
12100 }
12101
12102 #[test]
12103 fn catch_missing_open_paren_diagnoses() {
12104 let (_ast, diags) = parse_str("try { } catch e: Error) { }");
12105 assert!(
12106 diags
12107 .iter()
12108 .any(|d| d.message == "expected `(` after `catch`"),
12109 "expected open-paren diagnostic, got: {diags:?}",
12110 );
12111 }
12112
12113 fn class_members(ast: &Ast) -> &[crate::ClassMember] {
12114 match &single_stmt(ast).kind {
12115 StmtKind::ClassDecl { members, .. } => members,
12116 other => panic!("expected ClassDecl, got {other:?}"),
12117 }
12118 }
12119
12120 #[test]
12121 fn parse_animal_class() {
12122 let src = r#"
12123class Animal {
12124 name: string;
12125 private sound: string;
12126 readonly species: string;
12127
12128 constructor(name: string, sound: string, species: string) {
12129 this.name = name;
12130 this.sound = sound;
12131 this.species = species;
12132 }
12133
12134 speak(): string {
12135 return this.name + " says " + this.sound;
12136 }
12137}
12138"#;
12139 let (ast, diags) = parse_str(src);
12140 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12141 let StmtKind::ClassDecl {
12142 name,
12143 generics,
12144 extends,
12145 implements,
12146 members,
12147 ..
12148 } = &single_stmt(&ast).kind
12149 else {
12150 panic!("expected ClassDecl");
12151 };
12152 assert_eq!(name.name, "Animal");
12153 assert!(generics.is_empty());
12154 assert!(extends.is_none());
12155 assert!(implements.is_empty());
12156 assert_eq!(members.len(), 5);
12157
12158 let crate::ClassMember::Field {
12159 name, modifiers, ..
12160 } = &members[0]
12161 else {
12162 panic!("member 0 should be a field");
12163 };
12164 assert_eq!(name.name, "name");
12165 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12166 assert!(modifiers.readonly.is_none());
12167
12168 let crate::ClassMember::Field { modifiers, .. } = &members[1] else {
12169 panic!("member 1 should be a field");
12170 };
12171 assert_eq!(modifiers.visibility, crate::Visibility::Private);
12172
12173 let crate::ClassMember::Field { modifiers, .. } = &members[2] else {
12174 panic!("member 2 should be a field");
12175 };
12176 assert!(modifiers.readonly.is_some());
12177 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12178
12179 assert!(matches!(
12180 &members[3],
12181 crate::ClassMember::Constructor { .. }
12182 ));
12183
12184 let crate::ClassMember::Method { name, .. } = &members[4] else {
12185 panic!("member 4 should be a method");
12186 };
12187 assert_eq!(name.name, "speak");
12188 }
12189
12190 #[test]
12191 fn parse_dog_class_extends_super_and_void_method() {
12192 let src = r#"
12193class Dog extends Animal {
12194 private tricks: string[];
12195
12196 constructor(name: string) {
12197 super(name, "woof", "canis familiaris");
12198 this.tricks = [];
12199 }
12200
12201 learn(trick: string): void {
12202 this.tricks.push(trick);
12203 }
12204}
12205"#;
12206 let (ast, diags) = parse_str(src);
12207 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12208 let StmtKind::ClassDecl {
12209 extends, members, ..
12210 } = &single_stmt(&ast).kind
12211 else {
12212 panic!("expected ClassDecl");
12213 };
12214 assert!(extends.is_some(), "Dog should extend Animal");
12215 assert_eq!(members.len(), 3);
12216 assert!(matches!(&members[0], crate::ClassMember::Field { .. }));
12217 assert!(matches!(
12218 &members[1],
12219 crate::ClassMember::Constructor { .. }
12220 ));
12221 assert!(matches!(&members[2], crate::ClassMember::Method { .. }));
12222 }
12223
12224 #[test]
12225 fn parse_class_implements_list() {
12226 let (ast, diags) = parse_str("class C implements I, J { run(): void {} }");
12227 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12228 let StmtKind::ClassDecl { implements, .. } = &single_stmt(&ast).kind else {
12229 panic!("expected ClassDecl");
12230 };
12231 assert_eq!(implements.len(), 2);
12232 }
12233
12234 #[test]
12235 fn parse_class_extends_and_implements() {
12236 let (ast, diags) = parse_str("class C extends B implements I { run(): void {} }");
12237 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12238 let StmtKind::ClassDecl {
12239 extends,
12240 implements,
12241 ..
12242 } = &single_stmt(&ast).kind
12243 else {
12244 panic!("expected ClassDecl");
12245 };
12246 assert!(extends.is_some());
12247 assert_eq!(implements.len(), 1);
12248 }
12249
12250 #[test]
12251 fn parse_class_generics_optional_field_and_initializer() {
12252 let (ast, diags) = parse_str("class Box<T> { value?: T; items: T[] = []; }");
12253 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12254 let StmtKind::ClassDecl {
12255 generics, members, ..
12256 } = &single_stmt(&ast).kind
12257 else {
12258 panic!("expected ClassDecl");
12259 };
12260 assert_eq!(generics.len(), 1);
12261 assert_eq!(generics[0].name, "T");
12262
12263 let crate::ClassMember::Field {
12264 optional,
12265 initializer,
12266 ..
12267 } = &members[0]
12268 else {
12269 panic!("member 0 should be a field");
12270 };
12271 assert!(*optional);
12272 assert!(initializer.is_none());
12273
12274 let crate::ClassMember::Field { initializer, .. } = &members[1] else {
12275 panic!("member 1 should be a field");
12276 };
12277 assert!(initializer.is_some());
12278 }
12279
12280 #[test]
12281 fn export_class_is_marked() {
12282 let (ast, diags) = parse_str("export class Foo {}");
12283 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12284 assert_eq!(ast.top_level.len(), 1);
12285 assert_eq!(ast.exported_decls.len(), 1);
12286 assert!(matches!(
12287 &single_stmt(&ast).kind,
12288 StmtKind::ClassDecl { .. }
12289 ));
12290 }
12291
12292 #[test]
12293 fn parameterless_index_signature_is_reported_from_its_member_start() {
12294 for (source, expected) in [
12295 ("interface P { readonly []: number }", "readonly []"),
12296 ("let p: { readonly []: number } = {};", "readonly []"),
12297 ("let q: { []: number } = {};", "[]"),
12298 ] {
12299 let (_ast, diags) = parse_str(source);
12300 let diag = diags
12301 .iter()
12302 .find(|d| d.message.contains("must declare exactly one parameter"))
12303 .unwrap_or_else(|| panic!("no diagnostic for {source}"));
12304 let reported = &source[diag.span.start as usize..diag.span.end as usize];
12305 assert_eq!(reported, expected, "{source}");
12306 }
12307 }
12308
12309 #[test]
12310 fn unterminated_object_type_expects_closing_brace() {
12311 let (_ast, diags) = parse_str("let p: { a: number\n");
12312 assert!(
12313 diags.iter().any(|d| d.message == "expected `}`"),
12314 "{diags:?}"
12315 );
12316 }
12317
12318 #[test]
12319 fn conflicting_visibility_modifiers_diagnose() {
12320 let (_ast, diags) = parse_str("class C { public private x: number; }");
12321 assert!(
12322 diags
12323 .iter()
12324 .any(|d| d.message.contains("at most one visibility modifier")),
12325 "got: {diags:?}",
12326 );
12327 }
12328
12329 #[test]
12330 fn duplicate_readonly_modifier_diagnoses() {
12331 let (_ast, diags) = parse_str("class C { readonly readonly x: number; }");
12332 assert!(
12333 diags
12334 .iter()
12335 .any(|d| d.message.contains("duplicate `readonly`")),
12336 "got: {diags:?}",
12337 );
12338 }
12339
12340 #[test]
12341 fn field_named_like_a_modifier_is_allowed() {
12342 let (ast, diags) = parse_str("class C { private: number; }");
12343 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12344 let crate::ClassMember::Field {
12345 name, modifiers, ..
12346 } = &class_members(&ast)[0]
12347 else {
12348 panic!("expected a field");
12349 };
12350 assert_eq!(name.name, "private");
12351 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12352 }
12353
12354 #[test]
12355 fn method_named_get_is_allowed() {
12356 let (ast, diags) = parse_str("class C { get(): number { return 1; } }");
12357 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12358 let crate::ClassMember::Method { name, .. } = &class_members(&ast)[0] else {
12359 panic!("expected a method");
12360 };
12361 assert_eq!(name.name, "get");
12362 }
12363
12364 #[test]
12365 fn static_method_parses() {
12366 let (ast, diags) = parse_str("class C { static f(): number { return 1; } }");
12367 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12368 let crate::ClassMember::Method {
12369 name, modifiers, ..
12370 } = &class_members(&ast)[0]
12371 else {
12372 panic!("expected a method");
12373 };
12374 assert_eq!(name.name, "f");
12375 assert!(modifiers.static_span.is_some());
12376 }
12377
12378 #[test]
12379 fn static_readonly_field_parses() {
12380 let (ast, diags) = parse_str("class C { static readonly x: number = 1; }");
12381 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12382 let crate::ClassMember::Field {
12383 name,
12384 modifiers,
12385 initializer,
12386 ..
12387 } = &class_members(&ast)[0]
12388 else {
12389 panic!("expected a field");
12390 };
12391 assert_eq!(name.name, "x");
12392 assert!(modifiers.static_span.is_some());
12393 assert!(modifiers.readonly.is_some());
12394 assert!(initializer.is_some());
12395 }
12396
12397 #[test]
12398 fn private_static_method_parses() {
12399 let (ast, diags) = parse_str("class C { private static f(): void {} }");
12400 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12401 let crate::ClassMember::Method { modifiers, .. } = &class_members(&ast)[0] else {
12402 panic!("expected a method");
12403 };
12404 assert_eq!(modifiers.visibility, crate::Visibility::Private);
12405 assert!(modifiers.static_span.is_some());
12406 }
12407
12408 #[test]
12409 fn method_named_static_is_allowed() {
12410 let (ast, diags) = parse_str("class C { static(): number { return 1; } }");
12411 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12412 let crate::ClassMember::Method {
12413 name, modifiers, ..
12414 } = &class_members(&ast)[0]
12415 else {
12416 panic!("expected a method");
12417 };
12418 assert_eq!(name.name, "static");
12419 assert!(modifiers.static_span.is_none());
12420 }
12421
12422 #[test]
12423 fn visibility_after_static_diagnoses() {
12424 let (_ast, diags) = parse_str("class C { static private f(): void {} }");
12425 assert!(
12426 diags
12427 .iter()
12428 .any(|d| d.message.contains("`private` must come before `static`")),
12429 "got: {diags:?}",
12430 );
12431 }
12432
12433 #[test]
12434 fn duplicate_static_modifier_diagnoses() {
12435 let (_ast, diags) = parse_str("class C { static static f(): void {} }");
12436 assert!(
12437 diags
12438 .iter()
12439 .any(|d| d.message.contains("duplicate `static` modifier")),
12440 "got: {diags:?}",
12441 );
12442 }
12443
12444 #[test]
12445 fn static_accessor_is_rejected() {
12446 let (_ast, diags) = parse_str("class C { static get x(): number { return 1; } }");
12447 assert!(
12448 diags
12449 .iter()
12450 .any(|d| d.message.contains("static accessors are not supported")),
12451 "got: {diags:?}",
12452 );
12453 }
12454
12455 #[test]
12456 fn static_constructor_is_rejected() {
12457 let (_ast, diags) = parse_str("class C { static constructor() {} }");
12458 assert!(
12459 diags
12460 .iter()
12461 .any(|d| d.message.contains("a constructor cannot be `static`")),
12462 "got: {diags:?}",
12463 );
12464 }
12465
12466 #[test]
12467 fn optional_static_field_is_rejected() {
12468 let (_ast, diags) = parse_str("class C { static x?: number; }");
12469 assert!(
12470 diags
12471 .iter()
12472 .any(|d| d.message.contains("a static field cannot be optional")),
12473 "got: {diags:?}",
12474 );
12475 }
12476
12477 #[test]
12478 fn protected_member_is_rejected() {
12479 let (_ast, diags) = parse_str("class C { protected x: number; }");
12480 assert!(
12481 diags
12482 .iter()
12483 .any(|d| d.message.contains("`protected` is not supported")),
12484 "got: {diags:?}",
12485 );
12486 }
12487
12488 #[test]
12489 fn abstract_member_is_rejected() {
12490 let (_ast, diags) = parse_str("class C { abstract run(): void; }");
12491 assert!(
12492 diags
12493 .iter()
12494 .any(|d| d.message.contains("abstract classes are not supported")),
12495 "got: {diags:?}",
12496 );
12497 }
12498
12499 #[test]
12500 fn getter_parses_as_accessor() {
12501 let (ast, diags) = parse_str("class C { get value(): number { return 1; } }");
12502 assert!(diags.is_empty(), "got: {diags:?}");
12503 let crate::ClassMember::Accessor { kind, name, .. } = &class_members(&ast)[0] else {
12504 panic!("expected an accessor member");
12505 };
12506 assert_eq!(*kind, crate::AccessorKind::Get);
12507 assert_eq!(name.name, "value");
12508 }
12509
12510 #[test]
12511 fn getter_without_return_type_is_rejected() {
12512 let (ast, diags) = parse_str("class C { get value() { return 1; } }");
12513 assert_eq!(diags.len(), 1, "got: {diags:?}");
12514 assert_eq!(diags[0].message, "expected `:` and return type");
12515 assert_eq!(diags[0].help, vec!["get name(): T { … }".to_string()]);
12516 assert!(
12517 matches!(
12518 &class_members(&ast)[0],
12519 crate::ClassMember::Accessor {
12520 return_type: None,
12521 ..
12522 }
12523 ),
12524 "the getter is still recovered as a member"
12525 );
12526 }
12527
12528 #[test]
12529 fn setter_parses_as_accessor() {
12530 let (ast, diags) = parse_str("class C { set value(v: number) {} }");
12531 assert!(diags.is_empty(), "got: {diags:?}");
12532 let crate::ClassMember::Accessor { kind, param, .. } = &class_members(&ast)[0] else {
12533 panic!("expected an accessor member");
12534 };
12535 assert_eq!(*kind, crate::AccessorKind::Set);
12536 assert!(param.is_some(), "setter should have a parameter");
12537 }
12538
12539 #[test]
12540 fn param_property_modifier_outside_constructor_is_rejected() {
12541 let (_ast, diags) = parse_str("function f(public x: number): void {}");
12542 assert!(
12543 diags.iter().any(|d| d
12544 .message
12545 .contains("parameter properties are only allowed in a constructor")),
12546 "got: {diags:?}",
12547 );
12548 }
12549
12550 #[test]
12551 fn constructor_return_type_is_rejected() {
12552 let (_ast, diags) = parse_str("class C { constructor(): void {} }");
12553 assert!(
12554 diags.iter().any(|d| d
12555 .message
12556 .contains("constructor cannot declare a return type")),
12557 "got: {diags:?}",
12558 );
12559 }
12560
12561 #[test]
12562 fn field_initializer_without_type_is_rejected() {
12563 let (_ast, diags) = parse_str("class C { x = 1; }");
12564 assert!(
12565 diags
12566 .iter()
12567 .any(|d| d.message.contains("class fields require a type annotation")),
12568 "got: {diags:?}",
12569 );
12570 }
12571
12572 #[test]
12573 fn this_outside_method_is_left_to_typechecker() {
12574 let (_ast, diags) = parse_str("function f(): string { return this.name; }");
12575 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12576 }
12577
12578 #[test]
12579 fn super_outside_method_is_rejected() {
12580 let (_ast, diags) = parse_str("function f(): void { super(1); }");
12581 assert!(
12582 diags.iter().any(|d| d
12583 .message
12584 .contains("`super` is only valid inside a class method or constructor body")),
12585 "got: {diags:?}",
12586 );
12587 }
12588
12589 #[test]
12590 fn this_inside_method_is_accepted_by_parser() {
12591 let (_ast, diags) = parse_str("class C { x: number; read(): number { return this.x; } }");
12592 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12593 }
12594 #[test]
12595 fn parse_void_expression_preserves_operand_and_precedence() {
12596 let (ast, diags) = parse_str("void effect() === undefined;");
12597 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12598 let expression = expr_of_single_stmt(&ast);
12599 let crate::ExprKind::Binary { lhs, rhs, .. } = expression.kind else {
12600 panic!("expected equality")
12601 };
12602 let crate::ExprKind::Void { operand } = ast.try_expr(lhs).unwrap().kind else {
12603 panic!("expected void")
12604 };
12605 assert!(matches!(
12606 ast.try_expr(operand).unwrap().kind,
12607 crate::ExprKind::Call { .. }
12608 ));
12609 assert!(matches!(
12610 ast.try_expr(rhs).unwrap().kind,
12611 crate::ExprKind::Identifier(_)
12612 ));
12613 }
12614
12615 #[test]
12616 fn parse_void_newline_and_object_operands() {
12617 for source in [
12618 "void\n0;",
12619 "void { value: 1 };",
12620 "void\n{ value: 1 };",
12621 "void /x/;",
12622 "void void 0;",
12623 ] {
12624 let (_, diags) = parse_str(source);
12625 assert!(diags.is_empty(), "{source}: {diags:?}");
12626 }
12627 let (_, diags) = parse_str("void; 0;");
12628 assert!(
12629 !diags.is_empty(),
12630 "an explicit semicolon cannot split void from its operand"
12631 );
12632 }
12633
12634 #[test]
12635 fn parse_optional_parameters_and_defaults_in_callable_forms() {
12636 for source in [
12637 "function f(x = 1): number { return x; }",
12638 "function f(x: number = 1, y: number): number { return y; }",
12639 "function f({ x } = { x: 1 }): number { return x; }",
12640 "const f = (x?: number) => x;",
12641 "const f = (x: number = effect()) => x;",
12642 "const f = function(x: number = effect()) { return x; };",
12643 "const f = { m(x: number = effect()): number { return x; } };",
12644 "const f = ([x = 1]: number[] = values) => x;",
12645 "let f: (x?: number, ...rest: string[]) => void = value;",
12646 "let f: { method?(x?: number): void } = value;",
12647 "function f(x: number = 1, y?: number): void {}",
12648 ] {
12649 let (_, diags) = parse_str(source);
12650 assert!(diags.is_empty(), "{source}: {diags:?}");
12651 }
12652 }
12653
12654 #[test]
12655 fn parse_invalid_optional_parameters() {
12656 for source in [
12657 "function f(x?: number, y: number): void {}",
12658 "const f = (x?: number, y: number) => y;",
12659 "let f: (x?: number, y: number) => void = value;",
12660 "function f(...x?: number[]): void {}",
12661 "function f(x?: number = 1): void {}",
12662 "const f = (x?: number = 1) => x;",
12663 "function f({x}?: T): void {}",
12664 ] {
12665 let (_, diags) = parse_str(source);
12666 assert!(!diags.is_empty(), "expected diagnostic for {source}");
12667 }
12668 }
12669
12670 #[test]
12671 fn parse_optional_tuple_forms_and_order() {
12672 for source in [
12673 "let x: [number, string?] = value;",
12674 "let x: [x?: number, y?: string] = value;",
12675 "let x: readonly [(number | null)?, string?] = value;",
12676 ] {
12677 let (_, diags) = parse_str(source);
12678 assert!(diags.is_empty(), "{source}: {diags:?}");
12679 }
12680 for source in [
12681 "let x: [number?, string] = value;",
12682 "let x: [x: number?] = value;",
12683 ] {
12684 let (_, diags) = parse_str(source);
12685 assert!(!diags.is_empty(), "expected diagnostic for {source}");
12686 }
12687 }
12688
12689 #[test]
12690 fn parse_array_pattern_defaults_preserve_holes() {
12691 let (ast, diags) = parse_str("const [, a = effect(), b] = values;");
12692 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12693 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
12694 panic!("expected pattern")
12695 };
12696 let crate::Binding::Array {
12697 elems, defaults, ..
12698 } = binding
12699 else {
12700 panic!("expected array")
12701 };
12702 assert_eq!(elems.len(), 3);
12703 assert_eq!(defaults.len(), elems.len());
12704 assert!(elems[0].is_none());
12705 assert!(defaults[0].is_none());
12706 assert!(defaults[1].is_some());
12707 assert!(defaults[2].is_none());
12708 }
12709
12710 #[test]
12711 fn parse_optional_class_method_forms() {
12712 let (ast, diags) =
12713 parse_str("class C { declared?(x?: number): void; implemented?(): void\n{} }");
12714 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12715 let members = class_members(&ast);
12716 assert!(matches!(
12717 &members[0],
12718 crate::ClassMember::Field {
12719 optional: true,
12720 initializer: None,
12721 ty: crate::TypeAnnotation {
12722 kind: crate::TypeAnnotationKind::Function { .. },
12723 ..
12724 },
12725 ..
12726 }
12727 ));
12728 assert!(matches!(
12729 &members[1],
12730 crate::ClassMember::Method { optional: true, .. }
12731 ));
12732 }
12733 #[test]
12734 fn defaults_retain_their_callable_receiver_scope() {
12735 for source in [
12736 "class C { value: number = 1; method(value: number = this.value): number { return value; } }",
12737 "class C { constructor(value: number = this.value) {} value: number = 1; }",
12738 "const f = function(this: { value: number }, value: number = this.value): number { return value; };",
12739 ] {
12740 let (ast, diagnostics) = parse_str(source);
12741 assert!(diagnostics.is_empty(), "{source}: {diagnostics:?}");
12742 assert!(
12743 ast.source_expressions()
12744 .iter()
12745 .any(|expr| matches!(expr.kind, ExprKind::This))
12746 );
12747 assert!(
12748 !ast.source_expressions()
12749 .iter()
12750 .any(|expr| matches!(expr.kind, ExprKind::ThisOutsideReceiver))
12751 );
12752 }
12753 }
12754}