1use crate::compiler_error::{CompileError, CompilerFailure, CompilerStage};
2use crate::{
3 ArrayLiteralElement, ArrowBody, Ast, BinOp, Binding, CatchClause, ClassMember, Diagnostic,
4 EnumInitializer, EnumMember, ExportedDecl, Expr, ExprId, ExprKind, FileId, Ident, ImportKind,
5 ImportSpecifier, ObjectLiteralField, ObjectLiteralMember, ObjectPatternField, ParamDecl,
6 PostfixOp, Severity, Span, Stmt, StmtId, StmtKind, SwitchCase, SwitchDefault, Token, TokenKind,
7 TypeAnnotation, TypeAnnotationField, TypeAnnotationKind, UnOp,
8};
9
10const MAX_ERRORS: usize = 20;
11const MAX_PARSE_DEPTH: usize = 128;
14
15const BUILT_IN_TYPE_NAMES: &[&str] = &[
17 "any",
18 "bigint",
19 "boolean",
20 "never",
21 "number",
22 "object",
23 "string",
24 "symbol",
25 "undefined",
26 "unknown",
27 "void",
28];
29
30#[derive(Clone, Copy, PartialEq)]
36enum TypePos {
37 Anywhere,
38 ArrowReturn,
39}
40
41pub fn parse(source: &str, tokens: Vec<Token>, file: FileId) -> (Ast, Vec<Diagnostic>) {
43 match parse_checked(source, tokens, file) {
44 Ok(result) => result,
45 Err(error) => (Ast::new(), error.into_diagnostics(file)),
46 }
47}
48
49pub fn parse_checked(
50 source: &str,
51 tokens: Vec<Token>,
52 file: FileId,
53) -> Result<(Ast, Vec<Diagnostic>), CompileError> {
54 crate::source::SourceError::check_source_len(source.len())
55 .map_err(|error| error.into_compiler_failure(CompilerStage::Parse))?;
56 validate_token_stream(source, &tokens, file).map_err(|message| CompilerFailure::Internal {
57 stage: CompilerStage::Parse,
58 span: None,
59 message: message.into(),
60 })?;
61 let eof = tokens
62 .last()
63 .cloned()
64 .unwrap_or_else(|| Token::new(TokenKind::Eof, Span::at(file)));
65 let mut p = Parser {
66 source,
67 tokens,
68 file,
69 pos: 0,
70 ast: Ast::new(),
71 diagnostics: Vec::new(),
72 block_depth: 0,
73 class_member_body_depth: 0,
74 function_expression_body_depth: 0,
75 recursion_depth: 0,
76 recursion_limit_span: None,
77 resume_at_next_member: false,
78 last_as_type_end: None,
79 eof,
80 fatal: None,
81 };
82 p.parse_program();
83 if p.fatal.is_none() {
84 p.reject_use_strict_with_non_simple_params();
85 }
86 if let Some(span) = p.recursion_limit_span {
87 p.fatal = Some(CompilerFailure::Limit {
88 stage: CompilerStage::Parse,
89 span: Some(span),
90 message: "parser recursion limit exceeded".into(),
91 help: vec!["simplify nested syntax or split it into separate declarations".into()],
92 });
93 }
94 if let Some(fatal) = p.fatal {
95 return Err(CompileError {
96 diagnostics: p.diagnostics,
97 fatal: Some(fatal),
98 });
99 }
100 p.ast
101 .validate_source(source, file)
102 .map_err(|error| CompileError {
103 diagnostics: p.diagnostics.clone(),
104 fatal: Some(error.into_compiler_failure(CompilerStage::Parse)),
105 })?;
106 crate::tree_height::check_syntax(&p.ast).map_err(|fatal| CompileError {
107 diagnostics: p.diagnostics.clone(),
108 fatal: Some(fatal),
109 })?;
110 Ok((p.ast, p.diagnostics))
111}
112
113fn validate_token_stream(source: &str, tokens: &[Token], file: FileId) -> Result<(), &'static str> {
114 if !matches!(tokens.last().map(|token| &token.kind), Some(TokenKind::Eof)) {
115 return Err("token stream must end with EOF");
116 }
117 let mut previous = 0;
118 for (index, token) in tokens.iter().enumerate() {
119 let span = token.span;
120 if span.file != file
121 || span.start < previous
122 || span.start > span.end
123 || source.get(span.start as usize..span.end as usize).is_none()
124 {
125 return Err("token span is outside its source or out of order");
126 }
127 if matches!(token.kind, TokenKind::Eof) && index != tokens.len() - 1 {
128 return Err("tokens follow EOF");
129 }
130 if let Some(doc) = &token.leading_doc {
131 let span = doc.span;
132 if span.file != file
133 || span.start > span.end
134 || span.end > token.span.start
135 || source.get(span.start as usize..span.end as usize) != Some(doc.text.as_str())
136 || !doc.text.starts_with("/**")
137 || !doc.text.ends_with("*/")
138 {
139 return Err("documentation metadata does not match its source");
140 }
141 }
142 previous = span.start;
143 }
144 Ok(())
145}
146
147pub(crate) struct Parser<'a> {
148 source: &'a str,
149 tokens: Vec<Token>,
150 file: FileId,
151 pos: usize,
152 ast: Ast,
153 diagnostics: Vec<Diagnostic>,
154 block_depth: u32,
156 class_member_body_depth: u32,
158 function_expression_body_depth: u32,
159 recursion_depth: usize,
160 recursion_limit_span: Option<Span>,
161 resume_at_next_member: bool,
165 last_as_type_end: Option<u32>,
169 eof: Token,
170 fatal: Option<CompilerFailure>,
171}
172
173impl<'a> Parser<'a> {
174 fn span(&self, start: u32, end: u32) -> Span {
175 Span {
176 file: self.file,
177 start,
178 end,
179 }
180 }
181
182 fn type_name(&mut self, span: Span) -> Option<Ident> {
183 match span.text(self.source, self.file) {
184 Ok(name) => Some(Ident {
185 name: name.to_string(),
186 span,
187 }),
188 Err(error) => {
189 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
190 None
191 }
192 }
193 }
194
195 fn parse_program(&mut self) {
196 while !self.is_at_eof() {
197 if self.fatal.is_some() || self.error_count() >= MAX_ERRORS {
198 break;
199 }
200 if matches!(self.peek().kind, TokenKind::Semicolon) {
201 self.advance();
202 continue;
203 }
204 let pos_before = self.pos;
205 if let Some(id) = self.parse_statement() {
206 self.ast.top_level.push(id);
207 } else {
208 if self.fatal.is_some() {
209 break;
210 }
211 if self.pos != pos_before && self.peek_starts_top_level_item() {
216 continue;
217 }
218 self.recover();
219 if self.pos != pos_before && matches!(self.peek().kind, TokenKind::RightBrace) {
220 self.advance();
221 continue;
222 }
223 if self.pos == pos_before && !self.is_at_eof() {
225 self.advance();
226 }
227 }
228 }
229 }
230
231 fn parse_statement(&mut self) -> Option<StmtId> {
232 self.with_recursion_limit(Self::parse_statement_inner)
233 }
234
235 fn parse_statement_inner(&mut self) -> Option<StmtId> {
236 self.reject_async_modifier();
237 match self.peek().kind {
238 TokenKind::Let => self.parse_let_or_const(false),
239 TokenKind::Const => self.parse_let_or_const(true),
240 TokenKind::Function => self.parse_function_decl(),
241 TokenKind::Class => self.parse_class_decl(),
242 TokenKind::Interface => self.parse_interface_decl(),
243 TokenKind::Enum => self.parse_enum_decl(),
244 TokenKind::Identifier if self.at_type_alias_head() => self.parse_type_alias_decl(),
245 TokenKind::Export => self.parse_export(),
246 TokenKind::Import => self.parse_import_decl(),
247 TokenKind::If => self.parse_if(),
248 TokenKind::While => self.parse_while(),
249 TokenKind::Do => self.parse_do_while(),
250 TokenKind::For => self.parse_for(),
251 TokenKind::Switch => self.parse_switch(),
252 TokenKind::Break => self.parse_break(),
253 TokenKind::Continue => self.parse_continue(),
254 TokenKind::Return => self.parse_return(),
255 TokenKind::Throw => self.parse_throw(),
256 TokenKind::Try => self.parse_try(),
257 TokenKind::LeftBrace => self.parse_block(),
258 _ => self.parse_expression_statement(),
259 }
260 }
261
262 fn parse_expression_statement(&mut self) -> Option<StmtId> {
263 let expr_id = self.parse_expression()?;
264 let expr_span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
265 if !self.at_statement_end() {
266 let what = if matches!(
267 parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.kind,
268 ExprKind::Assign { .. }
269 ) {
270 "assignment"
271 } else {
272 "expression"
273 };
274 self.error_at_peek(format!("expected `;` after {what}"));
275 return None;
276 }
277 let end = self.finish_statement();
278 let span = self.span(expr_span.start, end);
279 let kind = self.statement_kind_for(expr_id)?;
280 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
281 }
282
283 fn statement_kind_for(&mut self, expr_id: ExprId) -> Option<StmtKind> {
286 let ExprKind::Assign {
287 target,
288 op,
289 op_span,
290 value,
291 } = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?
292 .kind
293 .clone()
294 else {
295 return Some(StmtKind::Expr(expr_id));
296 };
297 Some(
298 match (
299 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?
300 .kind
301 .clone(),
302 op,
303 ) {
304 (ExprKind::Identifier(target), None) => StmtKind::Assign { target, value },
305 (ExprKind::Identifier(target), Some(op)) => StmtKind::CompoundAssign {
306 target,
307 op,
308 op_span,
309 value,
310 },
311 (ExprKind::FieldAccess { receiver, name }, None) => StmtKind::AssignField {
312 receiver,
313 field_name: name,
314 value,
315 },
316 (ExprKind::FieldAccess { receiver, name }, Some(op)) => {
317 StmtKind::CompoundAssignField {
318 receiver,
319 field_name: name,
320 op,
321 op_span,
322 value,
323 }
324 }
325 (ExprKind::IndexAccess { receiver, index }, None) => StmtKind::AssignIndex {
326 receiver,
327 index,
328 value,
329 },
330 (ExprKind::IndexAccess { receiver, index }, Some(op)) => {
331 StmtKind::CompoundAssignIndex {
332 receiver,
333 index,
334 op,
335 op_span,
336 value,
337 }
338 }
339 _ => return self.invariant_failure("invalid assignment target"),
340 },
341 )
342 }
343
344 fn parse_let_or_const(&mut self, is_const: bool) -> Option<StmtId> {
345 let doc = self.take_leading_doc();
346 let kw = self.advance();
347
348 let binding = match self.peek().kind {
349 TokenKind::LeftBrace | TokenKind::LeftBracket => Some(self.parse_binding()?),
350 _ => None,
351 };
352
353 let name = if binding.is_none() {
354 let name_tok =
355 self.expect_binding_identifier("expected identifier after `let`/`const`")?;
356 Some(self.ident_from_token(&name_tok))
357 } else {
358 None
359 };
360
361 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
362 self.advance();
363 Some(self.parse_type_annotation()?)
364 } else {
365 None
366 };
367
368 let implicit = !is_const
369 && ty.is_some()
370 && !matches!(self.peek().kind, TokenKind::Equals)
371 && matches!(self.peek().kind, TokenKind::Semicolon);
372 let value = if let (true, Some(name)) = (implicit, &name) {
373 let value = parse_arena_result(
375 self.ast.try_push_expr(Expr {
376 kind: ExprKind::Identifier(Ident {
377 name: "undefined".to_string(),
378 span: name.span,
379 }),
380 span: name.span,
381 }),
382 &mut self.fatal,
383 )?;
384 self.ast.implicit_initializers.insert(value);
385 self.ast.synthetic_undefined.insert(value);
386 value
387 } else {
388 if !matches!(self.peek().kind, TokenKind::Equals) {
389 let (msg, help) = if is_const {
390 (
391 "`const` declaration requires an initializer",
392 vec!["const x: T = expr;".to_string()],
393 )
394 } else {
395 (
396 "`let` declaration requires an initializer",
397 vec![
398 "let x: T = expr;".to_string(),
399 "or declare one that starts empty: `let x: T | undefined;`".to_string(),
400 ],
401 )
402 };
403 self.error_at_peek_with_help(msg, help);
404 return None;
405 }
406 self.advance();
407 self.parse_expression()?
408 };
409
410 if !self.at_statement_end() {
411 if matches!(self.peek().kind, TokenKind::Comma) {
412 let keyword = if is_const { "const" } else { "let" };
413 let first = name.as_ref().map_or("a", |name| name.name.as_str());
414 let next = self.peek_at(1);
415 let second = if matches!(next.kind, TokenKind::Identifier) {
416 &self.source[next.span.start as usize..next.span.end as usize]
417 } else {
418 "b"
419 };
420 let help = format!(
421 "declare one variable per statement: `{keyword} {first} = …; {keyword} {second} = …;`; \
422 in a `for (…)` header, declare `{second}` before the loop"
423 );
424 self.error_at_peek_with_help("one declaration declares one variable", vec![help]);
425 } else {
426 self.error_at_peek("expected `;` after declaration");
427 }
428 return None;
429 }
430 let end = self.finish_statement();
431
432 let span = self.span(kw.span.start, end);
433 let kind = match (binding, name) {
434 (Some(binding), _) => {
435 if is_const {
436 StmtKind::ConstPattern {
437 binding,
438 ty,
439 value,
440 doc,
441 }
442 } else {
443 StmtKind::LetPattern {
444 binding,
445 ty,
446 value,
447 doc,
448 }
449 }
450 }
451 (None, Some(name)) => {
452 if is_const {
453 StmtKind::Const {
454 name,
455 ty,
456 value,
457 doc,
458 }
459 } else {
460 StmtKind::Let {
461 name,
462 ty,
463 value,
464 doc,
465 }
466 }
467 }
468 (None, None) => return self.invariant_failure("either binding or name must be Some"),
469 };
470 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
471 }
472
473 fn parse_binding(&mut self) -> Option<Binding> {
475 match self.peek().kind {
476 TokenKind::LeftBrace => self.parse_object_binding(),
477 TokenKind::LeftBracket => self.parse_array_binding(),
478 _ => {
479 self.error_at_peek("expected `{` or `[` to start a destructuring pattern");
480 None
481 }
482 }
483 }
484
485 fn parse_object_binding(&mut self) -> Option<Binding> {
486 let open = self.advance();
487 let mut fields: Vec<ObjectPatternField> = Vec::new();
488 let mut rest: Option<Ident> = None;
489
490 if matches!(self.peek().kind, TokenKind::RightBrace) {
491 self.error_at_peek("empty object destructuring pattern");
492 return None;
493 }
494
495 loop {
496 if matches!(self.peek().kind, TokenKind::DotDotDot) {
497 let dots = self.advance();
498 let name_tok = self.expect_binding_identifier("expected identifier after `...`")?;
499 let name = self.ident_from_token(&name_tok);
500 if !matches!(self.peek().kind, TokenKind::RightBrace) {
501 self.error_at_peek_with_help(
502 "rest element must be the last element in a destructuring pattern",
503 vec!["move `...` after every other binding".to_string()],
504 );
505 return None;
506 }
507 rest = Some(Ident {
508 name: name.name,
509 span: self.span(dots.span.start, name.span.end),
510 });
511 break;
512 }
513
514 let source_tok = self.expect_property_name("expected field name in object pattern")?;
515 let source = self.ident_from_token(&source_tok);
516 let field_span_start = source.span.start;
517
518 let (local, field_end) = if matches!(self.peek().kind, TokenKind::Colon) {
519 self.advance();
520 match self.peek().kind {
521 TokenKind::LeftBrace | TokenKind::LeftBracket => {
522 self.error_at_peek_with_help(
523 "nested destructuring is not supported",
524 vec![
525 "destructure once and access nested fields explicitly: const { a } = obj; const inner = a.b;"
526 .to_string(),
527 ],
528 );
529 return None;
530 }
531 _ => {
532 let local_tok = self.expect_binding_identifier(
533 "expected identifier after `:` in pattern",
534 )?;
535 let local = self.ident_from_token(&local_tok);
536 let end = local.span.end;
537 (local, end)
538 }
539 }
540 } else {
541 if !matches!(source_tok.kind, TokenKind::Identifier) {
542 self.error_at_peek("expected `:` after keyword field name in object pattern");
543 return None;
544 }
545 self.reject_strict_mode_reserved_word(&source_tok);
546 self.reject_eval_or_arguments(source_tok.span);
547 let end = source.span.end;
548 (source.clone(), end)
549 };
550
551 let default = if matches!(self.peek().kind, TokenKind::Equals) {
552 self.advance();
553 Some(self.parse_expression()?)
554 } else {
555 None
556 };
557 let field_end = match default {
558 Some(value) => {
559 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?
560 .span
561 .end
562 }
563 None => field_end,
564 };
565
566 fields.push(ObjectPatternField {
567 source,
568 local,
569 default,
570 span: self.span(field_span_start, field_end),
571 });
572
573 match self.peek().kind {
574 TokenKind::Comma => {
575 self.advance();
576 if matches!(self.peek().kind, TokenKind::RightBrace) {
577 break;
578 }
579 }
580 TokenKind::RightBrace => break,
581 _ => {
582 self.error_at_peek("expected `,` or `}` in object pattern");
583 return None;
584 }
585 }
586 }
587
588 if !matches!(self.peek().kind, TokenKind::RightBrace) {
589 self.error_at_peek("expected `}` to close object pattern");
590 return None;
591 }
592 let close = self.advance();
593
594 Some(Binding::Object {
595 fields,
596 rest,
597 span: self.span(open.span.start, close.span.end),
598 })
599 }
600
601 fn parse_array_binding(&mut self) -> Option<Binding> {
602 let open = self.advance();
603 let mut elems: Vec<Option<Ident>> = Vec::new();
604 let mut defaults = Vec::new();
605 let mut rest: Option<Ident> = None;
606
607 if matches!(self.peek().kind, TokenKind::RightBracket) {
608 self.error_at_peek("empty array destructuring pattern");
609 return None;
610 }
611
612 loop {
613 match self.peek().kind {
614 TokenKind::Comma => {
615 elems.push(None);
616 defaults.push(None);
617 self.advance();
618 if matches!(self.peek().kind, TokenKind::RightBracket) {
619 break;
620 }
621 continue;
622 }
623 TokenKind::RightBracket => break,
624 TokenKind::DotDotDot => {
625 let dots = self.advance();
626 let name_tok =
627 self.expect_binding_identifier("expected identifier after `...`")?;
628 let name = self.ident_from_token(&name_tok);
629 if !matches!(self.peek().kind, TokenKind::RightBracket) {
630 self.error_at_peek_with_help(
631 "rest element must be the last element in a destructuring pattern",
632 vec!["move `...` after every other binding".to_string()],
633 );
634 return None;
635 }
636 rest = Some(Ident {
637 name: name.name,
638 span: self.span(dots.span.start, name.span.end),
639 });
640 break;
641 }
642 TokenKind::LeftBrace | TokenKind::LeftBracket => {
643 self.error_at_peek_with_help(
644 "nested destructuring is not supported",
645 vec![
646 "destructure once and access nested elements explicitly: const [a] = arr; const inner = a[0];"
647 .to_string(),
648 ],
649 );
650 return None;
651 }
652 _ => {
653 let name_tok =
654 self.expect_binding_identifier("expected identifier in array pattern")?;
655 let name = self.ident_from_token(&name_tok);
656
657 let default = if matches!(self.peek().kind, TokenKind::Equals) {
658 self.advance();
659 Some(self.parse_expression()?)
660 } else {
661 None
662 };
663 defaults.push(default);
664 elems.push(Some(name));
665
666 match self.peek().kind {
667 TokenKind::Comma => {
668 self.advance();
669 if matches!(self.peek().kind, TokenKind::RightBracket) {
670 break;
671 }
672 }
673 TokenKind::RightBracket => break,
674 _ => {
675 self.error_at_peek("expected `,` or `]` in array pattern");
676 return None;
677 }
678 }
679 }
680 }
681 }
682
683 if !matches!(self.peek().kind, TokenKind::RightBracket) {
684 self.error_at_peek("expected `]` to close array pattern");
685 return None;
686 }
687 let close = self.advance();
688
689 Some(Binding::Array {
690 elems,
691 defaults,
692 rest,
693 span: self.span(open.span.start, close.span.end),
694 })
695 }
696
697 fn parse_function_decl(&mut self) -> Option<StmtId> {
698 let doc = self.take_leading_doc();
699 let kw = self.advance();
700
701 let name_tok = self.expect_binding_identifier("expected function name")?;
702 let name = self.ident_from_token(&name_tok);
703 let is_main = self.block_depth == 0 && name.name == "main";
704 let decl = self.parse_function_decl_after_name(doc, kw, name);
705 if decl.is_none() && is_main {
706 self.ast.unparsed_main = true;
707 }
708 decl
709 }
710
711 fn parse_function_decl_after_name(
712 &mut self,
713 doc: Option<crate::DocComment>,
714 kw: Token,
715 name: Ident,
716 ) -> Option<StmtId> {
717 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
718 self.parse_generic_param_list()?
719 } else {
720 Vec::new()
721 };
722
723 if !matches!(self.peek().kind, TokenKind::LeftParen) {
724 self.error_at_peek("expected `(` after function name");
725 return None;
726 }
727 self.advance();
728
729 let params = self.parse_param_list(false)?;
730 self.advance(); if !matches!(self.peek().kind, TokenKind::Colon) {
733 self.error_at_peek_with_help(
734 "expected `:` and return type",
735 vec![format!(
736 "write the return type: `function {}(…): T {{ … }}`, with `void` when it returns nothing",
737 name.name
738 )],
739 );
740 if matches!(self.peek().kind, TokenKind::LeftBrace) {
743 let _ = self.parse_outer_this_boundary(Self::parse_block);
744 }
745 return None;
746 }
747 self.advance();
748 let (return_type, type_predicate) =
749 self.parse_predicate_or_return_type(TypePos::Anywhere)?;
750
751 let body = self.parse_outer_this_boundary(Self::parse_block)?;
754 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
755 .span
756 .end;
757
758 parse_arena_result(
759 self.ast.try_push_stmt(Stmt {
760 kind: StmtKind::Function {
761 name,
762 generics,
763 params,
764 return_type,
765 type_predicate,
766 body,
767 doc,
768 },
769 span: self.span(kw.span.start, body_end),
770 }),
771 &mut self.fatal,
772 )
773 }
774
775 fn parse_class_decl(&mut self) -> Option<StmtId> {
776 let doc = self.take_leading_doc();
777 let kw = self.advance();
778 if self.block_depth > 0 {
781 self.error_at_with_help(
782 kw.span,
783 "a `class` must be declared at the top level of the module",
784 vec!["move this `class` outside any block or function body".to_string()],
785 );
786 }
787 let name = self.parse_type_decl_name("a", "class")?;
788
789 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
790 self.parse_generic_param_list()?
791 } else {
792 Vec::new()
793 };
794
795 let extends = if matches!(self.peek().kind, TokenKind::Extends) {
796 self.advance();
797 Some(self.parse_type_annotation()?)
798 } else {
799 None
800 };
801
802 let mut implements: Vec<crate::TypeAnnotation> = Vec::new();
803 if matches!(self.peek().kind, TokenKind::Implements) {
804 self.advance();
805 loop {
806 implements.push(self.parse_type_annotation()?);
807 if matches!(self.peek().kind, TokenKind::Comma) {
808 self.advance();
809 continue;
810 }
811 break;
812 }
813 }
814
815 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
816 self.error_at_peek("expected `{` after class header");
817 return None;
818 }
819 self.advance();
820
821 let mut members: Vec<crate::ClassMember> = Vec::new();
822 let mut member_failed = false;
823 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
827 && !(member_failed && self.peek_starts_top_level_item())
828 {
829 if matches!(self.peek().kind, TokenKind::Semicolon) {
831 self.advance();
832 continue;
833 }
834 match self.parse_class_member() {
835 Some(member) => members.push(member),
836 None if std::mem::take(&mut self.resume_at_next_member) => member_failed = true,
837 None => return None,
838 }
839 }
840
841 if !matches!(self.peek().kind, TokenKind::RightBrace) {
842 self.error_at_peek("expected `}` to close class");
843 return None;
844 }
845 let close = self.advance();
846 if member_failed {
847 return None;
848 }
849
850 parse_arena_result(
851 self.ast.try_push_stmt(Stmt {
852 kind: StmtKind::ClassDecl {
853 name,
854 generics,
855 extends,
856 implements,
857 members,
858 doc,
859 },
860 span: self.span(kw.span.start, close.span.end),
861 }),
862 &mut self.fatal,
863 )
864 }
865
866 fn parse_class_member(&mut self) -> Option<crate::ClassMember> {
867 let doc = self.take_leading_doc();
868 let member_start = self.peek().span.start;
869
870 if self.peek_identifier_text_is("protected") {
872 let tok = self.advance();
873 self.error_at_with_help(
874 tok.span,
875 "`protected` is not supported",
876 vec!["use `private` or `public`".to_string()],
877 );
878 return None;
879 }
880 if self.peek_identifier_text_is("abstract") {
881 let tok = self.advance();
882 self.error_at_with_help(
883 tok.span,
884 "abstract classes are not supported",
885 vec!["provide a concrete implementation instead of `abstract`".to_string()],
886 );
887 return None;
888 }
889
890 let modifiers = self.parse_class_modifiers();
891 self.reject_async_member_modifier();
892
893 for (kw, kind) in [
896 ("get", crate::AccessorKind::Get),
897 ("set", crate::AccessorKind::Set),
898 ] {
899 if self.peek_identifier_text_is(kw)
900 && is_property_name(&self.peek_at(1).kind)
901 && matches!(self.peek_at(2).kind, TokenKind::LeftParen)
902 {
903 if let Some(span) = modifiers.readonly {
904 self.reject_readonly_modifier(span);
905 }
906 if let Some(span) = modifiers.static_span {
907 self.error_at_with_help(
908 span,
909 "static accessors are not supported",
910 vec!["use a static method: `static name(): T { … }`".to_string()],
911 );
912 return None;
913 }
914 return self.parse_accessor(member_start, modifiers, kind, doc);
915 }
916 }
917
918 if self.peek_identifier_text_is("constructor")
920 && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
921 {
922 if let Some(span) = modifiers.static_span {
923 self.error_at_with_help(
924 span,
925 "a constructor cannot be `static`",
926 vec![
927 "remove `static` — the constructor already belongs to the class, not \
928 instances"
929 .to_string(),
930 ],
931 );
932 return None;
933 }
934 if let Some(span) = modifiers.readonly {
935 self.error_at_with_help(
936 span,
937 "a constructor cannot be `readonly`",
938 vec!["remove `readonly`; it applies to fields".to_string()],
939 );
940 return None;
941 }
942 return self.parse_constructor(member_start, modifiers.visibility, doc);
943 }
944
945 let name = self.expect_property_ident("expected class member name")?;
946 let optional_span = if matches!(self.peek().kind, TokenKind::Question) {
947 Some(self.advance().span)
948 } else {
949 None
950 };
951 let optional = optional_span.is_some();
952
953 if matches!(self.peek().kind, TokenKind::LessThan | TokenKind::LeftParen) {
955 if let Some(span) = modifiers.readonly {
956 self.reject_readonly_modifier(span);
957 }
958 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
959 self.parse_generic_param_list()?
960 } else {
961 Vec::new()
962 };
963 if !matches!(self.peek().kind, TokenKind::LeftParen) {
964 self.error_at_peek("expected `(` to start the method parameter list");
965 return None;
966 }
967 self.advance();
968 let params = self.parse_class_member_params(false)?;
969 self.advance(); if !matches!(self.peek().kind, TokenKind::Colon) {
971 self.error_at_peek_with_help(
972 "expected `:` and return type",
973 vec![format!(
974 "write the return type: `{}(…): T {{ … }}`, with `void` when it returns nothing",
975 name.name
976 )],
977 );
978 if matches!(self.peek().kind, TokenKind::LeftBrace)
980 && self.parse_class_member_body().is_some()
981 {
982 self.resume_at_next_member = true;
983 }
984 return None;
985 }
986 self.advance();
987 let return_type = self.parse_type_annotation()?;
988 if self.at_inserted_semicolon() && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
989 {
990 self.advance();
991 }
992 if optional
993 && matches!(
994 self.peek().kind,
995 TokenKind::Semicolon | TokenKind::RightBrace
996 )
997 {
998 if !generics.is_empty() {
999 self.error_at(
1000 name.span,
1001 "generic optional method declarations are not supported",
1002 );
1003 return None;
1004 }
1005 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1006 self.advance().span.end
1007 } else {
1008 return_type.span.end
1009 };
1010 let ty = self.method_declaration_type(
1011 params,
1012 return_type,
1013 self.span(name.span.start, end),
1014 )?;
1015 return Some(crate::ClassMember::Field {
1016 name,
1017 modifiers,
1018 optional: true,
1019 method_signature: true,
1020 ty,
1021 initializer: None,
1022 span: self.span(member_start, end),
1023 doc,
1024 });
1025 }
1026 let body = self.parse_class_member_body()?;
1027 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1028 .span
1029 .end;
1030 return Some(crate::ClassMember::Method {
1031 name,
1032 optional,
1033 modifiers,
1034 generics,
1035 params,
1036 return_type,
1037 body,
1038 span: self.span(member_start, body_end),
1039 doc,
1040 });
1041 }
1042
1043 if let Some(span) = optional_span
1045 && modifiers.static_span.is_some()
1046 {
1047 self.error_at_with_help(
1048 span,
1049 "a static field cannot be optional",
1050 vec!["a static field must be initialized; give it a value with `= …`".to_string()],
1051 );
1052 return None;
1053 }
1054 if !matches!(self.peek().kind, TokenKind::Colon) {
1055 if matches!(self.peek().kind, TokenKind::Equals) {
1056 self.error_at_peek_with_help(
1057 "class fields require a type annotation",
1058 vec!["add `: T` before the initializer, e.g. `name: string = …`".to_string()],
1059 );
1060 } else {
1061 self.error_at_peek("expected `:` and a type for the class field");
1062 }
1063 return None;
1064 }
1065 self.advance();
1066 let ty = self.parse_type_annotation()?;
1067 let initializer = if matches!(self.peek().kind, TokenKind::Equals) {
1068 self.advance();
1069 self.class_member_body_depth += 1;
1072 let init = self.parse_expression();
1073 self.class_member_body_depth -= 1;
1074 Some(init?)
1075 } else {
1076 None
1077 };
1078 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1079 self.error_at_peek("expected `;` after class field");
1080 return None;
1081 }
1082 let semi = self.advance();
1083 Some(crate::ClassMember::Field {
1084 name,
1085 modifiers,
1086 optional,
1087 method_signature: false,
1088 ty,
1089 initializer,
1090 span: self.span(member_start, semi.span.end),
1091 doc,
1092 })
1093 }
1094
1095 fn parse_constructor(
1096 &mut self,
1097 member_start: u32,
1098 visibility: crate::Visibility,
1099 doc: Option<crate::DocComment>,
1100 ) -> Option<crate::ClassMember> {
1101 self.advance(); self.advance(); let params = self.parse_class_member_params(true)?;
1104 self.advance(); if matches!(self.peek().kind, TokenKind::Colon) {
1106 let colon = self.advance();
1107 let _ = self.parse_type_annotation();
1108 self.error_at_with_help(
1109 colon.span,
1110 "a constructor cannot declare a return type",
1111 vec![
1112 "drop the `: T` — a constructor always returns the class instance".to_string(),
1113 ],
1114 );
1115 }
1116 let body = self.parse_class_member_body()?;
1117 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1118 .span
1119 .end;
1120 Some(crate::ClassMember::Constructor {
1121 visibility,
1122 params,
1123 body,
1124 span: self.span(member_start, body_end),
1125 doc,
1126 })
1127 }
1128
1129 fn parse_accessor(
1132 &mut self,
1133 member_start: u32,
1134 modifiers: crate::ClassModifiers,
1135 kind: crate::AccessorKind,
1136 doc: Option<crate::DocComment>,
1137 ) -> Option<crate::ClassMember> {
1138 self.advance(); let name = self.expect_property_ident("expected accessor name")?;
1140 self.advance(); let mut params = self.parse_class_member_params(false)?;
1142 self.advance(); let param = match kind {
1144 crate::AccessorKind::Get => {
1145 if !params.is_empty() {
1146 self.error_at(name.span, "a getter cannot declare parameters");
1147 }
1148 None
1149 }
1150 crate::AccessorKind::Set => {
1151 if params.len() != 1 {
1152 self.error_at(name.span, "a setter must declare exactly one parameter");
1153 return None;
1154 }
1155 let param = params.remove(0);
1156 if param.rest {
1159 self.error_at(
1160 param.name.span,
1161 "a setter's parameter can't be a rest parameter",
1162 );
1163 } else if param.default.is_some() {
1164 self.error_at(name.span, "a setter's parameter can't have a default value");
1165 } else if param.optional {
1166 self.error_at(param.name.span, "a setter's parameter can't be optional");
1167 }
1168 Some(Box::new(param))
1169 }
1170 };
1171 let return_type = if matches!(self.peek().kind, TokenKind::Colon) {
1172 self.advance();
1173 Some(self.parse_type_annotation()?)
1174 } else {
1175 if kind == crate::AccessorKind::Get {
1179 self.error_at_peek_with_help(
1180 "expected `:` and return type",
1181 vec!["get name(): T { … }".to_string()],
1182 );
1183 }
1184 None
1185 };
1186 let body = self.parse_class_member_body()?;
1187 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
1188 .span
1189 .end;
1190 Some(crate::ClassMember::Accessor {
1191 name,
1192 modifiers,
1193 kind,
1194 param,
1195 return_type,
1196 body,
1197 span: self.span(member_start, body_end),
1198 doc,
1199 })
1200 }
1201
1202 fn parse_class_modifiers(&mut self) -> crate::ClassModifiers {
1208 let mut visibility = crate::Visibility::Public;
1209 let mut visibility_span: Option<Span> = None;
1210 let mut readonly: Option<Span> = None;
1211 let mut static_span: Option<Span> = None;
1212 loop {
1213 if self.peek_word_is_class_modifier("public")
1214 || self.peek_word_is_class_modifier("private")
1215 {
1216 let (kind, word) = if self.peek_identifier_text_is("private") {
1217 (crate::Visibility::Private, "private")
1218 } else {
1219 (crate::Visibility::Public, "public")
1220 };
1221 let tok = self.advance();
1222 if visibility_span.is_some() {
1223 self.error_at_with_help(
1224 tok.span,
1225 "a class member may have at most one visibility modifier",
1226 vec!["keep a single `public` or `private`".to_string()],
1227 );
1228 continue;
1229 }
1230 let preceding = static_span
1233 .map(|_| "static")
1234 .or(readonly.map(|_| "readonly"));
1235 if let Some(preceding) = preceding {
1236 self.error_at_with_help(
1237 tok.span,
1238 format!("`{word}` must come before `{preceding}`"),
1239 vec![format!("write `{word} {preceding} <name>`")],
1240 );
1241 }
1242 visibility = kind;
1243 visibility_span = Some(tok.span);
1244 continue;
1245 }
1246 if self.peek_word_is_class_modifier("static") {
1247 let tok = self.advance();
1248 if static_span.is_some() {
1249 self.error_at(tok.span, "duplicate `static` modifier");
1250 } else {
1251 static_span = Some(tok.span);
1252 }
1253 continue;
1254 }
1255 if self.peek_word_is_class_modifier("readonly") {
1256 let tok = self.advance();
1257 if readonly.is_some() {
1258 self.error_at(tok.span, "duplicate `readonly` modifier");
1259 } else {
1260 readonly = Some(tok.span);
1261 }
1262 continue;
1263 }
1264 break;
1265 }
1266 crate::ClassModifiers {
1267 visibility,
1268 visibility_span,
1269 readonly,
1270 static_span,
1271 }
1272 }
1273
1274 fn peek_word_is_class_modifier(&self, word: &str) -> bool {
1280 if !self.peek_identifier_text_is(word) {
1281 return false;
1282 }
1283 if word != "static" && self.line_break_after_peek() {
1284 return false;
1285 }
1286 let next = &self.peek_at(1).kind;
1287 is_property_name(next) || matches!(next, TokenKind::StringLiteral(_))
1288 }
1289
1290 fn parse_class_member_params(
1291 &mut self,
1292 allow_param_properties: bool,
1293 ) -> Option<Vec<ParamDecl>> {
1294 let saved = self.class_member_body_depth;
1295 self.class_member_body_depth = 1;
1296 let params = self.parse_param_list(allow_param_properties);
1297 self.class_member_body_depth = saved;
1298 params
1299 }
1300
1301 fn parse_class_member_body(&mut self) -> Option<StmtId> {
1302 self.class_member_body_depth += 1;
1303 let body = self.parse_block();
1304 self.class_member_body_depth -= 1;
1305 body
1306 }
1307
1308 fn parse_interface_decl(&mut self) -> Option<StmtId> {
1309 let doc = self.take_leading_doc();
1310 let kw = self.advance();
1311 let name = self.parse_type_decl_name("an", "interface")?;
1312
1313 let generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1314 self.parse_generic_param_list()?
1315 } else {
1316 Vec::new()
1317 };
1318
1319 let mut extends = Vec::new();
1320 if matches!(self.peek().kind, TokenKind::Extends) {
1321 self.advance();
1322 loop {
1323 extends.push(self.parse_type_annotation()?);
1324 if !matches!(self.peek().kind, TokenKind::Comma) {
1325 break;
1326 }
1327 self.advance();
1328 }
1329 }
1330 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1331 self.error_at_peek("expected `{` after interface name");
1332 return None;
1333 }
1334 self.advance();
1335
1336 let mut members: Vec<crate::InterfaceMember> = Vec::new();
1337 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
1338 && !self.peek_starts_top_level_item()
1339 {
1340 let member_doc = self.take_leading_doc();
1344 if matches!(self.peek().kind, TokenKind::LeftParen) {
1345 let open = self.advance();
1346 let params = self.parse_param_list(false)?;
1347 self.advance();
1348 if !matches!(self.peek().kind, TokenKind::Colon) {
1349 self.error_at_peek("expected `:` and return type");
1350 return None;
1351 }
1352 self.advance();
1353 let return_type = self.parse_type_annotation()?;
1354 let end = self.finish_interface_member(return_type.span.end)?;
1355 let span = self.span(open.span.start, end);
1356 let sentinel = Ident {
1357 name: "@call".to_string(),
1358 span: open.span,
1359 };
1360 members.push(crate::InterfaceMember::Method {
1361 name: sentinel,
1362 optional: false,
1363 generics: Vec::new(),
1364 params,
1365 return_type,
1366 span,
1367 doc: member_doc,
1368 });
1369 continue;
1370 }
1371 if matches!(self.peek().kind, TokenKind::LessThan) {
1372 self.error_at_peek("generic call signatures are not yet supported");
1373 return None;
1374 }
1375 let member_start = self.peek().span.start;
1376 let readonly = self.eat_readonly_property_modifier();
1377 if self.peek_is_parameterless_index_signature() {
1378 self.skip_parameterless_index_signature(member_start)?;
1379 self.finish_interface_member(self.prev_token_end())?;
1380 continue;
1381 }
1382 if matches!(self.peek().kind, TokenKind::LeftBracket) {
1383 let signature = self.parse_index_signature(readonly)?;
1384 self.finish_interface_member(signature.span.end)?;
1385 members.push(crate::InterfaceMember::IndexSignature(signature));
1386 continue;
1387 }
1388 let member_name = self.expect_property_ident("expected interface member name")?;
1389 let optional = matches!(self.peek().kind, TokenKind::Question);
1390 if optional {
1391 self.advance();
1392 }
1393 if matches!(self.peek().kind, TokenKind::Colon) {
1394 self.advance();
1395 let ty = self.parse_type_annotation()?;
1396 let end = self.finish_interface_member(ty.span.end)?;
1397 let span = self.span(member_name.span.start, end);
1398 members.push(crate::InterfaceMember::Property {
1399 name: member_name,
1400 ty,
1401 optional,
1402 readonly,
1403 span,
1404 doc: member_doc,
1405 });
1406 continue;
1407 }
1408 if readonly {
1409 self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
1410 }
1411 let m_generics = if matches!(self.peek().kind, TokenKind::LessThan) {
1412 self.parse_generic_param_list()?
1413 } else {
1414 Vec::new()
1415 };
1416 if !matches!(self.peek().kind, TokenKind::LeftParen) {
1417 self.error_at_peek(
1418 "expected `(` to start a method signature or `:` to start a property",
1419 );
1420 return None;
1421 }
1422 self.advance();
1423 let params = self.parse_param_list(false)?;
1424 self.advance();
1425 if !matches!(self.peek().kind, TokenKind::Colon) {
1426 self.error_at_peek("expected `:` and return type");
1427 return None;
1428 }
1429 self.advance();
1430 let return_type = self.parse_type_annotation()?;
1431 let end = self.finish_interface_member(return_type.span.end)?;
1432 let span = self.span(member_name.span.start, end);
1433 if optional {
1434 if !m_generics.is_empty() {
1435 self.error_at(
1436 member_name.span,
1437 "generic optional method declarations are not supported",
1438 );
1439 return None;
1440 }
1441 let ty = self.method_declaration_type(params, return_type, span)?;
1442 members.push(crate::InterfaceMember::Property {
1443 name: member_name,
1444 ty,
1445 optional: true,
1446 readonly,
1447 span,
1448 doc: member_doc,
1449 });
1450 } else {
1451 members.push(crate::InterfaceMember::Method {
1452 name: member_name,
1453 optional,
1454 generics: m_generics,
1455 params,
1456 return_type,
1457 span,
1458 doc: member_doc,
1459 });
1460 }
1461 }
1462
1463 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1464 self.error_at_peek("expected `}` to close interface");
1465 return None;
1466 }
1467 let close = self.advance();
1468
1469 parse_arena_result(
1470 self.ast.try_push_stmt(Stmt {
1471 kind: StmtKind::InterfaceDecl {
1472 name,
1473 generics,
1474 extends,
1475 members,
1476 doc,
1477 },
1478 span: self.span(kw.span.start, close.span.end),
1479 }),
1480 &mut self.fatal,
1481 )
1482 }
1483
1484 fn method_declaration_type(
1485 &mut self,
1486 params: Vec<ParamDecl>,
1487 return_type: TypeAnnotation,
1488 span: Span,
1489 ) -> Option<TypeAnnotation> {
1490 let mut fields = Vec::new();
1491 for param in params {
1492 if param.pattern.is_some() || param.default.is_some() {
1493 self.error_at(
1494 param.name.span,
1495 "a method declaration cannot contain a parameter pattern or default value",
1496 );
1497 return None;
1498 }
1499 let Some(ty) = param.ty else {
1500 return self.invariant_failure("method parameter annotation was not retained");
1501 };
1502 fields.push(TypeAnnotationField {
1503 name: param.name,
1504 ty,
1505 optional: param.optional,
1506 readonly: false,
1507 rest: param.rest,
1508 method: false,
1509 });
1510 }
1511 Some(TypeAnnotation {
1512 kind: TypeAnnotationKind::Function {
1513 params: fields,
1514 return_type: Box::new(return_type),
1515 },
1516 span,
1517 })
1518 }
1519
1520 fn finish_interface_member(&mut self, body_end: u32) -> Option<u32> {
1521 self.finish_type_member(body_end, "expected `;`, `,`, or `}` after interface member")
1522 }
1523
1524 fn finish_type_member(&mut self, body_end: u32, message: &str) -> Option<u32> {
1530 match self.peek().kind {
1531 TokenKind::Semicolon | TokenKind::Comma => Some(self.advance().span.end),
1532 TokenKind::RightBrace => Some(body_end),
1533 _ if self.line_break_before_peek() => Some(body_end),
1534 _ => {
1535 self.error_at_peek(message);
1536 None
1537 }
1538 }
1539 }
1540
1541 fn parse_type_alias_decl(&mut self) -> Option<StmtId> {
1542 let doc = self.take_leading_doc();
1543 let kw = self.advance();
1544 let name = self.parse_type_decl_name("a", "type alias")?;
1545
1546 let generics: Vec<Ident> = if matches!(self.peek().kind, TokenKind::LessThan) {
1547 self.parse_generic_param_list()?
1548 } else {
1549 Vec::new()
1550 };
1551
1552 if !matches!(self.peek().kind, TokenKind::Equals) {
1553 self.error_at_peek("expected `=` after type alias name");
1554 return None;
1555 }
1556 self.advance();
1557
1558 let ty = self.parse_type_annotation()?;
1559
1560 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
1563 self.advance().span.end
1564 } else if self.line_break_before_peek() {
1565 self.prev_token_end()
1566 } else {
1567 self.error_at_peek("expected `;` after type alias body");
1568 return None;
1569 };
1570
1571 parse_arena_result(
1572 self.ast.try_push_stmt(Stmt {
1573 kind: StmtKind::TypeAliasDecl {
1574 name,
1575 generics,
1576 ty,
1577 doc,
1578 },
1579 span: self.span(kw.span.start, end),
1580 }),
1581 &mut self.fatal,
1582 )
1583 }
1584
1585 fn parse_enum_decl(&mut self) -> Option<StmtId> {
1587 #[derive(Copy, Clone)]
1588 enum Kind {
1589 Number,
1590 String,
1591 }
1592 let doc = self.take_leading_doc();
1593 let kw = self.advance();
1594 let name = self.parse_type_decl_name("an", "enum")?;
1595
1596 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
1597 self.error_at_peek("expected `{` after enum name");
1598 return None;
1599 }
1600 self.advance();
1601
1602 let mut members: Vec<EnumMember> = Vec::new();
1603 let mut first_explicit: Option<(Kind, Span)> = None;
1604 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1605 if matches!(self.peek().kind, TokenKind::Semicolon) {
1607 self.advance();
1608 continue;
1609 }
1610 let member_doc = self.take_leading_doc();
1611 let member_name_tok = self.expect_identifier_name("expected enum member name")?;
1612 let member_name = self.ident_from_token(&member_name_tok);
1613
1614 let value = if matches!(self.peek().kind, TokenKind::Equals) {
1615 self.advance();
1616 let init = self.parse_enum_initializer()?;
1617 let (this_kind, this_span) = match &init {
1618 EnumInitializer::Number { span, .. } => (Kind::Number, *span),
1619 EnumInitializer::String { span, .. } => (Kind::String, *span),
1620 };
1621 match first_explicit {
1622 None => first_explicit = Some((this_kind, this_span)),
1623 Some((prev, _)) => {
1624 let mismatched = matches!(
1625 (prev, this_kind),
1626 (Kind::Number, Kind::String) | (Kind::String, Kind::Number)
1627 );
1628 if mismatched {
1629 self.error_at(
1630 this_span,
1631 "mixed numeric and string enum members are not allowed",
1632 );
1633 }
1634 }
1635 }
1636 Some(init)
1637 } else {
1638 None
1639 };
1640
1641 let end_span = value
1642 .as_ref()
1643 .map_or(member_name.span, super::ast::EnumInitializer::span);
1644 let member_span = self.span(member_name.span.start, end_span.end);
1645 members.push(EnumMember {
1646 name: member_name,
1647 value,
1648 span: member_span,
1649 doc: member_doc,
1650 });
1651
1652 match self.peek().kind {
1653 TokenKind::Comma | TokenKind::Semicolon => {
1656 self.advance();
1657 }
1658 TokenKind::RightBrace | TokenKind::Eof => {}
1659 _ => {
1660 self.error_at_peek("expected `,` or `}` after enum member");
1661 return None;
1662 }
1663 }
1664 }
1665
1666 if !matches!(self.peek().kind, TokenKind::RightBrace) {
1667 self.error_at_peek("expected `}` to close enum");
1668 return None;
1669 }
1670 let close = self.advance();
1671
1672 parse_arena_result(
1673 self.ast.try_push_stmt(Stmt {
1674 kind: StmtKind::EnumDecl { name, members, doc },
1675 span: self.span(kw.span.start, close.span.end),
1676 }),
1677 &mut self.fatal,
1678 )
1679 }
1680
1681 fn parse_export(&mut self) -> Option<StmtId> {
1688 let export_tok = self.advance();
1689 let export_span = export_tok.span;
1690
1691 let nested = self.block_depth > 0;
1692 if nested {
1693 self.error_at_with_help(
1694 export_span,
1695 "`export` statements must appear at the top of the file",
1696 vec!["move this `export` to the top level of the module".to_string()],
1697 );
1698 }
1701
1702 if self.peek_identifier_text_is("type")
1705 && matches!(self.peek_at(1).kind, TokenKind::LeftBrace)
1706 {
1707 self.advance();
1708 }
1709
1710 self.reject_async_modifier();
1711 let declares = matches!(
1712 self.peek().kind,
1713 TokenKind::Function
1714 | TokenKind::Class
1715 | TokenKind::Const
1716 | TokenKind::Let
1717 | TokenKind::Interface
1718 | TokenKind::Enum
1719 ) || self.at_type_alias_head();
1720 if declares {
1721 if export_tok.leading_doc.is_some() && self.peek().leading_doc.is_none() {
1724 if let Some(token) = self.tokens.get_mut(self.pos) {
1725 token.leading_doc = export_tok.leading_doc;
1726 } else {
1727 return self.invariant_failure("export cursor is outside token stream");
1728 }
1729 }
1730 let id = self.parse_statement()?;
1731 if nested {
1732 return None;
1733 }
1734 self.ast.exported_decls.push(ExportedDecl {
1735 stmt: id,
1736 export_span,
1737 });
1738 return Some(id);
1739 }
1740
1741 match self.peek().kind {
1742 TokenKind::LeftBrace => {
1743 let doc = export_tok
1746 .leading_doc
1747 .as_ref()
1748 .and_then(|raw| self.parse_doc(raw))
1749 .or_else(|| self.take_leading_doc());
1750 self.parse_export_from(export_span, nested, doc)
1751 }
1752 TokenKind::Default => {
1753 self.error_at_peek_with_help(
1754 "`export default` is not supported",
1755 vec![
1756 "default exports are out of scope; drop `default` — a named \
1757 `export` marks the declaration visible to sibling modules"
1758 .to_string(),
1759 ],
1760 );
1761 None
1762 }
1763 _ => {
1764 self.error_at_peek_with_help(
1765 "expected a declaration after `export`",
1766 vec![
1767 "`export` may precede `function`, `class`, `const`, `let`, `type`, \
1768 `interface`, or `enum`, or take the form `export { … }`"
1769 .to_string(),
1770 ],
1771 );
1772 None
1773 }
1774 }
1775 }
1776
1777 fn parse_export_from(
1780 &mut self,
1781 export_span: Span,
1782 nested: bool,
1783 doc: Option<crate::DocComment>,
1784 ) -> Option<StmtId> {
1785 let (specs, open_span) = self.parse_specifier_list(SpecifierList::Export)?;
1786 if specs.is_empty() {
1787 self.error_at_with_help(
1788 open_span,
1789 "empty export specifier list",
1790 vec!["name what you re-export: `export { name } from \"./util\";`".to_string()],
1791 );
1792 return None;
1793 }
1794
1795 let source = if self.peek_identifier_text_is("from") {
1796 self.advance();
1797 let module_tok = self.peek().clone();
1798 let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1799 s.clone()
1800 } else {
1801 self.error_at_peek("expected module specifier string after `from`");
1802 return None;
1803 };
1804 self.advance();
1805 Some((module, module_tok.span))
1806 } else {
1807 None
1808 };
1809
1810 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1811 self.error_at_peek("expected `;` after export statement");
1812 return None;
1813 }
1814 let semi = self.advance();
1815
1816 if nested {
1817 return None;
1818 }
1819
1820 parse_arena_result(
1821 self.ast.try_push_stmt(Stmt {
1822 kind: StmtKind::ExportFrom { specs, source, doc },
1823 span: self.span(export_span.start, semi.span.end),
1824 }),
1825 &mut self.fatal,
1826 )
1827 }
1828
1829 fn parse_import_decl(&mut self) -> Option<StmtId> {
1830 let doc = self.take_leading_doc();
1831 let kw = self.advance();
1832 let kw_span = kw.span;
1833
1834 let nested = self.block_depth > 0;
1835 if nested {
1836 self.error_at_with_help(
1837 kw_span,
1838 "`import` statements must appear at the top of the file",
1839 vec!["move this `import` outside any block / function body".to_string()],
1840 );
1841 }
1843
1844 let kind = self.parse_import_kind()?;
1845
1846 if !self.peek_identifier_text_is("from") {
1847 self.error_at_peek("expected `from` after import specifier list");
1848 return None;
1849 }
1850 self.advance();
1851
1852 let module_tok = self.peek().clone();
1853 let module = if let TokenKind::StringLiteral(s) = &module_tok.kind {
1854 s.clone()
1855 } else {
1856 self.error_at_peek("expected module specifier string after `from`");
1857 return None;
1858 };
1859 let module_span = module_tok.span;
1860 self.advance();
1861
1862 if !matches!(self.peek().kind, TokenKind::Semicolon) {
1863 self.error_at_peek("expected `;` after import statement");
1864 return None;
1865 }
1866 let semi = self.advance();
1867
1868 if nested {
1869 return None;
1870 }
1871
1872 parse_arena_result(
1873 self.ast.try_push_stmt(Stmt {
1874 kind: StmtKind::Import {
1875 module,
1876 module_span,
1877 kind,
1878 doc,
1879 },
1880 span: self.span(kw_span.start, semi.span.end),
1881 }),
1882 &mut self.fatal,
1883 )
1884 }
1885
1886 fn parse_import_kind(&mut self) -> Option<ImportKind> {
1887 if self.peek_identifier_text_is("type")
1892 && match self.peek_at(1).kind {
1893 TokenKind::LeftBrace | TokenKind::Star => true,
1894 TokenKind::Identifier => {
1895 !(self.peek_at_is_word(1, "from")
1896 && matches!(self.peek_at(2).kind, TokenKind::StringLiteral(_)))
1897 }
1898 _ => false,
1899 }
1900 {
1901 self.advance();
1902 }
1903
1904 match self.peek().kind {
1905 TokenKind::LeftBrace => self.parse_named_imports(),
1906 TokenKind::Identifier => {
1907 let local_tok = self.advance();
1908 let local_name = self.ident_from_token(&local_tok);
1909 if matches!(self.peek().kind, TokenKind::Comma) {
1910 self.error_at_peek_with_help(
1911 "combining default and named imports is not supported",
1912 vec!["use two separate `import` statements".to_string()],
1913 );
1914 return None;
1915 }
1916 Some(ImportKind::Namespace { local_name })
1917 }
1918 TokenKind::Star => {
1921 self.advance();
1922 if !self.peek_identifier_text_is("as") {
1923 self.error_at_peek_with_help(
1924 "expected `as <name>` after `import *`",
1925 vec![
1926 "name the namespace binding: \
1927 `import * as ns from \"pkg\";` (or use the \
1928 short form: `import ns from \"pkg\";`)"
1929 .to_string(),
1930 ],
1931 );
1932 return None;
1933 }
1934 self.advance();
1935 let local_tok = self.expect_identifier("expected namespace name after `as`")?;
1936 let local_name = self.ident_from_token(&local_tok);
1937 Some(ImportKind::Namespace { local_name })
1938 }
1939 TokenKind::StringLiteral(_) => {
1940 self.error_at_peek_with_help(
1941 "side-effect-only imports are not supported",
1942 vec![
1943 "name what you import: `import { name } from \"pkg\";` \
1944 or `import ns from \"pkg\";`"
1945 .to_string(),
1946 ],
1947 );
1948 None
1949 }
1950 _ => {
1951 self.error_at_peek("expected `{` or namespace name after `import`");
1952 None
1953 }
1954 }
1955 }
1956
1957 fn parse_named_imports(&mut self) -> Option<ImportKind> {
1958 let (specs, open_span) = self.parse_specifier_list(SpecifierList::Import)?;
1959 if specs.is_empty() {
1960 self.error_at_with_help(
1961 open_span,
1962 "empty import specifier list",
1963 vec!["name what you import: `import { name } from \"pkg\";`".to_string()],
1964 );
1965 return None;
1966 }
1967 Some(ImportKind::Named(specs))
1968 }
1969
1970 fn parse_specifier_list(
1976 &mut self,
1977 list: SpecifierList,
1978 ) -> Option<(Vec<ImportSpecifier>, Span)> {
1979 let what = list.noun();
1980 let open = self.advance();
1981 let mut specs: Vec<ImportSpecifier> = Vec::new();
1982 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
1983 if self.peek_identifier_text_is("type")
1986 && matches!(self.peek_at(1).kind, TokenKind::Identifier)
1987 && !self.peek_at_is_word(1, "as")
1988 {
1989 self.advance();
1990 }
1991 let imported_tok =
1992 self.expect_identifier_name(&format!("expected {what} specifier name"))?;
1993 let imported_name = self.ident_from_token(&imported_tok);
1994
1995 let binds_local = list == SpecifierList::Import;
1996 let local_name = if self.peek_identifier_text_is("as") {
1997 self.advance();
1998 let local_tok = self.expect_identifier_name("expected local name after `as`")?;
1999 if binds_local {
2000 self.reject_strict_mode_reserved_word(&local_tok);
2001 }
2002 self.ident_from_token(&local_tok)
2003 } else {
2004 if binds_local {
2005 self.reject_strict_mode_reserved_word(&imported_tok);
2006 }
2007 imported_name.clone()
2008 };
2009
2010 specs.push(ImportSpecifier {
2011 imported_name,
2012 local_name,
2013 });
2014
2015 match self.peek().kind {
2016 TokenKind::Comma => {
2017 self.advance();
2018 }
2019 TokenKind::RightBrace | TokenKind::Eof => {}
2020 _ => {
2021 self.error_at_peek(format!("expected `,` or `}}` after {what} specifier"));
2022 return None;
2023 }
2024 }
2025 }
2026
2027 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2028 self.error_at_peek(format!("expected `}}` to close {what} specifier list"));
2029 return None;
2030 }
2031 self.advance();
2032
2033 Some((specs, open.span))
2034 }
2035
2036 fn parse_enum_initializer(&mut self) -> Option<EnumInitializer> {
2037 let neg = if matches!(self.peek().kind, TokenKind::Minus) {
2038 Some(self.advance())
2039 } else {
2040 None
2041 };
2042 let tok = self.advance();
2043 match &tok.kind {
2044 TokenKind::NumberLiteral(n) => {
2045 let value = if neg.is_some() { -n } else { *n };
2046 let value = if value == 0.0 { 0.0 } else { value };
2048 let start = neg.as_ref().map_or(tok.span.start, |t| t.span.start);
2049 Some(EnumInitializer::Number {
2050 value,
2051 span: self.span(start, tok.span.end),
2052 })
2053 }
2054 TokenKind::StringLiteral(s) => {
2055 if let Some(neg_tok) = &neg {
2056 self.error_at(
2057 self.span(neg_tok.span.start, tok.span.end),
2058 "cannot negate a string enum initializer",
2059 );
2060 return None;
2061 }
2062 Some(EnumInitializer::String {
2063 value: s.clone(),
2064 span: tok.span,
2065 })
2066 }
2067 _ => {
2068 let span = neg.map_or(tok.span, |t| self.span(t.span.start, tok.span.end));
2069 self.error_at(
2070 span,
2071 "expected a number or string literal for enum initializer",
2072 );
2073 None
2074 }
2075 }
2076 }
2077
2078 fn is_contextual_type_operator(&self, index: usize, operator: &str) -> bool {
2086 let Some(tok) = self.tokens.get(index) else {
2087 return false;
2088 };
2089 if !self.token_is_word(tok, operator) {
2090 return false;
2091 }
2092 matches!(
2093 self.tokens.get(index + 1).map(|t| &t.kind),
2094 Some(
2095 TokenKind::Identifier
2096 | TokenKind::Void
2097 | TokenKind::LeftBrace
2098 | TokenKind::LeftParen
2099 | TokenKind::LeftBracket
2100 | TokenKind::StringLiteral(_)
2101 | TokenKind::NumberLiteral(_)
2102 | TokenKind::BooleanLiteral(_)
2103 | TokenKind::NullLiteral
2104 )
2105 )
2106 }
2107
2108 fn parse_type_argument_list(&mut self) -> Option<(Vec<TypeAnnotation>, u32)> {
2109 let lt = self.advance();
2110 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2111 self.error_at(lt.span, "empty generic argument list `<>` is not allowed");
2112 return None;
2113 }
2114 let mut args = Vec::new();
2115 loop {
2116 args.push(self.parse_type_annotation()?);
2117 match self.peek().kind {
2118 TokenKind::Comma => {
2119 self.advance();
2120 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2121 let gt = self.advance();
2122 return Some((args, gt.span.end));
2123 }
2124 }
2125 TokenKind::GreaterThan => {
2126 let gt = self.advance();
2127 return Some((args, gt.span.end));
2128 }
2129 _ => {
2130 self.error_at(
2131 lt.span,
2132 "expected `,` or `>` to close generic argument list",
2133 );
2134 return None;
2135 }
2136 }
2137 }
2138 }
2139
2140 fn parse_type_decl_name(&mut self, article: &str, noun: &str) -> Option<Ident> {
2144 let name_tok = self.expect_identifier(&format!("expected {noun} name"))?;
2145 let name = self.ident_from_token(&name_tok);
2146 if BUILT_IN_TYPE_NAMES.contains(&name.name.as_str()) {
2147 self.error_at_with_help(
2148 name.span,
2149 format!(
2150 "`{}` is a built-in type and can't be used as {article} {noun} name",
2151 name.name
2152 ),
2153 vec![format!("rename the {noun}")],
2154 );
2155 }
2156 Some(name)
2157 }
2158
2159 fn parse_generic_param_list(&mut self) -> Option<Vec<Ident>> {
2160 let lt = self.advance();
2161 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2162 self.error_at(lt.span, "empty generic parameter list `<>` is not allowed");
2163 return None;
2164 }
2165 let mut generics = Vec::new();
2166 loop {
2167 let name_tok = self.expect_identifier("expected generic parameter name")?;
2168 let name = self.ident_from_token(&name_tok);
2169 if generics.iter().any(|g: &Ident| g.name == name.name) {
2173 self.error_at_with_help(
2174 name.span,
2175 format!("duplicate type parameter `{}`", name.name),
2176 vec!["give each type parameter its own name".to_string()],
2177 );
2178 } else {
2179 generics.push(name);
2180 }
2181 match self.peek().kind {
2182 TokenKind::Comma => {
2183 self.advance();
2184 if matches!(self.peek().kind, TokenKind::GreaterThan) {
2185 self.advance();
2186 return Some(generics);
2187 }
2188 }
2189 TokenKind::GreaterThan => {
2190 self.advance();
2191 return Some(generics);
2192 }
2193 _ => {
2194 self.error_at_peek("expected `,` or `>`");
2195 return None;
2196 }
2197 }
2198 }
2199 }
2200
2201 fn parse_param_list(&mut self, allow_param_properties: bool) -> Option<Vec<ParamDecl>> {
2202 if matches!(self.peek().kind, TokenKind::RightParen) {
2203 return Some(Vec::new());
2204 }
2205 let mut params = Vec::new();
2206 loop {
2207 let param = self.parse_param(allow_param_properties)?;
2208 let is_rest = param.rest;
2209 params.push(param);
2210 match self.peek().kind {
2211 TokenKind::Comma => {
2212 if is_rest {
2213 let comma = self.advance();
2214 self.error_at(comma.span, "rest parameter must be the last parameter");
2215 return None;
2216 }
2217 self.advance();
2218 if matches!(self.peek().kind, TokenKind::RightParen) {
2219 break;
2220 }
2221 }
2222 TokenKind::RightParen => break,
2223 _ => {
2224 self.error_at_peek("expected `,` or `)`");
2225 return None;
2226 }
2227 }
2228 }
2229 self.check_parameter_order(params.iter().map(ParamOrder::of_param));
2230 Some(params)
2231 }
2232
2233 fn parse_param(&mut self, allow_param_properties: bool) -> Option<ParamDecl> {
2234 let modifiers = if self.peek_word_is_class_modifier("public")
2238 || self.peek_word_is_class_modifier("private")
2239 || self.peek_word_is_class_modifier("readonly")
2240 {
2241 let span = self.peek().span;
2242 let parsed = self.parse_class_modifiers();
2243 if allow_param_properties {
2244 Some(parsed)
2245 } else {
2246 self.error_at_with_help(
2247 span,
2248 "parameter properties are only allowed in a constructor",
2249 vec![
2250 "move the `public`/`private`/`readonly` modifier to a constructor parameter"
2251 .to_string(),
2252 ],
2253 );
2254 None
2255 }
2256 } else {
2257 None
2258 };
2259
2260 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
2261 self.advance();
2262 true
2263 } else {
2264 false
2265 };
2266 if modifiers.is_some() && rest {
2267 self.error_at_peek("a parameter property cannot be a rest parameter");
2268 return None;
2269 }
2270
2271 let (name, pattern) = match self.peek().kind {
2274 TokenKind::LeftBrace | TokenKind::LeftBracket => {
2275 if rest {
2276 self.error_at_peek("rest parameter cannot be destructured");
2277 return None;
2278 }
2279 if modifiers.is_some() {
2280 self.error_at_peek("a parameter property cannot be destructured");
2281 return None;
2282 }
2283 let binding = self.parse_binding()?;
2284 let placeholder = Ident {
2285 name: String::new(),
2286 span: binding.span(),
2287 };
2288 (placeholder, Some(binding))
2289 }
2290 _ => {
2291 let name_tok = self.expect_binding_identifier(if rest {
2292 "expected identifier after `...`"
2293 } else {
2294 "expected parameter name"
2295 })?;
2296 let name = self.ident_from_token(&name_tok);
2297 (name, None)
2298 }
2299 };
2300
2301 let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
2302 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2303 self.advance();
2304 Some(self.parse_type_annotation()?)
2305 } else if !rest && matches!(self.peek().kind, TokenKind::Equals) {
2306 None
2307 } else {
2308 if rest {
2309 self.error_at_peek_with_help(
2310 "rest parameter requires a type annotation",
2311 vec!["function name(...args: T[]): R { … }".to_string()],
2312 );
2313 } else {
2314 self.error_at_peek_with_help(
2315 "parameter requires a type annotation",
2316 vec!["add a type (`x: T`) or a default initializer (`x = value`)".to_string()],
2317 );
2318 }
2319 return None;
2320 };
2321 let default = self.parse_parameter_default(rest, optional)?;
2322 Some(ParamDecl {
2323 name,
2324 ty,
2325 default,
2326 optional,
2327 pattern,
2328 rest,
2329 modifiers,
2330 })
2331 }
2332
2333 fn parse_parameter_optional(&mut self, rest: bool, pattern: bool) -> Option<bool> {
2334 if !matches!(self.peek().kind, TokenKind::Question) {
2335 return Some(false);
2336 }
2337 let question = self.advance();
2338 if rest || pattern {
2339 self.error_at(
2340 question.span,
2341 if rest {
2342 "a rest parameter cannot be optional"
2343 } else {
2344 "a destructured parameter cannot be optional; use a default value"
2345 },
2346 );
2347 return None;
2348 }
2349 Some(true)
2350 }
2351
2352 fn parse_parameter_default(&mut self, rest: bool, optional: bool) -> Option<Option<ExprId>> {
2355 if !matches!(self.peek().kind, TokenKind::Equals) {
2356 return Some(None);
2357 }
2358 if rest {
2359 self.error_rest_parameter_default();
2360 return None;
2361 }
2362 if optional {
2363 self.error_at_peek_with_help(
2364 "an optional parameter cannot have a default value",
2365 vec!["drop the `?`: a parameter with a default is already optional".to_string()],
2366 );
2367 }
2368 self.advance();
2369 Some(Some(self.parse_expression()?))
2370 }
2371
2372 fn error_rest_parameter_default(&mut self) {
2374 self.error_at_peek_with_help(
2375 "rest parameter cannot have a default value",
2376 vec!["omit the `= …`; an unspecified rest defaults to `[]`".to_string()],
2377 );
2378 }
2379
2380 fn check_parameter_order<'p>(&mut self, params: impl IntoIterator<Item = ParamOrder<'p>>) {
2384 let mut follows_optional = false;
2385 for param in params {
2386 if param.optional {
2387 follows_optional = true;
2388 continue;
2389 }
2390 if !follows_optional || !param.required {
2391 continue;
2392 }
2393 let label = match param.name {
2394 Some(name) => format!("parameter `{name}`"),
2395 None => "destructured parameter".to_string(),
2396 };
2397 self.error_at_with_help(
2398 param.span,
2399 format!("required {label} cannot follow an optional parameter"),
2400 vec!["make it optional too (`x?: T`), give it a default (`x: T = …`), or move it before the optional parameters".to_string()],
2401 );
2402 return;
2403 }
2404 }
2405
2406 fn eat_asi_semicolon_before(&mut self, next: TokenKind) {
2411 if matches!(self.peek().kind, TokenKind::Semicolon) && self.peek_at(1).kind == next {
2412 self.advance();
2413 }
2414 }
2415
2416 fn parse_block(&mut self) -> Option<StmtId> {
2417 self.eat_asi_semicolon_before(TokenKind::LeftBrace);
2418 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2419 self.error_at_peek("expected `{`");
2420 return None;
2421 }
2422 let open = self.advance();
2423 self.block_depth += 1;
2424
2425 let mut stmts = Vec::new();
2426 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof) {
2427 if matches!(self.peek().kind, TokenKind::Semicolon) {
2428 self.advance();
2429 continue;
2430 }
2431 let pos_before = self.pos;
2432 if let Some(id) = self.parse_statement() {
2433 stmts.push(id);
2434 } else {
2435 self.recover();
2436 if self.pos == pos_before && !self.is_at_eof() {
2437 self.advance();
2438 }
2439 }
2440 }
2441
2442 self.block_depth -= 1;
2443 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2444 self.error_at_peek("expected `}`");
2445 return None;
2446 }
2447 let close = self.advance();
2448
2449 let span = self.span(open.span.start, close.span.end);
2450 parse_arena_result(
2451 self.ast.try_push_stmt(Stmt {
2452 kind: StmtKind::Block(stmts),
2453 span,
2454 }),
2455 &mut self.fatal,
2456 )
2457 }
2458
2459 fn parse_control_body(&mut self) -> Option<StmtId> {
2464 if matches!(self.peek().kind, TokenKind::LeftBrace) {
2465 return self.parse_block();
2466 }
2467 let keyword = match self.peek().kind {
2472 TokenKind::Let => Some(("a", "let")),
2473 TokenKind::Const => Some(("a", "const")),
2474 TokenKind::Function => Some(("a", "function")),
2475 TokenKind::Interface => Some(("an", "interface")),
2477 TokenKind::Enum => Some(("an", "enum")),
2478 _ if self.peek_identifier_text_is("type") => Some(("a", "type")),
2479 _ => None,
2480 };
2481 let declares = match self.peek_at(1).kind {
2485 TokenKind::Identifier => true,
2486 TokenKind::LeftBracket | TokenKind::LeftBrace => !matches!(keyword, Some((_, "type"))),
2487 _ => false,
2488 };
2489 if let Some((article, keyword)) = keyword
2490 && declares
2491 {
2492 self.error_at_peek_with_help(
2493 format!(
2494 "{article} `{keyword}` declaration can't be the body of a statement without braces"
2495 ),
2496 vec![format!("wrap it in braces: `{{ {keyword} … }}`")],
2497 );
2498 }
2499 self.block_depth += 1;
2500 let stmt = self.parse_statement();
2501 self.block_depth -= 1;
2502 let stmt = stmt?;
2503 let span = parse_arena_result(self.ast.try_stmt(stmt), &mut self.fatal)?.span;
2504 parse_arena_result(
2505 self.ast.try_push_stmt(Stmt {
2506 kind: StmtKind::Block(vec![stmt]),
2507 span,
2508 }),
2509 &mut self.fatal,
2510 )
2511 }
2512
2513 fn parse_loop_body(&mut self) -> Option<StmtId> {
2516 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2517 return self.parse_control_body();
2518 }
2519 self.empty_body_at_semicolon()
2520 }
2521
2522 fn parse_branch_body(&mut self, keyword: &str) -> Option<StmtId> {
2526 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2527 return self.parse_control_body();
2528 }
2529 self.error_at_peek_with_help(
2530 format!("`{keyword}` has an empty body"),
2531 vec![format!("remove the `;` after `{keyword}`")],
2532 );
2533 self.empty_body_at_semicolon()
2534 }
2535
2536 fn empty_body_at_semicolon(&mut self) -> Option<StmtId> {
2538 let semi = self.advance();
2539 parse_arena_result(
2540 self.ast.try_push_stmt(Stmt {
2541 kind: StmtKind::Block(vec![]),
2542 span: semi.span,
2543 }),
2544 &mut self.fatal,
2545 )
2546 }
2547
2548 fn parse_if(&mut self) -> Option<StmtId> {
2549 self.with_recursion_limit(Self::parse_if_inner)
2550 }
2551
2552 fn parse_if_inner(&mut self) -> Option<StmtId> {
2553 let kw = self.advance();
2554 let condition = self.parse_paren_condition()?;
2555 let then_block = self.parse_branch_body("if")?;
2556
2557 let else_block = if matches!(self.peek().kind, TokenKind::Else) {
2558 self.advance();
2559 if matches!(self.peek().kind, TokenKind::If) {
2560 Some(self.parse_if()?)
2561 } else {
2562 Some(self.parse_branch_body("else")?)
2563 }
2564 } else {
2565 None
2566 };
2567
2568 let end = match else_block {
2569 Some(id) => {
2570 parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
2571 .span
2572 .end
2573 }
2574 None => {
2575 parse_arena_result(self.ast.try_stmt(then_block), &mut self.fatal)?
2576 .span
2577 .end
2578 }
2579 };
2580
2581 parse_arena_result(
2582 self.ast.try_push_stmt(Stmt {
2583 kind: StmtKind::If {
2584 condition,
2585 then_block,
2586 else_block,
2587 },
2588 span: self.span(kw.span.start, end),
2589 }),
2590 &mut self.fatal,
2591 )
2592 }
2593
2594 fn parse_while(&mut self) -> Option<StmtId> {
2595 let kw = self.advance();
2596 let condition = self.parse_paren_condition()?;
2597 let body = self.parse_loop_body()?;
2598 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2599 .span
2600 .end;
2601
2602 parse_arena_result(
2603 self.ast.try_push_stmt(Stmt {
2604 kind: StmtKind::While { condition, body },
2605 span: self.span(kw.span.start, body_end),
2606 }),
2607 &mut self.fatal,
2608 )
2609 }
2610
2611 fn parse_for(&mut self) -> Option<StmtId> {
2613 let kw = self.advance();
2614 if self.peek_identifier_text_is("await")
2617 && matches!(self.peek_at(1).kind, TokenKind::LeftParen)
2618 {
2619 let tok = self.advance();
2620 self.error_at_with_help(
2621 tok.span,
2622 "`for await` is not supported",
2623 vec![
2624 "calls return their values directly: remove `await` and write \
2625 `for (const e of …)`"
2626 .to_string(),
2627 ],
2628 );
2629 }
2630 if !matches!(self.peek().kind, TokenKind::LeftParen) {
2631 self.error_at_peek("expected `(` after `for`");
2632 return None;
2633 }
2634 self.advance();
2635
2636 if self.scan_for_of_shape() {
2637 self.parse_for_of_tail(kw)
2638 } else {
2639 self.parse_c_for_tail(kw)
2640 }
2641 }
2642
2643 fn scan_for_of_shape(&self) -> bool {
2645 if !matches!(self.peek().kind, TokenKind::Let | TokenKind::Const) {
2646 return false;
2647 }
2648 let (start, mut depth) = match self.peek_at(1).kind {
2650 TokenKind::Identifier => match self.peek_at(2).kind {
2651 TokenKind::Identifier if self.peek_at_is_word(2, "of") => return true,
2652 TokenKind::Colon => (3, 0i32),
2653 _ => return false,
2654 },
2655 TokenKind::LeftBracket | TokenKind::LeftBrace => (2, 1i32),
2656 _ => return false,
2657 };
2658 let mut i = start;
2659 loop {
2660 match self.peek_at(i).kind {
2661 TokenKind::Eof => return false,
2662 TokenKind::LessThan
2663 | TokenKind::LeftParen
2664 | TokenKind::LeftBracket
2665 | TokenKind::LeftBrace => {
2666 depth += 1;
2667 }
2668 TokenKind::GreaterThan
2669 | TokenKind::RightParen
2670 | TokenKind::RightBracket
2671 | TokenKind::RightBrace => {
2672 if depth == 0 {
2673 return false;
2674 }
2675 depth -= 1;
2676 }
2677 TokenKind::Identifier if depth == 0 && self.peek_at_is_word(i, "of") => {
2680 return true;
2681 }
2682 TokenKind::Equals | TokenKind::Semicolon if depth == 0 => return false,
2683 _ => {}
2684 }
2685 i += 1;
2686 }
2687 }
2688
2689 fn parse_c_for_tail(&mut self, kw: Token) -> Option<StmtId> {
2691 let init = if matches!(self.peek().kind, TokenKind::Semicolon) {
2692 self.advance();
2693 None
2694 } else {
2695 let init_id = match self.peek().kind {
2696 TokenKind::Let => self.parse_let_or_const(false)?,
2697 TokenKind::Const => self.parse_let_or_const(true)?,
2698 _ => self.parse_expression_statement()?,
2699 };
2700 Some(init_id)
2701 };
2702 let for_stmt = self.finish_c_for(kw, init);
2703 if for_stmt.is_none()
2704 && let Some(init) = init
2705 {
2706 self.discard_detached_pattern(init);
2707 }
2708 for_stmt
2709 }
2710
2711 fn finish_c_for(&mut self, kw: Token, init: Option<StmtId>) -> Option<StmtId> {
2712 let condition = if matches!(self.peek().kind, TokenKind::Semicolon) {
2713 None
2714 } else {
2715 Some(self.parse_expression()?)
2716 };
2717 if !matches!(self.peek().kind, TokenKind::Semicolon) {
2718 self.error_at_peek("expected `;` after `for` condition");
2719 return None;
2720 }
2721 self.advance();
2722
2723 let update = if matches!(self.peek().kind, TokenKind::RightParen) {
2724 None
2725 } else {
2726 Some(self.parse_for_update_stmt()?)
2727 };
2728 if !matches!(self.peek().kind, TokenKind::RightParen) {
2729 self.error_at_peek("expected `)` after `for` update");
2730 return None;
2731 }
2732 self.advance();
2733
2734 let body = self.parse_loop_body()?;
2735 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2736 .span
2737 .end;
2738 parse_arena_result(
2739 self.ast.try_push_stmt(Stmt {
2740 kind: StmtKind::For {
2741 init,
2742 condition,
2743 update,
2744 body,
2745 },
2746 span: self.span(kw.span.start, body_end),
2747 }),
2748 &mut self.fatal,
2749 )
2750 }
2751
2752 fn discard_detached_pattern(&mut self, init: StmtId) {
2755 let Some(stmt) = parse_arena_result(self.ast.try_stmt_mut(init), &mut self.fatal) else {
2756 return;
2757 };
2758 if matches!(
2759 stmt.kind,
2760 StmtKind::LetPattern { .. } | StmtKind::ConstPattern { .. }
2761 ) {
2762 stmt.kind = StmtKind::Block(Vec::new());
2763 }
2764 }
2765
2766 fn parse_for_update_stmt(&mut self) -> Option<StmtId> {
2767 let expr_id = self.parse_expression()?;
2768 let span = parse_arena_result(self.ast.try_expr(expr_id), &mut self.fatal)?.span;
2769 let kind = self.statement_kind_for(expr_id)?;
2770 parse_arena_result(self.ast.try_push_stmt(Stmt { kind, span }), &mut self.fatal)
2771 }
2772
2773 fn parse_for_of_tail(&mut self, kw: Token) -> Option<StmtId> {
2774 let binding_tok = self.advance();
2775 let binding_kind = match binding_tok.kind {
2776 TokenKind::Let => crate::BindingKind::Let,
2777 TokenKind::Const => crate::BindingKind::Const,
2778 _ => return self.invariant_failure("scan_for_of_shape gated this"),
2779 };
2780 let (name, binding) = match self.peek().kind {
2781 TokenKind::LeftBracket => {
2782 let b = self.parse_binding()?;
2783 (None, Some(b))
2784 }
2785 TokenKind::LeftBrace => {
2786 let b = self.parse_binding()?;
2787 self.error_at(
2788 b.span(),
2789 "object destructuring is not supported in `for-of`; use array destructuring or unpack inside the loop body",
2790 );
2791 return None;
2792 }
2793 _ => {
2794 let name_tok = self.expect_binding_identifier(
2795 "expected identifier after `let`/`const` in `for-of`",
2796 )?;
2797 (Some(self.ident_from_token(&name_tok)), None)
2798 }
2799 };
2800 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
2801 self.advance();
2802 Some(self.parse_type_annotation()?)
2803 } else {
2804 None
2805 };
2806 if !self.peek_identifier_text_is("of") {
2807 self.error_at_peek("expected `of` in `for-of`");
2808 return None;
2809 }
2810 self.advance();
2811
2812 let iter = self.parse_expression()?;
2813 if !matches!(self.peek().kind, TokenKind::RightParen) {
2814 self.error_at_peek("expected `)` after `for-of` iterable");
2815 return None;
2816 }
2817 self.advance();
2818
2819 let body = self.parse_loop_body()?;
2820 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
2821 .span
2822 .end;
2823 let kind = match (binding, name) {
2824 (Some(binding), _) => StmtKind::ForOfPattern {
2825 binding_kind,
2826 binding,
2827 ty,
2828 iter,
2829 body,
2830 },
2831 (None, Some(name)) => StmtKind::ForOf {
2832 binding_kind,
2833 name,
2834 ty,
2835 iter,
2836 body,
2837 },
2838 (None, None) => return self.invariant_failure("either binding or name must be Some"),
2839 };
2840 parse_arena_result(
2841 self.ast.try_push_stmt(Stmt {
2842 kind,
2843 span: self.span(kw.span.start, body_end),
2844 }),
2845 &mut self.fatal,
2846 )
2847 }
2848
2849 fn parse_do_while(&mut self) -> Option<StmtId> {
2850 let kw = self.advance();
2851 let body = self.parse_loop_body()?;
2852 self.eat_asi_semicolon_before(TokenKind::While);
2853 if !matches!(self.peek().kind, TokenKind::While) {
2854 self.error_at_peek("expected `while` after `do` body");
2855 return None;
2856 }
2857 self.advance();
2858 let condition = self.parse_paren_condition()?;
2859 let end = if matches!(self.peek().kind, TokenKind::Semicolon) {
2863 self.advance().span.end
2864 } else {
2865 parse_arena_result(self.ast.try_expr(condition), &mut self.fatal)?
2866 .span
2867 .end
2868 };
2869 parse_arena_result(
2870 self.ast.try_push_stmt(Stmt {
2871 kind: StmtKind::DoWhile { body, condition },
2872 span: self.span(kw.span.start, end),
2873 }),
2874 &mut self.fatal,
2875 )
2876 }
2877
2878 fn parse_switch(&mut self) -> Option<StmtId> {
2880 let kw = self.advance();
2881 if !matches!(self.peek().kind, TokenKind::LeftParen) {
2882 self.error_at_peek("expected `(` after `switch`");
2883 return None;
2884 }
2885 self.advance();
2886 let discriminant = self.parse_expression()?;
2887 if !matches!(self.peek().kind, TokenKind::RightParen) {
2888 self.error_at_peek("expected `)` after `switch` discriminant");
2889 return None;
2890 }
2891 self.advance();
2892
2893 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
2894 self.error_at_peek("expected `{` to start `switch` body");
2895 return None;
2896 }
2897 let open = self.advance();
2898 self.block_depth += 1;
2899
2900 let mut cases: Vec<SwitchCase> = Vec::new();
2901 let mut default: Option<SwitchDefault> = None;
2902 let mut pending_values: Vec<ExprId> = Vec::new();
2903 let mut pending_label_start: Option<u32> = None;
2904 let mut pending_default: Option<Span> = None;
2905
2906 loop {
2907 match self.peek().kind {
2908 TokenKind::Eof => break,
2909 TokenKind::Semicolon => {
2911 self.advance();
2912 }
2913 TokenKind::RightBrace => {
2914 if pending_label_start.is_some() {
2916 self.error_at_peek(
2917 "expected case body before `}` — case labels must be followed by statements",
2918 );
2919 return None;
2920 }
2921 break;
2922 }
2923 TokenKind::Case => {
2924 let case_kw = self.advance();
2925 if pending_label_start.is_none() {
2926 pending_label_start = Some(case_kw.span.start);
2927 }
2928 let value = self.parse_expression()?;
2929 if !matches!(self.peek().kind, TokenKind::Colon) {
2930 self.error_at_peek("expected `:` after `case` label");
2931 return None;
2932 }
2933 self.advance();
2934 pending_values.push(value);
2935 }
2936 TokenKind::Default => {
2937 let kw_tok = self.advance();
2938 if default.is_some() {
2939 self.error_at(kw_tok.span, "duplicate `default` clause in `switch`");
2940 return None;
2941 }
2942 if !matches!(self.peek().kind, TokenKind::Colon) {
2943 self.error_at_peek("expected `:` after `default`");
2944 return None;
2945 }
2946 self.advance();
2947 if pending_label_start.is_none() {
2948 pending_label_start = Some(kw_tok.span.start);
2949 }
2950 pending_default = Some(kw_tok.span);
2951 }
2952 _ => {
2953 if pending_label_start.is_none() {
2954 self.error_at_peek(
2955 "expected `case` or `default` at the start of a `switch` body",
2956 );
2957 return None;
2958 }
2959 let Some(label_start) = pending_label_start else {
2960 return self.invariant_failure("switch arm has no label");
2961 };
2962 let body = self.parse_switch_arm_body(label_start)?;
2963 let arm_span =
2964 parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
2965 if !pending_values.is_empty() {
2966 let first = parse_arena_result(
2967 self.ast.try_expr(pending_values[0]),
2968 &mut self.fatal,
2969 )?
2970 .span
2971 .start;
2972 let case_span = self.span(first, arm_span.end);
2973 cases.push(SwitchCase {
2974 values: std::mem::take(&mut pending_values),
2975 body,
2976 span: case_span,
2977 });
2978 }
2979 if let Some(def_kw_span) = pending_default.take() {
2980 default = Some(SwitchDefault {
2981 body,
2982 span: self.span(def_kw_span.start, arm_span.end),
2983 });
2984 }
2985 pending_label_start = None;
2986 }
2987 }
2988 }
2989
2990 self.block_depth -= 1;
2991 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2992 self.error_at_peek("expected `}` to close `switch` body");
2993 return None;
2994 }
2995 let close = self.advance();
2996
2997 let _ = open;
2998 parse_arena_result(
2999 self.ast.try_push_stmt(Stmt {
3000 kind: StmtKind::Switch {
3001 discriminant,
3002 cases,
3003 default,
3004 },
3005 span: self.span(kw.span.start, close.span.end),
3006 }),
3007 &mut self.fatal,
3008 )
3009 }
3010
3011 fn parse_switch_arm_body(&mut self, body_start: u32) -> Option<StmtId> {
3013 let mut stmts: Vec<StmtId> = Vec::new();
3014 let mut end_offset = body_start;
3015 loop {
3016 if matches!(
3017 self.peek().kind,
3018 TokenKind::Case | TokenKind::Default | TokenKind::RightBrace | TokenKind::Eof
3019 ) {
3020 break;
3021 }
3022 if matches!(self.peek().kind, TokenKind::Semicolon) {
3023 end_offset = self.peek().span.end;
3024 self.advance();
3025 continue;
3026 }
3027 let pos_before = self.pos;
3028 if let Some(id) = self.parse_statement() {
3029 end_offset = parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
3030 .span
3031 .end;
3032 stmts.push(id);
3033 } else {
3034 self.recover();
3035 if self.pos == pos_before && !self.is_at_eof() {
3036 self.advance();
3037 }
3038 end_offset = end_offset.max(self.prev_token_end());
3041 }
3042 }
3043 let span = self.span(body_start, end_offset);
3044 parse_arena_result(
3045 self.ast.try_push_stmt(Stmt {
3046 kind: StmtKind::Block(stmts),
3047 span,
3048 }),
3049 &mut self.fatal,
3050 )
3051 }
3052
3053 fn parse_break(&mut self) -> Option<StmtId> {
3054 let kw = self.advance();
3055 if !matches!(self.peek().kind, TokenKind::Semicolon) {
3056 self.error_at_peek("expected `;` after `break`");
3057 return None;
3058 }
3059 let semi = self.advance();
3060 parse_arena_result(
3061 self.ast.try_push_stmt(Stmt {
3062 kind: StmtKind::Break,
3063 span: self.span(kw.span.start, semi.span.end),
3064 }),
3065 &mut self.fatal,
3066 )
3067 }
3068
3069 fn parse_continue(&mut self) -> Option<StmtId> {
3070 let kw = self.advance();
3071 if !matches!(self.peek().kind, TokenKind::Semicolon) {
3072 self.error_at_peek("expected `;` after `continue`");
3073 return None;
3074 }
3075 let semi = self.advance();
3076 parse_arena_result(
3077 self.ast.try_push_stmt(Stmt {
3078 kind: StmtKind::Continue,
3079 span: self.span(kw.span.start, semi.span.end),
3080 }),
3081 &mut self.fatal,
3082 )
3083 }
3084
3085 fn parse_return(&mut self) -> Option<StmtId> {
3086 let kw = self.advance();
3087
3088 let value = if matches!(self.peek().kind, TokenKind::Semicolon) {
3089 None
3090 } else {
3091 Some(self.parse_expression()?)
3092 };
3093
3094 if !self.at_statement_end() {
3095 self.error_at_peek("expected `;` after return");
3096 return None;
3097 }
3098 let end = self.finish_statement();
3099
3100 parse_arena_result(
3101 self.ast.try_push_stmt(Stmt {
3102 kind: StmtKind::Return(value),
3103 span: self.span(kw.span.start, end),
3104 }),
3105 &mut self.fatal,
3106 )
3107 }
3108
3109 fn parse_throw(&mut self) -> Option<StmtId> {
3110 let kw = self.advance();
3111
3112 if matches!(self.peek().kind, TokenKind::Semicolon) {
3113 self.error_at_peek_with_help(
3114 "expected expression after `throw`",
3115 vec!["throw new Error(\"message\");".to_string()],
3116 );
3117 return None;
3118 }
3119
3120 let value = self.parse_expression()?;
3121
3122 if !self.at_statement_end() {
3123 self.error_at_peek("expected `;` after `throw`");
3124 return None;
3125 }
3126 let end = self.finish_statement();
3127
3128 parse_arena_result(
3129 self.ast.try_push_stmt(Stmt {
3130 kind: StmtKind::Throw { value },
3131 span: self.span(kw.span.start, end),
3132 }),
3133 &mut self.fatal,
3134 )
3135 }
3136
3137 fn parse_try(&mut self) -> Option<StmtId> {
3138 let kw = self.advance();
3139
3140 let body = self.parse_block()?;
3141
3142 let mut catches: Vec<CatchClause> = Vec::new();
3143 let mut finally: Option<StmtId> = None;
3144 let mut tail_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3145 .span
3146 .end;
3147
3148 loop {
3149 match self.peek().kind {
3150 TokenKind::Catch => {
3151 if finally.is_some() {
3152 self.error_at_peek("`catch` clause must appear before `finally`");
3153 return None;
3154 }
3155 let clause = self.parse_catch_clause()?;
3156 tail_end = clause.span.end;
3157 catches.push(clause);
3158 }
3159 TokenKind::Finally => {
3160 if finally.is_some() {
3161 self.error_at_peek("duplicate `finally` clause");
3162 return None;
3163 }
3164 self.advance();
3165 let block = self.parse_block()?;
3166 tail_end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
3167 .span
3168 .end;
3169 finally = Some(block);
3170 }
3171 _ => break,
3172 }
3173 }
3174
3175 if catches.is_empty() && finally.is_none() {
3176 self.error_at(
3177 self.span(kw.span.start, tail_end),
3178 "try requires at least one of `catch` or `finally`",
3179 );
3180 return None;
3181 }
3182
3183 parse_arena_result(
3184 self.ast.try_push_stmt(Stmt {
3185 kind: StmtKind::Try {
3186 body,
3187 catches,
3188 finally,
3189 },
3190 span: self.span(kw.span.start, tail_end),
3191 }),
3192 &mut self.fatal,
3193 )
3194 }
3195
3196 fn parse_catch_clause(&mut self) -> Option<CatchClause> {
3197 let kw = self.advance();
3198
3199 let (binding, ty) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
3200 (
3202 Ident {
3203 name: "#catch".into(),
3204 span: kw.span,
3205 },
3206 None,
3207 )
3208 } else {
3209 self.parse_catch_binding()?
3210 };
3211
3212 let body = self.parse_block()?;
3213 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3214 .span
3215 .end;
3216
3217 Some(CatchClause {
3218 binding,
3219 ty,
3220 body,
3221 span: self.span(kw.span.start, body_end),
3222 })
3223 }
3224
3225 fn parse_catch_binding(&mut self) -> Option<(Ident, Option<TypeAnnotation>)> {
3226 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3227 self.error_at_peek("expected `(` after `catch`");
3228 return None;
3229 }
3230 self.advance();
3231 let name_tok = self.expect_binding_identifier("expected catch binding name")?;
3232 let binding = self.ident_from_token(&name_tok);
3233 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3234 self.advance();
3235 if self.reject_any_catch_annotation() {
3236 None
3238 } else {
3239 Some(self.parse_type_annotation()?)
3240 }
3241 } else {
3242 None
3243 };
3244 if !matches!(self.peek().kind, TokenKind::RightParen) {
3245 self.error_at_peek("expected `)` after catch binding");
3246 return None;
3247 }
3248 self.advance();
3249 Some((binding, ty))
3250 }
3251
3252 fn reject_any_catch_annotation(&mut self) -> bool {
3255 let is_bare_any = self.peek_identifier_text_is("any")
3256 && matches!(self.peek_at(1).kind, TokenKind::RightParen);
3257 if !is_bare_any {
3258 return false;
3259 }
3260 let any_tok = self.advance();
3261 self.error_at_with_help(
3263 any_tok.span,
3264 "`any` is not supported",
3265 vec![
3266 "write `catch (e)` to catch every thrown error, or name an error class, \
3267 as in `catch (e: RangeError)`, to catch only that type"
3268 .to_string(),
3269 ],
3270 );
3271 true
3272 }
3273
3274 fn parse_paren_condition(&mut self) -> Option<ExprId> {
3275 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3276 self.error_at_peek("expected `(`");
3277 return None;
3278 }
3279 self.advance();
3280 let expr = self.parse_expression()?;
3281 if !matches!(self.peek().kind, TokenKind::RightParen) {
3282 self.error_at_peek("expected `)`");
3283 return None;
3284 }
3285 self.advance();
3286 Some(expr)
3287 }
3288
3289 fn parse_predicate_or_return_type(
3290 &mut self,
3291 type_pos: TypePos,
3292 ) -> Option<(
3293 Option<TypeAnnotation>,
3294 Option<crate::TypePredicateAnnotation>,
3295 )> {
3296 if matches!(self.peek().kind, TokenKind::Identifier) && self.peek_at_is_word(1, "is") {
3297 let param_tok = self.advance();
3298 let param = self.ident_from_token(¶m_tok);
3299 self.advance();
3300 let asserted = self.parse_type_annotation_in(type_pos)?;
3301 let span = self.span(param.span.start, asserted.span.end);
3302 return Some((
3303 None,
3304 Some(crate::TypePredicateAnnotation {
3305 param,
3306 asserted,
3307 span,
3308 }),
3309 ));
3310 }
3311 Some((Some(self.parse_type_annotation_in(type_pos)?), None))
3312 }
3313
3314 fn parse_type_annotation(&mut self) -> Option<TypeAnnotation> {
3315 self.parse_type_annotation_in(TypePos::Anywhere)
3316 }
3317
3318 fn parse_type_annotation_in(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3319 let leading_pipe = matches!(self.peek().kind, TokenKind::Pipe);
3321 if leading_pipe {
3322 self.advance();
3323 }
3324 let first = self.parse_type_array(type_pos)?;
3325 if !matches!(self.peek().kind, TokenKind::Pipe) {
3326 return Some(first);
3327 }
3328 let start = first.span.start;
3329 let mut end = first.span.end;
3330 let mut members = vec![first];
3331 while matches!(self.peek().kind, TokenKind::Pipe) {
3332 self.advance();
3333 let member = self.parse_type_array(type_pos)?;
3334 end = member.span.end;
3335 members.push(member);
3336 }
3337 Some(TypeAnnotation {
3338 kind: TypeAnnotationKind::Union(members),
3339 span: self.span(start, end),
3340 })
3341 }
3342
3343 fn parse_type_array(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3344 self.with_recursion_limit(|parser| parser.parse_type_array_inner(type_pos))
3345 }
3346
3347 fn parse_type_array_inner(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3348 if self.is_contextual_type_operator(self.pos, "keyof") {
3352 let kw = self.advance();
3353 let operand = self.parse_type_array(type_pos)?;
3354 let span = self.span(kw.span.start, operand.span.end);
3355 return Some(TypeAnnotation {
3356 kind: TypeAnnotationKind::KeyOf(Box::new(operand)),
3357 span,
3358 });
3359 }
3360 if self.is_contextual_type_operator(self.pos, "readonly") {
3361 return self.parse_readonly_type(type_pos);
3362 }
3363
3364 let mut ty = match self.peek().kind {
3365 TokenKind::Typeof => {
3371 let kw = self.advance();
3372 let first = self.expect_identifier("expected a value name after `typeof`")?;
3373 let mut path = vec![self.type_name(first.span)?];
3374 let mut end = first.span.end;
3375 while matches!(self.peek().kind, TokenKind::Dot) {
3376 self.advance();
3377 let seg = self.expect_identifier_name("expected a property name after `.`")?;
3378 end = seg.span.end;
3379 path.push(self.type_name(seg.span)?);
3380 }
3381 TypeAnnotation {
3382 kind: TypeAnnotationKind::TypeOf { path },
3383 span: self.span(kw.span.start, end),
3384 }
3385 }
3386 TokenKind::Identifier | TokenKind::Void => {
3387 let leading_is_identifier = matches!(self.peek().kind, TokenKind::Identifier);
3389 let tok = self.advance();
3390 let first_span = tok.span;
3391 let mut path_end = first_span.end;
3392 let first_name = self.type_name(first_span)?;
3393 let mut path = vec![first_name.clone()];
3394 if leading_is_identifier {
3395 while matches!(self.peek().kind, TokenKind::Dot) {
3396 self.advance();
3397 if !matches!(self.peek().kind, TokenKind::Identifier) {
3398 self.error_at_peek("expected identifier after `.` in type name");
3399 return None;
3400 }
3401 let seg = self.advance();
3402 path_end = seg.span.end;
3403 path.push(self.type_name(seg.span)?);
3404 }
3405 }
3406 let (args, end) = if matches!(self.peek().kind, TokenKind::LessThan)
3408 && !self.line_break_before_peek()
3409 {
3410 self.parse_type_argument_list()?
3411 } else {
3412 (Vec::new(), path_end)
3413 };
3414 let outer_span = self.span(first_span.start, end);
3415 let kind = if path.len() == 1 {
3416 if first_name.name == "any" {
3417 self.error_at_with_help(
3418 first_span,
3419 "`any` is not supported",
3420 vec![
3421 "annotate a concrete type instead — an object shape \
3422 like `{ id: string }`, a named `interface`, or \
3423 `unknown` (a safe dynamic type you narrow before use)"
3424 .to_string(),
3425 ],
3426 );
3427 }
3428 TypeAnnotationKind::Name {
3429 name: first_name,
3430 args,
3431 }
3432 } else {
3433 TypeAnnotationKind::Qualified { path, args }
3434 };
3435 TypeAnnotation {
3436 kind,
3437 span: outer_span,
3438 }
3439 }
3440 TokenKind::NullLiteral => {
3442 let tok = self.advance();
3443 TypeAnnotation {
3444 kind: TypeAnnotationKind::Name {
3445 name: self.type_name(tok.span)?,
3446 args: Vec::new(),
3447 },
3448 span: tok.span,
3449 }
3450 }
3451 TokenKind::StringLiteral(_) => {
3452 let tok = self.advance();
3453 let span = tok.span;
3454 let TokenKind::StringLiteral(s) = tok.kind else {
3455 unreachable!("advance preserves the matched StringLiteral variant");
3457 };
3458 TypeAnnotation {
3459 kind: TypeAnnotationKind::StringLiteral(s),
3460 span,
3461 }
3462 }
3463 TokenKind::NumberLiteral(_) => {
3464 let tok = self.advance();
3465 let span = tok.span;
3466 let TokenKind::NumberLiteral(v) = tok.kind else {
3467 unreachable!("advance preserves the matched NumberLiteral variant");
3469 };
3470 let canonical = if v == 0.0 { 0.0 } else { v };
3472 TypeAnnotation {
3473 kind: TypeAnnotationKind::NumberLiteral(crate::types::LiteralF64(canonical)),
3474 span,
3475 }
3476 }
3477 TokenKind::BigIntLiteral(_) => {
3478 let tok = self.advance();
3479 let TokenKind::BigIntLiteral(digits) = tok.kind else {
3480 unreachable!("advance preserves the matched BigIntLiteral variant");
3482 };
3483 TypeAnnotation {
3484 kind: TypeAnnotationKind::BigIntLiteral(digits),
3485 span: tok.span,
3486 }
3487 }
3488 TokenKind::Minus
3489 if matches!(
3490 self.peek_at(1).kind,
3491 TokenKind::NumberLiteral(_) | TokenKind::BigIntLiteral(_)
3492 ) =>
3493 {
3494 let minus = self.advance().span;
3495 let tok = self.advance();
3496 let span = self.span(minus.start, tok.span.end);
3497 let kind =
3498 match tok.kind {
3499 TokenKind::NumberLiteral(v) => TypeAnnotationKind::NumberLiteral(
3500 crate::types::LiteralF64(if v == 0.0 { 0.0 } else { -v }),
3501 ),
3502 TokenKind::BigIntLiteral(digits) => TypeAnnotationKind::BigIntLiteral(
3503 crate::types::negate_bigint_digits(&digits),
3504 ),
3505 _ => unreachable!("guarded to a numeric literal after `-`"),
3507 };
3508 TypeAnnotation { kind, span }
3509 }
3510 TokenKind::BooleanLiteral(value) => {
3511 let span = self.advance().span;
3512 TypeAnnotation {
3513 kind: TypeAnnotationKind::BooleanLiteral(value),
3514 span,
3515 }
3516 }
3517 TokenKind::LeftBrace => self.parse_object_type_annotation()?,
3518 TokenKind::LeftParen if self.paren_at_opens_function_type(self.pos, type_pos) => {
3519 self.parse_function_type_annotation()?
3520 }
3521 TokenKind::LeftParen => self.parse_grouped_or_function_type()?,
3522 TokenKind::LeftBracket => self.parse_tuple_type_annotation()?,
3524 _ => {
3525 self.error_at_peek("expected type");
3526 return None;
3527 }
3528 };
3529 let mut array_suffixes = 0usize;
3532 while matches!(self.peek().kind, TokenKind::LeftBracket) && !self.line_break_before_peek() {
3535 array_suffixes += 1;
3536 if self.recursion_depth.saturating_add(array_suffixes) >= MAX_PARSE_DEPTH {
3537 self.stop_at_recursion_limit();
3538 return None;
3539 }
3540 let open = self.advance();
3541 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3542 self.error_at(open.span, "expected `]` to close array type");
3543 return None;
3544 }
3545 let close = self.advance();
3546 let span = self.span(ty.span.start, close.span.end);
3547 ty = TypeAnnotation {
3548 kind: TypeAnnotationKind::Array(Box::new(ty)),
3549 span,
3550 };
3551 }
3552 Some(ty)
3553 }
3554
3555 fn parse_readonly_type(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3559 let kw = self.advance();
3560 let opens_group = matches!(self.peek().kind, TokenKind::LeftParen);
3561 let operand = self.parse_type_array(type_pos)?;
3562 let grouped = opens_group
3565 && matches!(
3566 self.pos
3567 .checked_sub(1)
3568 .and_then(|index| self.tokens.get(index))
3569 .map(|token| &token.kind),
3570 Some(TokenKind::RightParen)
3571 );
3572 if grouped
3573 || !matches!(
3574 operand.kind,
3575 TypeAnnotationKind::Array(_) | TypeAnnotationKind::Tuple(_)
3576 )
3577 {
3578 self.error_at_with_help(
3579 kw.span,
3580 "`readonly` only applies to array and tuple types",
3581 vec![
3582 "write `readonly T[]` or `readonly [A, B]`; to protect an object's \
3583 properties, mark each one `readonly` instead"
3584 .to_string(),
3585 ],
3586 );
3587 return Some(operand);
3588 }
3589 let span = self.span(kw.span.start, operand.span.end);
3590 Some(TypeAnnotation {
3591 kind: TypeAnnotationKind::Readonly(Box::new(operand)),
3592 span,
3593 })
3594 }
3595
3596 fn paren_at_opens_function_type(&self, index: usize, type_pos: TypePos) -> bool {
3607 let after = |offset: usize| self.tokens.get(index + offset).map(|t| &t.kind);
3608 match (after(1), after(2)) {
3609 (Some(TokenKind::RightParen | TokenKind::DotDotDot), _) => true,
3611 (Some(TokenKind::Identifier), Some(TokenKind::Colon | TokenKind::Question)) => true,
3613 (Some(TokenKind::Identifier), Some(TokenKind::Comma | TokenKind::RightParen)) => {
3622 type_pos == TypePos::Anywhere
3623 && self.index_after_matching_paren(index).is_some_and(|i| {
3624 matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow))
3625 })
3626 }
3627 _ => false,
3629 }
3630 }
3631
3632 fn parse_grouped_or_function_type(&mut self) -> Option<TypeAnnotation> {
3637 let open = self.pos;
3638 let diagnostics_before = self.diagnostics.len();
3639 if let Some(ty) = self.parse_grouped_type_annotation() {
3640 return Some(ty);
3641 }
3642 let arrow_follows = self
3643 .index_after_matching_paren(open)
3644 .is_some_and(|i| matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)));
3645 if !arrow_follows {
3646 return None;
3647 }
3648 self.pos = open;
3649 self.diagnostics.truncate(diagnostics_before);
3650 self.parse_function_type_annotation()
3651 }
3652
3653 fn parse_grouped_type_annotation(&mut self) -> Option<TypeAnnotation> {
3656 let open = self.advance();
3657 let inner = self.parse_type_annotation()?;
3658 if !matches!(self.peek().kind, TokenKind::RightParen) {
3659 self.error_at_peek("expected `)` to close a parenthesized type");
3660 return None;
3661 }
3662 let close = self.advance();
3663 Some(TypeAnnotation {
3664 span: self.span(open.span.start, close.span.end),
3665 ..inner
3666 })
3667 }
3668
3669 fn parse_function_type_annotation(&mut self) -> Option<TypeAnnotation> {
3671 let open = self.advance();
3672 let params = self.parse_function_type_params()?;
3673 if !matches!(self.peek().kind, TokenKind::Arrow) {
3674 self.error_at_peek("expected `=>` in function-type annotation");
3675 return None;
3676 }
3677 self.advance();
3678 let return_type = self.parse_type_annotation()?;
3679 let end = return_type.span.end;
3680 Some(TypeAnnotation {
3681 kind: TypeAnnotationKind::Function {
3682 params,
3683 return_type: Box::new(return_type),
3684 },
3685 span: self.span(open.span.start, end),
3686 })
3687 }
3688
3689 fn parse_function_type_params(&mut self) -> Option<Vec<TypeAnnotationField>> {
3693 let mut params: Vec<TypeAnnotationField> = Vec::new();
3694 if !matches!(self.peek().kind, TokenKind::RightParen) {
3695 loop {
3696 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
3697 self.advance();
3698 true
3699 } else {
3700 false
3701 };
3702 let name_tok = self.expect_binding_identifier(if rest {
3703 "expected identifier after `...` in function-type annotation"
3704 } else {
3705 "expected parameter name in function-type annotation"
3706 })?;
3707 let name = self.ident_from_token(&name_tok);
3708 let optional = self.parse_parameter_optional(rest, false)?;
3709 if !matches!(self.peek().kind, TokenKind::Colon) {
3710 if rest {
3711 self.error_at_peek_with_help(
3712 "rest parameter requires a type annotation",
3713 vec!["(...args: T[]) => R".to_string()],
3714 );
3715 } else {
3716 self.error_at_peek(
3717 "expected `:` after parameter name (function-type params \
3718 require named annotations in v1)",
3719 );
3720 }
3721 return None;
3722 }
3723 self.advance();
3724 let ty = self.parse_type_annotation()?;
3725 if rest && !is_rest_array_annotation(&ty) {
3726 self.error_at(ty.span, "rest parameter type must be an array");
3727 return None;
3728 }
3729 if matches!(self.peek().kind, TokenKind::Equals) {
3730 if rest {
3731 self.error_rest_parameter_default();
3732 } else {
3733 self.error_at_peek(
3734 "default values in function-type annotations are not yet supported",
3735 );
3736 }
3737 return None;
3738 }
3739 params.push(TypeAnnotationField {
3740 name,
3741 ty,
3742 optional,
3743 readonly: false,
3744 rest,
3745 method: false,
3746 });
3747 match self.peek().kind {
3748 TokenKind::Comma => {
3749 if rest {
3750 let comma = self.advance();
3751 self.error_at(comma.span, "rest parameter must be the last parameter");
3752 return None;
3753 }
3754 self.advance();
3755 if matches!(self.peek().kind, TokenKind::RightParen) {
3756 break;
3757 }
3758 }
3759 TokenKind::RightParen => break,
3760 _ => {
3761 self.error_at_peek("expected `,` or `)`");
3762 return None;
3763 }
3764 }
3765 }
3766 self.check_parameter_order(params.iter().map(ParamOrder::of_field));
3767 }
3768 if !matches!(self.peek().kind, TokenKind::RightParen) {
3769 self.error_at_peek("expected `)`");
3770 return None;
3771 }
3772 self.advance();
3773 Some(params)
3774 }
3775
3776 fn parse_tuple_type_annotation(&mut self) -> Option<TypeAnnotation> {
3777 let open = self.advance();
3778 if matches!(self.peek().kind, TokenKind::RightBracket) {
3779 let close = self.peek().span;
3780 self.error_at(
3781 self.span(open.span.start, close.end),
3782 "tuple types must have at least one element",
3783 );
3784 return None;
3785 }
3786 let mut elements: Vec<TypeAnnotation> = Vec::new();
3787 loop {
3788 if matches!(self.peek().kind, TokenKind::DotDotDot) {
3789 self.error_at_with_help(
3790 self.peek().span,
3791 "rest elements in tuple types are not supported",
3792 vec!["use an array type `T[]` for a list of varying length".to_string()],
3793 );
3794 return None;
3795 }
3796 let label = self.skip_tuple_element_label();
3799 let mut ty = self.parse_type_annotation()?;
3800 let suffix_optional = matches!(self.peek().kind, TokenKind::Question);
3801 if suffix_optional && label != TupleElementLabel::None {
3802 self.error_at_peek_with_help(
3803 "put the optional marker after the tuple element label",
3804 vec!["write `name?: T` instead of `name: T?`".to_string()],
3805 );
3806 return None;
3807 }
3808 let optional = label == TupleElementLabel::Optional || suffix_optional;
3809 if optional {
3810 let end = if suffix_optional {
3811 self.advance().span.end
3812 } else {
3813 ty.span.end
3814 };
3815 let span = self.span(ty.span.start, end);
3816 ty = TypeAnnotation {
3817 kind: TypeAnnotationKind::Optional(Box::new(ty)),
3818 span,
3819 };
3820 } else if elements
3821 .iter()
3822 .any(|element| matches!(element.kind, TypeAnnotationKind::Optional(_)))
3823 {
3824 self.error_at(
3825 ty.span,
3826 "a required tuple element cannot follow an optional element",
3827 );
3828 return None;
3829 }
3830 elements.push(ty);
3831 match self.peek().kind {
3832 TokenKind::Comma => {
3833 self.advance();
3834 if matches!(self.peek().kind, TokenKind::RightBracket) {
3835 break;
3836 }
3837 }
3838 TokenKind::RightBracket => break,
3839 _ => {
3840 self.error_at_peek("expected `,` or `]`");
3841 return None;
3842 }
3843 }
3844 }
3845 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3846 self.error_at_peek("expected `]`");
3847 return None;
3848 }
3849 let close = self.advance();
3850 Some(TypeAnnotation {
3851 kind: TypeAnnotationKind::Tuple(elements),
3852 span: self.span(open.span.start, close.span.end),
3853 })
3854 }
3855
3856 fn skip_tuple_element_label(&mut self) -> TupleElementLabel {
3858 if !is_property_name(&self.peek().kind) {
3859 return TupleElementLabel::None;
3860 }
3861 match self.peek_at(1).kind {
3862 TokenKind::Colon => {
3863 self.advance();
3864 self.advance();
3865 TupleElementLabel::Required
3866 }
3867 TokenKind::Question if matches!(self.peek_at(2).kind, TokenKind::Colon) => {
3868 self.advance();
3869 self.advance();
3870 self.advance();
3871 TupleElementLabel::Optional
3872 }
3873 _ => TupleElementLabel::None,
3874 }
3875 }
3876
3877 fn peek_is_parameterless_index_signature(&self) -> bool {
3880 matches!(self.peek().kind, TokenKind::LeftBracket)
3881 && matches!(self.peek_at(1).kind, TokenKind::RightBracket)
3882 }
3883
3884 fn skip_parameterless_index_signature(&mut self, member_start: u32) -> Option<()> {
3888 self.advance();
3889 let close = self.advance();
3890 self.error_at_with_help(
3891 self.span(member_start, close.span.end),
3892 "an index signature must declare exactly one parameter",
3893 vec!["write `[key: string]: V`".to_string()],
3894 );
3895 if matches!(self.peek().kind, TokenKind::Colon) {
3896 self.advance();
3897 self.parse_type_annotation()?;
3898 }
3899 Some(())
3900 }
3901
3902 fn parse_index_signature(&mut self, readonly: bool) -> Option<crate::IndexSignatureAnnotation> {
3903 let open = self.advance();
3904 let parameter = self.expect_property_ident("expected index parameter name")?;
3905 self.reject_eval_or_arguments(parameter.span);
3906 if !matches!(self.peek().kind, TokenKind::Colon) {
3907 self.error_at_peek("expected `:` after index parameter name");
3908 return None;
3909 }
3910 self.advance();
3911 if !self.peek_identifier_text_is("string") {
3912 self.error_at_peek("index signatures require string keys; use `[key: string]: V`");
3913 return None;
3914 }
3915 self.advance();
3916 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3917 self.error_at_peek("expected `]` after string index key type");
3918 return None;
3919 }
3920 self.advance();
3921 if !matches!(self.peek().kind, TokenKind::Colon) {
3922 self.error_at_peek("expected `:` before index value type");
3923 return None;
3924 }
3925 self.advance();
3926 let value = self.parse_type_annotation()?;
3927 Some(crate::IndexSignatureAnnotation {
3928 span: self.span(open.span.start, value.span.end),
3929 value,
3930 readonly,
3931 })
3932 }
3933
3934 fn parse_object_type_annotation(&mut self) -> Option<TypeAnnotation> {
3935 let open = self.advance();
3936 let mut fields: Vec<TypeAnnotationField> = Vec::new();
3937 let mut index = None;
3938 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
3939 && !self.peek_starts_top_level_item()
3940 {
3941 let member_start = self.peek().span.start;
3942 let readonly = self.eat_readonly_property_modifier();
3943 if self.peek_is_parameterless_index_signature() {
3944 self.skip_parameterless_index_signature(member_start)?;
3945 } else if self.peek_is_construct_signature() {
3946 self.reject_construct_signature()?;
3947 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
3948 let signature = self.parse_index_signature(readonly)?;
3949 if index.is_some() {
3950 self.error_at_with_help(
3951 signature.span,
3952 "duplicate string index signature",
3953 vec![],
3954 );
3955 }
3956 index = Some(Box::new(signature));
3957 } else {
3958 let field = self.parse_object_type_field(readonly, member_start)?;
3959 if let Some(existing) = fields.iter().find(|f| f.name.name == field.name.name) {
3960 self.diagnostics.push(Diagnostic {
3961 severity: Severity::Error,
3962 span: field.name.span,
3963 message: format!("duplicate field `{}` in object type", field.name.name),
3964 help: vec![],
3965 notes: vec![(existing.name.span, "first defined here".into())],
3966 });
3967 }
3968 fields.push(field);
3969 }
3970 self.finish_type_member(self.prev_token_end(), "expected `;`, `,`, or `}`")?;
3971 }
3972 if !matches!(self.peek().kind, TokenKind::RightBrace) {
3973 self.error_at_peek("expected `}`");
3974 return None;
3975 }
3976 let close = self.advance();
3977 Some(TypeAnnotation {
3978 kind: TypeAnnotationKind::Object { index, fields },
3979 span: self.span(open.span.start, close.span.end),
3980 })
3981 }
3982
3983 fn peek_starts_top_level_item(&self) -> bool {
3986 let import_statement = matches!(self.peek().kind, TokenKind::Import)
3987 && matches!(
3988 self.peek_at(1).kind,
3989 TokenKind::LeftBrace
3990 | TokenKind::Star
3991 | TokenKind::Identifier
3992 | TokenKind::StringLiteral(_)
3993 );
3994 import_statement || self.peek_starts_declaration()
3995 }
3996
3997 fn peek_starts_declaration(&self) -> bool {
4004 if matches!(self.peek().kind, TokenKind::Export)
4005 && matches!(
4006 self.peek_at(1).kind,
4007 TokenKind::Default | TokenKind::LeftBrace | TokenKind::Star
4008 )
4009 {
4010 return true;
4011 }
4012 let mut offset = 0;
4013 while self.peek_at_is_declaration_modifier(offset) {
4014 offset += 1;
4015 }
4016 let declaration_keyword = matches!(
4017 self.peek_at(offset).kind,
4018 TokenKind::Function
4019 | TokenKind::Class
4020 | TokenKind::Let
4021 | TokenKind::Const
4022 | TokenKind::Enum
4023 | TokenKind::Interface
4024 ) || self.peek_at_is_word(offset, "type");
4025 declaration_keyword && matches!(self.peek_at(offset + 1).kind, TokenKind::Identifier)
4026 }
4027
4028 fn peek_at_is_declaration_modifier(&self, offset: usize) -> bool {
4030 matches!(self.peek_at(offset).kind, TokenKind::Export)
4031 || ["declare", "async", "abstract"]
4032 .iter()
4033 .any(|word| self.peek_at_is_word(offset, word))
4034 }
4035
4036 fn peek_is_construct_signature(&self) -> bool {
4041 matches!(self.peek().kind, TokenKind::New)
4042 && matches!(
4043 self.peek_at(1).kind,
4044 TokenKind::LeftParen | TokenKind::LessThan
4045 )
4046 }
4047
4048 fn reject_construct_signature(&mut self) -> Option<()> {
4052 let keyword = self.advance();
4053 self.error_at_with_help(
4054 keyword.span,
4055 "construct signatures are not supported in object types",
4056 vec![
4057 "declare an `interface` with the `new (…)` signature, or pass a factory \
4058 function such as `() => T`"
4059 .to_string(),
4060 ],
4061 );
4062 if matches!(self.peek().kind, TokenKind::LessThan) {
4063 self.parse_generic_param_list()?;
4064 }
4065 if !matches!(self.peek().kind, TokenKind::LeftParen) {
4066 self.error_at_peek("expected `(` after construct signature type parameters");
4067 return None;
4068 }
4069 self.parse_object_type_method_signature(keyword.span.start)?;
4070 Some(())
4071 }
4072
4073 fn parse_object_type_field(
4075 &mut self,
4076 readonly: bool,
4077 member_start: u32,
4078 ) -> Option<TypeAnnotationField> {
4079 let name = self.expect_property_ident("expected field name in object type")?;
4080 let optional = matches!(self.peek().kind, TokenKind::Question);
4082 if optional {
4083 self.advance();
4084 }
4085 let method = matches!(self.peek().kind, TokenKind::LeftParen);
4086 let ty = if method {
4087 if readonly {
4088 self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
4089 }
4090 self.parse_object_type_method_signature(name.span.start)?
4091 } else {
4092 if !matches!(self.peek().kind, TokenKind::Colon) {
4093 self.error_at_peek("expected `:` after field name");
4094 return None;
4095 }
4096 self.advance();
4097 self.parse_type_annotation()?
4098 };
4099 Some(TypeAnnotationField {
4100 name,
4101 ty,
4102 optional,
4103 readonly,
4104 rest: false,
4105 method,
4106 })
4107 }
4108
4109 fn parse_object_type_method_signature(&mut self, start: u32) -> Option<TypeAnnotation> {
4114 self.advance();
4115 let params = self.parse_function_type_params()?;
4116 if !matches!(self.peek().kind, TokenKind::Colon) {
4117 self.error_at_peek("expected `:` and return type after method parameters");
4118 return None;
4119 }
4120 self.advance();
4121 let return_type = self.parse_type_annotation()?;
4122 let end = return_type.span.end;
4123 Some(TypeAnnotation {
4124 kind: TypeAnnotationKind::Function {
4125 params,
4126 return_type: Box::new(return_type),
4127 },
4128 span: self.span(start, end),
4129 })
4130 }
4131
4132 fn expect_identifier(&mut self, message: &str) -> Option<Token> {
4135 let tok = self.expect_identifier_name(message)?;
4136 self.reject_strict_mode_reserved_word(&tok);
4137 Some(tok)
4138 }
4139
4140 fn expect_binding_identifier(&mut self, message: &str) -> Option<Token> {
4144 let tok = self.expect_identifier(message)?;
4145 self.reject_eval_or_arguments(tok.span);
4146 Some(tok)
4147 }
4148
4149 fn expect_identifier_name(&mut self, message: &str) -> Option<Token> {
4152 if matches!(self.peek().kind, TokenKind::Identifier) {
4153 return Some(self.advance());
4154 }
4155 if is_reserved_identifier_word(&self.peek().kind) {
4156 let tok = self.advance();
4157 let keyword = &self.source[tok.span.start as usize..tok.span.end as usize];
4158 self.error_at_with_help(
4159 tok.span,
4160 format!("`{keyword}` is a reserved keyword and can't be used as a name"),
4161 vec![format!(
4162 "rename it, for example: `{}`",
4163 reserved_keyword_rename_example(keyword)
4164 )],
4165 );
4166 return None;
4167 }
4168 self.error_at_peek(message.to_string());
4169 None
4170 }
4171
4172 fn reject_strict_mode_reserved_word(&mut self, tok: &Token) {
4175 let word = &self.source[tok.span.start as usize..tok.span.end as usize];
4176 if !STRICT_MODE_RESERVED_WORDS.contains(&word) {
4177 return;
4178 }
4179 self.error_at_with_help(
4180 tok.span,
4181 format!("`{word}` is a reserved word in strict mode and can't be used as a name"),
4182 vec![format!(
4183 "rename it, for example: `{}`",
4184 reserved_keyword_rename_example(word)
4185 )],
4186 );
4187 }
4188
4189 fn reject_eval_or_arguments(&mut self, span: Span) {
4192 let word = &self.source[span.start as usize..span.end as usize];
4193 if !matches!(word, "eval" | "arguments") {
4194 return;
4195 }
4196 self.error_at_with_help(
4197 span,
4198 format!("`{word}` can't name a binding in strict mode"),
4199 vec![format!(
4200 "rename it, for example: `{}`",
4201 reserved_keyword_rename_example(word)
4202 )],
4203 );
4204 }
4205
4206 fn ident_from_token(&self, tok: &Token) -> Ident {
4207 Ident {
4208 name: self.source[tok.span.start as usize..tok.span.end as usize].to_string(),
4209 span: tok.span,
4210 }
4211 }
4212
4213 fn property_ident_from_token(&self, tok: &Token) -> Ident {
4214 if let TokenKind::StringLiteral(s) = &tok.kind {
4215 Ident {
4216 name: s.clone(),
4217 span: tok.span,
4218 }
4219 } else {
4220 self.ident_from_token(tok)
4221 }
4222 }
4223
4224 fn expect_property_name(&mut self, message: &str) -> Option<Token> {
4225 if !is_property_name(&self.peek().kind) {
4226 self.error_at_peek(message.to_string());
4227 return None;
4228 }
4229 Some(self.advance())
4230 }
4231
4232 fn expect_property_ident(&mut self, message: &str) -> Option<Ident> {
4233 if !is_property_name(&self.peek().kind)
4234 && !matches!(self.peek().kind, TokenKind::StringLiteral(_))
4235 {
4236 self.error_at_peek(message.to_string());
4237 return None;
4238 }
4239 let tok = self.advance();
4240 Some(self.property_ident_from_token(&tok))
4241 }
4242
4243 fn readonly_modifier_span(&self, member_start: u32) -> Span {
4246 self.span(member_start, member_start + "readonly".len() as u32)
4247 }
4248
4249 fn reject_readonly_modifier(&mut self, span: Span) {
4252 self.error_at_with_help(
4253 span,
4254 "`readonly` can only modify a property or index signature",
4255 vec!["remove `readonly`".to_string()],
4256 );
4257 }
4258
4259 fn eat_readonly_property_modifier(&mut self) -> bool {
4260 if self.peek_identifier_text_is("readonly")
4261 && !self.line_break_after_peek()
4262 && (self.peek_starts_property_after_readonly()
4263 || matches!(self.peek_at(1).kind, TokenKind::LeftBracket))
4264 {
4265 self.advance();
4266 return true;
4267 }
4268 false
4269 }
4270
4271 fn peek_starts_property_after_readonly(&self) -> bool {
4272 let name = &self.peek_at(1).kind;
4273 let after_name = &self.peek_at(2).kind;
4274 let after_optional = &self.peek_at(3).kind;
4275 let starts_member_type =
4278 |kind: &TokenKind| matches!(kind, TokenKind::Colon | TokenKind::LeftParen);
4279 (is_property_name(name) || matches!(name, TokenKind::StringLiteral(_)))
4280 && (starts_member_type(after_name)
4281 || matches!(after_name, TokenKind::Question) && starts_member_type(after_optional))
4282 }
4283
4284 fn peek_at_is_binary_word(&self, offset: usize) -> bool {
4286 self.peek_at_is_word(offset, "as") || self.peek_at_is_word(offset, "satisfies")
4287 }
4288
4289 fn peek_identifier_text_is(&self, expected: &str) -> bool {
4290 self.token_is_word(self.peek(), expected)
4291 }
4292
4293 fn peek_at_is_word(&self, offset: usize, expected: &str) -> bool {
4294 self.token_is_word(self.peek_at(offset), expected)
4295 }
4296
4297 fn token_is_word(&self, tok: &Token, expected: &str) -> bool {
4298 matches!(tok.kind, TokenKind::Identifier)
4299 && &self.source[tok.span.start as usize..tok.span.end as usize] == expected
4300 }
4301
4302 fn at_delete_operator(&self) -> bool {
4309 if !self.peek_identifier_text_is("delete") {
4310 return false;
4311 }
4312 if !matches!(
4313 self.peek_at(1).kind,
4314 TokenKind::Identifier | TokenKind::This
4315 ) {
4316 return false;
4317 }
4318 let next_is_cast_keyword =
4319 self.peek_at_is_word(1, "as") || self.peek_at_is_word(1, "satisfies");
4320 !next_is_cast_keyword || cannot_start_type(&self.peek_at(2).kind)
4321 }
4322
4323 fn at_type_alias_head(&self) -> bool {
4327 self.peek_identifier_text_is("type")
4328 && matches!(self.peek_at(1).kind, TokenKind::Identifier)
4329 }
4330
4331 fn parse_expression(&mut self) -> Option<ExprId> {
4334 self.with_recursion_limit(Self::parse_expression_inner)
4335 }
4336
4337 fn parse_expression_inner(&mut self) -> Option<ExprId> {
4338 let written = self.parse_conditional()?;
4339 let shift_assignment = self.peek_shift().filter(|shift| shift.assignment);
4340 if !is_assign_lookahead(&self.peek().kind) && shift_assignment.is_none() {
4341 return Some(written);
4342 }
4343 let target_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4344 let target = self.assignment_target(written)?;
4345 let op_tok = self.advance();
4346 let mut op_span = op_tok.span;
4347 let op = if let Some(ShiftToken { op, tokens, .. }) = shift_assignment {
4348 for _ in 1..tokens {
4349 op_span.end = self.advance().span.end;
4350 }
4351 Some(op)
4352 } else {
4353 compound_op_for_token(&op_tok.kind)
4354 };
4355 let value = self.parse_expression()?;
4356 let value_span = parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
4357 parse_arena_result(
4358 self.ast.try_push_expr(Expr {
4359 kind: ExprKind::Assign {
4360 target,
4361 op,
4362 op_span,
4363 value,
4364 },
4365 span: self.span(target_span.start, value_span.end),
4366 }),
4367 &mut self.fatal,
4368 )
4369 }
4370
4371 fn assignment_target(&mut self, written: ExprId) -> Option<ExprId> {
4374 let mut target = written;
4375 while let ExprKind::Paren(inner) =
4376 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind
4377 {
4378 target = inner;
4379 }
4380 if matches!(
4381 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind,
4382 ExprKind::Identifier(_) | ExprKind::FieldAccess { .. } | ExprKind::IndexAccess { .. }
4383 ) {
4384 return Some(target);
4385 }
4386 let error_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4387 self.error_at_with_help(
4388 error_span,
4389 "invalid assignment target",
4390 vec![
4391 "assign to a variable, a field (`o.f = …`), or an element (`a[i] = …`)".to_string(),
4392 ],
4393 );
4394 None
4395 }
4396
4397 fn parse_conditional(&mut self) -> Option<ExprId> {
4398 self.reject_async_modifier();
4399 if self.is_arrow_start() {
4400 return self.parse_arrow();
4401 }
4402 let cond = self.parse_binary(0)?;
4403 if !matches!(self.peek().kind, TokenKind::Question) {
4405 return Some(cond);
4406 }
4407 self.advance();
4408 let then_ = self.parse_expression()?;
4409 if !matches!(self.peek().kind, TokenKind::Colon) {
4410 self.error_at_peek_with_help(
4411 "expected `:` to complete ternary expression",
4412 vec!["const x = cond ? then : else;".to_string()],
4413 );
4414 return None;
4415 }
4416 self.advance();
4417 let else_ = self.parse_expression()?;
4418 let cond_span = parse_arena_result(self.ast.try_expr(cond), &mut self.fatal)?.span;
4419 let else_span = parse_arena_result(self.ast.try_expr(else_), &mut self.fatal)?.span;
4420 parse_arena_result(
4421 self.ast.try_push_expr(Expr {
4422 kind: ExprKind::Ternary { cond, then_, else_ },
4423 span: self.span(cond_span.start, else_span.end),
4424 }),
4425 &mut self.fatal,
4426 )
4427 }
4428
4429 fn is_arrow_start(&self) -> bool {
4430 match self.peek().kind {
4431 TokenKind::Identifier => matches!(self.peek_at(1).kind, TokenKind::Arrow),
4432 TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos),
4433 _ => false,
4434 }
4435 }
4436
4437 fn reject_async_modifier(&mut self) {
4440 if !self.peek_identifier_text_is("async") {
4441 return;
4442 }
4443 let modifies = match self.peek_at(1).kind {
4444 TokenKind::Function => true,
4445 TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos + 1),
4447 ref name
4451 if is_property_name(name)
4452 && !matches!(name, TokenKind::In)
4453 && !self.peek_at_is_binary_word(1) =>
4454 {
4455 matches!(
4456 self.peek_at(2).kind,
4457 TokenKind::Arrow | TokenKind::LeftParen | TokenKind::LessThan
4458 )
4459 }
4460 _ => false,
4461 };
4462 if modifies {
4463 self.report_async_modifier();
4464 }
4465 }
4466
4467 fn reject_async_member_modifier(&mut self) {
4472 if !self.peek_identifier_text_is("async") || self.line_break_after_peek() {
4473 return;
4474 }
4475 let modifies = match self.peek_at(1).kind {
4476 TokenKind::Star
4477 | TokenKind::LeftBracket
4478 | TokenKind::StringLiteral(_)
4479 | TokenKind::NumberLiteral(_) => true,
4480 ref name if is_property_name(name) => {
4481 matches!(
4482 self.peek_at(2).kind,
4483 TokenKind::LeftParen | TokenKind::LessThan
4484 )
4485 }
4486 _ => false,
4487 };
4488 if modifies {
4489 self.report_async_modifier();
4490 }
4491 }
4492
4493 fn report_async_modifier(&mut self) {
4497 let tok = self.advance();
4498 self.error_at_with_help(
4499 tok.span,
4500 "`async` functions are not supported",
4501 vec![
4502 "calls return their value directly: remove `async`, write the return type \
4503 `T` instead of `Promise<T>`, and drop `await`"
4504 .to_string(),
4505 ],
4506 );
4507 }
4508
4509 fn reject_await_operator(&mut self) -> bool {
4515 if !self.peek_identifier_text_is("await") {
4516 return false;
4517 }
4518 let operand_starts = matches!(
4519 self.peek_at(1).kind,
4520 TokenKind::Identifier
4521 | TokenKind::This
4522 | TokenKind::Super
4523 | TokenKind::New
4524 | TokenKind::Function
4525 | TokenKind::StringLiteral(_)
4526 | TokenKind::NumberLiteral(_)
4527 | TokenKind::NullLiteral
4528 | TokenKind::BooleanLiteral(_)
4529 | TokenKind::TemplateHead(_)
4532 | TokenKind::TemplateNoSubstitution(_)
4533 ) && !self.peek_at_is_binary_word(1);
4534 if !operand_starts || self.line_break_after_peek() {
4535 return false;
4536 }
4537 let tok = self.advance();
4538 self.error_at_with_help(
4539 tok.span,
4540 "`await` is not supported",
4541 vec!["calls return their value directly: remove `await`".to_string()],
4542 );
4543 true
4544 }
4545
4546 fn index_after_matching_paren(&self, index: usize) -> Option<usize> {
4549 if !matches!(
4550 self.tokens.get(index).map(|t| &t.kind),
4551 Some(TokenKind::LeftParen)
4552 ) {
4553 return None;
4554 }
4555 self.scan_past_balanced(
4556 index,
4557 |k| matches!(k, TokenKind::LeftParen),
4558 |k| matches!(k, TokenKind::RightParen),
4559 )
4560 }
4561
4562 fn find_arrow_after_matching_paren(&self, open: usize) -> bool {
4563 let Some(i) = self.index_after_matching_paren(open) else {
4564 return false;
4565 };
4566 let after_type = if matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Colon)) {
4567 match self.scan_past_type_annotation(i + 1, TypePos::ArrowReturn) {
4568 Some(p) => p,
4569 None => return false,
4570 }
4571 } else {
4572 i
4573 };
4574 matches!(
4575 self.tokens.get(after_type).map(|t| &t.kind),
4576 Some(TokenKind::Arrow),
4577 )
4578 }
4579
4580 fn scan_past_type_annotation(&self, start: usize, type_pos: TypePos) -> Option<usize> {
4586 self.scan_past_type_annotation_at_depth(start, type_pos, 0)
4587 }
4588
4589 fn scan_past_type_annotation_at_depth(
4590 &self,
4591 start: usize,
4592 type_pos: TypePos,
4593 depth: usize,
4594 ) -> Option<usize> {
4595 if depth >= MAX_PARSE_DEPTH {
4596 return None;
4597 }
4598 if matches!(
4599 self.tokens.get(start).map(|t| &t.kind),
4600 Some(TokenKind::Identifier)
4601 ) && self
4602 .tokens
4603 .get(start + 1)
4604 .is_some_and(|t| self.token_is_word(t, "is"))
4605 {
4606 return self.scan_past_type_annotation_at_depth(start + 2, type_pos, depth + 1);
4607 }
4608 let start = if matches!(
4610 self.tokens.get(start).map(|t| &t.kind),
4611 Some(TokenKind::Pipe)
4612 ) {
4613 start + 1
4614 } else {
4615 start
4616 };
4617 let mut i = self.scan_past_single_type_member(start, type_pos, depth + 1)?;
4618 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Pipe)) {
4619 i = self.scan_past_single_type_member(i + 1, type_pos, depth + 1)?;
4620 }
4621 Some(i)
4622 }
4623
4624 fn scan_past_single_type_member(
4625 &self,
4626 start: usize,
4627 type_pos: TypePos,
4628 depth: usize,
4629 ) -> Option<usize> {
4630 if depth >= MAX_PARSE_DEPTH {
4631 return None;
4632 }
4633 if self.is_contextual_type_operator(start, "keyof")
4634 || self.is_contextual_type_operator(start, "readonly")
4635 {
4636 return self.scan_past_single_type_member(start + 1, type_pos, depth + 1);
4637 }
4638 let mut i = start;
4639 match self.tokens.get(i)?.kind {
4640 TokenKind::Identifier | TokenKind::Void => {
4641 i += 1;
4642 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Dot)) {
4643 if !matches!(
4644 self.tokens.get(i + 1).map(|t| &t.kind),
4645 Some(TokenKind::Identifier)
4646 ) {
4647 return None;
4648 }
4649 i += 2;
4650 }
4651 if matches!(
4652 self.tokens.get(i).map(|t| &t.kind),
4653 Some(TokenKind::LessThan)
4654 ) {
4655 i = self.scan_past_balanced(
4656 i,
4657 |k| matches!(k, TokenKind::LessThan),
4658 |k| matches!(k, TokenKind::GreaterThan),
4659 )?;
4660 }
4661 }
4662 TokenKind::NullLiteral
4663 | TokenKind::StringLiteral(_)
4664 | TokenKind::NumberLiteral(_)
4665 | TokenKind::BooleanLiteral(_) => {
4666 i += 1;
4667 }
4668 TokenKind::LeftBrace => {
4669 i = self.scan_past_balanced(
4670 i,
4671 |k| matches!(k, TokenKind::LeftBrace),
4672 |k| matches!(k, TokenKind::RightBrace),
4673 )?;
4674 }
4675 TokenKind::LeftBracket => {
4676 i = self.scan_past_balanced(
4677 i,
4678 |k| matches!(k, TokenKind::LeftBracket),
4679 |k| matches!(k, TokenKind::RightBracket),
4680 )?;
4681 }
4682 TokenKind::LeftParen => {
4687 let is_function_type = self.paren_at_opens_function_type(i, type_pos);
4688 i = self.scan_past_balanced(
4689 i,
4690 |k| matches!(k, TokenKind::LeftParen),
4691 |k| matches!(k, TokenKind::RightParen),
4692 )?;
4693 if is_function_type {
4694 if !matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)) {
4695 return None;
4696 }
4697 return self.scan_past_type_annotation_at_depth(
4698 i + 1,
4699 TypePos::Anywhere,
4700 depth + 1,
4701 );
4702 }
4703 }
4704 _ => return None,
4705 }
4706 while matches!(
4707 self.tokens.get(i).map(|t| &t.kind),
4708 Some(TokenKind::LeftBracket)
4709 ) {
4710 i += 1;
4711 if !matches!(
4712 self.tokens.get(i).map(|t| &t.kind),
4713 Some(TokenKind::RightBracket)
4714 ) {
4715 return None;
4716 }
4717 i += 1;
4718 }
4719 Some(i)
4720 }
4721
4722 fn scan_past_balanced(
4725 &self,
4726 start: usize,
4727 is_open: fn(&TokenKind) -> bool,
4728 is_close: fn(&TokenKind) -> bool,
4729 ) -> Option<usize> {
4730 let mut depth: i32 = 1;
4731 let mut i = start + 1;
4732 while i < self.tokens.len() && depth > 0 {
4733 let token = self.tokens.get(i)?;
4734 let kind = &token.kind;
4735 if is_open(kind) {
4736 depth += 1;
4737 } else if is_close(kind) {
4738 depth -= 1;
4739 } else if matches!(kind, TokenKind::Eof) {
4740 return None;
4741 }
4742 i += 1;
4743 }
4744 if depth == 0 { Some(i) } else { None }
4745 }
4746
4747 fn parse_arrow(&mut self) -> Option<ExprId> {
4748 let start_span = self.peek().span;
4749
4750 let params = match self.peek().kind {
4751 TokenKind::Identifier => {
4752 let name_tok = self.advance();
4753 self.reject_eval_or_arguments(name_tok.span);
4754 let name = self.ident_from_token(&name_tok);
4755 vec![ParamDecl {
4756 name,
4757 ty: None,
4758 default: None,
4759 optional: false,
4760 pattern: None,
4761 rest: false,
4762 modifiers: None,
4763 }]
4764 }
4765 TokenKind::LeftParen => {
4766 self.advance();
4767 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4768 Vec::new()
4769 } else {
4770 self.parse_arrow_param_list()?
4771 };
4772 if !matches!(self.peek().kind, TokenKind::RightParen) {
4773 self.error_at_peek("expected `)`");
4774 return None;
4775 }
4776 self.advance();
4777 params
4778 }
4779 _ => return self.invariant_failure("is_arrow_start guarded the entry"),
4780 };
4781
4782 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4783 self.advance();
4784 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4785 } else {
4786 (None, None)
4787 };
4788
4789 if !matches!(self.peek().kind, TokenKind::Arrow) {
4790 self.error_at_peek("expected `=>` in arrow function");
4791 return None;
4792 }
4793 if self.line_break_before_peek() {
4795 let arrow_span = self.peek().span;
4796 self.error_at(arrow_span, "a line break is not allowed before `=>`");
4797 }
4798 self.advance();
4799
4800 let (body, end_pos) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
4801 let block = self.parse_block()?;
4802 let span = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?.span;
4803 (ArrowBody::Block(block), span.end)
4804 } else {
4805 let expr = self.parse_expression()?;
4806 let span = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?.span;
4807 (ArrowBody::Expr(expr), span.end)
4808 };
4809
4810 parse_arena_result(
4811 self.ast.try_push_expr(Expr {
4812 kind: ExprKind::Arrow {
4813 params,
4814 return_type,
4815 type_predicate,
4816 body,
4817 },
4818 span: self.span(start_span.start, end_pos),
4819 }),
4820 &mut self.fatal,
4821 )
4822 }
4823
4824 fn parse_function_expression(&mut self) -> Option<ExprId> {
4827 let kw = self.advance();
4828
4829 if matches!(self.peek().kind, TokenKind::LessThan) {
4830 self.error_at_peek("generic function expressions are not supported");
4831 return None;
4832 }
4833
4834 let self_name = if matches!(self.peek().kind, TokenKind::Identifier) {
4835 let name_tok = self.advance();
4836 self.reject_eval_or_arguments(name_tok.span);
4837 Some(self.ident_from_token(&name_tok))
4838 } else {
4839 None
4840 };
4841
4842 if !matches!(self.peek().kind, TokenKind::LeftParen) {
4843 self.error_at_peek("expected `(` after `function`");
4844 return None;
4845 }
4846 self.advance();
4847
4848 let this_type = if matches!(self.peek().kind, TokenKind::This) {
4849 self.advance();
4850 if !matches!(self.peek().kind, TokenKind::Colon) {
4851 self.error_at_peek("expected a type annotation after `this`");
4852 return None;
4853 }
4854 self.advance();
4855 let ty = self.parse_type_annotation()?;
4856 if matches!(self.peek().kind, TokenKind::Comma) {
4857 self.advance();
4858 } else if !matches!(self.peek().kind, TokenKind::RightParen) {
4859 self.error_at_peek("expected `,` or `)` after the `this` parameter");
4860 return None;
4861 }
4862 Some(ty)
4863 } else {
4864 None
4865 };
4866 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4867 Vec::new()
4868 } else {
4869 let saved_class = self.class_member_body_depth;
4870 let saved_function = self.function_expression_body_depth;
4871 self.class_member_body_depth = 0;
4872 self.function_expression_body_depth = 1;
4873 let params = self.parse_arrow_param_list();
4874 self.class_member_body_depth = saved_class;
4875 self.function_expression_body_depth = saved_function;
4876 params?
4877 };
4878 if !matches!(self.peek().kind, TokenKind::RightParen) {
4879 self.error_at_peek("expected `)`");
4880 return None;
4881 }
4882 self.advance();
4883
4884 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4885 self.advance();
4886 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4887 } else {
4888 (None, None)
4889 };
4890
4891 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
4892 self.error_at_peek("expected `{` to open the function body");
4893 return None;
4894 }
4895 let saved = self.class_member_body_depth;
4896 self.class_member_body_depth = 0;
4897 self.function_expression_body_depth += 1;
4898 let block = self.parse_block();
4899 self.function_expression_body_depth -= 1;
4900 self.class_member_body_depth = saved;
4901 let block = block?;
4902 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
4903 .span
4904 .end;
4905
4906 let function = parse_arena_result(
4907 self.ast.try_push_expr(Expr {
4908 kind: ExprKind::Arrow {
4909 params,
4910 return_type,
4911 type_predicate,
4912 body: ArrowBody::Block(block),
4913 },
4914 span: self.span(kw.span.start, end),
4915 }),
4916 &mut self.fatal,
4917 )?;
4918 parse_arena_result(
4919 self.ast.try_push_expr(Expr {
4920 kind: ExprKind::FunctionExpression {
4921 name: self_name,
4922 this_type,
4923 function,
4924 },
4925 span: self.span(kw.span.start, end),
4926 }),
4927 &mut self.fatal,
4928 )
4929 }
4930
4931 fn parse_outer_this_boundary<T>(
4934 &mut self,
4935 parse: impl FnOnce(&mut Self) -> Option<T>,
4936 ) -> Option<T> {
4937 let saved = self.class_member_body_depth;
4938 self.class_member_body_depth = 0;
4939 let saved_function = self.function_expression_body_depth;
4940 self.function_expression_body_depth = 0;
4941 let parsed = parse(self);
4942 self.class_member_body_depth = saved;
4943 self.function_expression_body_depth = saved_function;
4944 parsed
4945 }
4946
4947 fn parse_arrow_param_list(&mut self) -> Option<Vec<ParamDecl>> {
4948 let mut params = Vec::new();
4949 loop {
4950 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
4951 self.advance();
4952 true
4953 } else {
4954 false
4955 };
4956 let (name, pattern) = match self.peek().kind {
4959 TokenKind::LeftBrace | TokenKind::LeftBracket => {
4960 if rest {
4961 self.error_at_peek("rest parameter cannot be destructured");
4962 return None;
4963 }
4964 let binding = self.parse_binding()?;
4965 let placeholder = Ident {
4966 name: String::new(),
4967 span: binding.span(),
4968 };
4969 (placeholder, Some(binding))
4970 }
4971 _ => {
4972 let name_tok = self.expect_binding_identifier(if rest {
4973 "expected identifier after `...`"
4974 } else {
4975 "expected parameter name"
4976 })?;
4977 let name = self.ident_from_token(&name_tok);
4978 (name, None)
4979 }
4980 };
4981 let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
4982 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
4983 self.advance();
4984 Some(self.parse_type_annotation()?)
4985 } else {
4986 None
4989 };
4990 let default = self.parse_parameter_default(rest, optional)?;
4991 params.push(ParamDecl {
4992 name,
4993 ty,
4994 default,
4995 optional,
4996 pattern,
4997 rest,
4998 modifiers: None,
4999 });
5000 match self.peek().kind {
5001 TokenKind::Comma => {
5002 if rest {
5003 let comma = self.advance();
5004 self.error_at(comma.span, "rest parameter must be the last parameter");
5005 return None;
5006 }
5007 self.advance();
5008 if matches!(self.peek().kind, TokenKind::RightParen) {
5009 break;
5010 }
5011 }
5012 TokenKind::RightParen => break,
5013 _ => {
5014 self.error_at_peek("expected `,` or `)`");
5015 return None;
5016 }
5017 }
5018 }
5019 self.check_parameter_order(params.iter().map(ParamOrder::of_param));
5020 Some(params)
5021 }
5022
5023 fn parse_binary(&mut self, min_prec: u8) -> Option<ExprId> {
5024 self.with_recursion_limit(|parser| parser.parse_binary_inner(min_prec))
5025 }
5026
5027 fn peek_shift(&self) -> Option<ShiftToken> {
5030 let first = self.peek();
5031 let second = self.peek_at(1);
5032 if first.span.end != second.span.start {
5033 return None;
5034 }
5035 match (&first.kind, &second.kind) {
5036 (TokenKind::LessThan, TokenKind::LessThan) => {
5037 Some(ShiftToken::new(BinOp::Shl, 2, false))
5038 }
5039 (TokenKind::LessThan, TokenKind::LessEquals) => {
5040 Some(ShiftToken::new(BinOp::Shl, 2, true))
5041 }
5042 (TokenKind::GreaterThan, TokenKind::GreaterEquals) => {
5043 Some(ShiftToken::new(BinOp::Shr, 2, true))
5044 }
5045 (TokenKind::GreaterThan, TokenKind::GreaterThan) => {
5046 let third = self.peek_at(2);
5047 if second.span.end == third.span.start {
5048 match third.kind {
5049 TokenKind::GreaterThan => {
5050 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, false));
5051 }
5052 TokenKind::GreaterEquals => {
5053 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, true));
5054 }
5055 _ => {}
5056 }
5057 }
5058 Some(ShiftToken::new(BinOp::Shr, 2, false))
5059 }
5060 _ => None,
5061 }
5062 }
5063
5064 fn peek_binary(&self) -> Option<BinaryToken> {
5065 if let Some(shift) = self.peek_shift() {
5066 return (!shift.assignment).then_some(BinaryToken {
5067 op: shift.op,
5068 precedence: SHIFT_PRECEDENCE,
5069 tokens: shift.tokens,
5070 });
5071 }
5072 peek_binop(&self.peek().kind).map(|(op, precedence)| BinaryToken {
5073 op,
5074 precedence,
5075 tokens: 1,
5076 })
5077 }
5078
5079 fn parse_binary_inner(&mut self, min_prec: u8) -> Option<ExprId> {
5080 let mut lhs = self.parse_cast()?;
5081 let mut last_op: Option<BinOp> = None;
5085 let mut reported_mixing = false;
5086 while let Some(BinaryToken {
5087 op,
5088 precedence: prec,
5089 tokens: count,
5090 }) = self.peek_binary()
5091 {
5092 if prec < min_prec {
5093 break;
5094 }
5095 if !reported_mixing
5096 && let Some(last) = last_op
5097 && mixed_logical(last, op)
5098 {
5099 reported_mixing = true;
5100 let op_span = self.peek().span;
5101 self.error_at_with_help(
5105 op_span,
5106 "mixing `??` with `||` / `&&` requires parentheses",
5107 vec![mixed_logical_help(last, op)],
5108 );
5109 }
5110 if op == BinOp::Pow
5111 && matches!(
5112 parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.kind,
5113 ExprKind::Unary { .. }
5114 | ExprKind::Typeof { .. }
5115 | ExprKind::Delete { .. }
5116 | ExprKind::Void { .. }
5117 )
5118 {
5119 let error_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5120 self.error_at_with_help(
5121 error_span,
5122 "an unparenthesized unary expression cannot be the left operand of `**`",
5123 vec!["choose the grouping explicitly: `(-x) ** 2` or `-(x ** 2)`".into()],
5124 );
5125 return None;
5126 }
5127 let loose = matches!(self.peek().kind, TokenKind::EqEq | TokenKind::BangEq);
5128 for _ in 0..count {
5129 self.advance();
5130 }
5131 let rhs = self.parse_binary(right_operand_min_prec(op, prec))?;
5132 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5133 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
5134 if loose {
5135 self.reject_loose_nullish_comparison(op, lhs, rhs)?;
5136 }
5137 lhs = parse_arena_result(
5138 self.ast.try_push_expr(Expr {
5139 kind: ExprKind::Binary { op, lhs, rhs },
5140 span: self.span(lhs_span.start, rhs_span.end),
5141 }),
5142 &mut self.fatal,
5143 )?;
5144 last_op = Some(op);
5145 }
5146 Some(lhs)
5147 }
5148
5149 fn parse_cast(&mut self) -> Option<ExprId> {
5151 let mut expr = self.parse_unary()?;
5152 while matches!(self.peek().kind, TokenKind::Instanceof)
5153 || self.peek_identifier_text_is("as")
5154 {
5155 let is_instanceof = matches!(self.peek().kind, TokenKind::Instanceof);
5156 self.advance();
5157 let ty = self.parse_type_annotation()?;
5158 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5159 .span
5160 .start;
5161 let end = ty.span.end;
5162 let kind = if is_instanceof {
5163 ExprKind::InstanceOf { value: expr, ty }
5164 } else {
5165 self.last_as_type_end = Some(end);
5166 ExprKind::As { expr, ty }
5167 };
5168 expr = parse_arena_result(
5169 self.ast.try_push_expr(Expr {
5170 kind,
5171 span: self.span(start, end),
5172 }),
5173 &mut self.fatal,
5174 )?;
5175 }
5176 Some(expr)
5177 }
5178
5179 fn parse_unary(&mut self) -> Option<ExprId> {
5180 self.with_recursion_limit(Self::parse_unary_inner)
5181 }
5182
5183 fn parse_unary_inner(&mut self) -> Option<ExprId> {
5184 if self.reject_await_operator() {
5185 return self.parse_unary();
5186 }
5187 if matches!(self.peek().kind, TokenKind::Typeof) {
5190 return self.parse_prefix_expr(|operand| ExprKind::Typeof { operand });
5191 }
5192 if matches!(self.peek().kind, TokenKind::Void) {
5193 return self.parse_prefix_expr(|operand| ExprKind::Void { operand });
5194 }
5195 if self.at_delete_operator() {
5196 return self.parse_prefix_expr(|operand| ExprKind::Delete { operand });
5197 }
5198 if matches!(self.peek().kind, TokenKind::New) {
5199 let op_tok = self.advance();
5200 let mut callee = self.parse_atom()?;
5201 while matches!(self.peek().kind, TokenKind::Dot) {
5202 self.advance();
5203 if !matches!(self.peek().kind, TokenKind::Identifier) {
5204 self.error_at_peek("expected identifier after `.` in constructor name");
5205 return None;
5206 }
5207 let ident_tok = self.advance();
5208 let name = self.ident_from_token(&ident_tok);
5209 let receiver_span =
5210 parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5211 let combined_span = self.span(receiver_span.start, ident_tok.span.end);
5212 callee = parse_arena_result(
5213 self.ast.try_push_expr(Expr {
5214 kind: ExprKind::FieldAccess {
5215 receiver: callee,
5216 name,
5217 },
5218 span: combined_span,
5219 }),
5220 &mut self.fatal,
5221 )?;
5222 }
5223 let type_args = if matches!(self.peek().kind, TokenKind::LessThan) {
5224 let saved_pos = self.pos;
5225 let saved_diag_len = self.diagnostics.len();
5226 if let Some(args) = self.try_parse_type_args_only() {
5227 Some(args)
5228 } else {
5229 self.pos = saved_pos;
5230 self.diagnostics.truncate(saved_diag_len);
5231 None
5232 }
5233 } else {
5234 None
5235 };
5236 if !matches!(self.peek().kind, TokenKind::LeftParen) {
5237 self.error_at_peek("expected `(` after constructor name in `new` expression");
5238 return None;
5239 }
5240 self.advance();
5241 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5242 Vec::new()
5243 } else {
5244 self.parse_call_args()?
5245 };
5246 if !matches!(self.peek().kind, TokenKind::RightParen) {
5247 self.error_at_peek("expected `)`");
5248 return None;
5249 }
5250 let close = self.advance();
5251 let new_expr = parse_arena_result(
5252 self.ast.try_push_expr(Expr {
5253 kind: ExprKind::New {
5254 callee,
5255 type_args,
5256 args,
5257 },
5258 span: self.span(op_tok.span.start, close.span.end),
5259 }),
5260 &mut self.fatal,
5261 )?;
5262 return self.parse_postfix_from(new_expr);
5265 }
5266 let op = match self.peek().kind {
5267 TokenKind::Bang => UnOp::Not,
5268 TokenKind::Tilde => UnOp::BitNot,
5269 TokenKind::Minus => UnOp::Neg,
5270 TokenKind::Plus => UnOp::Pos,
5271 _ => return self.parse_postfix(),
5272 };
5273 self.parse_prefix_expr(|operand| ExprKind::Unary { op, operand })
5274 }
5275
5276 fn parse_prefix_expr(&mut self, kind: impl FnOnce(ExprId) -> ExprKind) -> Option<ExprId> {
5278 let op_tok = self.advance();
5279 if matches!(op_tok.kind, TokenKind::Void) && self.at_inserted_semicolon() {
5282 self.advance();
5283 }
5284 let operand = self.parse_unary()?;
5285 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5286 parse_arena_result(
5287 self.ast.try_push_expr(Expr {
5288 kind: kind(operand),
5289 span: self.span(op_tok.span.start, operand_span.end),
5290 }),
5291 &mut self.fatal,
5292 )
5293 }
5294
5295 fn parse_postfix(&mut self) -> Option<ExprId> {
5296 let expr = self.parse_atom()?;
5297 self.parse_postfix_from(expr)
5298 }
5299
5300 fn parse_postfix_from(&mut self, mut expr: ExprId) -> Option<ExprId> {
5301 let mut chain_parts: Option<(ExprId, Vec<crate::ChainPart>)> = None;
5305 loop {
5306 match self.peek().kind {
5307 TokenKind::LeftParen => {
5308 if let Some((_, parts)) = &mut chain_parts {
5309 let part = self.parse_chain_call_tail(false)?;
5310 parts.push(part);
5311 } else {
5312 expr = self.parse_call_tail(expr, None)?;
5313 }
5314 }
5315 TokenKind::Dot => {
5316 if let Some((_, parts)) = &mut chain_parts {
5317 let part = self.parse_chain_field_tail(false)?;
5318 parts.push(part);
5319 } else {
5320 expr = self.parse_field_access_tail(expr)?;
5321 }
5322 }
5323 TokenKind::LeftBracket => {
5324 if let Some((_, parts)) = &mut chain_parts {
5325 let part = self.parse_chain_index_tail(false)?;
5326 parts.push(part);
5327 } else {
5328 expr = self.parse_index_access_tail(expr)?;
5329 }
5330 }
5331 TokenKind::QuestionDot => {
5332 if chain_parts.is_none() {
5333 chain_parts = Some((expr, Vec::new()));
5334 }
5335 self.advance();
5336 let part = match self.peek().kind {
5337 TokenKind::LeftParen => self.parse_chain_call_tail(true)?,
5338 TokenKind::LeftBracket => self.parse_chain_index_tail(true)?,
5339 _ if is_property_name(&self.peek().kind) => {
5340 self.parse_chain_field_tail(true)?
5341 }
5342 _ => {
5343 self.error_at_peek(
5344 "expected field name, `(` for call, or `[` for index after `?.`",
5345 );
5346 return None;
5347 }
5348 };
5349 if let Some((_, parts)) = &mut chain_parts {
5350 parts.push(part);
5351 }
5352 }
5353 TokenKind::LessThan => {
5357 if chain_parts.is_some() {
5358 break;
5360 }
5361 let saved_pos = self.pos;
5362 let saved_diag_len = self.diagnostics.len();
5363 if let Some(call_id) = self.try_parse_generic_call_tail(expr) {
5364 expr = call_id;
5365 } else {
5366 self.pos = saved_pos;
5367 self.diagnostics.truncate(saved_diag_len);
5368 break;
5369 }
5370 }
5371 TokenKind::Bang => {
5372 if let Some((_, parts)) = &mut chain_parts {
5373 let tok = self.advance();
5374 parts.push(crate::ChainPart::NonNull { span: tok.span });
5375 continue;
5376 }
5377 let tok = self.advance();
5378 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5379 .span
5380 .start;
5381 expr = parse_arena_result(
5382 self.ast.try_push_expr(Expr {
5383 kind: ExprKind::PostfixUnary {
5384 op: PostfixOp::NonNullAssert,
5385 operand: expr,
5386 },
5387 span: self.span(start, tok.span.end),
5388 }),
5389 &mut self.fatal,
5390 )?;
5391 }
5392 TokenKind::PlusPlus | TokenKind::MinusMinus => {
5394 if chain_parts.is_some() {
5395 break;
5396 }
5397 let tok = self.advance();
5398 let op = if matches!(tok.kind, TokenKind::PlusPlus) {
5399 PostfixOp::Inc
5400 } else {
5401 PostfixOp::Dec
5402 };
5403 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5404 .span
5405 .start;
5406 expr = parse_arena_result(
5407 self.ast.try_push_expr(Expr {
5408 kind: ExprKind::PostfixUnary { op, operand: expr },
5409 span: self.span(start, tok.span.end),
5410 }),
5411 &mut self.fatal,
5412 )?;
5413 break;
5414 }
5415 _ => break,
5416 }
5417 }
5418 if let Some((base, parts)) = chain_parts {
5419 let base_span = parse_arena_result(self.ast.try_expr(base), &mut self.fatal)?.span;
5420 let end = parts.last().map_or(base_span.end, |p| p.span().end);
5421 Some(parse_arena_result(
5422 self.ast.try_push_expr(Expr {
5423 kind: ExprKind::OptionalChain { base, parts },
5424 span: self.span(base_span.start, end),
5425 }),
5426 &mut self.fatal,
5427 )?)
5428 } else {
5429 Some(expr)
5430 }
5431 }
5432
5433 fn parse_chain_field_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5435 let start_pos = self.peek().span.start;
5436 if !optional {
5437 self.advance();
5438 }
5439 let name_tok = if is_property_name(&self.peek().kind) {
5440 self.advance()
5441 } else {
5442 let what = if optional { "?." } else { "." };
5443 self.error_at_peek(format!("expected field name after `{what}`"));
5444 return None;
5445 };
5446 let name = self.ident_from_token(&name_tok);
5447 let end = name.span.end;
5448 Some(crate::ChainPart::Field {
5449 name,
5450 optional,
5451 span: self.span(start_pos, end),
5452 })
5453 }
5454
5455 fn parse_chain_index_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5456 let open = self.advance();
5457 let idx = self.parse_expression()?;
5458 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5459 self.error_at_peek("expected `]`");
5460 return None;
5461 }
5462 let close = self.advance();
5463 Some(crate::ChainPart::Index {
5464 idx,
5465 optional,
5466 span: self.span(open.span.start, close.span.end),
5467 })
5468 }
5469
5470 fn parse_chain_call_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5471 let open = self.advance();
5472 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5473 Vec::new()
5474 } else {
5475 self.parse_call_args()?
5476 };
5477 if !matches!(self.peek().kind, TokenKind::RightParen) {
5478 self.error_at_peek("expected `)`");
5479 return None;
5480 }
5481 let close = self.advance();
5482 Some(crate::ChainPart::Call {
5483 args,
5484 type_args: None,
5485 optional,
5486 span: self.span(open.span.start, close.span.end),
5487 })
5488 }
5489
5490 fn parse_field_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5491 self.advance();
5492 let name_tok = if is_property_name(&self.peek().kind) {
5494 self.advance()
5495 } else {
5496 self.error_at_peek("expected field name after `.`");
5497 return None;
5498 };
5499 let name = self.ident_from_token(&name_tok);
5500 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5501 parse_arena_result(
5502 self.ast.try_push_expr(Expr {
5503 kind: ExprKind::FieldAccess {
5504 receiver,
5505 name: name.clone(),
5506 },
5507 span: self.span(receiver_span.start, name.span.end),
5508 }),
5509 &mut self.fatal,
5510 )
5511 }
5512
5513 fn parse_index_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5514 self.advance();
5515 let index = self.parse_expression()?;
5516 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5517 self.error_at_peek("expected `]`");
5518 return None;
5519 }
5520 let close = self.advance();
5521 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5522 parse_arena_result(
5523 self.ast.try_push_expr(Expr {
5524 kind: ExprKind::IndexAccess { receiver, index },
5525 span: self.span(receiver_span.start, close.span.end),
5526 }),
5527 &mut self.fatal,
5528 )
5529 }
5530
5531 fn parse_call_tail(
5532 &mut self,
5533 callee: ExprId,
5534 type_args: Option<Vec<TypeAnnotation>>,
5535 ) -> Option<ExprId> {
5536 self.advance();
5537 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5538 Vec::new()
5539 } else {
5540 self.parse_call_args()?
5541 };
5542 if !matches!(self.peek().kind, TokenKind::RightParen) {
5543 self.error_at_peek("expected `)`");
5544 return None;
5545 }
5546 let close = self.advance();
5547
5548 let callee_span = parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5549 parse_arena_result(
5550 self.ast.try_push_expr(Expr {
5551 kind: ExprKind::Call {
5552 callee,
5553 type_args,
5554 args,
5555 },
5556 span: self.span(callee_span.start, close.span.end),
5557 }),
5558 &mut self.fatal,
5559 )
5560 }
5561
5562 fn try_parse_generic_call_tail(&mut self, callee: ExprId) -> Option<ExprId> {
5564 let type_args = self.try_parse_type_args_only()?;
5565 if !matches!(self.peek().kind, TokenKind::LeftParen) {
5566 return None;
5567 }
5568 self.parse_call_tail(callee, Some(type_args))
5569 }
5570
5571 fn try_parse_type_args_only(&mut self) -> Option<Vec<TypeAnnotation>> {
5572 if !matches!(self.peek().kind, TokenKind::LessThan) {
5573 return self.invariant_failure("type argument parser expected `<`");
5574 }
5575 self.advance();
5576
5577 if matches!(self.peek().kind, TokenKind::GreaterThan) {
5578 return None;
5579 }
5580
5581 let mut type_args = Vec::new();
5582 loop {
5583 let ta = self.parse_type_annotation()?;
5584 type_args.push(ta);
5585 match self.peek().kind {
5586 TokenKind::Comma => {
5587 self.advance();
5588 if matches!(self.peek().kind, TokenKind::GreaterThan) {
5589 self.advance();
5590 break;
5591 }
5592 }
5593 TokenKind::GreaterThan => {
5594 self.advance();
5595 break;
5596 }
5597 _ => return None,
5598 }
5599 }
5600 Some(type_args)
5601 }
5602
5603 fn parse_call_args(&mut self) -> Option<Vec<ExprId>> {
5604 let mut args = Vec::new();
5605 loop {
5606 let arg = self.parse_expression()?;
5607 args.push(arg);
5608 match self.peek().kind {
5609 TokenKind::Comma => {
5610 self.advance();
5611 if matches!(self.peek().kind, TokenKind::RightParen) {
5612 return Some(args);
5613 }
5614 }
5615 TokenKind::RightParen => return Some(args),
5616 _ => {
5617 self.error_at_peek("expected `,` or `)`");
5618 return None;
5619 }
5620 }
5621 }
5622 }
5623
5624 fn parse_angle_cast(&mut self) -> Option<ExprId> {
5631 let open = self.advance();
5632 let ty = self.parse_type_annotation()?;
5633 if !matches!(self.peek().kind, TokenKind::GreaterThan) {
5634 self.error_at_peek("expected `>` to close a type assertion");
5635 return None;
5636 }
5637 self.advance();
5638 let expr = self.parse_unary()?;
5639 let end = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5640 .span
5641 .end;
5642 parse_arena_result(
5643 self.ast.try_push_expr(Expr {
5644 kind: ExprKind::As { expr, ty },
5645 span: self.span(open.span.start, end),
5646 }),
5647 &mut self.fatal,
5648 )
5649 }
5650
5651 fn parse_atom(&mut self) -> Option<ExprId> {
5652 match self.peek().kind {
5653 TokenKind::LessThan => return self.parse_angle_cast(),
5658 TokenKind::LeftParen => return self.parse_paren(),
5659 TokenKind::LeftBrace => return self.parse_object_literal(),
5662 TokenKind::LeftBracket => return self.parse_array_literal(),
5663 TokenKind::TemplateHead(_) => return self.parse_template_literal(),
5664 TokenKind::This => return self.parse_this_or_super(true),
5665 TokenKind::Super => return self.parse_this_or_super(false),
5666 TokenKind::Function => return self.parse_function_expression(),
5667 _ => {}
5668 }
5669 if !matches!(
5670 self.peek().kind,
5671 TokenKind::NumberLiteral(_)
5672 | TokenKind::BigIntLiteral(_)
5673 | TokenKind::StringLiteral(_)
5674 | TokenKind::TemplateNoSubstitution(_)
5675 | TokenKind::BooleanLiteral(_)
5676 | TokenKind::NullLiteral
5677 | TokenKind::Identifier
5678 | TokenKind::RegexLiteral { .. }
5679 ) {
5680 self.error_at_peek("expected expression");
5681 return None;
5682 }
5683 let tok = self.advance();
5684 let span = tok.span;
5685 let kind = match tok.kind {
5686 TokenKind::NumberLiteral(v) => ExprKind::Number(v),
5687 TokenKind::BigIntLiteral(s) => ExprKind::BigInt(s),
5688 TokenKind::StringLiteral(s) => ExprKind::String(s),
5689 TokenKind::TemplateNoSubstitution(s) => ExprKind::String(s),
5690 TokenKind::BooleanLiteral(b) => ExprKind::Boolean(b),
5691 TokenKind::NullLiteral => ExprKind::Null,
5692 TokenKind::Identifier => ExprKind::Identifier(Ident {
5693 name: self.source[span.start as usize..span.end as usize].to_string(),
5694 span,
5695 }),
5696 TokenKind::RegexLiteral { source, flags } => ExprKind::Regex { source, flags },
5697 _ => unreachable!("advance preserves the accepted expression token"),
5699 };
5700 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5701 }
5702
5703 fn parse_this_or_super(&mut self, is_this: bool) -> Option<ExprId> {
5706 let tok = self.advance();
5707 let span = tok.span;
5708 if !is_this && self.class_member_body_depth == 0 {
5709 self.error_at_with_help(
5710 span,
5711 "`super` is only valid inside a class method or constructor body",
5712 vec![
5713 "call `super(...)` in a subclass constructor or `super.method(...)` \
5714 in a subclass method"
5715 .to_string(),
5716 ],
5717 );
5718 }
5719 let kind = if is_this
5720 && self.class_member_body_depth == 0
5721 && self.function_expression_body_depth == 0
5722 {
5723 ExprKind::ThisOutsideReceiver
5724 } else if is_this {
5725 ExprKind::This
5726 } else {
5727 ExprKind::Super
5728 };
5729 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5730 }
5731
5732 fn parse_template_literal(&mut self) -> Option<ExprId> {
5735 let head_tok = self.advance();
5736 let TokenKind::TemplateHead(head) = head_tok.kind else {
5737 unreachable!("template dispatch requires TemplateHead");
5738 };
5739 let start = head_tok.span.start;
5740 let mut parts: Vec<String> = vec![head];
5741 let mut exprs: Vec<ExprId> = Vec::new();
5742 let mut substitution_spans: Vec<Span> = Vec::new();
5743 let mut substitution_start = head_tok.span.end.saturating_sub(2);
5746 loop {
5747 let expr = self.parse_expression()?;
5748 exprs.push(expr);
5749 let closing = self.peek().span.start;
5750 let substitution_span = self.span(substitution_start, closing.saturating_add(1));
5751 match self.peek().kind.clone() {
5752 TokenKind::TemplateMiddle(s) => {
5753 let tok = self.advance();
5754 substitution_spans.push(substitution_span);
5755 substitution_start = tok.span.end.saturating_sub(2);
5756 parts.push(s);
5757 }
5758 TokenKind::TemplateTail(s) => {
5759 let tok = self.advance();
5760 substitution_spans.push(substitution_span);
5761 parts.push(s);
5762 return parse_arena_result(
5763 self.ast.try_push_expr(Expr {
5764 kind: ExprKind::TemplateLiteral {
5765 parts,
5766 exprs,
5767 substitution_spans,
5768 },
5769 span: self.span(start, tok.span.end),
5770 }),
5771 &mut self.fatal,
5772 );
5773 }
5774 _ => {
5775 self.error_at_peek("expected `}` to close template interpolation");
5776 return None;
5777 }
5778 }
5779 }
5780 }
5781
5782 fn parse_object_literal(&mut self) -> Option<ExprId> {
5783 let open = self.advance();
5784
5785 let mut members: Vec<ObjectLiteralMember> = Vec::new();
5786 if !matches!(self.peek().kind, TokenKind::RightBrace) {
5787 loop {
5788 if matches!(self.peek().kind, TokenKind::DotDotDot) {
5789 let dots = self.advance();
5790 let value = self.parse_expression()?;
5791 let value_span =
5792 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5793 members.push(ObjectLiteralMember::Spread {
5794 value,
5795 span: self.span(dots.span.start, value_span.end),
5796 });
5797 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
5798 self.advance();
5799 let key = self.parse_expression()?;
5800 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5801 self.error_at_peek("expected `]` after computed property key");
5802 return None;
5803 }
5804 self.advance();
5805 if !matches!(self.peek().kind, TokenKind::Colon) {
5806 self.error_at_peek("expected `:` after computed property key; computed methods are not supported");
5807 return None;
5808 }
5809 self.advance();
5810 let value = self.parse_expression()?;
5811 members.push(ObjectLiteralMember::Computed { key, value });
5812 } else {
5813 let field = self.parse_object_literal_field()?;
5814 if let Some(existing) = members.iter().find_map(|m| match m {
5817 ObjectLiteralMember::Field(f) if f.name.name == field.name.name => Some(f),
5818 _ => None,
5819 }) {
5820 self.diagnostics.push(Diagnostic {
5821 severity: Severity::Error,
5822 span: field.name.span,
5823 message: format!(
5824 "duplicate field `{}` in object literal",
5825 field.name.name
5826 ),
5827 help: vec![],
5828 notes: vec![(existing.name.span, "first defined here".into())],
5829 });
5830 }
5832 members.push(ObjectLiteralMember::Field(field));
5833 }
5834 if self.at_inserted_semicolon()
5835 && matches!(self.peek_at(1).kind, TokenKind::RightBrace)
5836 {
5837 self.advance();
5838 }
5839 match self.peek().kind {
5840 TokenKind::Comma => {
5841 self.advance();
5842 if matches!(self.peek().kind, TokenKind::RightBrace) {
5843 break;
5844 }
5845 }
5846 TokenKind::RightBrace => break,
5847 _ => {
5848 self.error_at_peek("expected `,` or `}`");
5849 return None;
5850 }
5851 }
5852 }
5853 }
5854
5855 if !matches!(self.peek().kind, TokenKind::RightBrace) {
5856 self.error_at_peek("expected `}`");
5857 return None;
5858 }
5859 let close = self.advance();
5860
5861 parse_arena_result(
5862 self.ast.try_push_expr(Expr {
5863 kind: ExprKind::ObjectLiteral { members },
5864 span: self.span(open.span.start, close.span.end),
5865 }),
5866 &mut self.fatal,
5867 )
5868 }
5869
5870 fn parse_object_literal_field(&mut self) -> Option<ObjectLiteralField> {
5871 self.reject_async_member_modifier();
5872 let (name, shorthandable) = match self.peek().kind.clone() {
5873 TokenKind::Identifier => {
5874 let tok = self.advance();
5875 (self.ident_from_token(&tok), true)
5876 }
5877 kind if is_property_name(&kind) => {
5878 let tok = self.advance();
5879 (self.ident_from_token(&tok), false)
5880 }
5881 TokenKind::StringLiteral(_) => {
5882 let tok = self.advance();
5883 (self.property_ident_from_token(&tok), false)
5884 }
5885 _ => {
5886 self.error_at_peek("expected field name");
5887 return None;
5888 }
5889 };
5890
5891 if matches!(self.peek().kind, TokenKind::LeftParen) {
5897 let value = self.parse_method_shorthand_body(name.span)?;
5898 return Some(ObjectLiteralField { name, value });
5899 }
5900
5901 if !matches!(self.peek().kind, TokenKind::Colon) {
5902 if shorthandable {
5905 let value = parse_arena_result(
5906 self.ast.try_push_expr(Expr {
5907 kind: ExprKind::Identifier(name.clone()),
5908 span: name.span,
5909 }),
5910 &mut self.fatal,
5911 )?;
5912 return Some(ObjectLiteralField { name, value });
5913 }
5914 self.error_at_peek("expected `:` after field name");
5915 return None;
5916 }
5917 self.advance();
5918
5919 let value = self.parse_expression()?;
5920 Some(ObjectLiteralField { name, value })
5921 }
5922
5923 fn parse_method_shorthand_body(&mut self, name_span: Span) -> Option<ExprId> {
5926 self.advance();
5927
5928 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
5929 Vec::new()
5930 } else {
5931 self.parse_arrow_param_list()?
5932 };
5933 if !matches!(self.peek().kind, TokenKind::RightParen) {
5934 self.error_at_peek("expected `)`");
5935 return None;
5936 }
5937 self.advance();
5938
5939 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
5940 self.advance();
5941 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
5942 } else {
5943 (None, None)
5944 };
5945
5946 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
5947 self.error_at_peek("expected `{` to open the method body");
5948 return None;
5949 }
5950 let block = self.parse_outer_this_boundary(Self::parse_block)?;
5951 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
5952 .span
5953 .end;
5954
5955 parse_arena_result(
5956 self.ast.try_push_expr(Expr {
5957 kind: ExprKind::Arrow {
5958 params,
5959 return_type,
5960 type_predicate,
5961 body: ArrowBody::Block(block),
5962 },
5963 span: self.span(name_span.start, end),
5964 }),
5965 &mut self.fatal,
5966 )
5967 }
5968
5969 fn parse_array_literal(&mut self) -> Option<ExprId> {
5970 let open = self.advance();
5971
5972 let mut elements: Vec<ArrayLiteralElement> = Vec::new();
5973 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5974 loop {
5975 let element = if matches!(self.peek().kind, TokenKind::DotDotDot) {
5976 let dots = self.advance();
5977 let value = self.parse_expression()?;
5978 let value_span =
5979 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5980 ArrayLiteralElement::Spread {
5981 value,
5982 span: self.span(dots.span.start, value_span.end),
5983 }
5984 } else {
5985 ArrayLiteralElement::Value(self.parse_expression()?)
5986 };
5987 elements.push(element);
5988 match self.peek().kind {
5989 TokenKind::Comma => {
5990 self.advance();
5991 if matches!(self.peek().kind, TokenKind::RightBracket) {
5992 break;
5993 }
5994 }
5995 TokenKind::RightBracket => break,
5996 _ => {
5997 self.error_at_peek("expected `,` or `]`");
5998 return None;
5999 }
6000 }
6001 }
6002 }
6003
6004 if !matches!(self.peek().kind, TokenKind::RightBracket) {
6005 self.error_at_peek("expected `]`");
6006 return None;
6007 }
6008 let close = self.advance();
6009
6010 parse_arena_result(
6011 self.ast.try_push_expr(Expr {
6012 kind: ExprKind::ArrayLiteral { elements },
6013 span: self.span(open.span.start, close.span.end),
6014 }),
6015 &mut self.fatal,
6016 )
6017 }
6018
6019 fn parse_paren(&mut self) -> Option<ExprId> {
6020 let open = self.advance();
6021 let inner = self.parse_expression()?;
6022 if !matches!(self.peek().kind, TokenKind::RightParen) {
6023 self.error_at_peek("expected `)`");
6024 return None;
6025 }
6026 let close = self.advance();
6027 let span = self.span(open.span.start, close.span.end);
6028 parse_arena_result(
6029 self.ast.try_push_expr(Expr {
6030 kind: ExprKind::Paren(inner),
6031 span,
6032 }),
6033 &mut self.fatal,
6034 )
6035 }
6036
6037 fn with_recursion_limit<T>(&mut self, parse: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
6038 if self.fatal.is_some() {
6039 return None;
6040 }
6041 if self.recursion_limit_span.is_some() || self.recursion_depth >= MAX_PARSE_DEPTH {
6042 self.stop_at_recursion_limit();
6043 return None;
6044 }
6045 self.recursion_depth += 1;
6046 let result = parse(self);
6047 self.recursion_depth -= 1;
6048 result
6049 }
6050
6051 fn stop_at_recursion_limit(&mut self) {
6052 self.recursion_limit_span.get_or_insert(self.peek().span);
6053 self.pos = self.tokens.len().saturating_sub(1);
6055 }
6056
6057 fn recover(&mut self) {
6058 while !self.is_at_eof() {
6059 match self.peek().kind {
6060 TokenKind::Semicolon => {
6061 self.advance();
6062 return;
6063 }
6064 TokenKind::RightBrace
6065 | TokenKind::Let
6066 | TokenKind::Const
6067 | TokenKind::Function
6068 | TokenKind::Export
6069 | TokenKind::If
6070 | TokenKind::While
6071 | TokenKind::Return
6072 | TokenKind::Try
6073 | TokenKind::Throw => return,
6074 _ => {
6075 self.advance();
6076 }
6077 }
6078 }
6079 }
6080
6081 fn peek(&self) -> &Token {
6082 self.tokens.get(self.pos).unwrap_or(&self.eof)
6083 }
6084
6085 fn peek_at(&self, offset: usize) -> &Token {
6086 self.pos
6087 .checked_add(offset)
6088 .and_then(|index| self.tokens.get(index))
6089 .unwrap_or(&self.eof)
6090 }
6091
6092 fn at_inserted_semicolon(&self) -> bool {
6094 let token = self.peek();
6095 matches!(token.kind, TokenKind::Semicolon) && token.span.start == token.span.end
6096 }
6097
6098 fn advance(&mut self) -> Token {
6099 let tok = self.peek().clone();
6100 if self.pos < self.tokens.len().saturating_sub(1) {
6101 self.pos += 1;
6102 }
6103 tok
6104 }
6105
6106 fn is_at_eof(&self) -> bool {
6107 self.fatal.is_some() || matches!(self.peek().kind, TokenKind::Eof)
6108 }
6109
6110 fn line_break_before_peek(&self) -> bool {
6112 self.source_has_line_break(self.prev_token_end(), self.peek().span.start)
6113 }
6114
6115 fn line_break_after_peek(&self) -> bool {
6117 self.source_has_line_break(self.peek().span.end, self.peek_at(1).span.start)
6118 }
6119
6120 fn source_has_line_break(&self, start: u32, end: u32) -> bool {
6123 self.source
6124 .get(start as usize..end as usize)
6125 .is_some_and(|gap| gap.contains(['\n', '\r']))
6126 }
6127
6128 fn at_statement_end(&self) -> bool {
6133 matches!(self.peek().kind, TokenKind::Semicolon)
6134 || (self.last_as_type_end == Some(self.prev_token_end())
6135 && self.line_break_before_peek())
6136 }
6137
6138 fn finish_statement(&mut self) -> u32 {
6141 if matches!(self.peek().kind, TokenKind::Semicolon) {
6142 return self.advance().span.end;
6143 }
6144 self.prev_token_end()
6145 }
6146
6147 fn prev_token_end(&self) -> u32 {
6150 self.tokens
6151 .get(self.pos.saturating_sub(1))
6152 .unwrap_or(&self.eof)
6153 .span
6154 .end
6155 }
6156
6157 fn take_leading_doc(&mut self) -> Option<crate::DocComment> {
6158 let parsed = self
6159 .peek()
6160 .leading_doc
6161 .as_ref()
6162 .map(crate::parse_doc_comment)
6163 .transpose();
6164 match parsed {
6165 Ok(doc) => doc,
6166 Err(error) => {
6167 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6168 None
6169 }
6170 }
6171 }
6172
6173 fn parse_doc(&mut self, raw: &crate::RawDoc) -> Option<crate::DocComment> {
6174 match crate::parse_doc_comment(raw) {
6175 Ok(doc) => Some(doc),
6176 Err(error) => {
6177 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6178 None
6179 }
6180 }
6181 }
6182
6183 fn reject_use_strict_with_non_simple_params(&mut self) {
6187 let mut functions: Vec<(Vec<Span>, StmtId)> = Vec::new();
6188 let non_simple = |param: &ParamDecl| {
6189 (param.default.is_some() || param.pattern.is_some() || param.rest).then(|| {
6190 param
6191 .pattern
6192 .as_ref()
6193 .map_or(param.name.span, Binding::span)
6194 })
6195 };
6196 for expr in self.ast.source_expressions() {
6197 if let ExprKind::Arrow {
6198 params,
6199 body: ArrowBody::Block(body),
6200 ..
6201 } = &expr.kind
6202 {
6203 functions.push((params.iter().filter_map(non_simple).collect(), *body));
6204 }
6205 }
6206 for stmt in self.ast.source_statements() {
6207 match &stmt.kind {
6208 StmtKind::Function { params, body, .. } => {
6209 functions.push((params.iter().filter_map(non_simple).collect(), *body));
6210 }
6211 StmtKind::ClassDecl { members, .. } => {
6212 for member in members {
6213 match member {
6214 ClassMember::Method { params, body, .. }
6215 | ClassMember::Constructor { params, body, .. } => {
6216 functions
6217 .push((params.iter().filter_map(non_simple).collect(), *body));
6218 }
6219 ClassMember::Accessor { param, body, .. } => {
6220 let params = param.as_deref().and_then(non_simple);
6221 functions.push((params.into_iter().collect(), *body));
6222 }
6223 _ => {}
6224 }
6225 }
6226 }
6227 _ => {}
6228 }
6229 }
6230 for (params, body) in functions {
6231 if params.is_empty() {
6232 continue;
6233 }
6234 let Some(directive) = self.use_strict_directive(body) else {
6235 continue;
6236 };
6237 for span in params {
6238 self.error_at(
6239 span,
6240 "this parameter is not allowed with a `\"use strict\"` directive",
6241 );
6242 }
6243 self.error_at_with_help(
6244 directive,
6245 "a `\"use strict\"` directive can't be used with a parameter list that has defaults, patterns, or a rest parameter",
6246 vec!["remove the directive; Submilli code is always strict".into()],
6247 );
6248 }
6249 }
6250
6251 fn use_strict_directive(&self, body: StmtId) -> Option<Span> {
6255 let StmtKind::Block(stmts) = &self.ast.try_stmt(body).ok()?.kind else {
6256 return None;
6257 };
6258 for stmt in stmts {
6259 let stmt = self.ast.try_stmt(*stmt).ok()?;
6260 let StmtKind::Expr(expr) = stmt.kind else {
6261 return None;
6262 };
6263 let expr = self.ast.try_expr(expr).ok()?;
6264 let ExprKind::String(value) = &expr.kind else {
6265 return None;
6266 };
6267 if self.source.as_bytes().get(expr.span.start as usize) == Some(&b'`') {
6268 return None;
6269 }
6270 if value == "use strict" {
6271 return Some(stmt.span);
6272 }
6273 }
6274 None
6275 }
6276
6277 fn error_at(&mut self, span: Span, message: impl Into<String>) {
6278 self.diagnostics.push(Diagnostic {
6279 severity: Severity::Error,
6280 span,
6281 message: message.into(),
6282 help: vec![],
6283 notes: vec![],
6284 });
6285 }
6286
6287 fn reject_loose_nullish_comparison(
6290 &mut self,
6291 op: BinOp,
6292 lhs: ExprId,
6293 rhs: ExprId,
6294 ) -> Option<()> {
6295 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
6296 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
6297 let (nullish, operand, nullish_first) =
6298 match (self.nullish_operand(lhs)?, self.nullish_operand(rhs)?) {
6299 (Some(left), Some(right)) => {
6300 self.reject_nullish_against_nullish(op, lhs_span, rhs_span, [left, right]);
6301 return Some(());
6302 }
6303 (None, Some(nullish)) => (nullish, lhs, false),
6304 (Some(nullish), None) => (nullish, rhs, true),
6305 (None, None) => return Some(()),
6306 };
6307 let comparison = LooseComparison {
6308 nullish,
6309 nullish_first,
6310 negated: op == BinOp::NotEq,
6311 };
6312 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
6313 let x = self.single_line_source(operand_span).unwrap_or("x");
6314 let strict = self.strict_suggestion(&comparison, x);
6315 let either = self.either_suggestion(&comparison, operand, x)?;
6316 let literal = comparison.nullish.literal.text();
6317 let written = self
6319 .single_line_source(self.span(lhs_span.start, rhs_span.end))
6320 .map_or_else(
6321 || {
6322 let written_op = if comparison.negated { "!=" } else { "==" };
6323 comparison.ordered(x, written_op, literal)
6324 },
6325 str::to_string,
6326 );
6327 self.error_at_with_help(
6328 self.span(lhs_span.start, rhs_span.end),
6329 format!(
6330 "`{written}` does not also match `{}`: `==` compares like `===`",
6331 comparison.nullish.literal.other().text()
6332 ),
6333 vec![
6334 either,
6335 format!("to test for `{literal}` alone, write `{strict}`"),
6336 ],
6337 );
6338 Some(())
6339 }
6340
6341 fn reject_nullish_against_nullish(
6345 &mut self,
6346 op: BinOp,
6347 lhs: Span,
6348 rhs: Span,
6349 sides: [NullishOperand; 2],
6350 ) {
6351 let span = self.span(lhs.start, rhs.end);
6352 let subject = self.single_line_source(span).map_or_else(
6355 || "this comparison".to_string(),
6356 |written| format!("`{written}`"),
6357 );
6358 let value = if op == BinOp::NotEq { "false" } else { "true" };
6359 let effects: Vec<String> = sides
6360 .iter()
6361 .filter_map(|side| side.effect)
6362 .map(|effect| {
6363 self.single_line_source(effect.operand).map_or_else(
6364 || "the `void` operand's expression".to_string(),
6365 |statement| format!("`{statement};`"),
6366 )
6367 })
6368 .collect();
6369 let help = if effects.is_empty() {
6370 format!("in TypeScript this is always `{value}`; write `{value}`")
6371 } else {
6372 format!(
6373 "in TypeScript this is always `{value}`; run {} {}, then use `{value}`",
6374 effects.join(" and "),
6375 if effects.len() > 1 {
6376 "each as its own statement"
6377 } else {
6378 "as its own statement"
6379 }
6380 )
6381 };
6382 self.error_at_with_help(
6383 span,
6384 format!("{subject} compares two nullish values: `==` compares like `===`"),
6385 vec![help],
6386 );
6387 }
6388
6389 fn strict_suggestion(&self, comparison: &LooseComparison, x: &str) -> String {
6392 let eq = comparison.strict_op();
6393 match comparison.nullish.effect {
6394 Some(effect) => {
6395 let void = self.single_line_source(effect.void).unwrap_or("void expr");
6396 comparison.ordered(x, eq, void)
6397 }
6398 None => format!("{x} {eq} {}", comparison.nullish.literal.text()),
6399 }
6400 }
6401
6402 fn either_suggestion(
6406 &mut self,
6407 comparison: &LooseComparison,
6408 operand: ExprId,
6409 x: &str,
6410 ) -> Option<String> {
6411 let mut steps = Vec::new();
6412 let tested = if self.repeats_without_effect(operand)? {
6413 x
6414 } else {
6415 steps.push(format!("bind `{x}` to a `const` (`const value = {x};`)"));
6416 "value"
6417 };
6418 if let Some(effect) = comparison.nullish.effect {
6419 let statement = self.single_line_source(effect.operand).unwrap_or("expr");
6420 let step = format!("run `{statement};` as its own statement");
6421 if comparison.nullish_first {
6422 steps.insert(0, step);
6423 } else {
6424 steps.push(step);
6425 }
6426 }
6427 let eq = comparison.strict_op();
6428 let joiner = if comparison.negated { "&&" } else { "||" };
6429 let both = format!("{tested} {eq} null {joiner} {tested} {eq} undefined");
6430 Some(if steps.is_empty() {
6431 format!("to test for either, write `{both}`")
6432 } else {
6433 format!(
6434 "to test for either, first {}, then write `{both}`",
6435 steps.join(", then ")
6436 )
6437 })
6438 }
6439
6440 fn repeats_without_effect(&mut self, id: ExprId) -> Option<bool> {
6443 let unwrapped = self.unparenthesized(id)?;
6444 let expr = parse_arena_result(self.ast.try_expr(unwrapped), &mut self.fatal)?;
6445 if is_literal(&expr.kind) {
6446 return Some(true);
6447 }
6448 if self
6449 .nullish_operand(unwrapped)?
6450 .is_some_and(|nullish| nullish.effect.is_none())
6451 {
6452 return Some(true);
6453 }
6454 self.is_reference_path(id)
6455 }
6456
6457 fn nullish_operand(&mut self, id: ExprId) -> Option<Option<NullishOperand>> {
6459 let id = self.unparenthesized(id)?;
6460 let expr = parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?;
6461 let span = expr.span;
6462 Some(match &expr.kind {
6463 ExprKind::Null => Some(NullishOperand {
6464 literal: Nullish::Null,
6465 effect: None,
6466 }),
6467 ExprKind::Identifier(ident) if ident.name == "undefined" => Some(NullishOperand {
6468 literal: Nullish::Undefined,
6469 effect: None,
6470 }),
6471 ExprKind::Void { operand } => {
6472 let operand_id = self.unparenthesized(*operand)?;
6473 let operand =
6474 parse_arena_result(self.ast.try_expr(operand_id), &mut self.fatal)?.clone();
6475 let effect_free =
6477 is_literal(&operand.kind) || self.is_reference_path(operand_id)?;
6478 let effect = (!effect_free).then_some(VoidEffect {
6479 void: span,
6480 operand: operand.span,
6481 });
6482 Some(NullishOperand {
6483 literal: Nullish::Undefined,
6484 effect,
6485 })
6486 }
6487 _ => None,
6488 })
6489 }
6490
6491 fn is_reference_path(&mut self, id: ExprId) -> Option<bool> {
6494 let mut id = id;
6495 loop {
6496 match &parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6497 ExprKind::Identifier(_) | ExprKind::This => return Some(true),
6498 ExprKind::FieldAccess { receiver, .. } => id = *receiver,
6499 ExprKind::Paren(inner) => id = *inner,
6500 _ => return Some(false),
6501 }
6502 }
6503 }
6504
6505 fn unparenthesized(&mut self, id: ExprId) -> Option<ExprId> {
6506 let mut id = id;
6507 loop {
6508 match parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6509 ExprKind::Paren(inner) => id = inner,
6510 _ => return Some(id),
6511 }
6512 }
6513 }
6514
6515 fn single_line_source(&self, span: Span) -> Option<&'a str> {
6516 self.source
6517 .get(span.start as usize..span.end as usize)
6518 .filter(|text| !text.contains('\n'))
6519 }
6520
6521 fn error_at_with_help(&mut self, span: Span, message: impl Into<String>, help: Vec<String>) {
6522 self.diagnostics.push(Diagnostic {
6523 severity: Severity::Error,
6524 span,
6525 message: message.into(),
6526 help,
6527 notes: vec![],
6528 });
6529 }
6530
6531 fn invariant_failure<T>(&mut self, message: &str) -> Option<T> {
6532 self.fatal.get_or_insert_with(|| CompilerFailure::Internal {
6533 stage: CompilerStage::Parse,
6534 span: None,
6535 message: message.into(),
6536 });
6537 self.pos = self.tokens.len();
6538 None
6539 }
6540
6541 fn error_at_peek(&mut self, message: impl Into<String>) {
6542 let span = self.peek().span;
6543 self.error_at(span, message);
6544 }
6545
6546 fn error_at_peek_with_help(&mut self, message: impl Into<String>, help: Vec<String>) {
6547 let span = self.peek().span;
6548 self.error_at_with_help(span, message, help);
6549 }
6550
6551 fn error_count(&self) -> usize {
6552 self.diagnostics
6553 .iter()
6554 .filter(|d| d.severity == Severity::Error)
6555 .count()
6556 }
6557}
6558
6559pub(crate) fn is_property_name(kind: &TokenKind) -> bool {
6560 matches!(
6561 kind,
6562 TokenKind::Identifier
6563 | TokenKind::BooleanLiteral(_)
6564 | TokenKind::NullLiteral
6565 | TokenKind::Let
6566 | TokenKind::Const
6567 | TokenKind::Function
6568 | TokenKind::If
6569 | TokenKind::Else
6570 | TokenKind::While
6571 | TokenKind::Do
6572 | TokenKind::For
6573 | TokenKind::Break
6574 | TokenKind::Continue
6575 | TokenKind::Return
6576 | TokenKind::Switch
6577 | TokenKind::Case
6578 | TokenKind::Default
6579 | TokenKind::Void
6580 | TokenKind::Interface
6581 | TokenKind::Enum
6582 | TokenKind::In
6583 | TokenKind::Typeof
6584 | TokenKind::Import
6585 | TokenKind::Export
6586 | TokenKind::New
6587 | TokenKind::Try
6588 | TokenKind::Catch
6589 | TokenKind::Finally
6590 | TokenKind::Throw
6591 | TokenKind::Class
6592 | TokenKind::Extends
6593 | TokenKind::Implements
6594 | TokenKind::Super
6595 | TokenKind::This
6596 )
6597}
6598
6599fn is_reserved_identifier_word(kind: &TokenKind) -> bool {
6600 matches!(
6601 kind,
6602 TokenKind::BooleanLiteral(_)
6603 | TokenKind::NullLiteral
6604 | TokenKind::Let
6605 | TokenKind::Const
6606 | TokenKind::Function
6607 | TokenKind::If
6608 | TokenKind::Else
6609 | TokenKind::While
6610 | TokenKind::Do
6611 | TokenKind::For
6612 | TokenKind::Break
6613 | TokenKind::Continue
6614 | TokenKind::Return
6615 | TokenKind::Switch
6616 | TokenKind::Case
6617 | TokenKind::Default
6618 | TokenKind::Export
6619 | TokenKind::Void
6620 | TokenKind::Interface
6621 | TokenKind::Enum
6622 | TokenKind::In
6623 | TokenKind::Typeof
6624 | TokenKind::Import
6625 | TokenKind::New
6626 | TokenKind::Try
6627 | TokenKind::Catch
6628 | TokenKind::Finally
6629 | TokenKind::Throw
6630 | TokenKind::Class
6631 | TokenKind::Extends
6632 | TokenKind::Implements
6633 | TokenKind::Super
6634 | TokenKind::This
6635 )
6636}
6637
6638#[derive(Clone, Copy, PartialEq, Eq)]
6642enum SpecifierList {
6643 Import,
6644 Export,
6645}
6646
6647impl SpecifierList {
6648 fn noun(self) -> &'static str {
6649 match self {
6650 SpecifierList::Import => "import",
6651 SpecifierList::Export => "export",
6652 }
6653 }
6654}
6655
6656const STRICT_MODE_RESERVED_WORDS: [&str; 6] = [
6660 "package",
6661 "private",
6662 "protected",
6663 "public",
6664 "static",
6665 "yield",
6666];
6667
6668fn reserved_keyword_rename_example(keyword: &str) -> &'static str {
6669 match keyword {
6670 "default" => "defaultValue",
6671 _ => "valueName",
6672 }
6673}
6674
6675fn mixed_logical(prev: BinOp, next: BinOp) -> bool {
6676 let prev_logical = matches!(prev, BinOp::Or | BinOp::And);
6677 let next_logical = matches!(next, BinOp::Or | BinOp::And);
6678 let prev_nullish = matches!(prev, BinOp::NullishCoalesce);
6679 let next_nullish = matches!(next, BinOp::NullishCoalesce);
6680 (prev_logical && next_nullish) || (prev_nullish && next_logical)
6681}
6682
6683fn mixed_logical_help(prev: BinOp, next: BinOp) -> String {
6686 let symbol = |op: BinOp| if op == BinOp::And { "&&" } else { "||" };
6687 let (first, second) = if prev == BinOp::NullishCoalesce {
6688 ("??", symbol(next))
6689 } else {
6690 (symbol(prev), "??")
6691 };
6692 format!(
6693 "add parentheses to choose the grouping: `(a {first} b) {second} c` or `a {first} (b {second} c)`"
6694 )
6695}
6696
6697const LOGICAL_AND_PREC: u8 = 2;
6698
6699#[derive(Clone, Copy)]
6701struct NullishOperand {
6702 literal: Nullish,
6703 effect: Option<VoidEffect>,
6705}
6706
6707#[derive(Clone, Copy, PartialEq, Eq)]
6708enum Nullish {
6709 Null,
6710 Undefined,
6711}
6712
6713impl Nullish {
6714 fn text(self) -> &'static str {
6715 match self {
6716 Nullish::Null => "null",
6717 Nullish::Undefined => "undefined",
6718 }
6719 }
6720
6721 fn other(self) -> Self {
6723 match self {
6724 Nullish::Null => Nullish::Undefined,
6725 Nullish::Undefined => Nullish::Null,
6726 }
6727 }
6728}
6729
6730struct LooseComparison {
6732 nullish: NullishOperand,
6733 nullish_first: bool,
6735 negated: bool,
6737}
6738
6739impl LooseComparison {
6740 fn strict_op(&self) -> &'static str {
6741 if self.negated { "!==" } else { "===" }
6742 }
6743
6744 fn ordered(&self, x: &str, op: &str, nullish: &str) -> String {
6746 if self.nullish_first {
6747 format!("{nullish} {op} {x}")
6748 } else {
6749 format!("{x} {op} {nullish}")
6750 }
6751 }
6752}
6753
6754#[derive(Clone, Copy)]
6755struct VoidEffect {
6756 void: Span,
6758 operand: Span,
6760}
6761
6762fn is_literal(kind: &ExprKind) -> bool {
6763 matches!(
6764 kind,
6765 ExprKind::Number(_)
6766 | ExprKind::BigInt(_)
6767 | ExprKind::String(_)
6768 | ExprKind::Boolean(_)
6769 | ExprKind::Null
6770 )
6771}
6772
6773struct ParamOrder<'p> {
6775 optional: bool,
6776 required: bool,
6778 name: Option<&'p str>,
6780 span: Span,
6781}
6782
6783impl<'p> ParamOrder<'p> {
6784 fn of_param(param: &'p ParamDecl) -> Self {
6785 let (name, span) = match ¶m.pattern {
6786 Some(binding) => (None, binding.span()),
6787 None => (Some(param.name.name.as_str()), param.name.span),
6788 };
6789 Self {
6790 optional: param.optional,
6791 required: !param.is_omittable() && !param.rest,
6792 name,
6793 span,
6794 }
6795 }
6796
6797 fn of_field(field: &'p TypeAnnotationField) -> Self {
6798 Self {
6799 optional: field.optional,
6800 required: !field.optional && !field.rest,
6801 name: Some(field.name.name.as_str()),
6802 span: field.name.span,
6803 }
6804 }
6805}
6806
6807struct BinaryToken {
6809 op: BinOp,
6810 precedence: u8,
6811 tokens: usize,
6812}
6813
6814struct ShiftToken {
6816 op: BinOp,
6817 tokens: usize,
6818 assignment: bool,
6820}
6821
6822impl ShiftToken {
6823 fn new(op: BinOp, tokens: usize, assignment: bool) -> Self {
6824 Self {
6825 op,
6826 tokens,
6827 assignment,
6828 }
6829 }
6830}
6831
6832const SHIFT_PRECEDENCE: u8 = 8;
6834
6835#[derive(Clone, Copy, PartialEq)]
6836enum TupleElementLabel {
6837 None,
6838 Required,
6840 Optional,
6842}
6843
6844fn peek_binop(kind: &TokenKind) -> Option<(BinOp, u8)> {
6845 Some(match kind {
6846 TokenKind::QuestionQuestion => (BinOp::NullishCoalesce, 0),
6847 TokenKind::PipePipe => (BinOp::Or, 1),
6848 TokenKind::AmpAmp => (BinOp::And, LOGICAL_AND_PREC),
6849 TokenKind::Pipe => (BinOp::BitOr, 3),
6850 TokenKind::Caret => (BinOp::BitXor, 4),
6851 TokenKind::Amp => (BinOp::BitAnd, 5),
6852 TokenKind::EqEqEq | TokenKind::EqEq => (BinOp::Eq, 6),
6853 TokenKind::BangEqEq | TokenKind::BangEq => (BinOp::NotEq, 6),
6854 TokenKind::LessThan => (BinOp::Lt, 7),
6855 TokenKind::GreaterThan => (BinOp::Gt, 7),
6856 TokenKind::LessEquals => (BinOp::Le, 7),
6857 TokenKind::GreaterEquals => (BinOp::Ge, 7),
6858 TokenKind::In => (BinOp::In, 7),
6859 TokenKind::Plus => (BinOp::Add, 9),
6862 TokenKind::Minus => (BinOp::Sub, 9),
6863 TokenKind::Star => (BinOp::Mul, 10),
6864 TokenKind::Slash => (BinOp::Div, 10),
6865 TokenKind::Percent => (BinOp::Rem, 10),
6866 TokenKind::StarStar => (BinOp::Pow, 11),
6868 _ => return None,
6869 })
6870}
6871
6872fn cannot_start_type(kind: &TokenKind) -> bool {
6876 matches!(
6877 kind,
6878 TokenKind::Dot
6879 | TokenKind::QuestionDot
6880 | TokenKind::LeftBracket
6881 | TokenKind::Bang
6882 | TokenKind::Semicolon
6883 | TokenKind::RightParen
6884 | TokenKind::RightBrace
6885 | TokenKind::Comma
6886 | TokenKind::Eof
6887 )
6888}
6889
6890fn right_operand_min_prec(op: BinOp, prec: u8) -> u8 {
6894 match op {
6895 BinOp::Pow => prec,
6896 BinOp::NullishCoalesce => LOGICAL_AND_PREC + 1,
6897 _ => prec + 1,
6898 }
6899}
6900
6901fn compound_op_for_token(kind: &TokenKind) -> Option<BinOp> {
6902 Some(match kind {
6903 TokenKind::PlusEquals => BinOp::Add,
6904 TokenKind::MinusEquals => BinOp::Sub,
6905 TokenKind::StarEquals => BinOp::Mul,
6906 TokenKind::SlashEquals => BinOp::Div,
6907 TokenKind::PercentEquals => BinOp::Rem,
6908 TokenKind::StarStarEquals => BinOp::Pow,
6909 TokenKind::AmpEquals => BinOp::BitAnd,
6910 TokenKind::PipeEquals => BinOp::BitOr,
6911 TokenKind::CaretEquals => BinOp::BitXor,
6912 _ => return None,
6913 })
6914}
6915
6916fn is_assign_lookahead(kind: &TokenKind) -> bool {
6917 matches!(kind, TokenKind::Equals) || compound_op_for_token(kind).is_some()
6918}
6919
6920fn parse_arena_result<T>(
6923 result: Result<T, crate::arena::ArenaError>,
6924 fatal: &mut Option<CompilerFailure>,
6925) -> Option<T> {
6926 match result {
6927 Ok(value) => Some(value),
6928 Err(error) => {
6929 fatal.get_or_insert_with(|| error.into_compiler_failure(CompilerStage::Parse));
6930 None
6931 }
6932 }
6933}
6934
6935fn is_rest_array_annotation(ty: &crate::TypeAnnotation) -> bool {
6937 use crate::ast::TypeAnnotationKind;
6938 match &ty.kind {
6939 TypeAnnotationKind::Array(_) => true,
6940 TypeAnnotationKind::Readonly(inner) => matches!(inner.kind, TypeAnnotationKind::Array(_)),
6941 _ => false,
6942 }
6943}
6944
6945#[cfg(test)]
6946mod tests {
6947 use super::{MAX_ERRORS, MAX_PARSE_DEPTH, Parser, parse};
6948 use crate::source::Sources;
6949 use crate::{Asi, Ast, ExprKind, FileId, ImportKind, StmtKind, Token, TokenKind, diagnostics};
6950
6951 const F: FileId = FileId(0);
6952
6953 #[test]
6954 fn malformed_direct_token_streams_return_typed_failures() {
6955 use crate::compiler_error::CompilerFailure;
6956 for tokens in [
6957 Vec::new(),
6958 vec![Token::new(
6959 TokenKind::Identifier,
6960 crate::Span::new(F, 0, 2).unwrap(),
6961 )],
6962 vec![
6963 Token::new(
6964 TokenKind::Identifier,
6965 crate::Span {
6966 file: F,
6967 start: 1,
6968 end: 2,
6969 },
6970 ),
6971 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
6972 ],
6973 vec![
6974 Token::new(TokenKind::Eof, crate::Span::at(F)),
6975 Token::new(TokenKind::Eof, crate::Span::at(F)),
6976 ],
6977 vec![
6978 Token::new(
6979 TokenKind::Identifier,
6980 crate::Span {
6981 file: F,
6982 start: 2,
6983 end: 0,
6984 },
6985 ),
6986 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
6987 ],
6988 vec![Token::new(TokenKind::Eof, crate::Span::at(FileId(9)))],
6989 ] {
6990 let error = super::parse_checked("é", tokens, F).expect_err("invalid token metadata");
6991 assert!(matches!(
6992 error.fatal,
6993 Some(CompilerFailure::Internal { .. })
6994 ));
6995 }
6996 }
6997
6998 #[test]
6999 fn malformed_documentation_metadata_returns_a_fatal_error() {
7000 let source = "/** hello */ function main(): void {}";
7001 let valid = tokens_of(source);
7002 for doc in [
7003 crate::RawDoc {
7004 text: "/** hi */".into(),
7005 span: crate::Span {
7006 file: F,
7007 start: u32::MAX,
7008 end: u32::MAX,
7009 },
7010 },
7011 crate::RawDoc {
7012 text: "/** different */".into(),
7013 span: crate::Span::new(F, 0, 12).unwrap(),
7014 },
7015 crate::RawDoc {
7016 text: "/** hello */".into(),
7017 span: crate::Span::new(FileId(9), 0, 12).unwrap(),
7018 },
7019 ] {
7020 let mut tokens = valid.clone();
7021 tokens[0].leading_doc = Some(doc);
7022 let error = super::parse_checked(source, tokens, F).expect_err("invalid doc metadata");
7023 assert!(error.to_string().contains("documentation metadata"));
7024 }
7025 assert!(super::parse_checked(source, valid, F).is_ok());
7026 }
7027
7028 #[test]
7029 fn invalid_parser_dispatch_survives_diagnostic_rollback() {
7030 let mut parser = parser_from_source("let x = 1;");
7031 assert!(parser.try_parse_type_args_only().is_none());
7032 parser.diagnostics.clear();
7033 assert!(parser.fatal.is_some());
7034 assert!(parser.parse_expression().is_none());
7035 }
7036
7037 #[test]
7038 fn invalid_arena_read_stops_parser_and_survives_diagnostic_rollback() {
7039 use crate::{
7040 ExprId,
7041 compiler_error::{CompilerFailure, CompilerStage},
7042 };
7043 let mut parser = parser_from_source("let x = 1;");
7044 assert!(parser.statement_kind_for(ExprId(u32::MAX)).is_none());
7045 assert!(matches!(
7046 parser.fatal,
7047 Some(CompilerFailure::Internal {
7048 stage: CompilerStage::Parse,
7049 span: None,
7050 ..
7051 })
7052 ));
7053 parser.diagnostics.clear();
7054 let position = parser.pos;
7055 assert!(parser.parse_statement().is_none());
7056 parser.recover();
7057 assert_eq!(parser.pos, position);
7058 assert!(parser.ast.expr_ids().unwrap().next().is_none());
7059 }
7060
7061 fn tokens_of(source: &str) -> Vec<Token> {
7062 let mut asi = Asi::new(source, crate::FileId(0));
7063 let mut tokens = Vec::new();
7064 loop {
7065 let tok = asi.next_token();
7066 let is_eof = matches!(tok.kind, TokenKind::Eof);
7067 tokens.push(tok);
7068 if is_eof {
7069 break;
7070 }
7071 }
7072 let diags = asi.into_diagnostics();
7073 assert!(diags.is_empty(), "unexpected lexer diagnostics: {diags:?}");
7074 tokens
7075 }
7076
7077 fn parse_str(source: &str) -> (Ast, Vec<crate::Diagnostic>) {
7078 parse(source, tokens_of(source), crate::FileId(0))
7079 }
7080
7081 fn parser_from_source(source: &str) -> Parser<'_> {
7082 Parser {
7083 source,
7084 tokens: tokens_of(source),
7085 file: F,
7086 pos: 0,
7087 ast: Ast::new(),
7088 diagnostics: Vec::new(),
7089 block_depth: 0,
7090 class_member_body_depth: 0,
7091 function_expression_body_depth: 0,
7092 recursion_depth: 0,
7093 recursion_limit_span: None,
7094 resume_at_next_member: false,
7095 last_as_type_end: None,
7096 eof: Token::new(
7097 TokenKind::Eof,
7098 crate::Span::new(F, source.len() as u32, source.len() as u32).unwrap(),
7099 ),
7100 fatal: None,
7101 }
7102 }
7103
7104 #[test]
7105 fn recursion_budget_accepts_boundary_and_restores_depth() {
7106 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH - 1));
7107 let mut parser = parser_from_source(&source);
7108 assert!(parser.parse_unary().is_some());
7109 assert_eq!(parser.recursion_depth, 0);
7110 assert!(parser.recursion_limit_span.is_none());
7111
7112 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH));
7113 let mut parser = parser_from_source(&source);
7114 assert!(parser.parse_unary().is_none());
7115 assert_eq!(parser.recursion_depth, 0);
7116 assert!(parser.recursion_limit_span.is_some());
7117 }
7118
7119 #[test]
7120 fn parse_empty_input() {
7121 let (ast, diags) = parse_str("");
7122 assert!(diags.is_empty());
7123 assert!(ast.top_level.is_empty());
7124 }
7125
7126 #[test]
7127 fn any_type_is_rejected() {
7128 for src in [
7131 "function main(): void { const x: any = 1; }",
7132 "function main(): void { const x = 1 as any; }",
7133 "function main(): void { const x: any[] = []; }",
7134 ] {
7135 let (_ast, diags) = parse_str(src);
7136 assert!(
7137 diags
7138 .iter()
7139 .any(|d| d.message.contains("`any` is not supported")),
7140 "expected an `any` rejection for {src:?}, got: {diags:?}",
7141 );
7142 assert!(
7143 diags
7144 .iter()
7145 .any(|d| d.help.iter().any(|h| h.contains("`unknown`"))),
7146 "expected the fix-shape help for {src:?}, got: {diags:?}",
7147 );
7148 }
7149 }
7150
7151 #[test]
7152 fn any_catch_binding_help_suggests_catch_forms() {
7153 let (_ast, diags) = parse_str("function main(): void { try { } catch (e: any) { } }");
7156 let any_diags: Vec<_> = diags
7157 .iter()
7158 .filter(|d| d.message.contains("`any` is not supported"))
7159 .collect();
7160 assert_eq!(any_diags.len(), 1, "got: {diags:?}");
7161 assert!(
7162 any_diags[0].help.iter().any(|h| h.contains("`catch (e)`")),
7163 "got: {diags:?}"
7164 );
7165 assert!(
7166 !any_diags[0].help.iter().any(|h| h.contains("`unknown`")),
7167 "got: {diags:?}"
7168 );
7169 }
7170
7171 #[test]
7172 fn arrow_return_type_scanner_matches_type_grammar() {
7173 for ty in [
7178 "number",
7179 "void",
7180 "null",
7181 "ns.Foo",
7182 "Map<string, number>",
7183 "ns.Foo<string>[]",
7184 "number[]",
7185 "[number, number]",
7186 "[number, [string, boolean]]",
7187 "[number, string][]",
7188 "{ a: number }",
7189 "{ a: number }[]",
7190 "\"on\" | \"off\"",
7191 "1 | 2",
7192 "Point | null",
7193 "| number | string",
7194 "(n: number) => number",
7195 "(n: number) => [number, number]",
7196 "readonly number[]",
7197 "readonly number[][]",
7198 "readonly [number, string]",
7199 "readonly number[] | null",
7200 "[first: number, second: string]",
7201 "[new: number, string]",
7202 "keyof { a: number }",
7203 ] {
7204 let annotation = format!("function f(): void {{ const a: {ty} = x; }}");
7205 let (_ast, diags) = parse_str(&annotation);
7206 assert!(
7207 diags.is_empty(),
7208 "annotation grammar rejected {ty:?}: {diags:?}",
7209 );
7210
7211 let arrow = format!("function f(): void {{ const g = (): {ty} => x; }}");
7212 let (_ast, diags) = parse_str(&arrow);
7213 assert!(
7214 diags.is_empty(),
7215 "arrow lookahead rejected return type {ty:?}: {diags:?}",
7216 );
7217 }
7218
7219 let (_ast, diags) =
7221 parse_str("function f(): void { const g = (v: unknown): v is string => x; }");
7222 assert!(
7223 diags.is_empty(),
7224 "arrow lookahead rejected predicate return: {diags:?}",
7225 );
7226 }
7227
7228 #[test]
7229 fn bare_semicolons_are_silent_empty_statements() {
7230 let (ast, diags) = parse_str(";");
7231 assert!(diags.is_empty());
7232 assert!(ast.top_level.is_empty());
7233 }
7234
7235 #[test]
7236 fn parse_single_non_expression_token_errors() {
7237 let (ast, diags) = parse_str("}");
7238 assert_eq!(diags.len(), 1);
7239 assert_eq!(diags[0].message, "expected expression");
7240 assert!(ast.top_level.is_empty());
7241 }
7242
7243 #[test]
7244 fn recovery_across_many_bad_starts() {
7245 let (ast, diags) = parse_str("} } } x;");
7246 assert_eq!(diags.len(), 3);
7247 for d in &diags {
7248 assert_eq!(d.message, "expected expression");
7249 }
7250 assert_eq!(ast.top_level.len(), 1);
7251 }
7252
7253 #[test]
7254 fn recovery_consumes_semicolon() {
7255 let mut p = parser_from_source("foo ;");
7256 p.error_at_peek("test");
7257 p.recover();
7258 assert!(matches!(
7259 p.peek().kind,
7260 TokenKind::Semicolon | TokenKind::Eof
7261 ));
7262 assert!(matches!(p.peek().kind, TokenKind::Eof));
7263 }
7264
7265 #[test]
7266 fn recovery_stops_at_right_brace() {
7267 let mut p = parser_from_source("foo }");
7268 p.error_at_peek("test");
7269 p.recover();
7270 assert!(matches!(p.peek().kind, TokenKind::RightBrace));
7271 }
7272
7273 #[test]
7274 fn recovery_stops_at_let_keyword() {
7275 let mut p = parser_from_source("foo let x");
7276 p.error_at_peek("test");
7277 p.recover();
7278 assert!(matches!(p.peek().kind, TokenKind::Let));
7279 }
7280
7281 #[test]
7282 fn recovery_stops_at_function_keyword() {
7283 let mut p = parser_from_source("foo function bar");
7284 p.error_at_peek("test");
7285 p.recover();
7286 assert!(matches!(p.peek().kind, TokenKind::Function));
7287 }
7288
7289 #[test]
7290 fn error_cap_at_20() {
7291 let source = "} ".repeat(25);
7292 let (_, diags) = parse_str(&source);
7293 assert_eq!(diags.len(), MAX_ERRORS);
7294 }
7295
7296 #[test]
7297 fn driver_makes_progress_past_unmatched_brace() {
7298 let (ast, diags) = parse_str("} x;");
7299 assert_eq!(diags.len(), 1);
7300 assert_eq!(diags[0].message, "expected expression");
7301 assert_eq!(ast.top_level.len(), 1);
7302 }
7303
7304 #[test]
7305 fn snapshot_multi_error_render() {
7306 let source = "foo bar;\nbaz qux;\nhello world;";
7307 let (_, diags) = parse_str(source);
7308 let (sources, _) = Sources::single("script.subm", source).unwrap();
7309 let rendered: String = diags
7310 .iter()
7311 .map(|d| diagnostics::render(d, &sources))
7312 .collect::<Vec<_>>()
7313 .join("\n");
7314 insta::assert_snapshot!(rendered);
7315 }
7316
7317 fn single_stmt(ast: &Ast) -> &crate::Stmt {
7318 assert_eq!(ast.top_level.len(), 1);
7319 ast.try_stmt(ast.top_level[0]).unwrap()
7320 }
7321
7322 fn expr_of_single_stmt(ast: &Ast) -> &crate::Expr {
7323 let stmt = single_stmt(ast);
7324 match stmt.kind {
7325 crate::StmtKind::Expr(id) => ast.try_expr(id).unwrap(),
7326 _ => panic!("expected expression statement, got {:?}", stmt.kind),
7327 }
7328 }
7329
7330 #[test]
7331 fn parse_number_literal() {
7332 let (ast, diags) = parse_str("42;");
7333 assert!(diags.is_empty());
7334 let expr = expr_of_single_stmt(&ast);
7335 assert_eq!(expr.kind, crate::ExprKind::Number(42.0));
7336 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
7337 }
7338
7339 #[test]
7340 fn parse_string_literal() {
7341 let (ast, diags) = parse_str(r#""s";"#);
7342 assert!(diags.is_empty());
7343 let expr = expr_of_single_stmt(&ast);
7344 assert_eq!(expr.kind, crate::ExprKind::String("s".to_string()));
7345 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7346 }
7347
7348 #[test]
7349 fn parse_boolean_true() {
7350 let (ast, diags) = parse_str("true;");
7351 assert!(diags.is_empty());
7352 let expr = expr_of_single_stmt(&ast);
7353 assert_eq!(expr.kind, crate::ExprKind::Boolean(true));
7354 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7355 }
7356
7357 #[test]
7358 fn parse_boolean_false() {
7359 let (ast, diags) = parse_str("false;");
7360 assert!(diags.is_empty());
7361 let expr = expr_of_single_stmt(&ast);
7362 assert_eq!(expr.kind, crate::ExprKind::Boolean(false));
7363 assert_eq!(expr.span, crate::Span::new(F, 0, 5).unwrap());
7364 }
7365
7366 #[test]
7367 fn parse_null_literal() {
7368 let (ast, diags) = parse_str("null;");
7369 assert!(diags.is_empty());
7370 let expr = expr_of_single_stmt(&ast);
7371 assert_eq!(expr.kind, crate::ExprKind::Null);
7372 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7373 }
7374
7375 #[test]
7376 fn parse_identifier() {
7377 let (ast, diags) = parse_str("x;");
7378 assert!(diags.is_empty());
7379 let expr = expr_of_single_stmt(&ast);
7380 assert!(matches!(expr.kind, crate::ExprKind::Identifier(_)));
7381 assert_eq!(expr.span, crate::Span::new(F, 0, 1).unwrap());
7382 }
7383
7384 #[test]
7385 fn reserved_keywords_remain_invalid_declaration_names() {
7386 let (_ast, diags) = parse_str("function default(): void {}");
7387 assert_eq!(diags.len(), 1, "{diags:#?}");
7388 assert_eq!(
7389 diags[0].message,
7390 "`default` is a reserved keyword and can't be used as a name"
7391 );
7392 assert!(
7393 diags[0]
7394 .help
7395 .iter()
7396 .any(|h| h == "rename it, for example: `defaultValue`"),
7397 "expected rename help, got: {diags:?}"
7398 );
7399 }
7400
7401 #[test]
7402 fn eval_and_arguments_cannot_name_bindings() {
7403 let bindings = [
7404 ("let arguments = 1;", "arguments"),
7405 ("const [eval] = [1];", "eval"),
7406 ("const { a: eval } = { a: 1 };", "eval"),
7407 ("const { eval } = { eval: 1 };", "eval"),
7408 ("const [...arguments] = [1];", "arguments"),
7409 ("function eval(): void {}", "eval"),
7410 ("function f(arguments: number): void {}", "arguments"),
7411 ("const f = arguments => arguments;", "arguments"),
7412 ("const f = (...eval: number[]) => 1;", "eval"),
7413 ("const f = function eval(): void {};", "eval"),
7414 ("for (const eval of [1]) {}", "eval"),
7415 ("try {} catch (arguments) {}", "arguments"),
7416 ("type F = (eval: number) => void;", "eval"),
7417 ("type G = (...arguments: number[]) => void;", "arguments"),
7418 ("type H = { [eval: string]: number };", "eval"),
7419 ];
7420 for (source, word) in bindings {
7421 let (_ast, diags) = parse_str(source);
7422 assert!(
7423 diags
7424 .iter()
7425 .any(|d| d.message == format!("`{word}` can't name a binding in strict mode")),
7426 "{source}: {diags:#?}"
7427 );
7428 }
7429 }
7430
7431 #[test]
7432 fn eval_and_arguments_still_name_types_and_properties() {
7433 for source in [
7434 "class eval {}",
7435 "interface arguments { eval: number }",
7436 "type eval = number;",
7437 "enum arguments { eval }",
7438 "const o = { eval: 1, arguments: 2 };",
7439 "import { eval } from \"./m\";",
7440 ] {
7441 let (_ast, diags) = parse_str(source);
7442 assert!(diags.is_empty(), "{source}: {diags:#?}");
7443 }
7444 }
7445
7446 #[test]
7447 fn reserved_keywords_remain_invalid_parameter_names() {
7448 let (_ast, diags) = parse_str("function f(default: string): void {}");
7449 assert_eq!(diags.len(), 1, "{diags:#?}");
7450 assert_eq!(
7451 diags[0].message,
7452 "`default` is a reserved keyword and can't be used as a name"
7453 );
7454 assert!(
7455 diags[0]
7456 .help
7457 .iter()
7458 .any(|h| h == "rename it, for example: `defaultValue`"),
7459 "expected rename help, got: {diags:?}"
7460 );
7461 }
7462
7463 #[test]
7464 fn contextual_keywords_are_valid_names() {
7465 for src in [
7466 "const type = \"x\";",
7467 "const from = 1;",
7468 "let of = 2;",
7469 "const as = 3;",
7470 "const is = 4;",
7471 "function f(from: string): void {}",
7472 ] {
7473 let (_ast, diags) = parse_str(src);
7474 assert!(
7475 diags.is_empty(),
7476 "unexpected diagnostics for {src:?}: {diags:#?}"
7477 );
7478 }
7479 }
7480
7481 #[test]
7482 fn type_alias_dispatch_table() {
7483 let (ast, diags) = parse_str("type X = number;");
7484 assert!(diags.is_empty(), "{diags:#?}");
7485 assert!(matches!(
7486 ast.try_stmt(ast.top_level[0]).unwrap().kind,
7487 crate::StmtKind::TypeAliasDecl { .. }
7488 ));
7489
7490 for src in ["type = 5;", "type;", "type(1);", "type < 3;", "type.foo;"] {
7492 let (ast, diags) = parse_str(src);
7493 assert!(
7494 diags.is_empty(),
7495 "unexpected diagnostics for {src:?}: {diags:#?}"
7496 );
7497 assert!(
7498 !matches!(
7499 ast.try_stmt(ast.top_level[0]).unwrap().kind,
7500 crate::StmtKind::TypeAliasDecl { .. }
7501 ),
7502 "{src:?} must not parse as a type alias"
7503 );
7504 }
7505
7506 let (_ast, diags) = parse_str("type X;");
7507 assert!(
7508 diags
7509 .iter()
7510 .any(|d| d.message == "expected `=` after type alias name"),
7511 "bare `type X` should commit to the alias diagnostic, got: {diags:#?}"
7512 );
7513 }
7514
7515 #[test]
7516 fn export_type_alias_dispatch() {
7517 let (ast, diags) = parse_str("export type X = number;");
7518 assert!(diags.is_empty(), "{diags:#?}");
7519 assert_eq!(ast.exported_decls.len(), 1);
7520 assert!(matches!(
7521 ast.try_stmt(ast.exported_decls[0].stmt).unwrap().kind,
7522 crate::StmtKind::TypeAliasDecl { .. }
7523 ));
7524
7525 let (_ast, diags) = parse_str("export type = 5;");
7526 assert!(
7527 diags
7528 .iter()
7529 .any(|d| d.message == "expected a declaration after `export`"),
7530 "`export type = 5` must not become an exported assignment, got: {diags:#?}"
7531 );
7532 }
7533
7534 #[test]
7535 fn parse_paren_simple() {
7536 let (ast, diags) = parse_str("(x);");
7537 assert!(diags.is_empty());
7538 let expr = expr_of_single_stmt(&ast);
7539 let crate::ExprKind::Paren(inner_id) = expr.kind else {
7540 panic!("expected Paren, got {:?}", expr.kind);
7541 };
7542 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7543 let inner = ast.try_expr(inner_id).unwrap();
7544 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7545 assert_eq!(inner.span, crate::Span::new(F, 1, 2).unwrap());
7546 }
7547
7548 #[test]
7549 fn parse_paren_nested() {
7550 let (ast, diags) = parse_str("((x));");
7551 assert!(diags.is_empty());
7552 let outer = expr_of_single_stmt(&ast);
7553 let crate::ExprKind::Paren(mid_id) = outer.kind else {
7554 panic!("expected outer Paren");
7555 };
7556 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7557 let mid = ast.try_expr(mid_id).unwrap();
7558 let crate::ExprKind::Paren(inner_id) = mid.kind else {
7559 panic!("expected inner Paren");
7560 };
7561 assert_eq!(mid.span, crate::Span::new(F, 1, 4).unwrap());
7562 let inner = ast.try_expr(inner_id).unwrap();
7563 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7564 assert_eq!(inner.span, crate::Span::new(F, 2, 3).unwrap());
7565 }
7566
7567 #[test]
7568 fn parse_sequence_of_atoms() {
7569 let (ast, diags) = parse_str(r#"42; "hi"; true; null; x; (x);"#);
7570 assert!(diags.is_empty());
7571 assert_eq!(ast.top_level.len(), 6);
7572 insta::assert_debug_snapshot!(ast);
7573 }
7574
7575 #[test]
7576 fn missing_semicolon_after_expression() {
7577 let (ast, diags) = parse_str("42 43");
7578 assert_eq!(diags.len(), 1);
7579 assert_eq!(diags[0].message, "expected `;` after expression");
7580 assert!(ast.top_level.is_empty());
7581 }
7582
7583 #[test]
7584 fn empty_paren_diagnoses() {
7585 let (ast, diags) = parse_str("();");
7586 assert_eq!(diags.len(), 1);
7587 assert_eq!(diags[0].message, "expected expression");
7588 assert!(ast.top_level.is_empty());
7589 }
7590
7591 #[test]
7592 fn unterminated_paren_diagnoses() {
7593 let (ast, diags) = parse_str("(x;");
7594 assert_eq!(diags.len(), 1);
7595 assert_eq!(diags[0].message, "expected `)`");
7596 assert!(ast.top_level.is_empty());
7597 }
7598
7599 fn binary(e: &crate::Expr) -> (crate::BinOp, crate::ExprId, crate::ExprId) {
7600 match e.kind {
7601 crate::ExprKind::Binary { op, lhs, rhs } => (op, lhs, rhs),
7602 _ => panic!("expected Binary, got {:?}", e.kind),
7603 }
7604 }
7605
7606 #[test]
7607 fn parse_binary_add() {
7608 let (ast, diags) = parse_str("a + b;");
7609 assert!(diags.is_empty());
7610 let outer = expr_of_single_stmt(&ast);
7611 let (op, lhs, rhs) = binary(outer);
7612 assert_eq!(op, crate::BinOp::Add);
7613 assert!(matches!(
7614 ast.try_expr(lhs).unwrap().kind,
7615 crate::ExprKind::Identifier(_)
7616 ));
7617 assert!(matches!(
7618 ast.try_expr(rhs).unwrap().kind,
7619 crate::ExprKind::Identifier(_)
7620 ));
7621 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7622 }
7623
7624 #[test]
7625 fn bitwise_precedence_and_shift_adjacency() {
7626 let (ast, diags) = parse_str("a | b ^ c & d == e >> f + g;");
7627 assert!(diags.is_empty(), "{diags:?}");
7628 let (op, _, mut rhs) = binary(expr_of_single_stmt(&ast));
7629 assert_eq!(op, crate::BinOp::BitOr);
7630 for expected in [
7631 crate::BinOp::BitXor,
7632 crate::BinOp::BitAnd,
7633 crate::BinOp::Eq,
7634 crate::BinOp::Shr,
7635 crate::BinOp::Add,
7636 ] {
7637 let (op, _, next) = binary(ast.try_expr(rhs).unwrap());
7638 assert_eq!(op, expected);
7639 rhs = next;
7640 }
7641 for source in ["a > > b;", "a >/*gap*/> b;", "a < < b;"] {
7642 let (_, diags) = parse_str(source);
7643 assert!(!diags.is_empty(), "{source}");
7644 }
7645 }
7646
7647 #[test]
7648 fn bitwise_shift_assignment_span() {
7649 let (ast, diags) = parse_str("a >>>= b;");
7650 assert!(diags.is_empty(), "{diags:?}");
7651 let crate::StmtKind::CompoundAssign { op, op_span, .. } = single_stmt(&ast).kind else {
7652 panic!("expected assignment");
7653 };
7654 assert_eq!(op, crate::BinOp::UnsignedShr);
7655 assert_eq!(op_span, crate::Span::new(F, 2, 6).unwrap());
7656 }
7657
7658 #[test]
7659 fn precedence_mul_binds_tighter_than_add() {
7660 let (ast, diags) = parse_str("a + b * c;");
7661 assert!(diags.is_empty());
7662 let outer = expr_of_single_stmt(&ast);
7663 let (op, lhs, rhs) = binary(outer);
7664 assert_eq!(op, crate::BinOp::Add);
7665 assert!(matches!(
7666 ast.try_expr(lhs).unwrap().kind,
7667 crate::ExprKind::Identifier(_)
7668 ));
7669 let (rop, rlhs, rrhs) = binary(ast.try_expr(rhs).unwrap());
7670 assert_eq!(rop, crate::BinOp::Mul);
7671 assert!(matches!(
7672 ast.try_expr(rlhs).unwrap().kind,
7673 crate::ExprKind::Identifier(_)
7674 ));
7675 assert!(matches!(
7676 ast.try_expr(rrhs).unwrap().kind,
7677 crate::ExprKind::Identifier(_)
7678 ));
7679 }
7680
7681 #[test]
7682 fn precedence_mul_left_of_add() {
7683 let (ast, diags) = parse_str("a * b + c;");
7684 assert!(diags.is_empty());
7685 let outer = expr_of_single_stmt(&ast);
7686 let (op, lhs, rhs) = binary(outer);
7687 assert_eq!(op, crate::BinOp::Add);
7688 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7689 assert_eq!(lop, crate::BinOp::Mul);
7690 assert!(matches!(
7691 ast.try_expr(rhs).unwrap().kind,
7692 crate::ExprKind::Identifier(_)
7693 ));
7694 }
7695
7696 #[test]
7697 fn left_associative_same_precedence() {
7698 let (ast, diags) = parse_str("a - b - c;");
7699 assert!(diags.is_empty());
7700 let outer = expr_of_single_stmt(&ast);
7701 let (op, lhs, rhs) = binary(outer);
7702 assert_eq!(op, crate::BinOp::Sub);
7703 let (lop, llhs, lrhs) = binary(ast.try_expr(lhs).unwrap());
7704 assert_eq!(lop, crate::BinOp::Sub);
7705 assert!(matches!(
7706 ast.try_expr(llhs).unwrap().kind,
7707 crate::ExprKind::Identifier(_)
7708 ));
7709 assert!(matches!(
7710 ast.try_expr(lrhs).unwrap().kind,
7711 crate::ExprKind::Identifier(_)
7712 ));
7713 assert!(matches!(
7714 ast.try_expr(rhs).unwrap().kind,
7715 crate::ExprKind::Identifier(_)
7716 ));
7717 }
7718
7719 #[test]
7720 fn equality_inside_logical() {
7721 let (ast, diags) = parse_str("a === b && c === d;");
7722 assert!(diags.is_empty());
7723 let outer = expr_of_single_stmt(&ast);
7724 let (op, lhs, rhs) = binary(outer);
7725 assert_eq!(op, crate::BinOp::And);
7726 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7727 assert_eq!(lop, crate::BinOp::Eq);
7728 let (rop, ..) = binary(ast.try_expr(rhs).unwrap());
7729 assert_eq!(rop, crate::BinOp::Eq);
7730 }
7731
7732 #[test]
7733 fn double_equal_is_same_variant_as_triple() {
7734 let (ast, _) = parse_str("a == b;");
7735 let outer = expr_of_single_stmt(&ast);
7736 let (op, ..) = binary(outer);
7737 assert_eq!(op, crate::BinOp::Eq);
7738 }
7739
7740 #[test]
7741 fn bang_eq_is_not_eq() {
7742 let (ast, _) = parse_str("a != b;");
7743 let outer = expr_of_single_stmt(&ast);
7744 let (op, ..) = binary(outer);
7745 assert_eq!(op, crate::BinOp::NotEq);
7746 }
7747
7748 #[test]
7749 fn comparison_has_higher_prec_than_logical() {
7750 let (ast, _) = parse_str("a < b && c;");
7751 let outer = expr_of_single_stmt(&ast);
7752 let (op, lhs, _) = binary(outer);
7753 assert_eq!(op, crate::BinOp::And);
7754 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7755 assert_eq!(lop, crate::BinOp::Lt);
7756 }
7757
7758 #[test]
7759 fn full_precedence_chain_snapshot() {
7760 let (ast, diags) = parse_str("a || b && c === d < e + f * g;");
7761 assert!(diags.is_empty());
7762 insta::assert_debug_snapshot!(ast);
7763 }
7764
7765 #[test]
7766 fn paren_overrides_precedence() {
7767 let (ast, _) = parse_str("(a + b) * c;");
7768 let outer = expr_of_single_stmt(&ast);
7769 let (op, lhs, rhs) = binary(outer);
7770 assert_eq!(op, crate::BinOp::Mul);
7771 let crate::ExprKind::Paren(inner_id) = ast.try_expr(lhs).unwrap().kind else {
7772 panic!("expected Paren on lhs");
7773 };
7774 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7775 assert_eq!(inner_op, crate::BinOp::Add);
7776 assert!(matches!(
7777 ast.try_expr(rhs).unwrap().kind,
7778 crate::ExprKind::Identifier(_)
7779 ));
7780 }
7781
7782 #[test]
7783 fn binary_missing_rhs_diagnoses() {
7784 let (ast, diags) = parse_str("a +;");
7785 assert_eq!(diags.len(), 1);
7786 assert_eq!(diags[0].message, "expected expression");
7787 assert!(ast.top_level.is_empty());
7788 }
7789
7790 fn unary(e: &crate::Expr) -> (crate::UnOp, crate::ExprId) {
7791 match e.kind {
7792 crate::ExprKind::Unary { op, operand } => (op, operand),
7793 _ => panic!("expected Unary, got {:?}", e.kind),
7794 }
7795 }
7796
7797 #[test]
7798 fn parse_unary_not() {
7799 let (ast, _) = parse_str("!x;");
7800 let outer = expr_of_single_stmt(&ast);
7801 let (op, operand) = unary(outer);
7802 assert_eq!(op, crate::UnOp::Not);
7803 assert!(matches!(
7804 ast.try_expr(operand).unwrap().kind,
7805 crate::ExprKind::Identifier(_)
7806 ));
7807 assert_eq!(outer.span, crate::Span::new(F, 0, 2).unwrap());
7808 }
7809
7810 #[test]
7811 fn parse_unary_neg() {
7812 let (ast, _) = parse_str("-x;");
7813 let (op, _) = unary(expr_of_single_stmt(&ast));
7814 assert_eq!(op, crate::UnOp::Neg);
7815 }
7816
7817 #[test]
7818 fn parse_unary_pos() {
7819 let (ast, _) = parse_str("+x;");
7820 let (op, _) = unary(expr_of_single_stmt(&ast));
7821 assert_eq!(op, crate::UnOp::Pos);
7822 }
7823
7824 #[test]
7825 fn repeated_unary_not() {
7826 let (ast, _) = parse_str("!!x;");
7827 let outer = expr_of_single_stmt(&ast);
7828 let (op, operand) = unary(outer);
7829 assert_eq!(op, crate::UnOp::Not);
7830 let (inner_op, inner_operand) = unary(ast.try_expr(operand).unwrap());
7831 assert_eq!(inner_op, crate::UnOp::Not);
7832 assert!(matches!(
7833 ast.try_expr(inner_operand).unwrap().kind,
7834 crate::ExprKind::Identifier(_)
7835 ));
7836 }
7837
7838 #[test]
7839 fn unary_binds_tighter_than_binary() {
7840 let (ast, _) = parse_str("-x + 1;");
7841 let outer = expr_of_single_stmt(&ast);
7842 let (op, lhs, rhs) = binary(outer);
7843 assert_eq!(op, crate::BinOp::Add);
7844 let (unop, _) = unary(ast.try_expr(lhs).unwrap());
7845 assert_eq!(unop, crate::UnOp::Neg);
7846 assert_eq!(
7847 ast.try_expr(rhs).unwrap().kind,
7848 crate::ExprKind::Number(1.0)
7849 );
7850 }
7851
7852 #[test]
7853 fn unary_over_paren() {
7854 let (ast, _) = parse_str("-(x + 1);");
7855 let outer = expr_of_single_stmt(&ast);
7856 let (op, operand) = unary(outer);
7857 assert_eq!(op, crate::UnOp::Neg);
7858 let crate::ExprKind::Paren(inner_id) = ast.try_expr(operand).unwrap().kind else {
7859 panic!("expected Paren");
7860 };
7861 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7862 assert_eq!(inner_op, crate::BinOp::Add);
7863 }
7864
7865 #[test]
7866 fn parse_is_as_expression_rejected() {
7867 let (_, diags) = parse_str("x is number;");
7868 assert!(!diags.is_empty(), "expected parse error for `x is T`");
7869 }
7870
7871 fn as_cast(e: &crate::Expr) -> (crate::ExprId, &crate::TypeAnnotation) {
7872 match &e.kind {
7873 crate::ExprKind::As { expr, ty } => (*expr, ty),
7874 other => panic!("expected ExprKind::As, got {other:?}"),
7875 }
7876 }
7877
7878 #[test]
7879 fn parse_instanceof_class() {
7880 let (ast, diags) = parse_str("x instanceof Foo;");
7881 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7882 let outer = expr_of_single_stmt(&ast);
7883 let crate::ExprKind::InstanceOf { value, ty } = &outer.kind else {
7884 panic!("expected ExprKind::InstanceOf, got {:?}", outer.kind);
7885 };
7886 assert!(matches!(
7887 ast.try_expr(*value).unwrap().kind,
7888 crate::ExprKind::Identifier(_)
7889 ));
7890 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7891 }
7892
7893 #[test]
7894 fn parse_instanceof_binds_tighter_than_logical_and() {
7895 let (ast, diags) = parse_str("a instanceof B && c;");
7897 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7898 let outer = expr_of_single_stmt(&ast);
7899 let (op, lhs, rhs) = binary(outer);
7900 assert_eq!(op, crate::BinOp::And);
7901 assert!(matches!(
7902 ast.try_expr(lhs).unwrap().kind,
7903 crate::ExprKind::InstanceOf { .. }
7904 ));
7905 assert!(matches!(
7906 ast.try_expr(rhs).unwrap().kind,
7907 crate::ExprKind::Identifier(_)
7908 ));
7909 }
7910
7911 #[test]
7914 fn parse_angle_cast_lowers_to_the_as_node() {
7915 let (ast, diags) = parse_str("<number>x;");
7916 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7917 let outer = expr_of_single_stmt(&ast);
7918 let (inner, ty) = as_cast(outer);
7919 assert!(matches!(
7920 ast.try_expr(inner).unwrap().kind,
7921 crate::ExprKind::Identifier(_)
7922 ));
7923 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7924 }
7925
7926 #[test]
7930 fn a_less_than_comparison_is_not_read_as_a_cast() {
7931 let (ast, diags) = parse_str("a < b;");
7932 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7933 let e = expr_of_single_stmt(&ast);
7934 assert!(matches!(e.kind, crate::ExprKind::Binary { .. }));
7935 }
7936
7937 #[test]
7938 fn parse_typeof_in_type_position() {
7939 let (_ast, diags) = parse_str("let a: typeof b = 1;");
7940 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7941 }
7942
7943 #[test]
7944 fn parse_typeof_with_a_dotted_path() {
7945 let (_ast, diags) = parse_str("let a: typeof b.c.d = 1;");
7946 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7947 }
7948
7949 #[test]
7950 fn parse_as_primitive() {
7951 let (ast, diags) = parse_str("x as number;");
7952 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7953 let outer = expr_of_single_stmt(&ast);
7954 let (inner, ty) = as_cast(outer);
7955 assert!(matches!(
7956 ast.try_expr(inner).unwrap().kind,
7957 crate::ExprKind::Identifier(_)
7958 ));
7959 match &ty.kind {
7960 crate::TypeAnnotationKind::Name { args, .. } => {
7961 assert!(args.is_empty());
7962 }
7963 other => panic!("expected Name target, got {other:?}"),
7964 }
7965 assert_eq!(outer.span, crate::Span::new(F, 0, 11).unwrap());
7966 }
7967
7968 #[test]
7969 fn parse_as_object_literal_type() {
7970 let (ast, diags) = parse_str("x as { a: number };");
7971 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7972 let outer = expr_of_single_stmt(&ast);
7973 let (_, ty) = as_cast(outer);
7974 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Object { .. }));
7975 }
7976
7977 #[test]
7978 fn parse_as_array_short() {
7979 let (ast, diags) = parse_str("x as string[];");
7980 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7981 let outer = expr_of_single_stmt(&ast);
7982 let (_, ty) = as_cast(outer);
7983 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
7984 }
7985
7986 #[test]
7987 fn parse_as_array_generic() {
7988 let (ast, diags) = parse_str("x as T[];");
7989 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7990 let outer = expr_of_single_stmt(&ast);
7991 let (_, ty) = as_cast(outer);
7992 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
7993 }
7994
7995 #[test]
7996 fn parse_as_generic_instantiation() {
7997 let (ast, diags) = parse_str("x as Foo<Bar>;");
7998 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7999 let outer = expr_of_single_stmt(&ast);
8000 let (_, ty) = as_cast(outer);
8001 match &ty.kind {
8002 crate::TypeAnnotationKind::Name { args, .. } => {
8003 assert_eq!(args.len(), 1, "Foo<Bar> has one type arg");
8004 }
8005 other => panic!("expected Name with type args, got {other:?}"),
8006 }
8007 }
8008
8009 #[test]
8010 fn parse_as_precedence_higher_than_additive() {
8011 let (ast, diags) = parse_str("x as number + 1;");
8012 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8013 let outer = expr_of_single_stmt(&ast);
8014 let (op, lhs, rhs) = binary(outer);
8015 assert_eq!(op, crate::BinOp::Add);
8016 assert!(matches!(
8017 ast.try_expr(lhs).unwrap().kind,
8018 crate::ExprKind::As { .. }
8019 ));
8020 assert!(matches!(
8021 ast.try_expr(rhs).unwrap().kind,
8022 crate::ExprKind::Number(_)
8023 ));
8024 }
8025
8026 #[test]
8027 fn parse_as_precedence_lower_than_unary() {
8028 let (ast, diags) = parse_str("!x as boolean;");
8029 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8030 let outer = expr_of_single_stmt(&ast);
8031 let (inner, _) = as_cast(outer);
8032 assert!(matches!(
8033 ast.try_expr(inner).unwrap().kind,
8034 crate::ExprKind::Unary {
8035 op: crate::UnOp::Not,
8036 ..
8037 }
8038 ));
8039 }
8040
8041 #[test]
8042 fn parse_as_left_associative_chain() {
8043 let (ast, diags) = parse_str("x as A as B;");
8044 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8045 let outer = expr_of_single_stmt(&ast);
8046 let (inner, _outer_ty) = as_cast(outer);
8047 let (innermost, _inner_ty) = as_cast(ast.try_expr(inner).unwrap());
8048 assert!(matches!(
8049 ast.try_expr(innermost).unwrap().kind,
8050 crate::ExprKind::Identifier(_)
8051 ));
8052 }
8053
8054 #[test]
8055 fn parse_as_inside_call_arg() {
8056 let (ast, diags) = parse_str("f(x as T);");
8057 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8058 let outer = expr_of_single_stmt(&ast);
8059 match &outer.kind {
8060 crate::ExprKind::Call { args, .. } => {
8061 assert_eq!(args.len(), 1);
8062 assert!(matches!(
8063 ast.try_expr(args[0]).unwrap().kind,
8064 crate::ExprKind::As { .. }
8065 ));
8066 }
8067 other => panic!("expected Call, got {other:?}"),
8068 }
8069 }
8070
8071 #[test]
8072 fn parse_as_after_field_access() {
8073 let (ast, diags) = parse_str("obj.field as T;");
8074 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8075 let outer = expr_of_single_stmt(&ast);
8076 let (inner, _) = as_cast(outer);
8077 assert!(matches!(
8078 ast.try_expr(inner).unwrap().kind,
8079 crate::ExprKind::FieldAccess { .. }
8080 ));
8081 }
8082
8083 #[test]
8084 fn parse_type_guard_return_annotation_on_function() {
8085 let src = "function isCircle(s: Shape): s is Circle { return true; }";
8086 let (ast, diags) = parse_str(src);
8087 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8088 let stmt = single_stmt(&ast);
8089 match stmt.kind {
8090 crate::StmtKind::Function {
8091 ref return_type,
8092 ref type_predicate,
8093 ..
8094 } => {
8095 assert!(
8096 return_type.is_none(),
8097 "predicate form leaves return_type empty",
8098 );
8099 let tp = type_predicate.as_ref().expect("predicate parsed");
8100 assert_eq!(tp.param.name, "s");
8101 assert!(matches!(
8102 tp.asserted.kind,
8103 crate::TypeAnnotationKind::Name { .. }
8104 ));
8105 assert_eq!(tp.span, crate::Span::new(F, 29, 40).unwrap());
8106 }
8107 _ => panic!("expected Function"),
8108 }
8109 }
8110
8111 #[test]
8112 fn parse_type_guard_return_annotation_on_arrow() {
8113 let src = "const f = (s: Shape): s is Circle => true;";
8114 let (ast, diags) = parse_str(src);
8115 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8116 let value = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
8117 crate::StmtKind::Const { value, .. } => *value,
8118 _ => panic!("expected const"),
8119 };
8120 match &ast.try_expr(value).unwrap().kind {
8121 crate::ExprKind::Arrow {
8122 return_type,
8123 type_predicate,
8124 ..
8125 } => {
8126 assert!(return_type.is_none());
8127 let tp = type_predicate.as_ref().expect("predicate parsed");
8128 assert_eq!(tp.param.name, "s");
8129 }
8130 other => panic!("expected Arrow, got {other:?}"),
8131 }
8132 }
8133
8134 #[test]
8135 fn parse_plain_return_type_unaffected_by_predicate_parser() {
8136 let src = "function f(s: Shape): boolean { return true; }";
8137 let (ast, diags) = parse_str(src);
8138 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8139 let stmt = single_stmt(&ast);
8140 match stmt.kind {
8141 crate::StmtKind::Function {
8142 ref return_type,
8143 ref type_predicate,
8144 ..
8145 } => {
8146 assert!(return_type.is_some(), "plain form has return_type");
8147 assert!(type_predicate.is_none(), "plain form has no type_predicate",);
8148 }
8149 _ => panic!("expected Function"),
8150 }
8151 }
8152
8153 #[test]
8154 fn parse_type_guard_rejects_non_return_position() {
8155 let (_, diags) = parse_str("let x: y is Foo = null;");
8156 assert!(
8157 !diags.is_empty(),
8158 "expected parse error for `is` outside return-type position",
8159 );
8160 }
8161
8162 #[test]
8163 fn parse_typeof_prefix() {
8164 let (ast, diags) = parse_str("typeof x;");
8165 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8166 let outer = expr_of_single_stmt(&ast);
8167 let crate::ExprKind::Typeof { operand } = outer.kind else {
8168 panic!("expected Typeof, got {:?}", outer.kind);
8169 };
8170 assert!(matches!(
8171 ast.try_expr(operand).unwrap().kind,
8172 crate::ExprKind::Identifier(_)
8173 ));
8174 assert_eq!(outer.span, crate::Span::new(F, 0, 8).unwrap());
8175 }
8176
8177 #[test]
8178 fn parse_typeof_in_equality() {
8179 let (ast, diags) = parse_str(r#"typeof x === "number";"#);
8180 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8181 let outer = expr_of_single_stmt(&ast);
8182 let (op, lhs, rhs) = binary(outer);
8183 assert_eq!(op, crate::BinOp::Eq);
8184 assert!(matches!(
8185 ast.try_expr(lhs).unwrap().kind,
8186 crate::ExprKind::Typeof { .. }
8187 ));
8188 assert!(matches!(
8189 ast.try_expr(rhs).unwrap().kind,
8190 crate::ExprKind::String(ref s) if s == "number"
8191 ));
8192 }
8193
8194 #[test]
8195 fn parse_let_without_type() {
8196 let (ast, diags) = parse_str("let x = 1;");
8197 assert!(diags.is_empty());
8198 let stmt = single_stmt(&ast);
8199 assert_eq!(stmt.span, crate::Span::new(F, 0, 10).unwrap());
8200 match stmt.kind {
8201 crate::StmtKind::Let {
8202 ref name,
8203 ref ty,
8204 value,
8205 ..
8206 } => {
8207 assert_eq!(name.name, "x");
8208 assert_eq!(name.span, crate::Span::new(F, 4, 5).unwrap());
8209 assert!(ty.is_none());
8210 assert_eq!(
8211 ast.try_expr(value).unwrap().kind,
8212 crate::ExprKind::Number(1.0)
8213 );
8214 }
8215 _ => panic!("expected Let"),
8216 }
8217 }
8218
8219 #[test]
8220 fn parse_const_with_type_and_string() {
8221 let (ast, diags) = parse_str(r#"const y: string = "hi";"#);
8222 assert!(diags.is_empty());
8223 let stmt = single_stmt(&ast);
8224 match stmt.kind {
8225 crate::StmtKind::Const {
8226 ref name,
8227 ref ty,
8228 value,
8229 ..
8230 } => {
8231 assert_eq!(name.name, "y");
8232 assert_eq!(name.span, crate::Span::new(F, 6, 7).unwrap());
8233 let ty = ty.as_ref().expect("expected type annotation");
8234 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8235 assert_eq!(ty.span, crate::Span::new(F, 9, 15).unwrap());
8236 assert_eq!(
8237 ast.try_expr(value).unwrap().kind,
8238 crate::ExprKind::String("hi".to_string())
8239 );
8240 }
8241 _ => panic!("expected Const"),
8242 }
8243 }
8244
8245 #[test]
8246 fn parse_let_with_type_and_binary_initializer() {
8247 let (ast, diags) = parse_str("let z: number = 1 + 2;");
8248 assert!(diags.is_empty());
8249 let stmt = single_stmt(&ast);
8250 match stmt.kind {
8251 crate::StmtKind::Let { ref ty, value, .. } => {
8252 assert!(ty.is_some());
8253 let (op, ..) = binary(ast.try_expr(value).unwrap());
8254 assert_eq!(op, crate::BinOp::Add);
8255 }
8256 _ => panic!("expected Let"),
8257 }
8258 }
8259
8260 #[test]
8261 fn void_type_annotation_is_accepted_syntactically() {
8262 let (ast, diags) = parse_str("let v: void = null;");
8263 assert!(diags.is_empty());
8264 let stmt = single_stmt(&ast);
8265 match stmt.kind {
8266 crate::StmtKind::Let { ref ty, .. } => {
8267 let ty = ty.as_ref().unwrap();
8268 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8269 assert_eq!(ty.span, crate::Span::new(F, 7, 11).unwrap());
8270 }
8271 _ => panic!("expected Let"),
8272 }
8273 }
8274
8275 #[test]
8276 fn const_missing_initializer_diagnoses() {
8277 let (ast, diags) = parse_str("const x;");
8278 assert_eq!(diags.len(), 1);
8279 assert_eq!(
8280 diags[0].message,
8281 "`const` declaration requires an initializer"
8282 );
8283 assert!(ast.top_level.is_empty());
8284 }
8285
8286 #[test]
8287 fn parse_const_object_shorthand_pattern() {
8288 let (ast, diags) = parse_str("const { a, b } = obj;");
8289 assert!(diags.is_empty(), "unexpected: {diags:?}");
8290 let stmt = single_stmt(&ast);
8291 match stmt.kind {
8292 crate::StmtKind::ConstPattern {
8293 binding:
8294 crate::Binding::Object {
8295 ref fields,
8296 ref rest,
8297 ..
8298 },
8299 ..
8300 } => {
8301 assert_eq!(fields.len(), 2);
8302 assert_eq!(fields[0].source.name, "a");
8303 assert_eq!(fields[0].local.name, "a");
8304 assert_eq!(fields[1].source.name, "b");
8305 assert_eq!(fields[1].local.name, "b");
8306 assert!(rest.is_none());
8307 }
8308 _ => panic!("expected ConstPattern Object, got {:?}", stmt.kind),
8309 }
8310 }
8311
8312 #[test]
8313 fn parse_const_object_pattern_keyword_source_key() {
8314 let (ast, diags) = parse_str("const { type: statusType, default: fallback } = obj;");
8315 assert!(diags.is_empty(), "unexpected: {diags:?}");
8316 let stmt = single_stmt(&ast);
8317 let crate::StmtKind::ConstPattern {
8318 binding: crate::Binding::Object { ref fields, .. },
8319 ..
8320 } = stmt.kind
8321 else {
8322 panic!("expected object const pattern");
8323 };
8324
8325 let names: Vec<(&str, &str)> = fields
8326 .iter()
8327 .map(|f| (f.source.name.as_str(), f.local.name.as_str()))
8328 .collect();
8329 assert_eq!(names, vec![("type", "statusType"), ("default", "fallback")]);
8330 }
8331
8332 #[test]
8333 fn parse_const_object_pattern_keyword_shorthand_rejected() {
8334 let (_ast, diags) = parse_str("const { default } = obj;");
8335 assert!(
8336 diags
8337 .iter()
8338 .any(|d| d.message == "expected `:` after keyword field name in object pattern"),
8339 "expected keyword-shorthand diagnostic, got: {diags:?}"
8340 );
8341 }
8342
8343 #[test]
8344 fn parse_const_object_renamed_pattern() {
8345 let (ast, diags) = parse_str("const { a: x, b: y } = obj;");
8346 assert!(diags.is_empty(), "unexpected: {diags:?}");
8347 match single_stmt(&ast).kind {
8348 crate::StmtKind::ConstPattern {
8349 binding: crate::Binding::Object { ref fields, .. },
8350 ..
8351 } => {
8352 assert_eq!(fields[0].source.name, "a");
8353 assert_eq!(fields[0].local.name, "x");
8354 assert_eq!(fields[1].source.name, "b");
8355 assert_eq!(fields[1].local.name, "y");
8356 }
8357 ref other => panic!("expected ConstPattern Object, got {other:?}"),
8358 }
8359 }
8360
8361 #[test]
8362 fn parse_const_object_with_rest() {
8363 let (ast, diags) = parse_str("const { a, ...rest } = obj;");
8364 assert!(diags.is_empty(), "unexpected: {diags:?}");
8365 match single_stmt(&ast).kind {
8366 crate::StmtKind::ConstPattern {
8367 binding:
8368 crate::Binding::Object {
8369 ref fields,
8370 ref rest,
8371 ..
8372 },
8373 ..
8374 } => {
8375 assert_eq!(fields.len(), 1);
8376 assert_eq!(fields[0].source.name, "a");
8377 let r = rest.as_ref().expect("expected rest");
8378 assert_eq!(r.name, "rest");
8379 }
8380 ref other => panic!("expected ConstPattern Object, got {other:?}"),
8381 }
8382 }
8383
8384 #[test]
8385 fn parse_const_array_pattern() {
8386 let (ast, diags) = parse_str("const [x, y] = arr;");
8387 assert!(diags.is_empty(), "unexpected: {diags:?}");
8388 match single_stmt(&ast).kind {
8389 crate::StmtKind::ConstPattern {
8390 binding:
8391 crate::Binding::Array {
8392 ref elems,
8393 ref rest,
8394 ..
8395 },
8396 ..
8397 } => {
8398 assert_eq!(elems.len(), 2);
8399 assert_eq!(elems[0].as_ref().unwrap().name, "x");
8400 assert_eq!(elems[1].as_ref().unwrap().name, "y");
8401 assert!(rest.is_none());
8402 }
8403 ref other => panic!("expected ConstPattern Array, got {other:?}"),
8404 }
8405 }
8406
8407 #[test]
8408 fn parse_const_array_with_hole_and_rest() {
8409 let (ast, diags) = parse_str("const [, x, ...rest] = arr;");
8410 assert!(diags.is_empty(), "unexpected: {diags:?}");
8411 match single_stmt(&ast).kind {
8412 crate::StmtKind::ConstPattern {
8413 binding:
8414 crate::Binding::Array {
8415 ref elems,
8416 ref rest,
8417 ..
8418 },
8419 ..
8420 } => {
8421 assert_eq!(elems.len(), 2);
8422 assert!(elems[0].is_none(), "first slot should be a hole");
8423 assert_eq!(elems[1].as_ref().unwrap().name, "x");
8424 assert_eq!(rest.as_ref().unwrap().name, "rest");
8425 }
8426 ref other => panic!("expected ConstPattern Array, got {other:?}"),
8427 }
8428 }
8429
8430 #[test]
8431 fn parse_let_object_pattern() {
8432 let (ast, diags) = parse_str("let { a, b } = obj;");
8433 assert!(diags.is_empty(), "unexpected: {diags:?}");
8434 assert!(matches!(
8435 single_stmt(&ast).kind,
8436 crate::StmtKind::LetPattern {
8437 binding: crate::Binding::Object { .. },
8438 ..
8439 }
8440 ));
8441 }
8442
8443 #[test]
8444 fn parse_function_param_object_pattern() {
8445 let src = "function f({ a, b }: T): void { return; }";
8446 let (ast, diags) = parse_str(src);
8447 assert!(diags.is_empty(), "unexpected: {diags:?}");
8448 match single_stmt(&ast).kind {
8449 crate::StmtKind::Function { ref params, .. } => {
8450 assert_eq!(params.len(), 1);
8451 let p = ¶ms[0];
8452 assert!(p.pattern.is_some(), "expected pattern param");
8453 assert_eq!(p.name.name, "", "pattern placeholder uses empty name");
8454 match p.pattern.as_ref().unwrap() {
8455 crate::Binding::Object { fields, .. } => {
8456 assert_eq!(fields.len(), 2);
8457 assert_eq!(fields[0].source.name, "a");
8458 }
8459 other => panic!("expected Object binding, got {other:?}"),
8460 }
8461 }
8462 ref other => panic!("expected Function, got {other:?}"),
8463 }
8464 }
8465
8466 #[test]
8467 fn parse_arrow_param_array_pattern() {
8468 let src = "const g = ([x, y]: number[]): number => x + y;";
8469 let (_ast, diags) = parse_str(src);
8470 assert!(diags.is_empty(), "unexpected: {diags:?}");
8471 }
8472
8473 #[test]
8474 fn parse_destructure_nested_rejected() {
8475 let (_ast, diags) = parse_str("const { a: { b } } = obj;");
8476 assert!(
8477 diags
8478 .iter()
8479 .any(|d| d.message.contains("nested destructuring")),
8480 "expected nested-destructuring diagnostic, got: {diags:?}",
8481 );
8482 }
8483
8484 #[test]
8485 fn parse_destructure_array_nested_rejected() {
8486 let (_ast, diags) = parse_str("const [[a]] = arr;");
8487 assert!(
8488 diags
8489 .iter()
8490 .any(|d| d.message.contains("nested destructuring")),
8491 "expected nested-destructuring diagnostic, got: {diags:?}",
8492 );
8493 }
8494
8495 #[test]
8496 fn parse_destructure_pattern_defaults() {
8497 let (ast, diags) = parse_str("const { a = 1, b: c = 2 } = obj;");
8498 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8499 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
8500 panic!("expected pattern")
8501 };
8502 let crate::Binding::Object { fields, .. } = binding else {
8503 panic!("expected object")
8504 };
8505 assert_eq!(fields.len(), 2);
8506 assert!(fields.iter().all(|field| field.default.is_some()));
8507 assert_eq!(fields[1].local.name, "c");
8508 }
8509
8510 #[test]
8511 fn parse_destructure_param_default() {
8512 let (ast, diags) = parse_str("function f({ a = 1 }: T = obj): void { return; }");
8513 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8514 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
8515 panic!("expected function")
8516 };
8517 assert!(params[0].default.is_some());
8518 assert!(params[0].pattern.is_some());
8519 }
8520
8521 #[test]
8522 fn parse_destructure_rest_not_last_rejected() {
8523 let (_ast, diags) = parse_str("const { ...rest, a } = obj;");
8524 assert!(
8525 diags
8526 .iter()
8527 .any(|d| d.message.contains("rest element must be the last element")),
8528 "expected rest-not-last diagnostic, got: {diags:?}",
8529 );
8530 }
8531
8532 #[test]
8533 fn parse_destructure_empty_object_rejected() {
8534 let (_ast, diags) = parse_str("const { } = obj;");
8535 assert!(
8536 diags
8537 .iter()
8538 .any(|d| d.message.contains("empty object destructuring pattern")),
8539 "expected empty-pattern diagnostic, got: {diags:?}",
8540 );
8541 }
8542
8543 #[test]
8544 fn parse_ternary_simple() {
8545 let (ast, diags) = parse_str("const x: number = a ? 1 : 2;");
8546 assert!(diags.is_empty(), "unexpected: {diags:?}");
8547 let stmt = single_stmt(&ast);
8548 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8549 panic!("expected Const, got {:?}", stmt.kind);
8550 };
8551 assert!(matches!(
8552 ast.try_expr(value).unwrap().kind,
8553 crate::ExprKind::Ternary { .. }
8554 ));
8555 }
8556
8557 #[test]
8558 fn parse_ternary_nested_right_assoc() {
8559 let (ast, diags) = parse_str("const x: number = a ? 1 : b ? 2 : 3;");
8560 assert!(diags.is_empty(), "unexpected: {diags:?}");
8561 let stmt = single_stmt(&ast);
8562 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8563 panic!("expected Const");
8564 };
8565 let crate::ExprKind::Ternary { else_, .. } = ast.try_expr(value).unwrap().kind else {
8566 panic!("expected outer Ternary");
8567 };
8568 assert!(matches!(
8569 ast.try_expr(else_).unwrap().kind,
8570 crate::ExprKind::Ternary { .. }
8571 ));
8572 }
8573
8574 #[test]
8575 fn parse_ternary_missing_colon_diagnoses() {
8576 let (_, diags) = parse_str("const x: number = a ? 1;");
8577 assert!(
8578 diags
8579 .iter()
8580 .any(|d| d.message.contains("expected `:` to complete ternary")),
8581 "expected colon diagnostic, got: {diags:?}",
8582 );
8583 }
8584
8585 #[test]
8586 fn parse_nullish_coalesce_binop() {
8587 let (ast, diags) = parse_str("const x: number = a ?? 1;");
8588 assert!(diags.is_empty(), "unexpected: {diags:?}");
8589 let stmt = single_stmt(&ast);
8590 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8591 panic!("expected Const");
8592 };
8593 match ast.try_expr(value).unwrap().kind {
8594 crate::ExprKind::Binary { op, .. } => {
8595 assert_eq!(op, crate::BinOp::NullishCoalesce);
8596 }
8597 ref other => panic!("expected Binary NullishCoalesce, got {other:?}"),
8598 }
8599 }
8600
8601 #[test]
8602 fn parse_nullish_left_associative() {
8603 let (ast, diags) = parse_str("const x: number = a ?? b ?? c;");
8604 assert!(diags.is_empty(), "unexpected: {diags:?}");
8605 let stmt = single_stmt(&ast);
8606 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8607 panic!("expected Const");
8608 };
8609 let crate::ExprKind::Binary { lhs, .. } = ast.try_expr(value).unwrap().kind else {
8610 panic!("expected outer Binary");
8611 };
8612 match ast.try_expr(lhs).unwrap().kind {
8613 crate::ExprKind::Binary { op, .. } => {
8614 assert_eq!(op, crate::BinOp::NullishCoalesce);
8615 }
8616 ref other => panic!("expected inner Binary, got {other:?}"),
8617 }
8618 }
8619
8620 #[test]
8621 fn parse_pow_is_right_associative() {
8622 let (ast, diags) = parse_str("const x: number = 2 ** 3 ** 2;");
8623 assert!(diags.is_empty(), "unexpected: {diags:?}");
8624 let stmt = single_stmt(&ast);
8625 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8626 panic!("expected Const");
8627 };
8628 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8629 panic!("expected outer Binary");
8630 };
8631 assert_eq!(op, crate::BinOp::Pow);
8632 match ast.try_expr(rhs).unwrap().kind {
8633 crate::ExprKind::Binary { op: inner_op, .. } => {
8634 assert_eq!(inner_op, crate::BinOp::Pow);
8635 }
8636 ref other => panic!("expected inner Pow Binary on RHS, got {other:?}"),
8637 }
8638 }
8639
8640 #[test]
8641 fn parse_pow_higher_than_multiply() {
8642 let (ast, diags) = parse_str("const x: number = 2 * 3 ** 2;");
8643 assert!(diags.is_empty(), "unexpected: {diags:?}");
8644 let stmt = single_stmt(&ast);
8645 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8646 panic!("expected Const");
8647 };
8648 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8649 panic!("expected outer Binary");
8650 };
8651 assert_eq!(op, crate::BinOp::Mul);
8652 match ast.try_expr(rhs).unwrap().kind {
8653 crate::ExprKind::Binary { op: inner_op, .. } => {
8654 assert_eq!(inner_op, crate::BinOp::Pow);
8655 }
8656 ref other => panic!("expected Pow on the RHS of *, got {other:?}"),
8657 }
8658 }
8659
8660 #[test]
8661 fn parse_compound_assign_ident() {
8662 let (ast, diags) = parse_str("function main(): void { let x: number = 0; x += 5; }");
8663 assert!(diags.is_empty(), "unexpected: {diags:?}");
8664 let func = ast.top_level.iter().find_map(|sid| {
8665 if let crate::StmtKind::Function { body, .. } = &ast.try_stmt(*sid).unwrap().kind {
8666 Some(*body)
8667 } else {
8668 None
8669 }
8670 });
8671 let crate::StmtKind::Block(stmts) = &ast.try_stmt(func.expect("main")).unwrap().kind else {
8672 panic!("expected function body to be a block");
8673 };
8674 let target = stmts
8675 .iter()
8676 .find_map(|sid| match &ast.try_stmt(*sid).unwrap().kind {
8677 crate::StmtKind::CompoundAssign { target, op, .. } => {
8678 Some((target.name.clone(), *op))
8679 }
8680 _ => None,
8681 });
8682 let (name, op) = target.expect("expected a CompoundAssign in the body");
8683 assert_eq!(name, "x");
8684 assert_eq!(op, crate::BinOp::Add);
8685 }
8686
8687 #[test]
8688 fn parse_nullish_mixed_with_or_rejected() {
8689 let (_, diags) = parse_str("const x: number = a || b ?? c;");
8690 assert!(
8691 diags
8692 .iter()
8693 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8694 "expected mixing diagnostic, got: {diags:?}",
8695 );
8696 }
8697
8698 #[test]
8699 fn parse_nullish_mixed_with_and_rejected() {
8700 let (_, diags) = parse_str("const x: number = a && b ?? c;");
8701 assert!(
8702 diags
8703 .iter()
8704 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8705 "expected mixing diagnostic, got: {diags:?}",
8706 );
8707 }
8708
8709 #[test]
8710 fn parse_nullish_followed_by_logical_rejected() {
8711 for (source, help) in [
8712 (
8713 "const x: number = a ?? b || c;",
8714 "`(a ?? b) || c` or `a ?? (b || c)`",
8715 ),
8716 (
8717 "const x: number = a ?? b && c;",
8718 "`(a ?? b) && c` or `a ?? (b && c)`",
8719 ),
8720 (
8721 "const x: number = a ?? b ?? c || d;",
8722 "`(a ?? b) || c` or `a ?? (b || c)`",
8723 ),
8724 (
8726 "const x: number = a || b ?? c || d;",
8727 "`(a || b) ?? c` or `a || (b ?? c)`",
8728 ),
8729 ] {
8730 let (_, diags) = parse_str(source);
8731 assert_eq!(diags.len(), 1, "diags for {source:?}: {diags:?}");
8732 assert!(
8733 diags[0].message.contains("mixing `??` with `||` / `&&`"),
8734 "message for {source:?}: {diags:?}",
8735 );
8736 assert!(
8737 diags[0].help.iter().any(|h| h.contains(help)),
8738 "help for {source:?}: {diags:?}",
8739 );
8740 }
8741 }
8742
8743 #[test]
8744 fn parse_nullish_operand_keeps_tighter_operators() {
8745 let (ast, diags) = parse_str("const x: boolean = a ?? b == c;");
8747 assert!(diags.is_empty(), "unexpected: {diags:?}");
8748 let stmt = single_stmt(&ast);
8749 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8750 panic!("expected Const");
8751 };
8752 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8753 panic!("expected outer Binary");
8754 };
8755 assert_eq!(op, crate::BinOp::NullishCoalesce);
8756 assert!(matches!(
8757 ast.try_expr(rhs).unwrap().kind,
8758 crate::ExprKind::Binary {
8759 op: crate::BinOp::Eq,
8760 ..
8761 }
8762 ));
8763 }
8764
8765 #[test]
8766 fn parse_nullish_with_parens_ok() {
8767 let (_, diags) = parse_str("const x: number = (a || b) ?? c;");
8768 assert!(diags.is_empty(), "unexpected: {diags:?}");
8769 }
8770
8771 #[test]
8772 fn parse_optional_field_chain() {
8773 let (ast, diags) = parse_str("const x: number = a?.b;");
8774 assert!(diags.is_empty(), "unexpected: {diags:?}");
8775 let stmt = single_stmt(&ast);
8776 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8777 panic!("expected Const");
8778 };
8779 match ast.try_expr(value).unwrap().kind {
8780 crate::ExprKind::OptionalChain { ref parts, .. } => {
8781 assert_eq!(parts.len(), 1);
8782 match &parts[0] {
8783 crate::ChainPart::Field { name, optional, .. } => {
8784 assert_eq!(name.name, "b");
8785 assert!(*optional);
8786 }
8787 other => panic!("expected Field, got {other:?}"),
8788 }
8789 }
8790 ref other => panic!("expected OptionalChain, got {other:?}"),
8791 }
8792 }
8793
8794 #[test]
8795 fn parse_optional_chain_continues_after_first_dot() {
8796 let (ast, diags) = parse_str("const x: number = a?.b.c;");
8797 assert!(diags.is_empty(), "unexpected: {diags:?}");
8798 let stmt = single_stmt(&ast);
8799 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8800 panic!("expected Const");
8801 };
8802 match ast.try_expr(value).unwrap().kind {
8803 crate::ExprKind::OptionalChain { ref parts, .. } => {
8804 assert_eq!(parts.len(), 2);
8805 if let crate::ChainPart::Field { optional, .. } = &parts[0] {
8806 assert!(*optional);
8807 }
8808 if let crate::ChainPart::Field { optional, .. } = &parts[1] {
8809 assert!(!*optional);
8810 }
8811 }
8812 ref other => panic!("expected OptionalChain, got {other:?}"),
8813 }
8814 }
8815
8816 #[test]
8817 fn parse_optional_chain_multiple_short_circuits() {
8818 let (ast, diags) = parse_str("const x: number = a?.b?.c;");
8819 assert!(diags.is_empty(), "unexpected: {diags:?}");
8820 let stmt = single_stmt(&ast);
8821 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8822 panic!("expected Const");
8823 };
8824 let crate::ExprKind::OptionalChain { parts, .. } = &ast.try_expr(value).unwrap().kind
8825 else {
8826 panic!("expected OptionalChain");
8827 };
8828 assert_eq!(parts.len(), 2);
8829 for p in parts {
8830 if let crate::ChainPart::Field { optional, .. } = p {
8831 assert!(*optional, "both parts should be optional");
8832 }
8833 }
8834 }
8835
8836 #[test]
8837 fn parse_optional_call_and_index() {
8838 let (_, diags) = parse_str("const x: number = f?.();");
8839 assert!(diags.is_empty(), "unexpected: {diags:?}");
8840 let (_, diags) = parse_str("const y: number = arr?.[0];");
8841 assert!(diags.is_empty(), "unexpected: {diags:?}");
8842 }
8843
8844 #[test]
8845 fn parse_optional_chain_terminates_at_questionmark_for_ternary() {
8846 let (ast, diags) = parse_str("const x: number = a?.b ? c : d;");
8847 assert!(diags.is_empty(), "unexpected: {diags:?}");
8848 let stmt = single_stmt(&ast);
8849 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8850 panic!("expected Const");
8851 };
8852 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8853 panic!(
8854 "expected top-level Ternary, got {:?}",
8855 ast.try_expr(value).unwrap().kind
8856 );
8857 };
8858 assert!(matches!(
8859 ast.try_expr(cond).unwrap().kind,
8860 crate::ExprKind::OptionalChain { .. }
8861 ));
8862 }
8863
8864 #[test]
8865 fn parse_nullish_below_ternary() {
8866 let (ast, diags) = parse_str("const x: number = a ?? b ? c : d;");
8867 assert!(diags.is_empty(), "unexpected: {diags:?}");
8868 let stmt = single_stmt(&ast);
8869 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8870 panic!("expected Const");
8871 };
8872 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8873 panic!("expected top-level Ternary");
8874 };
8875 match ast.try_expr(cond).unwrap().kind {
8876 crate::ExprKind::Binary { op, .. } => {
8877 assert_eq!(op, crate::BinOp::NullishCoalesce);
8878 }
8879 ref other => panic!("expected Binary ??, got {other:?}"),
8880 }
8881 }
8882
8883 #[test]
8884 fn let_missing_initializer_diagnoses() {
8885 let (ast, diags) = parse_str("let x;");
8886 assert_eq!(diags.len(), 1);
8887 assert_eq!(
8888 diags[0].message,
8889 "`let` declaration requires an initializer"
8890 );
8891 assert!(ast.top_level.is_empty());
8892 }
8893
8894 #[test]
8895 fn missing_identifier_after_let_diagnoses() {
8896 let (ast, diags) = parse_str("let = 1;");
8897 assert_eq!(diags.len(), 1);
8898 assert_eq!(diags[0].message, "expected identifier after `let`/`const`");
8899 assert!(ast.top_level.is_empty());
8900 }
8901
8902 #[test]
8903 fn missing_type_after_colon_diagnoses() {
8904 let (ast, diags) = parse_str("let x: = 1;");
8905 assert_eq!(diags.len(), 1);
8906 assert_eq!(diags[0].message, "expected type");
8907 assert!(ast.top_level.is_empty());
8908 }
8909
8910 #[test]
8911 fn missing_semicolon_after_declaration_diagnoses() {
8912 let (ast, diags) = parse_str("let x = 1 y;");
8913 assert_eq!(diags.len(), 1);
8914 assert_eq!(diags[0].message, "expected `;` after declaration");
8915 assert!(ast.top_level.is_empty());
8916 }
8917
8918 #[test]
8919 fn expression_statements_still_parse() {
8920 let (ast, diags) = parse_str("42;");
8921 assert!(diags.is_empty());
8922 assert_eq!(ast.top_level.len(), 1);
8923 }
8924
8925 #[test]
8926 fn snapshot_mixed_declarations() {
8927 let (ast, diags) =
8928 parse_str(r#"let x = 1; const y: string = "hi"; let z: number = 1 + 2;"#);
8929 assert!(diags.is_empty());
8930 assert_eq!(ast.top_level.len(), 3);
8931 insta::assert_debug_snapshot!(ast);
8932 }
8933
8934 #[test]
8935 fn parse_function_typical() {
8936 let (ast, diags) = parse_str("function f(a: number, b: string): boolean { }");
8937 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8938 let stmt = single_stmt(&ast);
8939 match stmt.kind {
8940 crate::StmtKind::Function {
8941 ref name,
8942 ref generics,
8943 ref params,
8944 ref return_type,
8945 body,
8946 ..
8947 } => {
8948 assert_eq!(name.name, "f");
8949 assert_eq!(name.span, crate::Span::new(F, 9, 10).unwrap());
8950 assert!(generics.is_empty());
8951 assert_eq!(params.len(), 2);
8952 assert_eq!(params[0].name.name, "a");
8953 assert_eq!(params[0].name.span, crate::Span::new(F, 11, 12).unwrap());
8954 let p0_ty = params[0]
8955 .ty
8956 .as_ref()
8957 .expect("function-decl param requires annotation");
8958 assert!(matches!(p0_ty.kind, crate::TypeAnnotationKind::Name { .. }));
8959 assert_eq!(p0_ty.span, crate::Span::new(F, 14, 20).unwrap()); assert_eq!(params[1].name.name, "b");
8961 assert_eq!(params[1].name.span, crate::Span::new(F, 22, 23).unwrap());
8962 let p1_ty = params[1]
8963 .ty
8964 .as_ref()
8965 .expect("function-decl param requires annotation");
8966 assert_eq!(p1_ty.span, crate::Span::new(F, 25, 31).unwrap()); let return_type = return_type.as_ref().expect("plain return type");
8968 assert!(matches!(
8969 return_type.kind,
8970 crate::TypeAnnotationKind::Name { .. }
8971 ));
8972 assert_eq!(return_type.span, crate::Span::new(F, 34, 41).unwrap()); let block = ast.try_stmt(body).unwrap();
8974 assert!(matches!(block.kind, crate::StmtKind::Block(ref v) if v.is_empty()));
8975 }
8976 _ => panic!("expected Function"),
8977 }
8978 }
8979
8980 #[test]
8981 fn parse_function_with_doc_comment() {
8982 let (ast, diags) = parse_str(
8983 "/**\n * Sum two numbers.\n * @param a First.\n * @param b Second.\n */\nfunction add(a: number, b: number): number { return a + b; }",
8984 );
8985 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8986 let stmt = single_stmt(&ast);
8987 match &stmt.kind {
8988 crate::StmtKind::Function { doc, .. } => {
8989 let d = doc.as_ref().expect("doc attached");
8990 assert_eq!(d.summary, "Sum two numbers.");
8991 assert_eq!(d.params.len(), 2);
8992 assert_eq!(d.params[0].name, "a");
8993 assert_eq!(d.params[1].name, "b");
8994 }
8995 _ => panic!("expected Function"),
8996 }
8997 }
8998
8999 #[test]
9000 fn parse_interface_member_docs() {
9001 let (ast, diags) = parse_str(
9002 "interface Foo {\n /** First method. */ first(): void;\n /** A field. */ field: number;\n}",
9003 );
9004 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9005 let stmt = single_stmt(&ast);
9006 match &stmt.kind {
9007 crate::StmtKind::InterfaceDecl { members, .. } => {
9008 assert_eq!(members.len(), 2);
9009 match &members[0] {
9010 crate::InterfaceMember::Method { doc, .. } => {
9011 assert_eq!(doc.as_ref().unwrap().summary, "First method.");
9012 }
9013 _ => panic!("expected Method first"),
9014 }
9015 match &members[1] {
9016 crate::InterfaceMember::Property { doc, .. } => {
9017 assert_eq!(doc.as_ref().unwrap().summary, "A field.");
9018 }
9019 _ => panic!("expected Property second"),
9020 }
9021 }
9022 _ => panic!("expected InterfaceDecl"),
9023 }
9024 }
9025
9026 #[test]
9027 fn parse_numeric_enum_all_implicit() {
9028 let (ast, diags) = parse_str("enum Direction { Up, Down, Left, Right }");
9029 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9030 let stmt = single_stmt(&ast);
9031 match &stmt.kind {
9032 crate::StmtKind::EnumDecl { name, members, doc } => {
9033 assert_eq!(name.name, "Direction");
9034 assert!(doc.is_none());
9035 assert_eq!(members.len(), 4);
9036 for (m, expected) in members.iter().zip(["Up", "Down", "Left", "Right"]) {
9037 assert_eq!(m.name.name, expected);
9038 assert!(m.value.is_none(), "expected no initializer on {expected}");
9039 }
9040 }
9041 _ => panic!("expected EnumDecl"),
9042 }
9043 }
9044
9045 #[test]
9046 fn parse_string_enum_all_explicit() {
9047 let (ast, diags) =
9048 parse_str("enum Status { Active = \"active\", Inactive = \"inactive\" }");
9049 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9050 let stmt = single_stmt(&ast);
9051 match &stmt.kind {
9052 crate::StmtKind::EnumDecl { name, members, .. } => {
9053 assert_eq!(name.name, "Status");
9054 assert_eq!(members.len(), 2);
9055 match &members[0].value {
9056 Some(crate::EnumInitializer::String { value, .. }) => {
9057 assert_eq!(value, "active");
9058 }
9059 other => panic!("expected String init, got {other:?}"),
9060 }
9061 match &members[1].value {
9062 Some(crate::EnumInitializer::String { value, .. }) => {
9063 assert_eq!(value, "inactive");
9064 }
9065 other => panic!("expected String init, got {other:?}"),
9066 }
9067 }
9068 _ => panic!("expected EnumDecl"),
9069 }
9070 }
9071
9072 #[test]
9073 fn parse_numeric_enum_with_negative_init() {
9074 let (ast, diags) = parse_str("enum D { Up = 1, Down = -1 }");
9075 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9076 let stmt = single_stmt(&ast);
9077 match &stmt.kind {
9078 crate::StmtKind::EnumDecl { members, .. } => {
9079 assert_eq!(members.len(), 2);
9080 match &members[0].value {
9081 Some(crate::EnumInitializer::Number { value, .. }) => {
9082 assert_eq!(*value, 1.0);
9083 }
9084 other => panic!("expected Number init, got {other:?}"),
9085 }
9086 match &members[1].value {
9087 Some(crate::EnumInitializer::Number { value, .. }) => {
9088 assert_eq!(*value, -1.0);
9089 }
9090 other => panic!("expected Number init, got {other:?}"),
9091 }
9092 }
9093 _ => panic!("expected EnumDecl"),
9094 }
9095 }
9096
9097 #[test]
9098 fn parse_enum_mixed_kinds_is_parse_error() {
9099 let (_ast, diags) = parse_str("enum Mix { A = 1, B = \"two\" }");
9100 assert!(
9101 diags
9102 .iter()
9103 .any(|d| d.message.contains("mixed numeric and string")),
9104 "expected mixed-kind diagnostic, got: {diags:?}"
9105 );
9106 }
9107
9108 #[test]
9109 fn parse_enum_member_docs() {
9110 let (ast, diags) = parse_str(
9111 "enum D {\n /** The first one. */ A = 1,\n /** The second one. */ B = 2,\n}",
9112 );
9113 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9114 let stmt = single_stmt(&ast);
9115 match &stmt.kind {
9116 crate::StmtKind::EnumDecl { members, .. } => {
9117 assert_eq!(members.len(), 2);
9118 assert_eq!(
9119 members[0].doc.as_ref().expect("first doc").summary,
9120 "The first one."
9121 );
9122 assert_eq!(
9123 members[1].doc.as_ref().expect("second doc").summary,
9124 "The second one."
9125 );
9126 }
9127 _ => panic!("expected EnumDecl"),
9128 }
9129 }
9130
9131 #[test]
9132 fn parse_enum_trailing_comma_ok() {
9133 let (ast, diags) = parse_str("enum D { A, B, }");
9134 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9135 let stmt = single_stmt(&ast);
9136 match &stmt.kind {
9137 crate::StmtKind::EnumDecl { members, .. } => assert_eq!(members.len(), 2),
9138 _ => panic!("expected EnumDecl"),
9139 }
9140 }
9141
9142 #[test]
9143 fn parse_function_empty_params() {
9144 let (ast, diags) = parse_str("function noop(): void { }");
9145 assert!(diags.is_empty());
9146 let stmt = single_stmt(&ast);
9147 match stmt.kind {
9148 crate::StmtKind::Function { ref params, .. } => assert!(params.is_empty()),
9149 _ => panic!("expected Function"),
9150 }
9151 }
9152
9153 #[test]
9154 fn parse_generic_function_single_param() {
9155 let (ast, diags) = parse_str("function identity<T>(x: T): T { return x; }");
9156 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9157 let stmt = single_stmt(&ast);
9158 match stmt.kind {
9159 crate::StmtKind::Function {
9160 ref name,
9161 ref generics,
9162 ref params,
9163 ..
9164 } => {
9165 assert_eq!(name.name, "identity");
9166 assert_eq!(generics.len(), 1);
9167 assert_eq!(generics[0].name, "T");
9168 assert_eq!(params.len(), 1);
9169 assert_eq!(params[0].name.name, "x");
9170 }
9171 _ => panic!("expected Function"),
9172 }
9173 }
9174
9175 #[test]
9176 fn parse_generic_function_multiple_params() {
9177 let (ast, diags) = parse_str("function pair<T, U>(a: T, b: U): T { return a; }");
9178 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9179 let stmt = single_stmt(&ast);
9180 match stmt.kind {
9181 crate::StmtKind::Function { ref generics, .. } => {
9182 assert_eq!(generics.len(), 2);
9183 assert_eq!(generics[0].name, "T");
9184 assert_eq!(generics[1].name, "U");
9185 }
9186 _ => panic!("expected Function"),
9187 }
9188 }
9189
9190 #[test]
9191 fn parse_non_generic_function_has_empty_generics() {
9192 let (ast, diags) = parse_str("function noop(): void { }");
9193 assert!(diags.is_empty());
9194 let stmt = single_stmt(&ast);
9195 match stmt.kind {
9196 crate::StmtKind::Function { ref generics, .. } => assert!(generics.is_empty()),
9197 _ => panic!("expected Function"),
9198 }
9199 }
9200
9201 #[test]
9202 fn parse_empty_generic_list_diagnoses() {
9203 let (_ast, diags) = parse_str("function f<>(): void { }");
9204 assert!(
9205 diags
9206 .iter()
9207 .any(|d| d.message.contains("empty generic parameter list")),
9208 "expected empty-generic-list diagnostic, got: {diags:?}",
9209 );
9210 }
9211
9212 #[test]
9213 fn parse_trailing_comma_in_generic_list_ok() {
9214 let (ast, diags) = parse_str("function f<T,>(): void { }");
9215 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9216 match single_stmt(&ast).kind {
9217 crate::StmtKind::Function { ref generics, .. } => assert_eq!(generics.len(), 1),
9218 _ => panic!("expected Function"),
9219 }
9220 }
9221
9222 #[test]
9223 fn parse_generic_call_single_type_arg() {
9224 let (ast, diags) = parse_str("identity<number>(42);");
9225 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9226 let stmt = single_stmt(&ast);
9227 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9228 panic!("expected Expr stmt");
9229 };
9230 match &ast.try_expr(call_id).unwrap().kind {
9231 crate::ExprKind::Call {
9232 type_args, args, ..
9233 } => {
9234 let ta = type_args.as_ref().expect("type_args set for generic call");
9235 assert_eq!(ta.len(), 1);
9236 assert_eq!(args.len(), 1);
9237 }
9238 other => panic!("expected Call, got {other:?}"),
9239 }
9240 }
9241
9242 #[test]
9243 fn parse_generic_call_trailing_comma_ok() {
9244 let (ast, diags) = parse_str("identity<number,>(42);");
9245 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9246 let stmt = single_stmt(&ast);
9247 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9248 panic!("expected Expr stmt");
9249 };
9250 match &ast.try_expr(call_id).unwrap().kind {
9251 crate::ExprKind::Call {
9252 type_args, args, ..
9253 } => {
9254 assert_eq!(type_args.as_ref().expect("type_args set").len(), 1);
9255 assert_eq!(args.len(), 1);
9256 }
9257 other => panic!("expected Call, got {other:?}"),
9258 }
9259 }
9260
9261 #[test]
9262 fn parse_generic_call_multi_type_args() {
9263 let (ast, diags) = parse_str("pair<number, string>(1, \"x\");");
9264 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9265 let stmt = single_stmt(&ast);
9266 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9267 panic!("expected Expr stmt");
9268 };
9269 match &ast.try_expr(call_id).unwrap().kind {
9270 crate::ExprKind::Call { type_args, .. } => {
9271 let ta = type_args.as_ref().expect("type_args set for generic call");
9272 assert_eq!(ta.len(), 2);
9273 }
9274 other => panic!("expected Call, got {other:?}"),
9275 }
9276 }
9277
9278 #[test]
9279 fn parse_comparison_chain_unaffected_by_generic_lookahead() {
9280 let (ast, diags) = parse_str("let z = a < b > c;");
9281 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9282 assert!(
9283 !ast_contains_call_with_type_args(&ast),
9284 "should not have parsed any generic call",
9285 );
9286 }
9287
9288 #[test]
9289 fn parse_lt_with_paren_rhs_is_comparison_not_generic() {
9290 let (ast, diags) = parse_str("let z = a < (b);");
9291 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9292 assert!(!ast_contains_call_with_type_args(&ast));
9293 }
9294
9295 #[test]
9296 fn parse_generic_call_no_args() {
9297 let (ast, diags) = parse_str("none<number>();");
9298 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9299 let stmt = single_stmt(&ast);
9300 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9301 panic!("expected Expr stmt");
9302 };
9303 match &ast.try_expr(call_id).unwrap().kind {
9304 crate::ExprKind::Call {
9305 type_args, args, ..
9306 } => {
9307 assert_eq!(type_args.as_ref().unwrap().len(), 1);
9308 assert!(args.is_empty());
9309 }
9310 other => panic!("expected Call, got {other:?}"),
9311 }
9312 }
9313
9314 fn ast_contains_call_with_type_args(ast: &crate::Ast) -> bool {
9315 for id in 0..ast.top_level.len() {
9316 let stmt = ast.try_stmt(ast.top_level[id]).unwrap();
9317 if walk_stmt_for_typed_call(ast, stmt) {
9318 return true;
9319 }
9320 }
9321 false
9322 }
9323
9324 fn walk_stmt_for_typed_call(ast: &crate::Ast, stmt: &crate::Stmt) -> bool {
9325 match &stmt.kind {
9326 crate::StmtKind::Let { value, .. } | crate::StmtKind::Const { value, .. } => {
9327 walk_expr_for_typed_call(ast, *value)
9328 }
9329 crate::StmtKind::Expr(e) => walk_expr_for_typed_call(ast, *e),
9330 crate::StmtKind::Block(stmts) => stmts
9331 .iter()
9332 .any(|sid| walk_stmt_for_typed_call(ast, ast.try_stmt(*sid).unwrap())),
9333 _ => false,
9334 }
9335 }
9336
9337 fn walk_expr_for_typed_call(ast: &crate::Ast, id: crate::ExprId) -> bool {
9338 match &ast.try_expr(id).unwrap().kind {
9339 crate::ExprKind::Call {
9340 type_args: Some(_), ..
9341 } => true,
9342 crate::ExprKind::Call { callee, args, .. } => {
9343 walk_expr_for_typed_call(ast, *callee)
9344 || args.iter().any(|a| walk_expr_for_typed_call(ast, *a))
9345 }
9346 crate::ExprKind::Binary { lhs, rhs, .. } => {
9347 walk_expr_for_typed_call(ast, *lhs) || walk_expr_for_typed_call(ast, *rhs)
9348 }
9349 crate::ExprKind::Unary { operand, .. } => walk_expr_for_typed_call(ast, *operand),
9350 crate::ExprKind::Paren(e) => walk_expr_for_typed_call(ast, *e),
9351 crate::ExprKind::FieldAccess { receiver, .. } => {
9352 walk_expr_for_typed_call(ast, *receiver)
9353 }
9354 crate::ExprKind::IndexAccess { receiver, index } => {
9355 walk_expr_for_typed_call(ast, *receiver) || walk_expr_for_typed_call(ast, *index)
9356 }
9357 _ => false,
9358 }
9359 }
9360
9361 #[test]
9362 fn parse_function_body_with_statements() {
9363 let (ast, diags) = parse_str("function g(x: number): number { let y = x; y; }");
9364 assert!(diags.is_empty());
9365 let stmt = single_stmt(&ast);
9366 match stmt.kind {
9367 crate::StmtKind::Function { body, .. } => {
9368 let block = ast.try_stmt(body).unwrap();
9369 match block.kind {
9370 crate::StmtKind::Block(ref v) => assert_eq!(v.len(), 2),
9371 _ => panic!("expected Block"),
9372 }
9373 }
9374 _ => panic!("expected Function"),
9375 }
9376 }
9377
9378 #[test]
9379 fn function_missing_return_type_diagnoses() {
9380 let (ast, diags) = parse_str("function f() { }");
9381 assert!(!diags.is_empty());
9382 assert_eq!(diags[0].message, "expected `:` and return type");
9383 assert!(ast.top_level.is_empty());
9384 }
9385
9386 #[test]
9387 fn method_missing_return_type_still_reads_the_body() {
9388 let (_ast, diags) = parse_str(
9389 "class Box {\n async run() {\n const x = await f();\n return x;\n }\n ok(): void {}\n}",
9390 );
9391 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9392 assert_eq!(
9393 messages,
9394 [
9395 "`async` functions are not supported",
9396 "expected `:` and return type",
9397 "`await` is not supported"
9398 ],
9399 "{diags:?}"
9400 );
9401 assert_eq!(
9402 diags[1].help,
9403 ["write the return type: `run(…): T { … }`, with `void` when it returns nothing"]
9404 );
9405 }
9406
9407 #[test]
9408 fn an_unclosed_method_body_leaves_the_next_declaration_alone() {
9409 let (ast, diags) =
9410 parse_str("class Box {\n a() { return 1;\n}\nfunction main(): number { return 0; }");
9411 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9412 assert_eq!(
9413 messages,
9414 [
9415 "expected `:` and return type",
9416 "expected `}` to close class"
9417 ],
9418 "{diags:?}"
9419 );
9420 assert_eq!(ast.top_level.len(), 1, "`main` is still declared");
9421 }
9422
9423 #[test]
9424 fn every_declaration_after_an_unclosed_method_body_is_kept() {
9425 for next in [
9426 "class B { ok(): number { return 2; } }",
9427 "interface I { a: number; }",
9428 "enum E { A }",
9429 "type X = number;",
9430 "import * as fs from \"submilli:fs\";",
9431 "async function g(): number { return 1; }",
9432 ] {
9433 let source = format!(
9434 "class A {{\n bad() {{\n return 1;\n}}\n{next}\nfunction main(): void {{}}"
9435 );
9436 let (ast, diags) = parse_str(&source);
9437 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9438 let expected_async = next.starts_with("async");
9439 let mut expected = vec![
9440 "expected `:` and return type",
9441 "expected `}` to close class",
9442 ];
9443 if expected_async {
9444 expected.push("`async` functions are not supported");
9445 }
9446 assert_eq!(messages, expected, "{next}: {diags:?}");
9447 assert_eq!(
9448 ast.top_level.len(),
9449 2,
9450 "{next} and `main` are kept: {diags:?}"
9451 );
9452 }
9453 }
9454
9455 #[test]
9456 fn a_failed_statement_leaves_the_next_declaration_to_be_parsed() {
9457 for (source, kept) in [
9458 ("let x = 1 +\nimport { exists } from \"submilli:fs\";", 1),
9459 ("const y =\nclass C {}\nfunction main(): void {}", 2),
9460 (
9461 "interface I {\n a: number;\nimport * as fs from \"submilli:fs\";",
9462 1,
9463 ),
9464 ] {
9465 let (ast, diags) = parse_str(source);
9466 assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9467 assert_eq!(ast.top_level.len(), kept, "{source}: {diags:?}");
9468 }
9469 }
9470
9471 #[test]
9472 fn a_dynamic_import_after_a_failed_statement_reports_once() {
9473 for source in [
9474 "const m = import(\"submilli:fs\");\nclass C {}",
9475 "const d = import.meta.dirname;\nclass C {}",
9476 ] {
9477 let (_ast, diags) = parse_str(source);
9478 assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9479 }
9480 }
9481
9482 #[test]
9483 fn members_named_like_declarations_after_a_bad_method_stay_members() {
9484 let (_ast, diags) = parse_str(
9485 "class A {\n bad() {}\n type: number = 1;\n function(): void {}\n declare(): void {}\n async(): void {}\n}",
9486 );
9487 assert_eq!(diags.len(), 1, "{diags:?}");
9488 }
9489
9490 #[test]
9491 fn every_member_after_a_bad_method_is_still_read() {
9492 let (_ast, diags) = parse_str(
9493 "class A {\n a() { return 1; }\n x: number = 1;\n get g(): number { return 1; }\n constructor() {}\n b() {}\n ok(): void {}\n}\nclass B {\n c() {}\n}",
9494 );
9495 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9496 assert_eq!(messages, ["expected `:` and return type"; 3], "{diags:?}");
9497 }
9498
9499 #[test]
9500 fn a_member_failing_after_a_type_literal_reports_once() {
9501 let (_ast, diags) = parse_str("class A {\n x: { a: number } 5\n}");
9502 assert_eq!(diags.len(), 1, "{diags:?}");
9503 }
9504
9505 #[test]
9506 fn multiple_declarators_name_the_split() {
9507 let (_ast, diags) = parse_str("let a = 0, b = 0;");
9508 assert_eq!(diags.len(), 1, "{diags:?}");
9509 assert_eq!(diags[0].message, "one declaration declares one variable");
9510 assert_eq!(
9511 diags[0].help,
9512 [
9513 "declare one variable per statement: `let a = …; let b = …;`; in a `for (…)` header, declare `b` before the loop"
9514 ]
9515 );
9516 let (_ast, diags) = parse_str("for (let i = 0, j = 10; i < j; i++) {}");
9517 assert_eq!(
9518 diags[0].message, "one declaration declares one variable",
9519 "{diags:?}"
9520 );
9521 assert!(
9522 diags[0].help[0].ends_with("declare `j` before the loop"),
9523 "{diags:?}"
9524 );
9525 }
9526
9527 #[test]
9528 fn missing_return_type_still_reads_the_body() {
9529 let (_ast, diags) = parse_str(
9530 "async function main() {\n const x = await f();\n if (x) { return 1; }\n return 2;\n}\nfunction g(): void {}",
9531 );
9532 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9533 assert_eq!(
9534 messages,
9535 [
9536 "`async` functions are not supported",
9537 "expected `:` and return type",
9538 "`await` is not supported"
9539 ],
9540 "{diags:?}"
9541 );
9542 assert_eq!(
9543 diags[1].help,
9544 [
9545 "write the return type: `function main(…): T { … }`, with `void` when it returns nothing"
9546 ]
9547 );
9548 }
9549
9550 #[test]
9551 fn function_missing_param_type_diagnoses() {
9552 let (ast, diags) = parse_str("function f(a): void { }");
9553 assert!(!diags.is_empty());
9554 assert_eq!(diags[0].message, "parameter requires a type annotation");
9555 assert!(ast.top_level.is_empty());
9556 }
9557
9558 #[test]
9559 fn function_missing_name_diagnoses() {
9560 let (ast, diags) = parse_str("function (x: number): void { }");
9561 assert!(!diags.is_empty());
9562 assert_eq!(diags[0].message, "expected function name");
9563 assert!(ast.top_level.is_empty());
9564 }
9565
9566 #[test]
9567 fn function_trailing_param_comma_ok() {
9568 let (ast, diags) = parse_str("function f(a: number,): void { }");
9569 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9570 match single_stmt(&ast).kind {
9571 crate::StmtKind::Function { ref params, .. } => assert_eq!(params.len(), 1),
9572 _ => panic!("expected Function"),
9573 }
9574 }
9575
9576 #[test]
9577 fn function_missing_open_brace_diagnoses() {
9578 let (ast, diags) = parse_str("function f(): void");
9579 assert_eq!(diags.len(), 1);
9580 assert_eq!(diags[0].message, "expected `{`");
9581 assert!(ast.top_level.is_empty());
9582 }
9583
9584 #[test]
9585 fn snapshot_function_declaration() {
9586 let (ast, diags) =
9587 parse_str("function add(a: number, b: number): number { let sum = a + b; sum; }");
9588 assert!(diags.is_empty());
9589 insta::assert_debug_snapshot!(ast);
9590 }
9591
9592 #[test]
9593 fn rest_param_basic_parses() {
9594 let (_, diags) = parse_str("function sum(...nums: number[]): number { return 0; }");
9595 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9596 }
9597
9598 #[test]
9599 fn rest_param_with_fixed_prefix_parses() {
9600 let (_, diags) =
9601 parse_str("function tag(label: string, ...vals: number[]): string { return label; }");
9602 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9603 }
9604
9605 #[test]
9606 fn rest_param_in_arrow_parses() {
9607 let (_, diags) = parse_str("const f = (...n: number[]) => n.length;");
9608 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9609 }
9610
9611 #[test]
9612 fn rest_param_in_function_type_annotation_parses() {
9613 let (_, diags) =
9614 parse_str("let f: (a: number, ...rest: string[]) => void = (a, ...r) => {};");
9615 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9616 }
9617
9618 #[test]
9619 fn rest_param_not_last_rejected() {
9620 let (_, diags) = parse_str("function f(...xs: number[], y: number): void {}");
9621 assert!(
9622 diags.iter().any(|d| d
9623 .message
9624 .contains("rest parameter must be the last parameter")),
9625 "expected 'must be last' diagnostic, got {diags:?}",
9626 );
9627 }
9628
9629 #[test]
9630 fn rest_param_with_default_rejected() {
9631 let (_, diags) = parse_str("function f(...xs: number[] = []): void {}");
9632 assert!(
9633 diags.iter().any(|d| d
9634 .message
9635 .contains("rest parameter cannot have a default value")),
9636 "expected default-rejection diagnostic, got {diags:?}",
9637 );
9638 }
9639
9640 #[test]
9641 fn rest_param_without_annotation_rejected() {
9642 let (_, diags) = parse_str("function f(...xs): void {}");
9643 assert!(
9644 diags.iter().any(|d| d
9645 .message
9646 .contains("rest parameter requires a type annotation")),
9647 "expected missing-annotation diagnostic, got {diags:?}",
9648 );
9649 }
9650
9651 #[test]
9652 fn rest_param_destructured_rejected() {
9653 let (_, diags) = parse_str("function f(...{a, b}): void {}");
9654 assert!(
9655 diags
9656 .iter()
9657 .any(|d| d.message.contains("rest parameter cannot be destructured")),
9658 "expected destructured-rejection diagnostic, got {diags:?}",
9659 );
9660 }
9661
9662 #[test]
9663 fn rest_param_in_function_type_with_non_array_rejected() {
9664 let (_, diags) = parse_str("let f: (...xs: number) => void = () => {};");
9665 assert!(
9666 diags
9667 .iter()
9668 .any(|d| d.message.contains("rest parameter type must be an array")),
9669 "expected non-array-type diagnostic, got {diags:?}",
9670 );
9671 }
9672
9673 fn call(e: &crate::Expr) -> (crate::ExprId, &[crate::ExprId]) {
9674 match e.kind {
9675 crate::ExprKind::Call {
9676 callee, ref args, ..
9677 } => (callee, args.as_slice()),
9678 _ => panic!("expected Call, got {:?}", e.kind),
9679 }
9680 }
9681
9682 #[test]
9683 fn parse_call_no_args() {
9684 let (ast, diags) = parse_str("f();");
9685 assert!(diags.is_empty());
9686 let outer = expr_of_single_stmt(&ast);
9687 let (callee, args) = call(outer);
9688 assert!(matches!(
9689 ast.try_expr(callee).unwrap().kind,
9690 crate::ExprKind::Identifier(_)
9691 ));
9692 assert!(args.is_empty());
9693 assert_eq!(outer.span, crate::Span::new(F, 0, 3).unwrap());
9694 }
9695
9696 #[test]
9697 fn parse_call_one_arg() {
9698 let (ast, diags) = parse_str("f(1);");
9699 assert!(diags.is_empty());
9700 let outer = expr_of_single_stmt(&ast);
9701 let (callee, args) = call(outer);
9702 assert!(matches!(
9703 ast.try_expr(callee).unwrap().kind,
9704 crate::ExprKind::Identifier(_)
9705 ));
9706 assert_eq!(args.len(), 1);
9707 assert_eq!(
9708 ast.try_expr(args[0]).unwrap().kind,
9709 crate::ExprKind::Number(1.0)
9710 );
9711 }
9712
9713 #[test]
9714 fn parse_call_three_args() {
9715 let (ast, diags) = parse_str("f(1, 2, 3);");
9716 assert!(diags.is_empty());
9717 let outer = expr_of_single_stmt(&ast);
9718 let (_callee, args) = call(outer);
9719 assert_eq!(args.len(), 3);
9720 assert_eq!(
9721 ast.try_expr(args[0]).unwrap().kind,
9722 crate::ExprKind::Number(1.0)
9723 );
9724 assert_eq!(
9725 ast.try_expr(args[1]).unwrap().kind,
9726 crate::ExprKind::Number(2.0)
9727 );
9728 assert_eq!(
9729 ast.try_expr(args[2]).unwrap().kind,
9730 crate::ExprKind::Number(3.0)
9731 );
9732 }
9733
9734 #[test]
9735 fn parse_call_nested() {
9736 let (ast, diags) = parse_str("f(g(x));");
9737 assert!(diags.is_empty());
9738 let outer = expr_of_single_stmt(&ast);
9739 let (_callee, args) = call(outer);
9740 assert_eq!(args.len(), 1);
9741 let (_inner_callee, inner_args) = call(ast.try_expr(args[0]).unwrap());
9742 assert_eq!(inner_args.len(), 1);
9743 assert!(matches!(
9744 ast.try_expr(inner_args[0]).unwrap().kind,
9745 crate::ExprKind::Identifier(_)
9746 ));
9747 }
9748
9749 #[test]
9750 fn parse_curried_call() {
9751 let (ast, diags) = parse_str("f()(x);");
9752 assert!(diags.is_empty());
9753 let outer = expr_of_single_stmt(&ast);
9754 let (callee, args) = call(outer);
9755 assert_eq!(args.len(), 1);
9756 let (_inner_callee, inner_args) = call(ast.try_expr(callee).unwrap());
9757 assert!(inner_args.is_empty());
9758 }
9759
9760 #[test]
9761 fn unary_binds_looser_than_call() {
9762 let (ast, diags) = parse_str("-f();");
9763 assert!(diags.is_empty());
9764 let outer = expr_of_single_stmt(&ast);
9765 let (op, operand) = unary(outer);
9766 assert_eq!(op, crate::UnOp::Neg);
9767 let (_callee, args) = call(ast.try_expr(operand).unwrap());
9768 assert!(args.is_empty());
9769 }
9770
9771 #[test]
9772 fn call_inside_binary() {
9773 let (ast, diags) = parse_str("f() + 1;");
9774 assert!(diags.is_empty());
9775 let outer = expr_of_single_stmt(&ast);
9776 let (op, lhs, rhs) = binary(outer);
9777 assert_eq!(op, crate::BinOp::Add);
9778 let (_callee, args) = call(ast.try_expr(lhs).unwrap());
9779 assert!(args.is_empty());
9780 assert_eq!(
9781 ast.try_expr(rhs).unwrap().kind,
9782 crate::ExprKind::Number(1.0)
9783 );
9784 }
9785
9786 #[test]
9787 fn call_with_complex_args() {
9788 let (ast, diags) = parse_str("f(1 + 2, g(x));");
9789 assert!(diags.is_empty());
9790 let outer = expr_of_single_stmt(&ast);
9791 let (_callee, args) = call(outer);
9792 assert_eq!(args.len(), 2);
9793 let (op, ..) = binary(ast.try_expr(args[0]).unwrap());
9794 assert_eq!(op, crate::BinOp::Add);
9795 let (_, inner_args) = call(ast.try_expr(args[1]).unwrap());
9796 assert_eq!(inner_args.len(), 1);
9797 }
9798
9799 #[test]
9800 fn call_trailing_comma_ok() {
9801 let (ast, diags) = parse_str("f(1, 2,);");
9802 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9803 let (_callee, args) = call(expr_of_single_stmt(&ast));
9804 assert_eq!(args.len(), 2);
9805 }
9806
9807 #[test]
9808 fn unclosed_call_diagnoses() {
9809 let (ast, diags) = parse_str("f(1, 2");
9810 assert!(!diags.is_empty());
9811 assert!(diags[0].message == "expected `,` or `)`" || diags[0].message == "expected `)`");
9812 assert!(ast.top_level.is_empty());
9813 }
9814
9815 #[test]
9816 fn call_expression_statement() {
9817 let (ast, diags) = parse_str(r#"greet("world");"#);
9818 assert!(diags.is_empty());
9819 let outer = expr_of_single_stmt(&ast);
9820 let (callee, args) = call(outer);
9821 assert!(matches!(
9822 ast.try_expr(callee).unwrap().kind,
9823 crate::ExprKind::Identifier(_)
9824 ));
9825 assert_eq!(args.len(), 1);
9826 assert_eq!(
9827 ast.try_expr(args[0]).unwrap().kind,
9828 crate::ExprKind::String("world".to_string())
9829 );
9830 }
9831
9832 #[test]
9833 fn snapshot_multi_call_program() {
9834 let (ast, diags) = parse_str("f(); g(1); h(f(2), 3);");
9835 assert!(diags.is_empty());
9836 assert_eq!(ast.top_level.len(), 3);
9837 insta::assert_debug_snapshot!(ast);
9838 }
9839
9840 #[test]
9841 fn parse_if_simple() {
9842 let (ast, diags) = parse_str("if (a) { }");
9843 assert!(diags.is_empty());
9844 let stmt = single_stmt(&ast);
9845 match stmt.kind {
9846 crate::StmtKind::If {
9847 condition,
9848 then_block,
9849 else_block,
9850 } => {
9851 assert!(matches!(
9852 ast.try_expr(condition).unwrap().kind,
9853 crate::ExprKind::Identifier(_)
9854 ));
9855 assert!(matches!(
9856 ast.try_stmt(then_block).unwrap().kind,
9857 crate::StmtKind::Block(ref v) if v.is_empty()
9858 ));
9859 assert!(else_block.is_none());
9860 }
9861 _ => panic!("expected If"),
9862 }
9863 }
9864
9865 #[test]
9866 fn parse_if_else() {
9867 let (ast, diags) = parse_str("if (a) { } else { }");
9868 assert!(diags.is_empty());
9869 let stmt = single_stmt(&ast);
9870 match stmt.kind {
9871 crate::StmtKind::If { else_block, .. } => {
9872 let else_id = else_block.expect("expected else block");
9873 assert!(matches!(
9874 ast.try_stmt(else_id).unwrap().kind,
9875 crate::StmtKind::Block(ref v) if v.is_empty()
9876 ));
9877 }
9878 _ => panic!("expected If"),
9879 }
9880 }
9881
9882 #[test]
9883 fn parse_else_if_chain() {
9884 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
9885 assert!(diags.is_empty());
9886 let stmt = single_stmt(&ast);
9887 let crate::StmtKind::If {
9888 else_block: Some(inner_id),
9889 ..
9890 } = stmt.kind
9891 else {
9892 panic!("expected outer If with else_block");
9893 };
9894 let inner = ast.try_stmt(inner_id).unwrap();
9895 let crate::StmtKind::If {
9896 else_block: Some(tail_id),
9897 ..
9898 } = inner.kind
9899 else {
9900 panic!("expected inner If for `else if`");
9901 };
9902 assert!(matches!(
9903 ast.try_stmt(tail_id).unwrap().kind,
9904 crate::StmtKind::Block(_)
9905 ));
9906 }
9907
9908 #[test]
9909 fn parse_if_with_body_statements() {
9910 let (ast, diags) = parse_str("if (a) { let x = 1; }");
9911 assert!(diags.is_empty());
9912 let stmt = single_stmt(&ast);
9913 match stmt.kind {
9914 crate::StmtKind::If { then_block, .. } => {
9915 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind
9916 else {
9917 panic!("expected Block")
9918 };
9919 assert_eq!(body.len(), 1);
9920 }
9921 _ => panic!("expected If"),
9922 }
9923 }
9924
9925 #[test]
9926 fn parse_if_braceless_body() {
9927 let (ast, diags) = parse_str("if (a) return b;");
9928 assert!(diags.is_empty(), "{diags:?}");
9929 let stmt = single_stmt(&ast);
9930 let crate::StmtKind::If {
9931 then_block,
9932 else_block,
9933 ..
9934 } = stmt.kind
9935 else {
9936 panic!("expected If");
9937 };
9938 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9939 panic!("braceless body should wrap in a Block");
9940 };
9941 assert_eq!(body.len(), 1);
9942 assert!(matches!(
9943 ast.try_stmt(body[0]).unwrap().kind,
9944 crate::StmtKind::Return(_)
9945 ));
9946 assert!(else_block.is_none());
9947 }
9948
9949 #[test]
9950 fn parse_braceless_dangling_else_binds_nearest_if() {
9951 let (ast, diags) = parse_str("if (a) if (b) x(); else y();");
9953 assert!(diags.is_empty(), "{diags:?}");
9954 let crate::StmtKind::If {
9955 then_block,
9956 else_block: outer_else,
9957 ..
9958 } = single_stmt(&ast).kind
9959 else {
9960 panic!("expected outer If");
9961 };
9962 assert!(outer_else.is_none(), "else must bind to the inner if");
9963 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9964 panic!("expected wrapped Block");
9965 };
9966 let crate::StmtKind::If {
9967 else_block: Some(_),
9968 ..
9969 } = ast.try_stmt(body[0]).unwrap().kind
9970 else {
9971 panic!("inner If should own the else");
9972 };
9973 }
9974
9975 #[test]
9976 fn parse_while_braceless_body() {
9977 let (ast, diags) = parse_str("while (a) x();");
9978 assert!(diags.is_empty(), "{diags:?}");
9979 let crate::StmtKind::While { body, .. } = single_stmt(&ast).kind else {
9980 panic!("expected While");
9981 };
9982 assert!(matches!(
9983 ast.try_stmt(body).unwrap().kind,
9984 crate::StmtKind::Block(ref v) if v.len() == 1
9985 ));
9986 }
9987
9988 #[test]
9989 fn parse_loop_with_empty_body() {
9990 for src in [
9991 "while (a());",
9992 "for (const x of xs);",
9993 "for (let i = 0; i < 3; i++);",
9994 "do ; while (a());",
9995 ] {
9996 let (ast, diags) = parse_str(src);
9997 assert!(diags.is_empty(), "{src}: {diags:?}");
9998 let body = match single_stmt(&ast).kind {
9999 crate::StmtKind::While { body, .. }
10000 | crate::StmtKind::DoWhile { body, .. }
10001 | crate::StmtKind::For { body, .. }
10002 | crate::StmtKind::ForOf { body, .. } => body,
10003 ref other => panic!("{src}: expected a loop, got {other:?}"),
10004 };
10005 assert!(
10006 matches!(ast.try_stmt(body).unwrap().kind, crate::StmtKind::Block(ref v) if v.is_empty()),
10007 "{src}"
10008 );
10009 }
10010 }
10011
10012 #[test]
10013 fn parse_if_with_empty_body_is_an_error() {
10014 let (_ast, diags) = parse_str("if (a);");
10015 assert_eq!(diags[0].message, "`if` has an empty body");
10016 }
10017
10018 #[test]
10019 fn parse_empty_branch_body_reports_once() {
10020 let (_ast, diags) = parse_str("if (a) ; else b();");
10021 assert_eq!(diags.len(), 1, "{diags:?}");
10022 assert_eq!(diags[0].message, "`if` has an empty body");
10023 let (_ast, diags) = parse_str("if (a) b(); else ;");
10024 assert_eq!(diags.len(), 1, "{diags:?}");
10025 assert_eq!(diags[0].message, "`else` has an empty body");
10026 let (_ast, diags) = parse_str("if (a) ; else if (b) ; else ;");
10028 let messages: Vec<_> = diags.iter().map(|d| d.message.as_str()).collect();
10029 assert_eq!(
10030 messages,
10031 [
10032 "`if` has an empty body",
10033 "`if` has an empty body",
10034 "`else` has an empty body"
10035 ]
10036 );
10037 }
10038
10039 #[test]
10040 fn parse_for_of_braceless_body() {
10041 let (ast, diags) = parse_str("for (const x of xs) f(x);");
10042 assert!(diags.is_empty(), "{diags:?}");
10043 let crate::StmtKind::ForOf { body, .. } = single_stmt(&ast).kind else {
10044 panic!("expected ForOf");
10045 };
10046 assert!(matches!(
10047 ast.try_stmt(body).unwrap().kind,
10048 crate::StmtKind::Block(ref v) if v.len() == 1
10049 ));
10050 }
10051
10052 #[test]
10053 fn parse_nested_if() {
10054 let (ast, diags) = parse_str("if (a) { if (b) { } }");
10055 assert!(diags.is_empty());
10056 let outer = single_stmt(&ast);
10057 let crate::StmtKind::If { then_block, .. } = outer.kind else {
10058 panic!("expected outer If");
10059 };
10060 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10061 panic!("expected Block")
10062 };
10063 assert_eq!(body.len(), 1);
10064 assert!(matches!(
10065 ast.try_stmt(body[0]).unwrap().kind,
10066 crate::StmtKind::If { .. }
10067 ));
10068 }
10069
10070 #[test]
10071 fn if_missing_open_paren_diagnoses() {
10072 let (ast, diags) = parse_str("if a { }");
10073 assert!(!diags.is_empty());
10074 assert_eq!(diags[0].message, "expected `(`");
10075 assert!(ast.top_level.is_empty());
10076 }
10077
10078 #[test]
10079 fn if_missing_close_paren_diagnoses() {
10080 let (ast, diags) = parse_str("if (a { }");
10081 assert!(!diags.is_empty());
10082 assert_eq!(diags[0].message, "expected `)`");
10083 assert!(ast.top_level.is_empty());
10084 }
10085
10086 #[test]
10087 fn if_braceless_body_parses() {
10088 let (ast, diags) = parse_str("if (a) x;");
10089 assert!(diags.is_empty(), "{diags:?}");
10090 let crate::StmtKind::If { then_block, .. } = single_stmt(&ast).kind else {
10091 panic!("expected If");
10092 };
10093 assert!(matches!(
10094 ast.try_stmt(then_block).unwrap().kind,
10095 crate::StmtKind::Block(ref v) if v.len() == 1
10096 ));
10097 }
10098
10099 #[test]
10100 fn snapshot_else_if_chain() {
10101 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10102 assert!(diags.is_empty());
10103 insta::assert_debug_snapshot!(ast);
10104 }
10105
10106 #[test]
10107 fn parse_while_simple() {
10108 let (ast, diags) = parse_str("while (a) { }");
10109 assert!(diags.is_empty());
10110 let stmt = single_stmt(&ast);
10111 match stmt.kind {
10112 crate::StmtKind::While { condition, body } => {
10113 assert!(matches!(
10114 ast.try_expr(condition).unwrap().kind,
10115 crate::ExprKind::Identifier(_)
10116 ));
10117 assert!(matches!(
10118 ast.try_stmt(body).unwrap().kind,
10119 crate::StmtKind::Block(ref v) if v.is_empty()
10120 ));
10121 }
10122 _ => panic!("expected While"),
10123 }
10124 }
10125
10126 #[test]
10127 fn parse_while_with_body() {
10128 let (ast, diags) = parse_str("while (a) { x; y; }");
10129 assert!(diags.is_empty());
10130 let stmt = single_stmt(&ast);
10131 let crate::StmtKind::While { body, .. } = stmt.kind else {
10132 panic!("expected While");
10133 };
10134 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10135 panic!("expected Block")
10136 };
10137 assert_eq!(stmts.len(), 2);
10138 }
10139
10140 #[test]
10141 fn while_missing_paren_diagnoses() {
10142 let (ast, diags) = parse_str("while a { }");
10143 assert!(!diags.is_empty());
10144 assert_eq!(diags[0].message, "expected `(`");
10145 assert!(ast.top_level.is_empty());
10146 }
10147
10148 #[test]
10149 fn parse_for_all_slots() {
10150 let (ast, diags) = parse_str("for (let i = 0; i < 10; i = i + 1) { x; }");
10151 assert!(diags.is_empty(), "{diags:?}");
10152 let stmt = single_stmt(&ast);
10153 let crate::StmtKind::For {
10154 init,
10155 condition,
10156 update,
10157 body,
10158 } = stmt.kind
10159 else {
10160 panic!("expected For");
10161 };
10162 assert!(init.is_some(), "init missing");
10163 assert!(condition.is_some(), "condition missing");
10164 assert!(update.is_some(), "update missing");
10165 assert!(matches!(
10166 ast.try_stmt(body).unwrap().kind,
10167 crate::StmtKind::Block(_)
10168 ));
10169 }
10170
10171 #[test]
10172 fn parse_for_empty_slots() {
10173 let (ast, diags) = parse_str("for (;;) { break; }");
10174 assert!(diags.is_empty(), "{diags:?}");
10175 let stmt = single_stmt(&ast);
10176 let crate::StmtKind::For {
10177 init,
10178 condition,
10179 update,
10180 ..
10181 } = stmt.kind
10182 else {
10183 panic!("expected For");
10184 };
10185 assert!(init.is_none() && condition.is_none() && update.is_none());
10186 }
10187
10188 #[test]
10189 fn parse_for_of_const_binding() {
10190 let (ast, diags) = parse_str("for (const x of arr) { y; }");
10191 assert!(diags.is_empty(), "{diags:?}");
10192 let stmt = single_stmt(&ast);
10193 let crate::StmtKind::ForOf {
10194 binding_kind,
10195 ref name,
10196 ref ty,
10197 ..
10198 } = stmt.kind
10199 else {
10200 panic!("expected ForOf");
10201 };
10202 assert!(matches!(binding_kind, crate::BindingKind::Const));
10203 assert_eq!(name.name, "x");
10204 assert!(ty.is_none());
10205 }
10206
10207 #[test]
10208 fn parse_for_of_let_with_annotation() {
10209 let (ast, diags) = parse_str("for (let x: number of arr) { y; }");
10210 assert!(diags.is_empty(), "{diags:?}");
10211 let stmt = single_stmt(&ast);
10212 let crate::StmtKind::ForOf {
10213 binding_kind,
10214 ref ty,
10215 ..
10216 } = stmt.kind
10217 else {
10218 panic!("expected ForOf");
10219 };
10220 assert!(matches!(binding_kind, crate::BindingKind::Let));
10221 assert!(ty.is_some());
10222 }
10223
10224 #[test]
10225 fn parse_do_while_simple() {
10226 let (ast, diags) = parse_str("do { x; } while (a);");
10227 assert!(diags.is_empty(), "{diags:?}");
10228 let stmt = single_stmt(&ast);
10229 assert!(matches!(stmt.kind, crate::StmtKind::DoWhile { .. }));
10230 }
10231
10232 #[test]
10233 fn parse_break_and_continue() {
10234 let (ast, diags) = parse_str("while (a) { break; continue; }");
10235 assert!(diags.is_empty(), "{diags:?}");
10236 let stmt = single_stmt(&ast);
10237 let crate::StmtKind::While { body, .. } = stmt.kind else {
10238 panic!("expected While");
10239 };
10240 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10241 panic!("expected Block");
10242 };
10243 assert!(matches!(
10244 ast.try_stmt(stmts[0]).unwrap().kind,
10245 crate::StmtKind::Break
10246 ));
10247 assert!(matches!(
10248 ast.try_stmt(stmts[1]).unwrap().kind,
10249 crate::StmtKind::Continue
10250 ));
10251 }
10252
10253 #[test]
10254 fn parse_bare_return_in_function() {
10255 let (ast, diags) = parse_str("function f(): void { return; }");
10256 assert!(diags.is_empty());
10257 let f = single_stmt(&ast);
10258 let crate::StmtKind::Function { body, .. } = f.kind else {
10259 panic!("expected Function");
10260 };
10261 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10262 panic!("expected Block")
10263 };
10264 assert_eq!(stmts.len(), 1);
10265 match ast.try_stmt(stmts[0]).unwrap().kind {
10266 crate::StmtKind::Return(None) => {}
10267 _ => panic!("expected Return(None)"),
10268 }
10269 }
10270
10271 #[test]
10272 fn parse_return_with_value() {
10273 let (ast, diags) = parse_str("function f(): number { return 1; }");
10274 assert!(diags.is_empty());
10275 let f = single_stmt(&ast);
10276 let crate::StmtKind::Function { body, .. } = f.kind else {
10277 panic!("expected Function");
10278 };
10279 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10280 panic!("expected Block")
10281 };
10282 match ast.try_stmt(stmts[0]).unwrap().kind {
10283 crate::StmtKind::Return(Some(value)) => {
10284 assert_eq!(
10285 ast.try_expr(value).unwrap().kind,
10286 crate::ExprKind::Number(1.0)
10287 );
10288 }
10289 _ => panic!("expected Return(Some)"),
10290 }
10291 }
10292
10293 #[test]
10294 fn parse_return_with_expression() {
10295 let (ast, diags) = parse_str("function f(): number { return 1 + 2; }");
10296 assert!(diags.is_empty());
10297 let f = single_stmt(&ast);
10298 let crate::StmtKind::Function { body, .. } = f.kind else {
10299 panic!("expected Function");
10300 };
10301 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10302 panic!("expected Block")
10303 };
10304 match ast.try_stmt(stmts[0]).unwrap().kind {
10305 crate::StmtKind::Return(Some(value)) => {
10306 let (op, ..) = binary(ast.try_expr(value).unwrap());
10307 assert_eq!(op, crate::BinOp::Add);
10308 }
10309 _ => panic!("expected Return(Some(Binary))"),
10310 }
10311 }
10312
10313 #[test]
10314 fn return_missing_semicolon_diagnoses() {
10315 let (_, diags) = parse_str("function f(): number { return x x; }");
10316 assert!(!diags.is_empty());
10317 assert_eq!(diags[0].message, "expected `;` after return");
10318 }
10319
10320 #[test]
10321 fn parse_bare_block() {
10322 let (ast, diags) = parse_str("{ x; y; }");
10323 assert!(diags.is_empty());
10324 let stmt = single_stmt(&ast);
10325 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10326 panic!("expected Block");
10327 };
10328 assert_eq!(stmts.len(), 2);
10329 }
10330
10331 #[test]
10332 fn parse_empty_bare_block() {
10333 let (ast, diags) = parse_str("{ }");
10334 assert!(diags.is_empty());
10335 let stmt = single_stmt(&ast);
10336 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10337 panic!("expected Block");
10338 };
10339 assert!(stmts.is_empty());
10340 }
10341
10342 #[test]
10343 fn parse_nested_bare_blocks() {
10344 let (ast, diags) = parse_str("{ { } }");
10345 assert!(diags.is_empty());
10346 let outer = single_stmt(&ast);
10347 let crate::StmtKind::Block(ref stmts) = outer.kind else {
10348 panic!("expected outer Block");
10349 };
10350 assert_eq!(stmts.len(), 1);
10351 assert!(matches!(
10352 ast.try_stmt(stmts[0]).unwrap().kind,
10353 crate::StmtKind::Block(ref v) if v.is_empty()
10354 ));
10355 }
10356
10357 #[test]
10358 fn snapshot_function_with_if_and_return() {
10359 let source = "function isPositive(n: number): boolean {\n if (n > 0) {\n return true;\n } else {\n return false;\n }\n}";
10360 let (ast, diags) = parse_str(source);
10361 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10362 insta::assert_debug_snapshot!(ast);
10363 }
10364
10365 fn object_literal(e: &crate::Expr) -> &[crate::ObjectLiteralMember] {
10366 match e.kind {
10367 crate::ExprKind::ObjectLiteral { ref members } => members.as_slice(),
10368 _ => panic!("expected ObjectLiteral, got {:?}", e.kind),
10369 }
10370 }
10371
10372 fn object_literal_field(m: &crate::ObjectLiteralMember) -> &crate::ObjectLiteralField {
10373 match m {
10374 crate::ObjectLiteralMember::Field(f) => f,
10375 crate::ObjectLiteralMember::Spread { .. }
10376 | crate::ObjectLiteralMember::Computed { .. } => {
10377 panic!("expected Field member, got Spread")
10378 }
10379 }
10380 }
10381
10382 fn array_literal(e: &crate::Expr) -> &[crate::ArrayLiteralElement] {
10383 match e.kind {
10384 crate::ExprKind::ArrayLiteral { ref elements } => elements.as_slice(),
10385 _ => panic!("expected ArrayLiteral, got {:?}", e.kind),
10386 }
10387 }
10388
10389 fn array_literal_value(el: &crate::ArrayLiteralElement) -> crate::ExprId {
10390 match el {
10391 crate::ArrayLiteralElement::Value(id) => *id,
10392 crate::ArrayLiteralElement::Spread { .. } => {
10393 panic!("expected Value element, got Spread")
10394 }
10395 }
10396 }
10397
10398 #[test]
10399 fn parse_empty_object_literal() {
10400 let (ast, diags) = parse_str("let x = {};");
10401 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10402 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10403 panic!("expected Let");
10404 };
10405 assert!(object_literal(ast.try_expr(value).unwrap()).is_empty());
10406 }
10407
10408 #[test]
10409 fn parse_object_literal_simple() {
10410 let (ast, diags) = parse_str("let p = { x: 1, y: 2 };");
10411 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10412 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10413 panic!("expected Let");
10414 };
10415 let members = object_literal(ast.try_expr(value).unwrap());
10416 assert_eq!(members.len(), 2);
10417 let f0 = object_literal_field(&members[0]);
10418 let f1 = object_literal_field(&members[1]);
10419 assert_eq!(f0.name.name, "x");
10420 assert_eq!(f1.name.name, "y");
10421 assert_eq!(
10422 ast.try_expr(f0.value).unwrap().kind,
10423 crate::ExprKind::Number(1.0)
10424 );
10425 assert_eq!(
10426 ast.try_expr(f1.value).unwrap().kind,
10427 crate::ExprKind::Number(2.0)
10428 );
10429 }
10430
10431 #[test]
10432 fn parse_object_literal_keyword_keys() {
10433 let (ast, diags) = parse_str("let p = { type: 1, default: 2, null: 3, true: 4 };");
10434 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10435 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10436 panic!("expected Let");
10437 };
10438 let names: Vec<&str> = object_literal(ast.try_expr(value).unwrap())
10439 .iter()
10440 .map(object_literal_field)
10441 .map(|f| f.name.name.as_str())
10442 .collect();
10443 assert_eq!(names, vec!["type", "default", "null", "true"]);
10444 }
10445
10446 #[test]
10447 fn parse_object_literal_string_key() {
10448 let (ast, diags) = parse_str(r#"let p = { "hello": 1 };"#);
10449 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10450 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10451 panic!("expected Let");
10452 };
10453 let members = object_literal(ast.try_expr(value).unwrap());
10454 assert_eq!(members.len(), 1);
10455 let f0 = object_literal_field(&members[0]);
10456 assert_eq!(f0.name.name, "hello");
10457 }
10458
10459 #[test]
10460 fn parse_object_literal_string_key_decodes_escape() {
10461 let (ast, diags) = parse_str(r#"let p = { "content\u002dtype": 1 };"#);
10462 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10463 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10464 panic!("expected Let");
10465 };
10466 let members = object_literal(ast.try_expr(value).unwrap());
10467 assert_eq!(members.len(), 1);
10468 let f0 = object_literal_field(&members[0]);
10469 assert_eq!(f0.name.name, "content-type");
10470 }
10471
10472 #[test]
10473 fn parse_object_literal_shorthand() {
10474 let (ast, diags) = parse_str("let p = { x, y: 2 };");
10475 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10476 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10477 panic!("expected Let");
10478 };
10479 let members = object_literal(ast.try_expr(value).unwrap());
10480 assert_eq!(members.len(), 2);
10481 let f0 = object_literal_field(&members[0]);
10482 assert_eq!(f0.name.name, "x");
10483 assert_eq!(
10485 ast.try_expr(f0.value).unwrap().kind,
10486 crate::ExprKind::Identifier(crate::Ident {
10487 name: "x".into(),
10488 span: f0.name.span,
10489 })
10490 );
10491 let f1 = object_literal_field(&members[1]);
10492 assert_eq!(f1.name.name, "y");
10493 assert_eq!(
10494 ast.try_expr(f1.value).unwrap().kind,
10495 crate::ExprKind::Number(2.0)
10496 );
10497 }
10498
10499 #[test]
10500 fn parse_object_literal_string_key_shorthand_rejected() {
10501 let (_ast, diags) = parse_str(r#"let p = { "x" };"#);
10502 assert!(
10503 diags
10504 .iter()
10505 .any(|d| d.message.contains("expected `:` after field name")),
10506 "string-key shorthand should be rejected: {diags:?}"
10507 );
10508 }
10509
10510 #[test]
10511 fn parse_object_literal_trailing_comma() {
10512 let (ast, diags) = parse_str("let p = { x: 1, y: 2, };");
10513 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10514 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10515 panic!("expected Let");
10516 };
10517 assert_eq!(object_literal(ast.try_expr(value).unwrap()).len(), 2);
10518 }
10519
10520 #[test]
10521 fn parse_object_literal_duplicate_key_diagnoses() {
10522 let (_, diags) = parse_str("let p = { x: 1, x: 2 };");
10523 assert_eq!(diags.len(), 1);
10524 assert_eq!(diags[0].message, "duplicate field `x` in object literal");
10525 }
10526
10527 #[test]
10528 fn parse_empty_array_literal() {
10529 let (ast, diags) = parse_str("let xs = [];");
10530 assert!(diags.is_empty());
10531 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10532 panic!("expected Let");
10533 };
10534 assert!(array_literal(ast.try_expr(value).unwrap()).is_empty());
10535 }
10536
10537 #[test]
10538 fn parse_array_literal_simple() {
10539 let (ast, diags) = parse_str("let xs = [1, 2, 3];");
10540 assert!(diags.is_empty());
10541 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10542 panic!("expected Let");
10543 };
10544 let elements = array_literal(ast.try_expr(value).unwrap());
10545 assert_eq!(elements.len(), 3);
10546 assert_eq!(
10547 ast.try_expr(array_literal_value(&elements[0]))
10548 .unwrap()
10549 .kind,
10550 crate::ExprKind::Number(1.0)
10551 );
10552 assert_eq!(
10553 ast.try_expr(array_literal_value(&elements[2]))
10554 .unwrap()
10555 .kind,
10556 crate::ExprKind::Number(3.0)
10557 );
10558 }
10559
10560 #[test]
10561 fn parse_array_literal_trailing_comma() {
10562 let (ast, diags) = parse_str("let xs = [1, 2,];");
10563 assert!(diags.is_empty());
10564 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10565 panic!("expected Let");
10566 };
10567 assert_eq!(array_literal(ast.try_expr(value).unwrap()).len(), 2);
10568 }
10569
10570 #[test]
10571 fn parse_nested_array_in_object() {
10572 let (ast, diags) = parse_str("let p = { items: [1, 2] };");
10573 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10574 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10575 panic!("expected Let");
10576 };
10577 let members = object_literal(ast.try_expr(value).unwrap());
10578 let f0 = object_literal_field(&members[0]);
10579 assert_eq!(f0.name.name, "items");
10580 let inner = array_literal(ast.try_expr(f0.value).unwrap());
10581 assert_eq!(inner.len(), 2);
10582 }
10583
10584 #[test]
10585 fn parse_nested_object_in_array() {
10586 let (ast, diags) = parse_str("let xs = [{ x: 1 }, { x: 2 }];");
10587 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10588 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10589 panic!("expected Let");
10590 };
10591 let elements = array_literal(ast.try_expr(value).unwrap());
10592 assert_eq!(elements.len(), 2);
10593 assert_eq!(
10594 object_literal(ast.try_expr(array_literal_value(&elements[0])).unwrap()).len(),
10595 1
10596 );
10597 assert_eq!(
10598 object_literal(ast.try_expr(array_literal_value(&elements[1])).unwrap()).len(),
10599 1
10600 );
10601 }
10602
10603 #[test]
10604 fn brace_at_statement_start_is_block_not_object() {
10605 let (_, diags) = parse_str("{ x: 1 }");
10606 assert!(
10607 !diags.is_empty(),
10608 "expected a diagnostic for `x:` inside block"
10609 );
10610 }
10611
10612 fn field_access(e: &crate::Expr) -> (crate::ExprId, &str) {
10613 match e.kind {
10614 crate::ExprKind::FieldAccess { receiver, ref name } => (receiver, name.name.as_str()),
10615 _ => panic!("expected FieldAccess, got {:?}", e.kind),
10616 }
10617 }
10618
10619 fn index_access(e: &crate::Expr) -> (crate::ExprId, crate::ExprId) {
10620 match e.kind {
10621 crate::ExprKind::IndexAccess { receiver, index } => (receiver, index),
10622 _ => panic!("expected IndexAccess, got {:?}", e.kind),
10623 }
10624 }
10625
10626 #[test]
10627 fn parse_field_access_simple() {
10628 let (ast, diags) = parse_str("a.b;");
10629 assert!(diags.is_empty());
10630 let outer = expr_of_single_stmt(&ast);
10631 let (receiver, name) = field_access(outer);
10632 assert!(matches!(
10633 ast.try_expr(receiver).unwrap().kind,
10634 crate::ExprKind::Identifier(_)
10635 ));
10636 assert_eq!(name, "b");
10637 }
10638
10639 #[test]
10640 fn parse_field_access_keyword_name() {
10641 let (ast, diags) = parse_str("status.type.default.null;");
10642 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10643 let outer = expr_of_single_stmt(&ast);
10644 let (receiver, name) = field_access(outer);
10645 assert_eq!(name, "null");
10646 let (receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10647 assert_eq!(name, "default");
10648 let (_receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10649 assert_eq!(name, "type");
10650 }
10651
10652 #[test]
10653 fn parse_field_access_chain_left_assoc() {
10654 let (ast, diags) = parse_str("a.b.c;");
10655 assert!(diags.is_empty());
10656 let outer = expr_of_single_stmt(&ast);
10657 let (mid_id, c) = field_access(outer);
10658 assert_eq!(c, "c");
10659 let (a_id, b) = field_access(ast.try_expr(mid_id).unwrap());
10660 assert_eq!(b, "b");
10661 assert!(matches!(
10662 ast.try_expr(a_id).unwrap().kind,
10663 crate::ExprKind::Identifier(_)
10664 ));
10665 }
10666
10667 #[test]
10668 fn parse_index_access_simple() {
10669 let (ast, diags) = parse_str("a[0];");
10670 assert!(diags.is_empty());
10671 let outer = expr_of_single_stmt(&ast);
10672 let (receiver, idx) = index_access(outer);
10673 assert!(matches!(
10674 ast.try_expr(receiver).unwrap().kind,
10675 crate::ExprKind::Identifier(_)
10676 ));
10677 assert_eq!(
10678 ast.try_expr(idx).unwrap().kind,
10679 crate::ExprKind::Number(0.0)
10680 );
10681 }
10682
10683 #[test]
10684 fn parse_index_access_chain() {
10685 let (ast, diags) = parse_str("a[0][1];");
10686 assert!(diags.is_empty());
10687 let outer = expr_of_single_stmt(&ast);
10688 let (mid_id, one) = index_access(outer);
10689 assert_eq!(
10690 ast.try_expr(one).unwrap().kind,
10691 crate::ExprKind::Number(1.0)
10692 );
10693 let (a_id, zero) = index_access(ast.try_expr(mid_id).unwrap());
10694 assert_eq!(
10695 ast.try_expr(zero).unwrap().kind,
10696 crate::ExprKind::Number(0.0)
10697 );
10698 assert!(matches!(
10699 ast.try_expr(a_id).unwrap().kind,
10700 crate::ExprKind::Identifier(_)
10701 ));
10702 }
10703
10704 #[test]
10705 fn parse_mixed_access_and_call_chain() {
10706 let (ast, diags) = parse_str("a.b[c].d();");
10707 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10708 let outer = expr_of_single_stmt(&ast);
10709 let (callee, args) = call(outer);
10710 assert!(args.is_empty());
10711 let (idx_id, d_name) = field_access(ast.try_expr(callee).unwrap());
10712 assert_eq!(d_name, "d");
10713 let (b_id, c_idx) = index_access(ast.try_expr(idx_id).unwrap());
10714 assert!(matches!(
10715 ast.try_expr(c_idx).unwrap().kind,
10716 crate::ExprKind::Identifier(_)
10717 ));
10718 let (a_id, b_name) = field_access(ast.try_expr(b_id).unwrap());
10719 assert_eq!(b_name, "b");
10720 assert!(matches!(
10721 ast.try_expr(a_id).unwrap().kind,
10722 crate::ExprKind::Identifier(_)
10723 ));
10724 }
10725
10726 #[test]
10727 fn parse_call_then_field() {
10728 let (ast, diags) = parse_str("f().x;");
10729 assert!(diags.is_empty());
10730 let outer = expr_of_single_stmt(&ast);
10731 let (call_id, x_name) = field_access(outer);
10732 assert_eq!(x_name, "x");
10733 let (_callee, args) = call(ast.try_expr(call_id).unwrap());
10734 assert!(args.is_empty());
10735 }
10736
10737 #[test]
10738 fn parse_index_with_complex_expression() {
10739 let (ast, diags) = parse_str("a[i + 1];");
10740 assert!(diags.is_empty());
10741 let outer = expr_of_single_stmt(&ast);
10742 let (_, idx) = index_access(outer);
10743 let (op, ..) = binary(ast.try_expr(idx).unwrap());
10744 assert_eq!(op, crate::BinOp::Add);
10745 }
10746
10747 #[test]
10748 fn field_access_missing_name_diagnoses() {
10749 let (_, diags) = parse_str("a.;");
10750 assert!(!diags.is_empty());
10751 assert_eq!(diags[0].message, "expected field name after `.`");
10752 }
10753
10754 #[test]
10755 fn parse_postfix_increment() {
10756 let (ast, diags) = parse_str("x++;");
10757 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10758 let expr = expr_of_single_stmt(&ast);
10759 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10760 panic!("expected PostfixUnary, got {:?}", expr.kind);
10761 };
10762 assert_eq!(op, crate::PostfixOp::Inc);
10763 assert!(matches!(
10764 ast.try_expr(operand).unwrap().kind,
10765 crate::ExprKind::Identifier(_)
10766 ));
10767 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
10768 }
10769
10770 #[test]
10771 fn parse_postfix_decrement() {
10772 let (ast, diags) = parse_str("x--;");
10773 assert!(diags.is_empty());
10774 let expr = expr_of_single_stmt(&ast);
10775 let crate::ExprKind::PostfixUnary { op, .. } = expr.kind else {
10776 panic!("expected PostfixUnary");
10777 };
10778 assert_eq!(op, crate::PostfixOp::Dec);
10779 }
10780
10781 #[test]
10782 fn parse_postfix_non_null_assertion() {
10783 let (ast, diags) = parse_str("x!;");
10784 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10785 let expr = expr_of_single_stmt(&ast);
10786 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10787 panic!("expected PostfixUnary, got {:?}", expr.kind);
10788 };
10789 assert_eq!(op, crate::PostfixOp::NonNullAssert);
10790 assert!(matches!(
10791 ast.try_expr(operand).unwrap().kind,
10792 crate::ExprKind::Identifier(_)
10793 ));
10794 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
10795 }
10796
10797 #[test]
10798 fn parse_non_null_assertion_continues_chain() {
10799 let (ast, diags) = parse_str("x!.y;");
10800 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10801 let expr = expr_of_single_stmt(&ast);
10802 let crate::ExprKind::FieldAccess { receiver, name } = &expr.kind else {
10803 panic!("expected FieldAccess, got {:?}", expr.kind);
10804 };
10805 assert_eq!(name.name, "y");
10806 assert!(matches!(
10807 ast.try_expr(*receiver).unwrap().kind,
10808 crate::ExprKind::PostfixUnary {
10809 op: crate::PostfixOp::NonNullAssert,
10810 ..
10811 }
10812 ));
10813 }
10814
10815 #[test]
10816 fn parse_postfix_on_field_access() {
10817 let (ast, diags) = parse_str("o.f++;");
10818 assert!(diags.is_empty());
10819 let expr = expr_of_single_stmt(&ast);
10820 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
10821 panic!("expected PostfixUnary");
10822 };
10823 assert!(matches!(
10824 ast.try_expr(operand).unwrap().kind,
10825 crate::ExprKind::FieldAccess { .. }
10826 ));
10827 }
10828
10829 #[test]
10830 fn parse_postfix_on_index_access() {
10831 let (ast, diags) = parse_str("xs[0]++;");
10832 assert!(diags.is_empty());
10833 let expr = expr_of_single_stmt(&ast);
10834 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
10835 panic!("expected PostfixUnary");
10836 };
10837 assert!(matches!(
10838 ast.try_expr(operand).unwrap().kind,
10839 crate::ExprKind::IndexAccess { .. }
10840 ));
10841 }
10842
10843 #[test]
10844 fn parse_postfix_terminates_chain() {
10845 let (_, diags) = parse_str("x++.y;");
10846 assert!(!diags.is_empty());
10847 assert_eq!(diags[0].message, "expected `;` after expression");
10848 }
10849
10850 #[test]
10851 fn parse_postfix_in_binary_continues() {
10852 let (ast, diags) = parse_str("let y = x++ + 1;");
10853 assert!(diags.is_empty(), "unexpected: {diags:?}");
10854 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10855 panic!("expected Let");
10856 };
10857 let (op, lhs, _) = binary(ast.try_expr(value).unwrap());
10858 assert_eq!(op, crate::BinOp::Add);
10859 assert!(matches!(
10860 ast.try_expr(lhs).unwrap().kind,
10861 crate::ExprKind::PostfixUnary { .. }
10862 ));
10863 }
10864
10865 #[test]
10866 fn parse_index_assignment() {
10867 let (ast, diags) = parse_str("xs[0] = 1;");
10868 assert!(diags.is_empty(), "unexpected: {diags:?}");
10869 let stmt = single_stmt(&ast);
10870 let crate::StmtKind::AssignIndex {
10871 receiver,
10872 index,
10873 value,
10874 } = stmt.kind
10875 else {
10876 panic!("expected AssignIndex, got {:?}", stmt.kind);
10877 };
10878 assert!(matches!(
10879 ast.try_expr(receiver).unwrap().kind,
10880 crate::ExprKind::Identifier(_)
10881 ));
10882 assert_eq!(
10883 ast.try_expr(index).unwrap().kind,
10884 crate::ExprKind::Number(0.0)
10885 );
10886 assert_eq!(
10887 ast.try_expr(value).unwrap().kind,
10888 crate::ExprKind::Number(1.0)
10889 );
10890 }
10891
10892 #[test]
10893 fn parse_for_update_postfix() {
10894 let (ast, diags) = parse_str("function main(): void { for (let i = 0; i < 3; i++) {} }");
10895 assert!(diags.is_empty(), "unexpected: {diags:?}");
10896 assert_eq!(ast.top_level.len(), 1);
10897 }
10898
10899 #[test]
10900 fn parse_for_update_index_assignment() {
10901 let (ast, diags) =
10902 parse_str("function main(): void { for (let i = 0; i < 3; xs[i] = i) {} }");
10903 assert!(diags.is_empty(), "unexpected: {diags:?}");
10904 assert_eq!(ast.top_level.len(), 1);
10905 }
10906
10907 #[test]
10908 fn index_access_missing_close_bracket_diagnoses() {
10909 let (_, diags) = parse_str("a[1;");
10910 assert!(!diags.is_empty());
10911 assert_eq!(diags[0].message, "expected `]`");
10912 }
10913
10914 #[test]
10915 fn field_access_inside_object_literal_value_does_not_steal_dot() {
10916 let (ast, diags) = parse_str("let x = { a: 1 }.foo;");
10917 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10918 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10919 panic!("expected Let");
10920 };
10921 let (recv_id, name) = field_access(ast.try_expr(value).unwrap());
10922 assert_eq!(name, "foo");
10923 assert_eq!(object_literal(ast.try_expr(recv_id).unwrap()).len(), 1);
10924 }
10925
10926 fn type_of_let(stmt: &crate::Stmt) -> &crate::TypeAnnotation {
10927 match stmt.kind {
10928 crate::StmtKind::Let { ref ty, .. } => ty.as_ref().expect("expected type annotation"),
10929 _ => panic!("expected Let"),
10930 }
10931 }
10932
10933 #[test]
10934 fn parse_array_type_annotation() {
10935 let (ast, diags) = parse_str("let xs: number[] = null;");
10936 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10937 let ty = type_of_let(single_stmt(&ast));
10938 match ty.kind {
10939 crate::TypeAnnotationKind::Array(ref inner) => {
10940 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Name { .. }));
10941 }
10942 _ => panic!("expected Array, got {:?}", ty.kind),
10943 }
10944 }
10945
10946 #[test]
10947 fn parse_nested_array_type_annotation() {
10948 let (ast, diags) = parse_str("let xs: number[][] = null;");
10949 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10950 let ty = type_of_let(single_stmt(&ast));
10951 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
10952 panic!("expected outer Array");
10953 };
10954 let crate::TypeAnnotationKind::Array(ref inner2) = inner.kind else {
10955 panic!("expected inner Array");
10956 };
10957 assert!(matches!(
10958 inner2.kind,
10959 crate::TypeAnnotationKind::Name { .. }
10960 ));
10961 }
10962
10963 #[test]
10964 fn parse_object_type_annotation_simple() {
10965 let (ast, diags) = parse_str("let p: { x: number; y: number } = null;");
10966 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10967 let ty = type_of_let(single_stmt(&ast));
10968 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10969 panic!("expected Object, got {:?}", ty.kind);
10970 };
10971 assert_eq!(fields.len(), 2);
10972 assert_eq!(fields[0].name.name, "x");
10973 assert_eq!(fields[1].name.name, "y");
10974 }
10975
10976 #[test]
10977 fn readonly_is_rejected_on_methods_accessors_and_method_signatures() {
10978 let source = "class K {\n\
10979 readonly m(): number { return 1; }\n\
10980 public readonly n<T>(): number { return 1; }\n\
10981 readonly get g(): number { return 1; }\n\
10982 readonly p: number = 1;\n\
10983 }\n\
10984 type T = { readonly r(a: number): boolean; readonly f: () => void };\n\
10985 interface I { readonly s(): void; readonly q: number; }\n";
10986 let (_, diags) = parse_str(source);
10987 let lines: Vec<usize> = diags
10988 .iter()
10989 .map(|d| {
10990 assert_eq!(
10991 d.message,
10992 "`readonly` can only modify a property or index signature"
10993 );
10994 source[..d.span.start as usize].matches('\n').count() + 1
10995 })
10996 .collect();
10997 assert_eq!(lines, vec![2, 3, 4, 7, 8]);
10998 }
10999
11000 #[test]
11001 fn parse_object_type_method_members() {
11002 let (ast, diags) =
11005 parse_str("let p: { m(): number; opt?(): string; r(a: number): boolean } = null;");
11006 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11007 let ty = type_of_let(single_stmt(&ast));
11008 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11009 panic!("expected Object, got {:?}", ty.kind);
11010 };
11011 assert_eq!(fields.len(), 3);
11012 for field in fields {
11013 assert!(
11014 matches!(field.ty.kind, crate::TypeAnnotationKind::Function { .. }),
11015 "member `{}` should be function-typed, got {:?}",
11016 field.name.name,
11017 field.ty.kind,
11018 );
11019 }
11020 assert!(fields[1].optional, "`opt?()` is an optional member");
11021 let crate::TypeAnnotationKind::Function { ref params, .. } = fields[2].ty.kind else {
11022 unreachable!("checked above");
11023 };
11024 assert_eq!(params.len(), 1);
11025 assert_eq!(params[0].name.name, "a");
11026 }
11027
11028 #[test]
11029 fn parse_object_type_annotation_with_commas() {
11030 let (ast, diags) = parse_str("let p: { x: number, y: number } = null;");
11031 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11032 let ty = type_of_let(single_stmt(&ast));
11033 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11034 panic!("expected Object");
11035 };
11036 assert_eq!(fields.len(), 2);
11037 }
11038
11039 #[test]
11040 fn parse_object_type_annotation_empty() {
11041 let (ast, diags) = parse_str("let p: {} = null;");
11042 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11043 let ty = type_of_let(single_stmt(&ast));
11044 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11045 panic!("expected Object");
11046 };
11047 assert!(fields.is_empty());
11048 }
11049
11050 #[test]
11051 fn parse_object_type_with_optional_field() {
11052 let (ast, diags) = parse_str("let p: { id: number; nick?: string } = null;");
11053 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11054 let ty = type_of_let(single_stmt(&ast));
11055 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11056 panic!("expected Object");
11057 };
11058 assert_eq!(fields.len(), 2);
11059 assert_eq!(fields[0].name.name, "id");
11060 assert!(!fields[0].optional);
11061 assert_eq!(fields[1].name.name, "nick");
11062 assert!(fields[1].optional);
11063 }
11064
11065 #[test]
11066 fn parse_object_type_with_readonly_fields() {
11067 let (ast, diags) = parse_str(
11068 "let issue: { readonly id: string; readonly title?: string; body: string } = null;",
11069 );
11070 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11071 let ty = type_of_let(single_stmt(&ast));
11072 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11073 panic!("expected Object");
11074 };
11075 let fields: Vec<(&str, bool, bool)> = fields
11076 .iter()
11077 .map(|field| (field.name.name.as_str(), field.optional, field.readonly))
11078 .collect();
11079 assert_eq!(
11080 fields,
11081 vec![
11082 ("id", false, true),
11083 ("title", true, true),
11084 ("body", false, false),
11085 ]
11086 );
11087 }
11088
11089 #[test]
11090 fn parse_object_type_mixed_optional_and_required() {
11091 let (ast, diags) = parse_str("let p: { a?: number; b: string; c?: boolean } = null;");
11092 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11093 let ty = type_of_let(single_stmt(&ast));
11094 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11095 panic!("expected Object");
11096 };
11097 let opt: Vec<(&str, bool)> = fields
11098 .iter()
11099 .map(|f| (f.name.name.as_str(), f.optional))
11100 .collect();
11101 assert_eq!(opt, vec![("a", true), ("b", false), ("c", true)]);
11102 }
11103
11104 #[test]
11105 fn parse_object_type_keyword_fields() {
11106 let (ast, diags) =
11107 parse_str("let p: { type: string; default?: number; null: boolean } = null;");
11108 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11109 let ty = type_of_let(single_stmt(&ast));
11110 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11111 panic!("expected Object");
11112 };
11113 let opt: Vec<(&str, bool)> = fields
11114 .iter()
11115 .map(|f| (f.name.name.as_str(), f.optional))
11116 .collect();
11117 assert_eq!(
11118 opt,
11119 vec![("type", false), ("default", true), ("null", false)]
11120 );
11121 }
11122
11123 #[test]
11124 fn parse_object_type_quoted_fields() {
11125 let (ast, diags) =
11126 parse_str(r#"let p: { "content-type": string; "x\u002drequest-id"?: string } = null;"#);
11127 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11128 let ty = type_of_let(single_stmt(&ast));
11129 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11130 panic!("expected Object");
11131 };
11132 let opt: Vec<(&str, bool)> = fields
11133 .iter()
11134 .map(|f| (f.name.name.as_str(), f.optional))
11135 .collect();
11136 assert_eq!(opt, vec![("content-type", false), ("x-request-id", true)]);
11137 }
11138
11139 #[test]
11140 fn parse_interface_with_optional_property() {
11141 let (ast, diags) = parse_str("interface User { id: number; deletedAt?: string; }");
11142 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11143 let stmt = single_stmt(&ast);
11144 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11145 panic!("expected interface decl");
11146 };
11147 let opt: Vec<(&str, bool)> = members
11148 .iter()
11149 .map(|m| match m {
11150 crate::InterfaceMember::Property { name, optional, .. } => {
11151 (name.name.as_str(), *optional)
11152 }
11153 _ => panic!("expected property"),
11154 })
11155 .collect();
11156 assert_eq!(opt, vec![("id", false), ("deletedAt", true)]);
11157 }
11158
11159 #[test]
11160 fn parse_interface_with_readonly_properties() {
11161 let (ast, diags) = parse_str(
11162 "interface Issue { readonly id: string; readonly title?: string; body: string; }",
11163 );
11164 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11165 let stmt = single_stmt(&ast);
11166 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11167 panic!("expected interface decl");
11168 };
11169 let opt: Vec<(&str, bool, bool)> = members
11170 .iter()
11171 .map(|member| match member {
11172 crate::InterfaceMember::Property {
11173 name,
11174 optional,
11175 readonly,
11176 ..
11177 } => (name.name.as_str(), *optional, *readonly),
11178 _ => panic!("expected property"),
11179 })
11180 .collect();
11181 assert_eq!(
11182 opt,
11183 vec![
11184 ("id", false, true),
11185 ("title", true, true),
11186 ("body", false, false),
11187 ]
11188 );
11189 }
11190
11191 #[test]
11192 fn parse_readonly_property_name_without_modifier() {
11193 let (ast, diags) = parse_str(
11194 "interface Meta { readonly: boolean; }\nlet meta: { readonly: boolean } = null;",
11195 );
11196 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11197 let crate::StmtKind::InterfaceDecl { ref members, .. } =
11198 ast.try_stmt(ast.top_level[0]).unwrap().kind
11199 else {
11200 panic!("expected interface decl");
11201 };
11202 let crate::InterfaceMember::Property {
11203 ref name, readonly, ..
11204 } = members[0]
11205 else {
11206 panic!("expected property");
11207 };
11208 assert_eq!(name.name, "readonly");
11209 assert!(
11210 !readonly,
11211 "`readonly` here is the property name, not a modifier"
11212 );
11213
11214 let ty = type_of_let(ast.try_stmt(ast.top_level[1]).unwrap());
11215 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11216 panic!("expected Object");
11217 };
11218 assert_eq!(fields[0].name.name, "readonly");
11219 assert!(!fields[0].readonly);
11220 }
11221
11222 #[test]
11223 fn parse_interface_keyword_members() {
11224 let (ast, diags) =
11225 parse_str("interface IssueStatus { type: string; default(): string; null: boolean; }");
11226 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11227 let stmt = single_stmt(&ast);
11228 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11229 panic!("expected interface decl");
11230 };
11231 let names: Vec<&str> = members
11232 .iter()
11233 .map(|m| match m {
11234 crate::InterfaceMember::Method { name, .. }
11235 | crate::InterfaceMember::Property { name, .. } => name.name.as_str(),
11236 crate::InterfaceMember::IndexSignature(_) => panic!("unexpected index signature"),
11237 })
11238 .collect();
11239 assert_eq!(names, vec!["type", "default", "null"]);
11240 }
11241
11242 #[test]
11243 fn parse_interface_quoted_property_members() {
11244 let (ast, diags) = parse_str(
11245 r#"interface Headers { "content-type": string; "x\u002drequest-id"?: string; }"#,
11246 );
11247 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11248 let stmt = single_stmt(&ast);
11249 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11250 panic!("expected interface decl");
11251 };
11252 let names: Vec<(&str, bool)> = members
11253 .iter()
11254 .map(|m| match m {
11255 crate::InterfaceMember::Property { name, optional, .. } => {
11256 (name.name.as_str(), *optional)
11257 }
11258 _ => panic!("expected property"),
11259 })
11260 .collect();
11261 assert_eq!(names, vec![("content-type", false), ("x-request-id", true)]);
11262 }
11263
11264 #[test]
11265 fn parse_param_optional_marker() {
11266 let (ast, diags) = parse_str("function f(x?: number): void {}");
11267 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11268 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
11269 panic!("expected function")
11270 };
11271 assert!(params[0].optional);
11272 assert!(params[0].default.is_none());
11273 }
11274
11275 #[test]
11276 fn parse_interface_optional_method() {
11277 let (ast, diags) = parse_str("interface F { foo?(x?: number): void; }");
11278 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11279 let crate::StmtKind::InterfaceDecl { ref members, .. } = single_stmt(&ast).kind else {
11280 panic!("expected interface")
11281 };
11282 let crate::InterfaceMember::Property { optional, ty, .. } = &members[0] else {
11283 panic!("expected callable property")
11284 };
11285 assert!(*optional);
11286 let crate::TypeAnnotationKind::Function { params, .. } = &ty.kind else {
11287 panic!("expected function type")
11288 };
11289 assert!(params[0].optional);
11290 }
11291
11292 #[test]
11293 fn parse_interface_with_call_signature() {
11294 let (ast, diags) = parse_str("interface F { (s: string): number; }");
11295 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11296 let stmt = single_stmt(&ast);
11297 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11298 panic!("expected interface decl");
11299 };
11300 assert_eq!(members.len(), 1);
11301 match &members[0] {
11302 crate::InterfaceMember::Method { name, params, .. } => {
11303 assert_eq!(name.name, "@call");
11304 assert_eq!(params.len(), 1);
11305 assert_eq!(params[0].name.name, "s");
11306 }
11307 other => panic!("expected method (call signature), got {other:?}"),
11308 }
11309 }
11310
11311 #[test]
11312 fn parse_interface_mixed_call_signature_and_method() {
11313 let (ast, diags) = parse_str("interface F { greet(): string; (x: number): number; }");
11314 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11315 let stmt = single_stmt(&ast);
11316 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11317 panic!("expected interface decl");
11318 };
11319 let names: Vec<&str> = members
11320 .iter()
11321 .map(|m| match m {
11322 crate::InterfaceMember::Method { name, .. } => name.name.as_str(),
11323 _ => "<property>",
11324 })
11325 .collect();
11326 assert_eq!(names, vec!["greet", "@call"]);
11327 }
11328
11329 #[test]
11330 fn parse_interface_rejects_generic_call_signature() {
11331 let (_ast, diags) = parse_str("interface F { <T>(x: T): T; }");
11332 assert!(
11333 diags
11334 .iter()
11335 .any(|d| d.message.contains("generic call signatures")),
11336 "expected generic-call-signature diagnostic, got {diags:?}"
11337 );
11338 }
11339
11340 #[test]
11341 fn parse_object_type_with_array_field() {
11342 let (ast, diags) = parse_str("let p: { xs: number[] } = null;");
11343 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11344 let ty = type_of_let(single_stmt(&ast));
11345 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11346 panic!("expected Object");
11347 };
11348 assert_eq!(fields[0].name.name, "xs");
11349 assert!(matches!(
11350 fields[0].ty.kind,
11351 crate::TypeAnnotationKind::Array(_)
11352 ));
11353 }
11354
11355 #[test]
11356 fn parse_array_of_objects() {
11357 let (ast, diags) = parse_str("let xs: { x: number }[] = null;");
11358 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11359 let ty = type_of_let(single_stmt(&ast));
11360 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11361 panic!("expected Array");
11362 };
11363 assert!(matches!(
11364 inner.kind,
11365 crate::TypeAnnotationKind::Object { .. }
11366 ));
11367 }
11368
11369 #[test]
11370 fn parse_tuple_type_annotation() {
11371 let (ast, diags) = parse_str("let p: [string, number] = null;");
11372 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11373 let ty = type_of_let(single_stmt(&ast));
11374 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11375 panic!("expected Tuple, got {:?}", ty.kind);
11376 };
11377 assert_eq!(elems.len(), 2);
11378 assert!(matches!(
11379 elems[0].kind,
11380 crate::TypeAnnotationKind::Name { .. }
11381 ));
11382 assert!(matches!(
11383 elems[1].kind,
11384 crate::TypeAnnotationKind::Name { .. }
11385 ));
11386 }
11387
11388 #[test]
11389 fn parse_singleton_tuple_type() {
11390 let (ast, diags) = parse_str("let p: [number] = null;");
11391 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11392 let ty = type_of_let(single_stmt(&ast));
11393 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11394 panic!("expected Tuple");
11395 };
11396 assert_eq!(elems.len(), 1);
11397 }
11398
11399 #[test]
11400 fn parse_nested_tuple_type() {
11401 let (ast, diags) = parse_str("let p: [[number, string], boolean] = null;");
11402 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11403 let ty = type_of_let(single_stmt(&ast));
11404 let crate::TypeAnnotationKind::Tuple(ref outer) = ty.kind else {
11405 panic!("expected outer Tuple");
11406 };
11407 assert_eq!(outer.len(), 2);
11408 let crate::TypeAnnotationKind::Tuple(ref inner) = outer[0].kind else {
11409 panic!("expected inner Tuple");
11410 };
11411 assert_eq!(inner.len(), 2);
11412 }
11413
11414 #[test]
11415 fn parse_array_of_tuple_type() {
11416 let (ast, diags) = parse_str("let xs: [string, number][] = null;");
11417 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11418 let ty = type_of_let(single_stmt(&ast));
11419 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11420 panic!("expected outer Array, got {:?}", ty.kind);
11421 };
11422 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Tuple(_)));
11423 }
11424
11425 #[test]
11426 fn parse_trailing_comma_tuple_type() {
11427 let (ast, diags) = parse_str("let p: [number, string,] = null;");
11428 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11429 let ty = type_of_let(single_stmt(&ast));
11430 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11431 panic!("expected Tuple");
11432 };
11433 assert_eq!(elems.len(), 2);
11434 }
11435
11436 #[test]
11437 fn parse_empty_tuple_type_rejected() {
11438 let (_ast, diags) = parse_str("let p: [] = null;");
11439 assert!(
11440 diags.iter().any(|d| d
11441 .message
11442 .contains("tuple types must have at least one element")),
11443 "expected empty-tuple diagnostic, got: {diags:?}"
11444 );
11445 }
11446
11447 #[test]
11448 fn parse_labeled_tuple_drops_labels() {
11449 let (ast, diags) = parse_str("let p: [first: number, string] = null;");
11450 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11451 let ty = type_of_let(single_stmt(&ast));
11452 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11453 panic!("expected Tuple, got {:?}", ty.kind);
11454 };
11455 assert_eq!(elems.len(), 2);
11456 assert!(
11457 elems
11458 .iter()
11459 .all(|e| matches!(e.kind, crate::TypeAnnotationKind::Name { .. }))
11460 );
11461 }
11462
11463 #[test]
11464 fn parse_labeled_optional_tuple_element() {
11465 let (ast, diags) = parse_str("let p: [first: number, second?: string] = null;");
11466 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11467 let ty = type_of_let(single_stmt(&ast));
11468 let crate::TypeAnnotationKind::Tuple(elements) = &ty.kind else {
11469 panic!("expected tuple")
11470 };
11471 assert!(matches!(
11472 elements[1].kind,
11473 crate::TypeAnnotationKind::Optional(_)
11474 ));
11475 }
11476
11477 #[test]
11478 fn parse_readonly_binds_to_the_postfix_type() {
11479 let (ast, diags) = parse_str("let p: readonly number[][] | null = null;");
11480 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11481 let ty = type_of_let(single_stmt(&ast));
11482 let crate::TypeAnnotationKind::Union(ref members) = ty.kind else {
11483 panic!("expected Union, got {:?}", ty.kind);
11484 };
11485 let crate::TypeAnnotationKind::Readonly(ref operand) = members[0].kind else {
11486 panic!("expected Readonly, got {:?}", members[0].kind);
11487 };
11488 let crate::TypeAnnotationKind::Array(ref inner) = operand.kind else {
11489 panic!("expected Array, got {:?}", operand.kind);
11490 };
11491 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Array(_)));
11492 }
11493
11494 #[test]
11495 fn parse_readonly_as_a_type_name_is_not_the_operator() {
11496 let (ast, diags) = parse_str("let p: readonly = null;");
11497 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11498 let ty = type_of_let(single_stmt(&ast));
11499 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11500 }
11501
11502 #[test]
11503 fn parse_generic_one_arg() {
11504 let (ast, diags) = parse_str("let x: Foo<number> = null;");
11505 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11506 let ty = type_of_let(single_stmt(&ast));
11507 let crate::TypeAnnotationKind::Name { ref name, ref args } = ty.kind else {
11508 panic!("expected Name, got {:?}", ty.kind);
11509 };
11510 assert_eq!(name.span, crate::Span::new(F, 7, 10).unwrap());
11511 assert_eq!(ty.span, crate::Span::new(F, 7, 18).unwrap());
11512 assert_eq!(args.len(), 1);
11513 assert!(matches!(
11514 args[0].kind,
11515 crate::TypeAnnotationKind::Name { .. }
11516 ));
11517 }
11518
11519 #[test]
11520 fn parse_generic_two_args() {
11521 let (ast, diags) = parse_str("let m: Map<string, number> = null;");
11522 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11523 let ty = type_of_let(single_stmt(&ast));
11524 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11525 panic!("expected Name");
11526 };
11527 assert_eq!(args.len(), 2);
11528 }
11529
11530 #[test]
11531 fn parse_generic_nested() {
11532 let (ast, diags) = parse_str("let b: Box<Box<T>> = null;");
11533 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11534 let ty = type_of_let(single_stmt(&ast));
11535 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11536 panic!("expected outer Name");
11537 };
11538 assert_eq!(args.len(), 1);
11539 let crate::TypeAnnotationKind::Name {
11540 args: ref inner, ..
11541 } = args[0].kind
11542 else {
11543 panic!("expected inner Name");
11544 };
11545 assert_eq!(inner.len(), 1);
11546 }
11547
11548 #[test]
11549 fn parse_generic_array_postfix() {
11550 let (ast, diags) = parse_str("let xs: Foo<number>[] = null;");
11551 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11552 let ty = type_of_let(single_stmt(&ast));
11553 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11554 panic!("expected Array");
11555 };
11556 assert!(
11557 matches!(inner.kind, crate::TypeAnnotationKind::Name { ref args, .. } if args.len() == 1)
11558 );
11559 }
11560
11561 #[test]
11562 fn parse_generic_empty_diagnoses() {
11563 let (_ast, diags) = parse_str("let x: Foo<> = null;");
11564 assert!(
11565 diags
11566 .iter()
11567 .any(|d| d.message.contains("empty generic argument list")),
11568 "expected empty-generic-args diagnostic, got: {diags:?}"
11569 );
11570 }
11571
11572 #[test]
11573 fn parse_generic_trailing_comma_ok() {
11574 let (_ast, diags) = parse_str("let x: Foo<number,> = null;");
11575 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11576 }
11577
11578 #[test]
11579 fn parse_generic_missing_close_diagnoses() {
11580 let (_ast, diags) = parse_str("let x: Foo<number = null;");
11581 assert!(
11582 diags
11583 .iter()
11584 .any(|d| d.message.contains("expected `,` or `>`")),
11585 "expected unterminated-generic diagnostic, got: {diags:?}"
11586 );
11587 }
11588
11589 #[test]
11590 fn parse_qualified_two_segments() {
11591 let (ast, diags) = parse_str("let x: Foo.Bar = null;");
11592 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11593 let ty = type_of_let(single_stmt(&ast));
11594 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11595 panic!("expected Qualified, got {:?}", ty.kind);
11596 };
11597 assert_eq!(path.len(), 2);
11598 assert_eq!(path[0].span, crate::Span::new(F, 7, 10).unwrap());
11600 assert_eq!(path[1].span, crate::Span::new(F, 11, 14).unwrap());
11601 assert!(args.is_empty());
11602 assert_eq!(ty.span, crate::Span::new(F, 7, 14).unwrap());
11603 }
11604
11605 #[test]
11606 fn parse_qualified_three_segments() {
11607 let (ast, diags) = parse_str("let x: Foo.Bar.Baz = null;");
11608 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11609 let ty = type_of_let(single_stmt(&ast));
11610 let crate::TypeAnnotationKind::Qualified { ref path, .. } = ty.kind else {
11611 panic!("expected Qualified, got {:?}", ty.kind);
11612 };
11613 assert_eq!(path.len(), 3);
11614 }
11615
11616 #[test]
11617 fn parse_qualified_with_generic_args() {
11618 let (ast, diags) = parse_str("let x: Foo.Box<number> = null;");
11619 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11620 let ty = type_of_let(single_stmt(&ast));
11621 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11622 panic!("expected Qualified, got {:?}", ty.kind);
11623 };
11624 assert_eq!(path.len(), 2);
11625 assert_eq!(args.len(), 1);
11626 assert!(matches!(
11627 args[0].kind,
11628 crate::TypeAnnotationKind::Name { .. }
11629 ));
11630 }
11631
11632 #[test]
11633 fn parse_qualified_with_array_postfix() {
11634 let (ast, diags) = parse_str("let xs: Foo.Bar[] = null;");
11635 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11636 let ty = type_of_let(single_stmt(&ast));
11637 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11638 panic!("expected Array, got {:?}", ty.kind);
11639 };
11640 assert!(matches!(
11641 inner.kind,
11642 crate::TypeAnnotationKind::Qualified { .. }
11643 ));
11644 }
11645
11646 #[test]
11647 fn parse_qualified_trailing_dot_diagnoses() {
11648 let (_ast, diags) = parse_str("let x: Foo. = null;");
11649 assert!(
11650 diags.iter().any(|d| d
11651 .message
11652 .contains("expected identifier after `.` in type name")),
11653 "expected dotted-name diagnostic, got: {diags:?}"
11654 );
11655 }
11656
11657 #[test]
11658 fn parse_qualified_void_root_rejected_dot() {
11659 let (ast, diags) = parse_str("function f(): void.Bar { }");
11660 assert!(!diags.is_empty(), "expected a diagnostic for `void.Bar`");
11661 let _ = ast;
11662 }
11663
11664 fn union_members(ty: &crate::TypeAnnotation) -> &[crate::TypeAnnotation] {
11665 match &ty.kind {
11666 crate::TypeAnnotationKind::Union(members) => members,
11667 other => panic!("expected Union, got {other:?}"),
11668 }
11669 }
11670
11671 #[test]
11672 fn parse_union_two_members() {
11673 let (ast, diags) = parse_str("let x: number | string = null;");
11674 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11675 let ty = type_of_let(single_stmt(&ast));
11676 let members = union_members(ty);
11677 assert_eq!(members.len(), 2);
11678 assert!(matches!(
11679 members[0].kind,
11680 crate::TypeAnnotationKind::Name { .. }
11681 ));
11682 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
11684 members[1].kind,
11685 crate::TypeAnnotationKind::Name { .. }
11686 ));
11687 assert_eq!(members[1].span, crate::Span::new(F, 16, 22).unwrap()); assert_eq!(ty.span, crate::Span::new(F, 7, 22).unwrap());
11689 }
11690
11691 #[test]
11692 fn parse_union_three_members() {
11693 let (ast, diags) = parse_str("let x: A | B | C = null;");
11694 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11695 let ty = type_of_let(single_stmt(&ast));
11696 let members = union_members(ty);
11697 assert_eq!(members.len(), 3);
11698 for m in members {
11699 assert!(matches!(m.kind, crate::TypeAnnotationKind::Name { .. }));
11700 }
11701 }
11702
11703 #[test]
11704 fn parse_union_lower_than_array() {
11705 let (ast, diags) = parse_str("let x: number[] | string = null;");
11706 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11707 let ty = type_of_let(single_stmt(&ast));
11708 let members = union_members(ty);
11709 assert_eq!(members.len(), 2);
11710 assert!(
11711 matches!(members[0].kind, crate::TypeAnnotationKind::Array(_)),
11712 "expected first member to be Array, got {:?}",
11713 members[0].kind
11714 );
11715 assert!(matches!(
11716 members[1].kind,
11717 crate::TypeAnnotationKind::Name { .. }
11718 ));
11719 }
11720
11721 #[test]
11722 fn parse_union_with_null_member() {
11723 let (ast, diags) = parse_str("let x: number | null = null;");
11724 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11725 let ty = type_of_let(single_stmt(&ast));
11726 let members = union_members(ty);
11727 assert_eq!(members.len(), 2);
11728 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
11730 members[1].kind,
11731 crate::TypeAnnotationKind::Name { .. }
11732 ));
11733 assert_eq!(members[1].span, crate::Span::new(F, 16, 20).unwrap()); }
11735
11736 #[test]
11737 fn parse_union_in_function_return_type() {
11738 let (ast, diags) = parse_str("function f(): number | string { }");
11739 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11740 let stmt = single_stmt(&ast);
11741 match stmt.kind {
11742 crate::StmtKind::Function {
11743 ref return_type, ..
11744 } => {
11745 let return_type = return_type.as_ref().expect("plain return type");
11746 let members = union_members(return_type);
11747 assert_eq!(members.len(), 2);
11748 }
11749 _ => panic!("expected Function"),
11750 }
11751 }
11752
11753 #[test]
11754 fn parse_union_in_function_param_type() {
11755 let (ast, diags) = parse_str("function f(x: number | string): void { }");
11756 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11757 let stmt = single_stmt(&ast);
11758 match stmt.kind {
11759 crate::StmtKind::Function { ref params, .. } => {
11760 let p_ty = params[0].ty.as_ref().expect("param type");
11761 let members = union_members(p_ty);
11762 assert_eq!(members.len(), 2);
11763 }
11764 _ => panic!("expected Function"),
11765 }
11766 }
11767
11768 #[test]
11769 fn parse_union_in_object_field_type() {
11770 let (ast, diags) = parse_str("let p: { x: number | string } = null;");
11771 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11772 let ty = type_of_let(single_stmt(&ast));
11773 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11774 panic!("expected Object, got {:?}", ty.kind);
11775 };
11776 assert_eq!(fields.len(), 1);
11777 let members = union_members(&fields[0].ty);
11778 assert_eq!(members.len(), 2);
11779 }
11780
11781 #[test]
11782 fn parse_union_leading_pipe_is_forgiven() {
11783 let (ast, diags) = parse_str("let x: | number | string = null;");
11784 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11785 let ty = type_of_let(single_stmt(&ast));
11786 let members = union_members(ty);
11787 assert_eq!(members.len(), 2);
11788 }
11789
11790 #[test]
11791 fn parse_union_leading_pipe_single_member_unwraps() {
11792 let (ast, diags) = parse_str("let x: | number = null;");
11793 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11794 let ty = type_of_let(single_stmt(&ast));
11795 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11796 }
11797
11798 #[test]
11799 fn parse_union_trailing_pipe_diagnoses() {
11800 let (_, diags) = parse_str("let x: number | = null;");
11801 assert!(!diags.is_empty());
11802 assert_eq!(diags[0].message, "expected type");
11803 }
11804
11805 #[test]
11806 fn snapshot_union_type_mix() {
11807 let (ast, diags) = parse_str("let xs: number[] | string | null = null;");
11808 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11809 insta::assert_debug_snapshot!(ast);
11810 }
11811
11812 #[test]
11813 fn parse_object_type_duplicate_field_diagnoses() {
11814 let (_, diags) = parse_str("let p: { readonly x: number; x: string } = null;");
11815 assert!(!diags.is_empty());
11816 assert!(
11817 diags
11818 .iter()
11819 .any(|d| d.message == "duplicate field `x` in object type"),
11820 "expected duplicate-field diagnostic, got: {diags:?}"
11821 );
11822 }
11823
11824 #[test]
11825 fn snapshot_object_and_array_literals() {
11826 let (ast, diags) = parse_str(r#"let mix = [{ a: 1, b: "hi" }, {}];"#);
11827 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11828 insta::assert_debug_snapshot!(ast);
11829 }
11830
11831 #[test]
11832 fn snapshot_function_with_while() {
11833 let source = "function loop(n: number): void {\n while (n > 0) {\n log(n);\n }\n}";
11834 let (ast, diags) = parse_str(source);
11835 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11836 insta::assert_debug_snapshot!(ast);
11837 }
11838
11839 fn parse_arrow_const(
11840 source: &str,
11841 ) -> (
11842 Ast,
11843 Vec<crate::ParamDecl>,
11844 Option<crate::TypeAnnotation>,
11845 crate::ArrowBody,
11846 ) {
11847 let (ast, diags) = parse_str(source);
11848 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11849 assert_eq!(ast.top_level.len(), 1);
11850 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11851 let value = match &stmt.kind {
11852 StmtKind::Const { value, .. } => *value,
11853 _ => panic!("expected const declaration"),
11854 };
11855 let value = match ast.try_expr(value).unwrap().kind {
11856 ExprKind::FunctionExpression { function, .. } => function,
11857 _ => value,
11858 };
11859 let expr = ast.try_expr(value).unwrap().clone();
11860 let (params, return_type, body) = match expr.kind {
11861 ExprKind::Arrow {
11862 params,
11863 return_type,
11864 body,
11865 ..
11866 } => (params, return_type, body),
11867 other => panic!("expected Arrow, got {other:?}"),
11868 };
11869 (ast, params, return_type, body)
11870 }
11871
11872 #[test]
11873 fn parses_anonymous_function_expression_as_arrow() {
11874 let (ast, params, return_type, body) =
11875 parse_arrow_const("const f = function (x: number): number { return x + 1; };");
11876 assert_eq!(params.len(), 1);
11877 assert_eq!(params[0].name.name, "x");
11878 assert!(return_type.is_some());
11879 assert!(matches!(body, crate::ArrowBody::Block(_)));
11880 let _ = ast;
11881 }
11882
11883 #[test]
11884 fn parses_function_expression_with_no_params() {
11885 let (_ast, params, _return_type, body) =
11886 parse_arrow_const("const f = function (): void {};");
11887 assert!(params.is_empty());
11888 assert!(matches!(body, crate::ArrowBody::Block(_)));
11889 }
11890
11891 #[test]
11893 fn parses_named_function_expression_signature() {
11894 let (_ast, params, _return_type, _body) =
11895 parse_arrow_const("const f = function named(x: number): number { return x + 1; };");
11896 assert_eq!(params.len(), 1);
11897 }
11898
11899 #[test]
11901 fn accepts_a_named_function_expression_that_calls_itself() {
11902 let (_ast, diags) =
11903 parse_str("const f = function bar(x: number): number { return bar(x); };");
11904 assert!(diags.is_empty(), "{diags:?}");
11905 }
11906
11907 #[test]
11909 fn accepts_a_named_function_expression_using_the_name_as_a_property() {
11910 let (_ast, diags) = parse_str("const f = function bar(o: O): number { return o.bar; };");
11911 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11912 }
11913
11914 #[test]
11916 fn accepts_a_named_function_expression_using_the_name_as_an_object_key() {
11917 let (_ast, diags) = parse_str(
11918 "const f = function bar(x: number): number { const o = { bar: 1 }; return o.bar + x; };",
11919 );
11920 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11921 }
11922
11923 #[test]
11925 fn accepts_a_named_function_expression_with_its_name_in_a_string() {
11926 let (_ast, diags) =
11927 parse_str("const f = function bar(x: number): string { return \"bar\"; };");
11928 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11929 }
11930
11931 #[test]
11933 fn accepts_a_self_call_inside_a_template_substitution() {
11934 let (_ast, diags) =
11935 parse_str("const f = function bar(x: number): string { return `${bar(0)}`; };");
11936 assert!(diags.is_empty(), "{diags:?}");
11937 }
11938
11939 #[test]
11940 fn rejects_a_generic_function_expression() {
11941 let (_ast, diags) = parse_str("const f = function <T>(x: T): T { return x; };");
11942 assert!(
11943 diags
11944 .iter()
11945 .any(|d| d.message.contains("generic function expressions")),
11946 "generic function expression should be rejected: {diags:?}"
11947 );
11948 }
11949
11950 #[test]
11951 fn accepts_this_inside_a_function_expression_in_a_class_method() {
11952 let (_ast, diags) = parse_str(
11953 "class C { x: number = 1; m(): number { \
11954 const f = function (this: { x: number }): number { return this.x; }; return f(); } }",
11955 );
11956 assert!(diags.is_empty(), "{diags:?}");
11957 }
11958
11959 #[test]
11960 fn retains_this_boundary_inside_method_shorthand() {
11961 let (ast, diags) = parse_str(
11962 "class C { x: number = 1; m(): number { \
11963 const o = { x: 2, g(): number { return this.x; } }; return o.g(); } }",
11964 );
11965 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11966 assert!((0..ast.exprs_len()).any(|index| matches!(
11967 ast.try_expr(crate::ExprId(index as u32)).unwrap().kind,
11968 ExprKind::ThisOutsideReceiver
11969 )));
11970 }
11971
11972 #[test]
11973 fn this_still_parses_in_an_ordinary_class_method() {
11974 let (_ast, diags) = parse_str("class C { x: number = 1; m(): number { return this.x; } }");
11975 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11976 }
11977
11978 #[test]
11980 fn function_keyword_still_parses_as_an_object_key() {
11981 let (_ast, diags) = parse_str("let o = { function: 1 };");
11982 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11983 }
11984
11985 fn object_literal_member_values(source: &str) -> (Ast, Vec<crate::ExprId>) {
11986 let (ast, diags) = parse_str(source);
11987 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11988 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11989 let value = match &stmt.kind {
11990 StmtKind::Const { value, .. } => *value,
11991 other => panic!("expected a const declaration, got {other:?}"),
11992 };
11993 let members = match &ast.try_expr(value).unwrap().kind {
11994 ExprKind::ObjectLiteral { members } => members
11995 .iter()
11996 .map(crate::ObjectLiteralMember::value)
11997 .collect::<Vec<_>>(),
11998 other => panic!("expected ObjectLiteral, got {other:?}"),
11999 };
12000 (ast, members)
12001 }
12002
12003 #[test]
12004 fn parses_object_literal_method_shorthand_as_arrow() {
12005 let (ast, members) =
12006 object_literal_member_values("const o = { m(x: number): number { return x; } };");
12007 assert_eq!(members.len(), 1);
12008 match &ast.try_expr(members[0]).unwrap().kind {
12009 ExprKind::Arrow { params, body, .. } => {
12010 assert_eq!(params.len(), 1);
12011 assert_eq!(params[0].name.name, "x");
12012 assert!(matches!(body, crate::ArrowBody::Block(_)));
12013 }
12014 other => panic!("expected Arrow, got {other:?}"),
12015 }
12016 }
12017
12018 #[test]
12020 fn parses_method_shorthand_with_a_keyword_name() {
12021 let (ast, members) =
12022 object_literal_member_values("const o = { if(x: number): number { return x; } };");
12023 assert!(matches!(
12024 ast.try_expr(members[0]).unwrap().kind,
12025 ExprKind::Arrow { .. }
12026 ));
12027 }
12028
12029 #[test]
12030 fn parses_method_shorthand_alongside_plain_and_shorthand_properties() {
12031 let (ast, members) =
12032 object_literal_member_values("const o = { a: 1, m(): void {}, b: 2 };");
12033 assert_eq!(members.len(), 3);
12034 assert!(matches!(
12035 ast.try_expr(members[1]).unwrap().kind,
12036 ExprKind::Arrow { .. }
12037 ));
12038 }
12039
12040 #[test]
12041 fn parses_bare_ident_arrow_expression_body() {
12042 let (_ast, params, return_type, body) = parse_arrow_const("const f = x => x + 1;");
12043 assert_eq!(params.len(), 1);
12044 assert_eq!(params[0].name.name, "x");
12045 assert!(params[0].ty.is_none());
12046 assert!(return_type.is_none());
12047 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12048 }
12049
12050 #[test]
12051 fn parses_zero_param_arrow() {
12052 let (_ast, params, return_type, body) = parse_arrow_const("const f = () => 0;");
12053 assert!(params.is_empty());
12054 assert!(return_type.is_none());
12055 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12056 }
12057
12058 #[test]
12059 fn parses_paren_unannotated_arrow() {
12060 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x) => x;");
12061 assert_eq!(params.len(), 1);
12062 assert_eq!(params[0].name.name, "x");
12063 assert!(params[0].ty.is_none());
12064 }
12065
12066 #[test]
12067 fn parses_typed_param_arrow() {
12068 let (_ast, params, return_type, body) =
12069 parse_arrow_const("const f = (x: number) => x * 2;");
12070 assert_eq!(params.len(), 1);
12071 assert!(params[0].ty.is_some());
12072 assert!(return_type.is_none());
12073 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12074 }
12075
12076 #[test]
12077 fn parses_typed_param_and_return_arrow() {
12078 let (_ast, params, return_type, body) =
12079 parse_arrow_const("const f = (x: number): number => x * 2;");
12080 assert_eq!(params.len(), 1);
12081 assert!(params[0].ty.is_some());
12082 assert!(return_type.is_some());
12083 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12084 }
12085
12086 #[test]
12087 fn parses_multi_param_unannotated_arrow() {
12088 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x, y) => x + y;");
12089 assert_eq!(params.len(), 2);
12090 assert_eq!(params[0].name.name, "x");
12091 assert_eq!(params[1].name.name, "y");
12092 assert!(params[0].ty.is_none());
12093 assert!(params[1].ty.is_none());
12094 }
12095
12096 #[test]
12097 fn parses_multi_param_typed_arrow() {
12098 let (_ast, params, _return_type, _body) =
12099 parse_arrow_const("const f = (x: number, y: number) => x + y;");
12100 assert_eq!(params.len(), 2);
12101 assert!(params[0].ty.is_some());
12102 assert!(params[1].ty.is_some());
12103 }
12104
12105 #[test]
12106 fn parses_block_body_arrow() {
12107 let (_ast, params, _return_type, body) =
12108 parse_arrow_const("const f = (x: number) => { return x; };");
12109 assert_eq!(params.len(), 1);
12110 assert!(matches!(body, crate::ArrowBody::Block(_)));
12111 }
12112
12113 #[test]
12114 fn rejects_typed_bare_ident_arrow() {
12115 let (_ast, diags) = parse_str("const f = x: number => x;");
12116 assert!(
12117 !diags.is_empty(),
12118 "expected diagnostics for `x: number => …`",
12119 );
12120 }
12121
12122 #[test]
12123 fn arrow_inside_call_argument_parses() {
12124 let (ast, diags) = parse_str("f((x: number) => x + 1, 2);");
12125 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12126 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12127 let call = match &stmt.kind {
12128 StmtKind::Expr(eid) => ast.try_expr(*eid).unwrap(),
12129 _ => panic!("expected expression statement"),
12130 };
12131 let args = match &call.kind {
12132 ExprKind::Call { args, .. } => args.clone(),
12133 other => panic!("expected Call, got {other:?}"),
12134 };
12135 assert_eq!(args.len(), 2);
12136 assert!(matches!(
12137 ast.try_expr(args[0]).unwrap().kind,
12138 ExprKind::Arrow { .. }
12139 ));
12140 }
12141
12142 #[test]
12143 fn function_decl_param_still_requires_annotation() {
12144 let (_ast, diags) = parse_str("function f(x): number { return x; }");
12145 assert!(
12146 diags.iter().any(|d| d.message.contains("type annotation")),
12147 "expected `parameter requires a type annotation` diagnostic, got {diags:?}",
12148 );
12149 }
12150
12151 #[test]
12152 fn paren_expression_still_parses_when_no_arrow() {
12153 let (ast, diags) = parse_str("const a = (1 + 2);");
12154 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12155 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12156 let value = match &stmt.kind {
12157 StmtKind::Const { value, .. } => *value,
12158 _ => panic!("expected const declaration"),
12159 };
12160 assert!(matches!(
12161 ast.try_expr(value).unwrap().kind,
12162 ExprKind::Paren(_)
12163 ));
12164 }
12165
12166 #[test]
12167 fn parse_named_import_single() {
12168 let (ast, diags) = parse_str("import { v4 } from \"submilli:uuid\";");
12169 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12170 assert_eq!(ast.top_level.len(), 1);
12171 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12172 let (module, kind) = match &stmt.kind {
12173 StmtKind::Import { module, kind, .. } => (module, kind),
12174 other => panic!("expected Import, got {other:?}"),
12175 };
12176 assert_eq!(module, "submilli:uuid");
12177 let specs = match kind {
12178 ImportKind::Named(specs) => specs,
12179 other => panic!("expected Named, got {other:?}"),
12180 };
12181 assert_eq!(specs.len(), 1);
12182 assert_eq!(specs[0].imported_name.name, "v4");
12183 assert_eq!(specs[0].local_name.name, "v4");
12184 }
12185
12186 #[test]
12187 fn parse_named_import_multi_and_alias() {
12188 let (ast, diags) =
12189 parse_str("import { v4, v7 as makeId, validate } from \"submilli:uuid\";");
12190 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12191 let specs = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12192 StmtKind::Import {
12193 kind: ImportKind::Named(s),
12194 ..
12195 } => s.clone(),
12196 other => panic!("expected named import, got {other:?}"),
12197 };
12198 assert_eq!(specs.len(), 3);
12199 assert_eq!(specs[0].imported_name.name, "v4");
12200 assert_eq!(specs[0].local_name.name, "v4");
12201 assert_eq!(specs[1].imported_name.name, "v7");
12202 assert_eq!(specs[1].local_name.name, "makeId");
12203 assert_eq!(specs[2].imported_name.name, "validate");
12204 assert_eq!(specs[2].local_name.name, "validate");
12205 }
12206
12207 #[test]
12208 fn parse_namespace_import() {
12209 let (ast, diags) = parse_str("import uuid from \"submilli:uuid\";");
12210 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12211 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12212 StmtKind::Import { kind, .. } => kind.clone(),
12213 other => panic!("expected import, got {other:?}"),
12214 };
12215 let local = match kind {
12216 ImportKind::Namespace { local_name } => local_name,
12217 other => panic!("expected Namespace, got {other:?}"),
12218 };
12219 assert_eq!(local.name, "uuid");
12220 }
12221
12222 #[test]
12223 fn parse_wildcard_namespace_import_synonym() {
12224 let (ast, diags) = parse_str("import * as uuid from \"submilli:uuid\";");
12225 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12226 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12227 StmtKind::Import { kind, .. } => kind.clone(),
12228 other => panic!("expected import, got {other:?}"),
12229 };
12230 let local = match kind {
12231 ImportKind::Namespace { local_name } => local_name,
12232 other => panic!("expected Namespace, got {other:?}"),
12233 };
12234 assert_eq!(local.name, "uuid");
12235 }
12236
12237 #[test]
12238 fn parse_wildcard_namespace_import_without_as_rejected() {
12239 let (_ast, diags) = parse_str("import * from \"submilli:uuid\";");
12240 assert!(
12241 diags
12242 .iter()
12243 .any(|d| d.message.contains("expected `as <name>`")),
12244 "expected missing-as diagnostic, got: {diags:?}",
12245 );
12246 }
12247
12248 #[test]
12249 fn parse_combined_default_and_named_rejected() {
12250 let (_ast, diags) = parse_str("import uuid, { v4 } from \"submilli:uuid\";");
12251 assert!(
12252 diags
12253 .iter()
12254 .any(|d| d.message.contains("combining default and named")),
12255 "expected combined-import diagnostic, got: {diags:?}",
12256 );
12257 }
12258
12259 #[test]
12260 fn parse_side_effect_import_rejected() {
12261 let (_ast, diags) = parse_str("import \"submilli:uuid\";");
12262 assert!(
12263 diags
12264 .iter()
12265 .any(|d| d.message.contains("side-effect-only imports")),
12266 "expected side-effect-import diagnostic, got: {diags:?}",
12267 );
12268 }
12269
12270 #[test]
12271 fn parse_empty_specifier_list_rejected() {
12272 let (_ast, diags) = parse_str("import {} from \"submilli:uuid\";");
12273 assert!(
12274 diags
12275 .iter()
12276 .any(|d| d.message.contains("empty import specifier list")),
12277 "expected empty-specifier diagnostic, got: {diags:?}",
12278 );
12279 }
12280
12281 #[test]
12282 fn parse_import_inside_function_body_rejected() {
12283 let (_ast, diags) =
12284 parse_str("function main(): void { import { v4 } from \"submilli:uuid\"; }");
12285 assert!(
12286 diags
12287 .iter()
12288 .any(|d| d.message.contains("must appear at the top of the file")),
12289 "expected top-level diagnostic, got: {diags:?}",
12290 );
12291 }
12292
12293 #[test]
12294 fn parse_missing_from_rejected() {
12295 let (_ast, diags) = parse_str("import { v4 } \"submilli:uuid\";");
12296 assert!(
12297 diags.iter().any(|d| d.message.contains("expected `from`")),
12298 "expected missing-from diagnostic, got: {diags:?}",
12299 );
12300 }
12301
12302 #[test]
12303 fn parse_missing_module_specifier_rejected() {
12304 let (_ast, diags) = parse_str("import { v4 } from;");
12305 assert!(
12306 diags
12307 .iter()
12308 .any(|d| d.message.contains("expected module specifier")),
12309 "expected missing-module diagnostic, got: {diags:?}",
12310 );
12311 }
12312
12313 #[test]
12314 fn parse_export_function_marks_exported() {
12315 let (ast, diags) = parse_str("export function main(): void {}");
12318 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12319 let stmt = single_stmt(&ast);
12320 assert!(
12321 matches!(stmt.kind, crate::StmtKind::Function { .. }),
12322 "expected Function decl, got {:?}",
12323 stmt.kind,
12324 );
12325 assert_eq!(ast.exported_decls.len(), 1, "one exported decl");
12326 assert_eq!(ast.exported_decls[0].stmt, ast.top_level[0]);
12327 }
12328
12329 #[test]
12330 fn parse_export_const_type_interface_enum_are_marked() {
12331 for src in [
12332 "export const X: number = 1;",
12333 "export type Id = string;",
12334 "export interface P { x: number; }",
12335 "export enum E { A, B }",
12336 ] {
12337 let (ast, diags) = parse_str(src);
12338 assert!(diags.is_empty(), "unexpected diags for {src:?}: {diags:?}");
12339 assert_eq!(ast.top_level.len(), 1, "one decl for {src:?}");
12340 assert_eq!(ast.exported_decls.len(), 1, "one exported decl for {src:?}");
12341 }
12342 }
12343
12344 #[test]
12345 fn parse_export_default_is_rejected_with_focused_message() {
12346 let (_ast, diags) = parse_str("export default function main(): void {}");
12347 assert!(
12348 diags
12349 .iter()
12350 .any(|d| d.message.contains("`export default` is not supported")),
12351 "expected focused export-default diagnostic, got: {diags:?}",
12352 );
12353 assert!(
12354 !diags.iter().any(|d| d.message.contains("expected `;`")),
12355 "the spurious missing-semicolon error must be gone: {diags:?}",
12356 );
12357 }
12358
12359 #[test]
12360 fn parse_export_list_parses_as_export_from() {
12361 let (ast, diags) = parse_str("function main(): void {}\nexport { main };");
12364 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12365 assert_eq!(ast.top_level.len(), 2, "function + export-from");
12366 let export = ast.try_stmt(ast.top_level[1]).unwrap();
12367 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12368 panic!("expected ExportFrom, got {:?}", export.kind);
12369 };
12370 assert!(source.is_none(), "bare `export {{ x }}` has no source");
12371 assert_eq!(specs.len(), 1);
12372 assert_eq!(specs[0].imported_name.name, "main");
12373 assert_eq!(specs[0].local_name.name, "main");
12374 }
12375
12376 #[test]
12377 fn parse_export_from_with_source_and_alias_parses() {
12378 let (ast, diags) = parse_str("export { foo, bar as baz } from \"./util\";");
12379 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12380 let export = ast.try_stmt(ast.top_level[0]).unwrap();
12381 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12382 panic!("expected ExportFrom, got {:?}", export.kind);
12383 };
12384 let Some((module, _)) = source else {
12385 panic!("expected a `from` source");
12386 };
12387 assert_eq!(module, "./util");
12388 assert_eq!(specs.len(), 2);
12389 assert_eq!(specs[1].imported_name.name, "bar");
12390 assert_eq!(specs[1].local_name.name, "baz");
12391 }
12392
12393 #[test]
12394 fn parse_export_inside_function_body_rejected() {
12395 let (_ast, diags) = parse_str("function main(): void { export const X: number = 1; }");
12396 assert!(
12397 diags
12398 .iter()
12399 .any(|d| d.message.contains("must appear at the top of the file")),
12400 "expected top-level diagnostic, got: {diags:?}",
12401 );
12402 }
12403
12404 #[test]
12405 fn parse_throw_with_expression() {
12406 let (ast, diags) = parse_str(r#"throw new Error("oops");"#);
12407 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12408 let stmt = single_stmt(&ast);
12409 let crate::StmtKind::Throw { value } = stmt.kind else {
12410 panic!("expected Throw, got {:?}", stmt.kind);
12411 };
12412 assert!(matches!(
12413 ast.try_expr(value).unwrap().kind,
12414 crate::ExprKind::New { .. }
12415 ));
12416 }
12417
12418 #[test]
12419 fn parse_new_with_dotted_callee() {
12420 let (ast, diags) = parse_str("new Temporal.Duration({});");
12421 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12422 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12423 panic!("expected expression statement");
12424 };
12425 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12426 panic!("expected New");
12427 };
12428 let crate::ExprKind::FieldAccess { name, .. } = &ast.try_expr(*callee).unwrap().kind else {
12429 panic!("expected FieldAccess callee");
12430 };
12431 assert_eq!(name.name, "Duration");
12432 }
12433
12434 #[test]
12435 fn parse_new_with_three_level_dotted_callee() {
12436 let (ast, diags) = parse_str("new Foo.Bar.Baz();");
12437 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12438 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12439 panic!("expected expression statement");
12440 };
12441 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12442 panic!("expected New");
12443 };
12444 let crate::ExprKind::FieldAccess { receiver, name } = &ast.try_expr(*callee).unwrap().kind
12445 else {
12446 panic!("expected outer FieldAccess");
12447 };
12448 assert_eq!(name.name, "Baz");
12449 let crate::ExprKind::FieldAccess { name: mid_name, .. } =
12450 &ast.try_expr(*receiver).unwrap().kind
12451 else {
12452 panic!("expected inner FieldAccess");
12453 };
12454 assert_eq!(mid_name.name, "Bar");
12455 }
12456
12457 #[test]
12458 fn parse_new_with_trailing_dot_diagnoses() {
12459 let (_ast, diags) = parse_str("new Foo.;");
12460 assert!(
12461 diags.iter().any(|d| d
12462 .message
12463 .contains("expected identifier after `.` in constructor name")),
12464 "expected trailing-dot diagnostic, got: {diags:?}",
12465 );
12466 }
12467
12468 #[test]
12469 fn throw_without_expression_diagnoses() {
12470 let (_ast, diags) = parse_str("throw;");
12471 assert!(
12472 diags
12473 .iter()
12474 .any(|d| d.message == "expected expression after `throw`"),
12475 "expected bare-throw diagnostic, got: {diags:?}",
12476 );
12477 }
12478
12479 #[test]
12480 fn parse_try_catch() {
12481 let (ast, diags) = parse_str("try { } catch (e: Error) { }");
12482 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12483 let stmt = single_stmt(&ast);
12484 let crate::StmtKind::Try {
12485 body,
12486 ref catches,
12487 ref finally,
12488 } = stmt.kind
12489 else {
12490 panic!("expected Try, got {:?}", stmt.kind);
12491 };
12492 assert!(matches!(
12493 ast.try_stmt(body).unwrap().kind,
12494 crate::StmtKind::Block(ref v) if v.is_empty()
12495 ));
12496 let [clause] = catches.as_slice() else {
12497 panic!("expected one catch clause, got {catches:?}");
12498 };
12499 assert_eq!(clause.binding.name, "e");
12500 assert!(clause.ty.is_some());
12501 assert!(finally.is_none());
12502 }
12503
12504 #[test]
12505 fn parse_try_finally() {
12506 let (ast, diags) = parse_str("try { } finally { }");
12507 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12508 let stmt = single_stmt(&ast);
12509 let crate::StmtKind::Try {
12510 ref catches,
12511 ref finally,
12512 ..
12513 } = stmt.kind
12514 else {
12515 panic!("expected Try");
12516 };
12517 assert!(catches.is_empty());
12518 let finally_id = finally.expect("finally block");
12519 assert!(matches!(
12520 ast.try_stmt(finally_id).unwrap().kind,
12521 crate::StmtKind::Block(_)
12522 ));
12523 }
12524
12525 #[test]
12526 fn parse_try_catch_finally() {
12527 let (ast, diags) = parse_str("try { } catch (e: Error) { } finally { }");
12528 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12529 let stmt = single_stmt(&ast);
12530 let crate::StmtKind::Try {
12531 ref catches,
12532 ref finally,
12533 ..
12534 } = stmt.kind
12535 else {
12536 panic!("expected Try");
12537 };
12538 assert_eq!(catches.len(), 1);
12539 assert!(finally.is_some());
12540 }
12541
12542 #[test]
12543 fn bare_try_diagnoses() {
12544 let (_ast, diags) = parse_str("try { }");
12545 assert!(
12546 diags
12547 .iter()
12548 .any(|d| d.message == "try requires at least one of `catch` or `finally`"),
12549 "expected bare-try diagnostic, got: {diags:?}",
12550 );
12551 }
12552
12553 #[test]
12554 fn parse_untyped_catch() {
12555 let (ast, diags) = parse_str("try { } catch (e) { }");
12556 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12557 let stmt = single_stmt(&ast);
12558 let crate::StmtKind::Try {
12559 ref catches,
12560 ref finally,
12561 ..
12562 } = stmt.kind
12563 else {
12564 panic!("expected Try");
12565 };
12566 let [clause] = catches.as_slice() else {
12567 panic!("expected one catch clause, got {catches:?}");
12568 };
12569 assert_eq!(clause.binding.name, "e");
12570 assert!(clause.ty.is_none());
12571 assert!(finally.is_none());
12572 }
12573
12574 #[test]
12575 fn parse_multiple_catch_clauses() {
12576 let (ast, diags) = parse_str("try { } catch (e: HttpError) { } catch (f) { }");
12577 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12578 let stmt = single_stmt(&ast);
12579 let crate::StmtKind::Try {
12580 ref catches,
12581 ref finally,
12582 ..
12583 } = stmt.kind
12584 else {
12585 panic!("expected Try");
12586 };
12587 let [first, second] = catches.as_slice() else {
12588 panic!("expected two catch clauses, got {catches:?}");
12589 };
12590 assert_eq!(first.binding.name, "e");
12591 assert!(first.ty.is_some());
12592 assert_eq!(second.binding.name, "f");
12593 assert!(second.ty.is_none());
12594 assert!(finally.is_none());
12595 }
12596
12597 #[test]
12598 fn catch_after_finally_diagnoses() {
12599 let (_ast, diags) = parse_str("try { } finally { } catch (e: Error) { }");
12600 assert!(
12601 !diags.is_empty(),
12602 "expected diagnostic for catch-after-finally"
12603 );
12604 }
12605
12606 #[test]
12607 fn catch_missing_open_paren_diagnoses() {
12608 let (_ast, diags) = parse_str("try { } catch e: Error) { }");
12609 assert!(
12610 diags
12611 .iter()
12612 .any(|d| d.message == "expected `(` after `catch`"),
12613 "expected open-paren diagnostic, got: {diags:?}",
12614 );
12615 }
12616
12617 fn class_members(ast: &Ast) -> &[crate::ClassMember] {
12618 match &single_stmt(ast).kind {
12619 StmtKind::ClassDecl { members, .. } => members,
12620 other => panic!("expected ClassDecl, got {other:?}"),
12621 }
12622 }
12623
12624 #[test]
12625 fn parse_animal_class() {
12626 let src = r#"
12627class Animal {
12628 name: string;
12629 private sound: string;
12630 readonly species: string;
12631
12632 constructor(name: string, sound: string, species: string) {
12633 this.name = name;
12634 this.sound = sound;
12635 this.species = species;
12636 }
12637
12638 speak(): string {
12639 return this.name + " says " + this.sound;
12640 }
12641}
12642"#;
12643 let (ast, diags) = parse_str(src);
12644 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12645 let StmtKind::ClassDecl {
12646 name,
12647 generics,
12648 extends,
12649 implements,
12650 members,
12651 ..
12652 } = &single_stmt(&ast).kind
12653 else {
12654 panic!("expected ClassDecl");
12655 };
12656 assert_eq!(name.name, "Animal");
12657 assert!(generics.is_empty());
12658 assert!(extends.is_none());
12659 assert!(implements.is_empty());
12660 assert_eq!(members.len(), 5);
12661
12662 let crate::ClassMember::Field {
12663 name, modifiers, ..
12664 } = &members[0]
12665 else {
12666 panic!("member 0 should be a field");
12667 };
12668 assert_eq!(name.name, "name");
12669 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12670 assert!(modifiers.readonly.is_none());
12671
12672 let crate::ClassMember::Field { modifiers, .. } = &members[1] else {
12673 panic!("member 1 should be a field");
12674 };
12675 assert_eq!(modifiers.visibility, crate::Visibility::Private);
12676
12677 let crate::ClassMember::Field { modifiers, .. } = &members[2] else {
12678 panic!("member 2 should be a field");
12679 };
12680 assert!(modifiers.readonly.is_some());
12681 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12682
12683 assert!(matches!(
12684 &members[3],
12685 crate::ClassMember::Constructor { .. }
12686 ));
12687
12688 let crate::ClassMember::Method { name, .. } = &members[4] else {
12689 panic!("member 4 should be a method");
12690 };
12691 assert_eq!(name.name, "speak");
12692 }
12693
12694 #[test]
12695 fn parse_dog_class_extends_super_and_void_method() {
12696 let src = r#"
12697class Dog extends Animal {
12698 private tricks: string[];
12699
12700 constructor(name: string) {
12701 super(name, "woof", "canis familiaris");
12702 this.tricks = [];
12703 }
12704
12705 learn(trick: string): void {
12706 this.tricks.push(trick);
12707 }
12708}
12709"#;
12710 let (ast, diags) = parse_str(src);
12711 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12712 let StmtKind::ClassDecl {
12713 extends, members, ..
12714 } = &single_stmt(&ast).kind
12715 else {
12716 panic!("expected ClassDecl");
12717 };
12718 assert!(extends.is_some(), "Dog should extend Animal");
12719 assert_eq!(members.len(), 3);
12720 assert!(matches!(&members[0], crate::ClassMember::Field { .. }));
12721 assert!(matches!(
12722 &members[1],
12723 crate::ClassMember::Constructor { .. }
12724 ));
12725 assert!(matches!(&members[2], crate::ClassMember::Method { .. }));
12726 }
12727
12728 #[test]
12729 fn parse_class_implements_list() {
12730 let (ast, diags) = parse_str("class C implements I, J { run(): void {} }");
12731 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12732 let StmtKind::ClassDecl { implements, .. } = &single_stmt(&ast).kind else {
12733 panic!("expected ClassDecl");
12734 };
12735 assert_eq!(implements.len(), 2);
12736 }
12737
12738 #[test]
12739 fn parse_class_extends_and_implements() {
12740 let (ast, diags) = parse_str("class C extends B implements I { run(): void {} }");
12741 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12742 let StmtKind::ClassDecl {
12743 extends,
12744 implements,
12745 ..
12746 } = &single_stmt(&ast).kind
12747 else {
12748 panic!("expected ClassDecl");
12749 };
12750 assert!(extends.is_some());
12751 assert_eq!(implements.len(), 1);
12752 }
12753
12754 #[test]
12755 fn parse_class_generics_optional_field_and_initializer() {
12756 let (ast, diags) = parse_str("class Box<T> { value?: T; items: T[] = []; }");
12757 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12758 let StmtKind::ClassDecl {
12759 generics, members, ..
12760 } = &single_stmt(&ast).kind
12761 else {
12762 panic!("expected ClassDecl");
12763 };
12764 assert_eq!(generics.len(), 1);
12765 assert_eq!(generics[0].name, "T");
12766
12767 let crate::ClassMember::Field {
12768 optional,
12769 initializer,
12770 ..
12771 } = &members[0]
12772 else {
12773 panic!("member 0 should be a field");
12774 };
12775 assert!(*optional);
12776 assert!(initializer.is_none());
12777
12778 let crate::ClassMember::Field { initializer, .. } = &members[1] else {
12779 panic!("member 1 should be a field");
12780 };
12781 assert!(initializer.is_some());
12782 }
12783
12784 #[test]
12785 fn export_class_is_marked() {
12786 let (ast, diags) = parse_str("export class Foo {}");
12787 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12788 assert_eq!(ast.top_level.len(), 1);
12789 assert_eq!(ast.exported_decls.len(), 1);
12790 assert!(matches!(
12791 &single_stmt(&ast).kind,
12792 StmtKind::ClassDecl { .. }
12793 ));
12794 }
12795
12796 #[test]
12797 fn parameterless_index_signature_is_reported_from_its_member_start() {
12798 for (source, expected) in [
12799 ("interface P { readonly []: number }", "readonly []"),
12800 ("let p: { readonly []: number } = {};", "readonly []"),
12801 ("let q: { []: number } = {};", "[]"),
12802 ] {
12803 let (_ast, diags) = parse_str(source);
12804 let diag = diags
12805 .iter()
12806 .find(|d| d.message.contains("must declare exactly one parameter"))
12807 .unwrap_or_else(|| panic!("no diagnostic for {source}"));
12808 let reported = &source[diag.span.start as usize..diag.span.end as usize];
12809 assert_eq!(reported, expected, "{source}");
12810 }
12811 }
12812
12813 #[test]
12814 fn unterminated_object_type_expects_closing_brace() {
12815 let (_ast, diags) = parse_str("let p: { a: number\n");
12816 assert!(
12817 diags.iter().any(|d| d.message == "expected `}`"),
12818 "{diags:?}"
12819 );
12820 }
12821
12822 #[test]
12823 fn conflicting_visibility_modifiers_diagnose() {
12824 let (_ast, diags) = parse_str("class C { public private x: number; }");
12825 assert!(
12826 diags
12827 .iter()
12828 .any(|d| d.message.contains("at most one visibility modifier")),
12829 "got: {diags:?}",
12830 );
12831 }
12832
12833 #[test]
12834 fn duplicate_readonly_modifier_diagnoses() {
12835 let (_ast, diags) = parse_str("class C { readonly readonly x: number; }");
12836 assert!(
12837 diags
12838 .iter()
12839 .any(|d| d.message.contains("duplicate `readonly`")),
12840 "got: {diags:?}",
12841 );
12842 }
12843
12844 #[test]
12845 fn field_named_like_a_modifier_is_allowed() {
12846 let (ast, diags) = parse_str("class C { private: number; }");
12847 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12848 let crate::ClassMember::Field {
12849 name, modifiers, ..
12850 } = &class_members(&ast)[0]
12851 else {
12852 panic!("expected a field");
12853 };
12854 assert_eq!(name.name, "private");
12855 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12856 }
12857
12858 #[test]
12859 fn method_named_get_is_allowed() {
12860 let (ast, diags) = parse_str("class C { get(): number { return 1; } }");
12861 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12862 let crate::ClassMember::Method { name, .. } = &class_members(&ast)[0] else {
12863 panic!("expected a method");
12864 };
12865 assert_eq!(name.name, "get");
12866 }
12867
12868 #[test]
12869 fn static_method_parses() {
12870 let (ast, diags) = parse_str("class C { static f(): number { return 1; } }");
12871 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12872 let crate::ClassMember::Method {
12873 name, modifiers, ..
12874 } = &class_members(&ast)[0]
12875 else {
12876 panic!("expected a method");
12877 };
12878 assert_eq!(name.name, "f");
12879 assert!(modifiers.static_span.is_some());
12880 }
12881
12882 #[test]
12883 fn static_readonly_field_parses() {
12884 let (ast, diags) = parse_str("class C { static readonly x: number = 1; }");
12885 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12886 let crate::ClassMember::Field {
12887 name,
12888 modifiers,
12889 initializer,
12890 ..
12891 } = &class_members(&ast)[0]
12892 else {
12893 panic!("expected a field");
12894 };
12895 assert_eq!(name.name, "x");
12896 assert!(modifiers.static_span.is_some());
12897 assert!(modifiers.readonly.is_some());
12898 assert!(initializer.is_some());
12899 }
12900
12901 #[test]
12902 fn private_static_method_parses() {
12903 let (ast, diags) = parse_str("class C { private static f(): void {} }");
12904 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12905 let crate::ClassMember::Method { modifiers, .. } = &class_members(&ast)[0] else {
12906 panic!("expected a method");
12907 };
12908 assert_eq!(modifiers.visibility, crate::Visibility::Private);
12909 assert!(modifiers.static_span.is_some());
12910 }
12911
12912 #[test]
12913 fn method_named_static_is_allowed() {
12914 let (ast, diags) = parse_str("class C { static(): number { return 1; } }");
12915 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12916 let crate::ClassMember::Method {
12917 name, modifiers, ..
12918 } = &class_members(&ast)[0]
12919 else {
12920 panic!("expected a method");
12921 };
12922 assert_eq!(name.name, "static");
12923 assert!(modifiers.static_span.is_none());
12924 }
12925
12926 #[test]
12927 fn visibility_after_static_diagnoses() {
12928 let (_ast, diags) = parse_str("class C { static private f(): void {} }");
12929 assert!(
12930 diags
12931 .iter()
12932 .any(|d| d.message.contains("`private` must come before `static`")),
12933 "got: {diags:?}",
12934 );
12935 }
12936
12937 #[test]
12938 fn duplicate_static_modifier_diagnoses() {
12939 let (_ast, diags) = parse_str("class C { static static f(): void {} }");
12940 assert!(
12941 diags
12942 .iter()
12943 .any(|d| d.message.contains("duplicate `static` modifier")),
12944 "got: {diags:?}",
12945 );
12946 }
12947
12948 #[test]
12949 fn static_accessor_is_rejected() {
12950 let (_ast, diags) = parse_str("class C { static get x(): number { return 1; } }");
12951 assert!(
12952 diags
12953 .iter()
12954 .any(|d| d.message.contains("static accessors are not supported")),
12955 "got: {diags:?}",
12956 );
12957 }
12958
12959 #[test]
12960 fn static_constructor_is_rejected() {
12961 let (_ast, diags) = parse_str("class C { static constructor() {} }");
12962 assert!(
12963 diags
12964 .iter()
12965 .any(|d| d.message.contains("a constructor cannot be `static`")),
12966 "got: {diags:?}",
12967 );
12968 }
12969
12970 #[test]
12971 fn optional_static_field_is_rejected() {
12972 let (_ast, diags) = parse_str("class C { static x?: number; }");
12973 assert!(
12974 diags
12975 .iter()
12976 .any(|d| d.message.contains("a static field cannot be optional")),
12977 "got: {diags:?}",
12978 );
12979 }
12980
12981 #[test]
12982 fn async_function_names_the_synchronous_rewrite() {
12983 let (_ast, diags) = parse_str(
12984 "async function main(): Promise<string> {\n const x = await f();\n return x;\n}",
12985 );
12986 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
12987 assert_eq!(
12988 messages,
12989 [
12990 "`async` functions are not supported",
12991 "`await` is not supported"
12992 ],
12993 "{diags:?}"
12994 );
12995 assert_eq!(
12996 diags[0].help,
12997 [
12998 "calls return their value directly: remove `async`, write the return type `T` \
12999 instead of `Promise<T>`, and drop `await`"
13000 ]
13001 );
13002 assert_eq!(
13003 diags[1].help,
13004 ["calls return their value directly: remove `await`"]
13005 );
13006 }
13007
13008 #[test]
13009 fn async_is_rejected_on_every_function_form() {
13010 for source in [
13011 "export async function f(): void {}",
13012 "const f = async (): void => {};",
13013 "const f = async x => x;",
13014 "const f = async function (): void {};",
13015 "class C { async run(): void {} }",
13016 "class C { static async run(): void {} }",
13017 ] {
13018 let (_ast, diags) = parse_str(source);
13019 assert_eq!(
13020 diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13021 ["`async` functions are not supported"],
13022 "{source}: {diags:?}"
13023 );
13024 }
13025 }
13026
13027 #[test]
13028 fn await_before_a_template_is_reported() {
13029 for source in ["const a = await `hi`;", "const a = await `a${1}b`;"] {
13030 let (_ast, diags) = parse_str(source);
13031 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13032 assert_eq!(
13033 messages,
13034 ["`await` is not supported"],
13035 "{source}: {diags:?}"
13036 );
13037 }
13038 }
13039
13040 #[test]
13041 fn async_and_await_stay_ordinary_names() {
13042 for source in [
13043 "const async = 1; const y = async + 1;",
13044 "function async(n: number): number { return n; } async(1);",
13045 "const await = 2; const z = await * 2;",
13046 "const await = (n: number): number => n; const a = await(1);",
13047 "const await = [1, 2]; const b = await[0];",
13048 "const async = { k: 1 }; const c = \"k\" in async; const d = async in ({ o: 1 });",
13049 "const async: unknown = 1; const n = async as (number);",
13050 "const await: unknown = 1; const m = await as number;",
13051 "const await = 1; const t = `${await}`;",
13052 "class C { async(): void {} async: number = 1; }",
13053 ] {
13054 let (_ast, diags) = parse_str(source);
13055 assert!(diags.is_empty(), "{source}: {diags:?}");
13056 }
13057 }
13058
13059 #[test]
13060 fn an_async_object_literal_method_names_the_rewrite_once() {
13061 assert_eq!(
13062 crate::typechecker::rules::test_util::compile_messages(
13063 "function main(): string {\n const o = { async run(): string { return \"a\"; } };\n return o.run();\n}\n"
13064 ),
13065 ["`async` functions are not supported"]
13066 );
13067 let (_ast, diags) = parse_str("const o = { async run(): string { return \"a\"; } };");
13068 assert_eq!(
13069 diags[0].help,
13070 [
13071 "calls return their value directly: remove `async`, write the return type `T` \
13072 instead of `Promise<T>`, and drop `await`"
13073 ]
13074 );
13075 }
13076
13077 #[test]
13078 fn await_in_an_async_object_literal_method_is_reported_too() {
13079 let (_ast, diags) =
13080 parse_str("const o = { async run(): string { const x = await f(); return x; } };");
13081 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13082 assert_eq!(
13083 messages,
13084 [
13085 "`async` functions are not supported",
13086 "`await` is not supported"
13087 ],
13088 "{diags:?}"
13089 );
13090 }
13091
13092 #[test]
13093 fn every_async_member_form_reports_async() {
13094 for source in [
13095 "const o = { async *run(): void {} };",
13096 "class C { async *run(): void {} }",
13097 "const o = { async [k](): void {} };",
13098 "const o = { async in(): void {} };",
13099 "class C { async as(): void {} }",
13100 ] {
13101 let (_ast, diags) = parse_str(source);
13102 assert_eq!(
13103 diags[0].message, "`async` functions are not supported",
13104 "{source}: {diags:?}"
13105 );
13106 }
13107 }
13108
13109 #[test]
13110 fn async_string_key_methods_report_only_async() {
13111 for source in [
13112 "const o = { async \"s\"(): string { return \"a\"; } };",
13113 "class C { async \"s\"(): string { return \"a\"; } }",
13114 ] {
13115 let (_ast, diags) = parse_str(source);
13116 assert_eq!(
13117 diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13118 ["`async` functions are not supported"],
13119 "{source}: {diags:?}"
13120 );
13121 }
13122 }
13123
13124 #[test]
13125 fn async_before_a_line_break_is_a_member_of_its_own() {
13126 for source in [
13127 "class C {\n async\n run(): void {}\n}",
13128 "const o = { async\n run(): void {} };",
13129 ] {
13130 let (_ast, diags) = parse_str(source);
13131 assert!(
13132 diags
13133 .iter()
13134 .all(|d| d.message != "`async` functions are not supported"),
13135 "{source}: {diags:?}"
13136 );
13137 }
13138 }
13139
13140 #[test]
13141 fn async_stays_an_object_literal_key() {
13142 let messages = crate::typechecker::rules::test_util::compile_messages(
13143 "function main(): number {\n const async = 2;\n const a = { async: 1 };\n const b = { async };\n const c = { async(): number { return 3; } };\n const d = { async, e: async * 2 };\n return a.async + b.async + c.async() + d.e;\n}\n",
13144 );
13145 assert!(messages.is_empty(), "{messages:?}");
13146 }
13147
13148 #[test]
13149 fn for_await_names_the_rewrite_once() {
13150 assert_eq!(
13151 crate::typechecker::rules::test_util::compile_messages(
13152 "import * as fs from \"submilli:fs\";\nfunction main(): string {\n for await (const e of fs.list(\".\", false)) { }\n return \"x\";\n}\n"
13153 ),
13154 ["`for await` is not supported"]
13155 );
13156 let (ast, diags) = parse_str("for await (const e of [1]) { }");
13157 assert_eq!(
13158 diags[0].help,
13159 ["calls return their values directly: remove `await` and write `for (const e of …)`"]
13160 );
13161 assert_eq!(ast.top_level.len(), 1, "the loop is still read");
13162 }
13163
13164 #[test]
13165 fn protected_member_is_rejected() {
13166 let (_ast, diags) = parse_str("class C { protected x: number; }");
13167 assert!(
13168 diags
13169 .iter()
13170 .any(|d| d.message.contains("`protected` is not supported")),
13171 "got: {diags:?}",
13172 );
13173 }
13174
13175 #[test]
13176 fn abstract_member_is_rejected() {
13177 let (_ast, diags) = parse_str("class C { abstract run(): void; }");
13178 assert!(
13179 diags
13180 .iter()
13181 .any(|d| d.message.contains("abstract classes are not supported")),
13182 "got: {diags:?}",
13183 );
13184 }
13185
13186 #[test]
13187 fn getter_parses_as_accessor() {
13188 let (ast, diags) = parse_str("class C { get value(): number { return 1; } }");
13189 assert!(diags.is_empty(), "got: {diags:?}");
13190 let crate::ClassMember::Accessor { kind, name, .. } = &class_members(&ast)[0] else {
13191 panic!("expected an accessor member");
13192 };
13193 assert_eq!(*kind, crate::AccessorKind::Get);
13194 assert_eq!(name.name, "value");
13195 }
13196
13197 #[test]
13198 fn getter_without_return_type_is_rejected() {
13199 let (ast, diags) = parse_str("class C { get value() { return 1; } }");
13200 assert_eq!(diags.len(), 1, "got: {diags:?}");
13201 assert_eq!(diags[0].message, "expected `:` and return type");
13202 assert_eq!(diags[0].help, vec!["get name(): T { … }".to_string()]);
13203 assert!(
13204 matches!(
13205 &class_members(&ast)[0],
13206 crate::ClassMember::Accessor {
13207 return_type: None,
13208 ..
13209 }
13210 ),
13211 "the getter is still recovered as a member"
13212 );
13213 }
13214
13215 #[test]
13216 fn setter_parses_as_accessor() {
13217 let (ast, diags) = parse_str("class C { set value(v: number) {} }");
13218 assert!(diags.is_empty(), "got: {diags:?}");
13219 let crate::ClassMember::Accessor { kind, param, .. } = &class_members(&ast)[0] else {
13220 panic!("expected an accessor member");
13221 };
13222 assert_eq!(*kind, crate::AccessorKind::Set);
13223 assert!(param.is_some(), "setter should have a parameter");
13224 }
13225
13226 #[test]
13227 fn param_property_modifier_outside_constructor_is_rejected() {
13228 let (_ast, diags) = parse_str("function f(public x: number): void {}");
13229 assert!(
13230 diags.iter().any(|d| d
13231 .message
13232 .contains("parameter properties are only allowed in a constructor")),
13233 "got: {diags:?}",
13234 );
13235 }
13236
13237 #[test]
13238 fn constructor_return_type_is_rejected() {
13239 let (_ast, diags) = parse_str("class C { constructor(): void {} }");
13240 assert!(
13241 diags.iter().any(|d| d
13242 .message
13243 .contains("constructor cannot declare a return type")),
13244 "got: {diags:?}",
13245 );
13246 }
13247
13248 #[test]
13249 fn field_initializer_without_type_is_rejected() {
13250 let (_ast, diags) = parse_str("class C { x = 1; }");
13251 assert!(
13252 diags
13253 .iter()
13254 .any(|d| d.message.contains("class fields require a type annotation")),
13255 "got: {diags:?}",
13256 );
13257 }
13258
13259 #[test]
13260 fn this_outside_method_is_left_to_typechecker() {
13261 let (_ast, diags) = parse_str("function f(): string { return this.name; }");
13262 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13263 }
13264
13265 #[test]
13266 fn super_outside_method_is_rejected() {
13267 let (_ast, diags) = parse_str("function f(): void { super(1); }");
13268 assert!(
13269 diags.iter().any(|d| d
13270 .message
13271 .contains("`super` is only valid inside a class method or constructor body")),
13272 "got: {diags:?}",
13273 );
13274 }
13275
13276 #[test]
13277 fn this_inside_method_is_accepted_by_parser() {
13278 let (_ast, diags) = parse_str("class C { x: number; read(): number { return this.x; } }");
13279 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13280 }
13281 #[test]
13282 fn parse_void_expression_preserves_operand_and_precedence() {
13283 let (ast, diags) = parse_str("void effect() === undefined;");
13284 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13285 let expression = expr_of_single_stmt(&ast);
13286 let crate::ExprKind::Binary { lhs, rhs, .. } = expression.kind else {
13287 panic!("expected equality")
13288 };
13289 let crate::ExprKind::Void { operand } = ast.try_expr(lhs).unwrap().kind else {
13290 panic!("expected void")
13291 };
13292 assert!(matches!(
13293 ast.try_expr(operand).unwrap().kind,
13294 crate::ExprKind::Call { .. }
13295 ));
13296 assert!(matches!(
13297 ast.try_expr(rhs).unwrap().kind,
13298 crate::ExprKind::Identifier(_)
13299 ));
13300 }
13301
13302 #[test]
13303 fn parse_void_newline_and_object_operands() {
13304 for source in [
13305 "void\n0;",
13306 "void { value: 1 };",
13307 "void\n{ value: 1 };",
13308 "void /x/;",
13309 "void void 0;",
13310 ] {
13311 let (_, diags) = parse_str(source);
13312 assert!(diags.is_empty(), "{source}: {diags:?}");
13313 }
13314 let (_, diags) = parse_str("void; 0;");
13315 assert!(
13316 !diags.is_empty(),
13317 "an explicit semicolon cannot split void from its operand"
13318 );
13319 }
13320
13321 #[test]
13322 fn parse_optional_parameters_and_defaults_in_callable_forms() {
13323 for source in [
13324 "function f(x = 1): number { return x; }",
13325 "function f(x: number = 1, y: number): number { return y; }",
13326 "function f({ x } = { x: 1 }): number { return x; }",
13327 "const f = (x?: number) => x;",
13328 "const f = (x: number = effect()) => x;",
13329 "const f = function(x: number = effect()) { return x; };",
13330 "const f = { m(x: number = effect()): number { return x; } };",
13331 "const f = ([x = 1]: number[] = values) => x;",
13332 "let f: (x?: number, ...rest: string[]) => void = value;",
13333 "let f: { method?(x?: number): void } = value;",
13334 "function f(x: number = 1, y?: number): void {}",
13335 ] {
13336 let (_, diags) = parse_str(source);
13337 assert!(diags.is_empty(), "{source}: {diags:?}");
13338 }
13339 }
13340
13341 #[test]
13342 fn parse_invalid_optional_parameters() {
13343 for source in [
13344 "function f(x?: number, y: number): void {}",
13345 "const f = (x?: number, y: number) => y;",
13346 "let f: (x?: number, y: number) => void = value;",
13347 "function f(...x?: number[]): void {}",
13348 "function f(x?: number = 1): void {}",
13349 "const f = (x?: number = 1) => x;",
13350 "function f({x}?: T): void {}",
13351 ] {
13352 let (_, diags) = parse_str(source);
13353 assert!(!diags.is_empty(), "expected diagnostic for {source}");
13354 }
13355 }
13356
13357 #[test]
13358 fn parse_optional_tuple_forms_and_order() {
13359 for source in [
13360 "let x: [number, string?] = value;",
13361 "let x: [x?: number, y?: string] = value;",
13362 "let x: readonly [(number | null)?, string?] = value;",
13363 ] {
13364 let (_, diags) = parse_str(source);
13365 assert!(diags.is_empty(), "{source}: {diags:?}");
13366 }
13367 for source in [
13368 "let x: [number?, string] = value;",
13369 "let x: [x: number?] = value;",
13370 ] {
13371 let (_, diags) = parse_str(source);
13372 assert!(!diags.is_empty(), "expected diagnostic for {source}");
13373 }
13374 }
13375
13376 #[test]
13377 fn parse_array_pattern_defaults_preserve_holes() {
13378 let (ast, diags) = parse_str("const [, a = effect(), b] = values;");
13379 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13380 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
13381 panic!("expected pattern")
13382 };
13383 let crate::Binding::Array {
13384 elems, defaults, ..
13385 } = binding
13386 else {
13387 panic!("expected array")
13388 };
13389 assert_eq!(elems.len(), 3);
13390 assert_eq!(defaults.len(), elems.len());
13391 assert!(elems[0].is_none());
13392 assert!(defaults[0].is_none());
13393 assert!(defaults[1].is_some());
13394 assert!(defaults[2].is_none());
13395 }
13396
13397 #[test]
13398 fn parse_optional_class_method_forms() {
13399 let (ast, diags) =
13400 parse_str("class C { declared?(x?: number): void; implemented?(): void\n{} }");
13401 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13402 let members = class_members(&ast);
13403 assert!(matches!(
13404 &members[0],
13405 crate::ClassMember::Field {
13406 optional: true,
13407 initializer: None,
13408 ty: crate::TypeAnnotation {
13409 kind: crate::TypeAnnotationKind::Function { .. },
13410 ..
13411 },
13412 ..
13413 }
13414 ));
13415 assert!(matches!(
13416 &members[1],
13417 crate::ClassMember::Method { optional: true, .. }
13418 ));
13419 }
13420 #[test]
13421 fn defaults_retain_their_callable_receiver_scope() {
13422 for source in [
13423 "class C { value: number = 1; method(value: number = this.value): number { return value; } }",
13424 "class C { constructor(value: number = this.value) {} value: number = 1; }",
13425 "const f = function(this: { value: number }, value: number = this.value): number { return value; };",
13426 ] {
13427 let (ast, diagnostics) = parse_str(source);
13428 assert!(diagnostics.is_empty(), "{source}: {diagnostics:?}");
13429 assert!(
13430 ast.source_expressions()
13431 .iter()
13432 .any(|expr| matches!(expr.kind, ExprKind::This))
13433 );
13434 assert!(
13435 !ast.source_expressions()
13436 .iter()
13437 .any(|expr| matches!(expr.kind, ExprKind::ThisOutsideReceiver))
13438 );
13439 }
13440 }
13441}