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 self.advance();
2898 self.block_depth += 1;
2899 let arms = self.parse_switch_arms();
2900 self.block_depth -= 1;
2901 let (cases, default) = arms?;
2902
2903 if !matches!(self.peek().kind, TokenKind::RightBrace) {
2904 self.error_at_peek("expected `}` to close `switch` body");
2905 return None;
2906 }
2907 let close = self.advance();
2908
2909 parse_arena_result(
2910 self.ast.try_push_stmt(Stmt {
2911 kind: StmtKind::Switch {
2912 discriminant,
2913 cases,
2914 default,
2915 },
2916 span: self.span(kw.span.start, close.span.end),
2917 }),
2918 &mut self.fatal,
2919 )
2920 }
2921
2922 fn parse_switch_arms(&mut self) -> Option<(Vec<SwitchCase>, Option<SwitchDefault>)> {
2924 let mut cases: Vec<SwitchCase> = Vec::new();
2925 let mut default: Option<SwitchDefault> = None;
2926 let mut pending_values: Vec<ExprId> = Vec::new();
2927 let mut pending_label_start: Option<u32> = None;
2928 let mut pending_default: Option<Span> = None;
2929
2930 loop {
2931 match self.peek().kind {
2932 TokenKind::Eof => break,
2933 TokenKind::Semicolon => {
2935 self.advance();
2936 }
2937 TokenKind::RightBrace => {
2938 if pending_label_start.is_some() {
2940 self.error_at_peek(
2941 "expected case body before `}` — case labels must be followed by statements",
2942 );
2943 return None;
2944 }
2945 break;
2946 }
2947 TokenKind::Case => {
2948 let case_kw = self.advance();
2949 if pending_label_start.is_none() {
2950 pending_label_start = Some(case_kw.span.start);
2951 }
2952 let value = self.parse_expression()?;
2953 if !matches!(self.peek().kind, TokenKind::Colon) {
2954 self.error_at_peek("expected `:` after `case` label");
2955 return None;
2956 }
2957 self.advance();
2958 pending_values.push(value);
2959 }
2960 TokenKind::Default => {
2961 let kw_tok = self.advance();
2962 if default.is_some() {
2963 self.error_at(kw_tok.span, "duplicate `default` clause in `switch`");
2964 return None;
2965 }
2966 if !matches!(self.peek().kind, TokenKind::Colon) {
2967 self.error_at_peek("expected `:` after `default`");
2968 return None;
2969 }
2970 self.advance();
2971 if pending_label_start.is_none() {
2972 pending_label_start = Some(kw_tok.span.start);
2973 }
2974 pending_default = Some(kw_tok.span);
2975 }
2976 _ => {
2977 if pending_label_start.is_none() {
2978 self.error_at_peek(
2979 "expected `case` or `default` at the start of a `switch` body",
2980 );
2981 return None;
2982 }
2983 let Some(label_start) = pending_label_start else {
2984 return self.invariant_failure("switch arm has no label");
2985 };
2986 let body = self.parse_switch_arm_body(label_start)?;
2987 let arm_span =
2988 parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?.span;
2989 if !pending_values.is_empty() {
2990 let first = parse_arena_result(
2991 self.ast.try_expr(pending_values[0]),
2992 &mut self.fatal,
2993 )?
2994 .span
2995 .start;
2996 let case_span = self.span(first, arm_span.end);
2997 cases.push(SwitchCase {
2998 values: std::mem::take(&mut pending_values),
2999 body,
3000 span: case_span,
3001 });
3002 }
3003 if let Some(def_kw_span) = pending_default.take() {
3004 default = Some(SwitchDefault {
3005 body,
3006 span: self.span(def_kw_span.start, arm_span.end),
3007 });
3008 }
3009 pending_label_start = None;
3010 }
3011 }
3012 }
3013 Some((cases, default))
3014 }
3015
3016 fn parse_switch_arm_body(&mut self, body_start: u32) -> Option<StmtId> {
3018 let mut stmts: Vec<StmtId> = Vec::new();
3019 let mut end_offset = body_start;
3020 loop {
3021 if matches!(
3022 self.peek().kind,
3023 TokenKind::Case | TokenKind::Default | TokenKind::RightBrace | TokenKind::Eof
3024 ) {
3025 break;
3026 }
3027 if matches!(self.peek().kind, TokenKind::Semicolon) {
3028 end_offset = self.peek().span.end;
3029 self.advance();
3030 continue;
3031 }
3032 let pos_before = self.pos;
3033 if let Some(id) = self.parse_statement() {
3034 end_offset = parse_arena_result(self.ast.try_stmt(id), &mut self.fatal)?
3035 .span
3036 .end;
3037 stmts.push(id);
3038 } else {
3039 self.recover();
3040 if self.pos == pos_before && !self.is_at_eof() {
3041 self.advance();
3042 }
3043 end_offset = end_offset.max(self.prev_token_end());
3046 }
3047 }
3048 let span = self.span(body_start, end_offset);
3049 parse_arena_result(
3050 self.ast.try_push_stmt(Stmt {
3051 kind: StmtKind::Block(stmts),
3052 span,
3053 }),
3054 &mut self.fatal,
3055 )
3056 }
3057
3058 fn parse_break(&mut self) -> Option<StmtId> {
3059 let kw = self.advance();
3060 if !matches!(self.peek().kind, TokenKind::Semicolon) {
3061 self.error_at_peek("expected `;` after `break`");
3062 return None;
3063 }
3064 let semi = self.advance();
3065 parse_arena_result(
3066 self.ast.try_push_stmt(Stmt {
3067 kind: StmtKind::Break,
3068 span: self.span(kw.span.start, semi.span.end),
3069 }),
3070 &mut self.fatal,
3071 )
3072 }
3073
3074 fn parse_continue(&mut self) -> Option<StmtId> {
3075 let kw = self.advance();
3076 if !matches!(self.peek().kind, TokenKind::Semicolon) {
3077 self.error_at_peek("expected `;` after `continue`");
3078 return None;
3079 }
3080 let semi = self.advance();
3081 parse_arena_result(
3082 self.ast.try_push_stmt(Stmt {
3083 kind: StmtKind::Continue,
3084 span: self.span(kw.span.start, semi.span.end),
3085 }),
3086 &mut self.fatal,
3087 )
3088 }
3089
3090 fn parse_return(&mut self) -> Option<StmtId> {
3091 let kw = self.advance();
3092
3093 let value = if matches!(self.peek().kind, TokenKind::Semicolon) {
3094 None
3095 } else {
3096 Some(self.parse_expression()?)
3097 };
3098
3099 if !self.at_statement_end() {
3100 self.error_at_peek("expected `;` after return");
3101 return None;
3102 }
3103 let end = self.finish_statement();
3104
3105 parse_arena_result(
3106 self.ast.try_push_stmt(Stmt {
3107 kind: StmtKind::Return(value),
3108 span: self.span(kw.span.start, end),
3109 }),
3110 &mut self.fatal,
3111 )
3112 }
3113
3114 fn parse_throw(&mut self) -> Option<StmtId> {
3115 let kw = self.advance();
3116
3117 if matches!(self.peek().kind, TokenKind::Semicolon) {
3118 self.error_at_peek_with_help(
3119 "expected expression after `throw`",
3120 vec!["throw new Error(\"message\");".to_string()],
3121 );
3122 return None;
3123 }
3124
3125 let value = self.parse_expression()?;
3126
3127 if !self.at_statement_end() {
3128 self.error_at_peek("expected `;` after `throw`");
3129 return None;
3130 }
3131 let end = self.finish_statement();
3132
3133 parse_arena_result(
3134 self.ast.try_push_stmt(Stmt {
3135 kind: StmtKind::Throw { value },
3136 span: self.span(kw.span.start, end),
3137 }),
3138 &mut self.fatal,
3139 )
3140 }
3141
3142 fn parse_try(&mut self) -> Option<StmtId> {
3143 let kw = self.advance();
3144
3145 let body = self.parse_block()?;
3146
3147 let mut catches: Vec<CatchClause> = Vec::new();
3148 let mut finally: Option<StmtId> = None;
3149 let mut tail_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3150 .span
3151 .end;
3152
3153 loop {
3154 match self.peek().kind {
3155 TokenKind::Catch => {
3156 if finally.is_some() {
3157 self.error_at_peek("`catch` clause must appear before `finally`");
3158 return None;
3159 }
3160 let clause = self.parse_catch_clause()?;
3161 tail_end = clause.span.end;
3162 catches.push(clause);
3163 }
3164 TokenKind::Finally => {
3165 if finally.is_some() {
3166 self.error_at_peek("duplicate `finally` clause");
3167 return None;
3168 }
3169 self.advance();
3170 let block = self.parse_block()?;
3171 tail_end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
3172 .span
3173 .end;
3174 finally = Some(block);
3175 }
3176 _ => break,
3177 }
3178 }
3179
3180 if catches.is_empty() && finally.is_none() {
3181 self.error_at(
3182 self.span(kw.span.start, tail_end),
3183 "try requires at least one of `catch` or `finally`",
3184 );
3185 return None;
3186 }
3187
3188 parse_arena_result(
3189 self.ast.try_push_stmt(Stmt {
3190 kind: StmtKind::Try {
3191 body,
3192 catches,
3193 finally,
3194 },
3195 span: self.span(kw.span.start, tail_end),
3196 }),
3197 &mut self.fatal,
3198 )
3199 }
3200
3201 fn parse_catch_clause(&mut self) -> Option<CatchClause> {
3202 let kw = self.advance();
3203
3204 let (binding, ty) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
3205 (
3207 Ident {
3208 name: "#catch".into(),
3209 span: kw.span,
3210 },
3211 None,
3212 )
3213 } else {
3214 self.parse_catch_binding()?
3215 };
3216
3217 let body = self.parse_block()?;
3218 let body_end = parse_arena_result(self.ast.try_stmt(body), &mut self.fatal)?
3219 .span
3220 .end;
3221
3222 Some(CatchClause {
3223 binding,
3224 ty,
3225 body,
3226 span: self.span(kw.span.start, body_end),
3227 })
3228 }
3229
3230 fn parse_catch_binding(&mut self) -> Option<(Ident, Option<TypeAnnotation>)> {
3231 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3232 self.error_at_peek("expected `(` after `catch`");
3233 return None;
3234 }
3235 self.advance();
3236 let name_tok = self.expect_binding_identifier("expected catch binding name")?;
3237 let binding = self.ident_from_token(&name_tok);
3238 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
3239 self.advance();
3240 if self.reject_any_catch_annotation() {
3241 None
3243 } else {
3244 Some(self.parse_type_annotation()?)
3245 }
3246 } else {
3247 None
3248 };
3249 if !matches!(self.peek().kind, TokenKind::RightParen) {
3250 self.error_at_peek("expected `)` after catch binding");
3251 return None;
3252 }
3253 self.advance();
3254 Some((binding, ty))
3255 }
3256
3257 fn reject_any_catch_annotation(&mut self) -> bool {
3260 let is_bare_any = self.peek_identifier_text_is("any")
3261 && matches!(self.peek_at(1).kind, TokenKind::RightParen);
3262 if !is_bare_any {
3263 return false;
3264 }
3265 let any_tok = self.advance();
3266 self.error_at_with_help(
3268 any_tok.span,
3269 "`any` is not supported",
3270 vec![
3271 "write `catch (e)` to catch every thrown error, or name an error class, \
3272 as in `catch (e: RangeError)`, to catch only that type"
3273 .to_string(),
3274 ],
3275 );
3276 true
3277 }
3278
3279 fn parse_paren_condition(&mut self) -> Option<ExprId> {
3280 if !matches!(self.peek().kind, TokenKind::LeftParen) {
3281 self.error_at_peek("expected `(`");
3282 return None;
3283 }
3284 self.advance();
3285 let expr = self.parse_expression()?;
3286 if !matches!(self.peek().kind, TokenKind::RightParen) {
3287 self.error_at_peek("expected `)`");
3288 return None;
3289 }
3290 self.advance();
3291 Some(expr)
3292 }
3293
3294 fn parse_predicate_or_return_type(
3295 &mut self,
3296 type_pos: TypePos,
3297 ) -> Option<(
3298 Option<TypeAnnotation>,
3299 Option<crate::TypePredicateAnnotation>,
3300 )> {
3301 if matches!(self.peek().kind, TokenKind::Identifier) && self.peek_at_is_word(1, "is") {
3302 let param_tok = self.advance();
3303 let param = self.ident_from_token(¶m_tok);
3304 self.advance();
3305 let asserted = self.parse_type_annotation_in(type_pos)?;
3306 let span = self.span(param.span.start, asserted.span.end);
3307 return Some((
3308 None,
3309 Some(crate::TypePredicateAnnotation {
3310 param,
3311 asserted,
3312 span,
3313 }),
3314 ));
3315 }
3316 Some((Some(self.parse_type_annotation_in(type_pos)?), None))
3317 }
3318
3319 fn parse_type_annotation(&mut self) -> Option<TypeAnnotation> {
3320 self.parse_type_annotation_in(TypePos::Anywhere)
3321 }
3322
3323 fn parse_type_annotation_in(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3324 let leading_pipe = matches!(self.peek().kind, TokenKind::Pipe);
3326 if leading_pipe {
3327 self.advance();
3328 }
3329 let first = self.parse_type_array(type_pos)?;
3330 if !matches!(self.peek().kind, TokenKind::Pipe) {
3331 return Some(first);
3332 }
3333 let start = first.span.start;
3334 let mut end = first.span.end;
3335 let mut members = vec![first];
3336 while matches!(self.peek().kind, TokenKind::Pipe) {
3337 self.advance();
3338 let member = self.parse_type_array(type_pos)?;
3339 end = member.span.end;
3340 members.push(member);
3341 }
3342 Some(TypeAnnotation {
3343 kind: TypeAnnotationKind::Union(members),
3344 span: self.span(start, end),
3345 })
3346 }
3347
3348 fn parse_type_array(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3349 self.with_recursion_limit(|parser| parser.parse_type_array_inner(type_pos))
3350 }
3351
3352 fn parse_type_array_inner(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3353 if self.is_contextual_type_operator(self.pos, "keyof") {
3357 let kw = self.advance();
3358 let operand = self.parse_type_array(type_pos)?;
3359 let span = self.span(kw.span.start, operand.span.end);
3360 return Some(TypeAnnotation {
3361 kind: TypeAnnotationKind::KeyOf(Box::new(operand)),
3362 span,
3363 });
3364 }
3365 if self.is_contextual_type_operator(self.pos, "readonly") {
3366 return self.parse_readonly_type(type_pos);
3367 }
3368
3369 let mut ty = match self.peek().kind {
3370 TokenKind::Typeof => {
3376 let kw = self.advance();
3377 let first = self.expect_identifier("expected a value name after `typeof`")?;
3378 let mut path = vec![self.type_name(first.span)?];
3379 let mut end = first.span.end;
3380 while matches!(self.peek().kind, TokenKind::Dot) {
3381 self.advance();
3382 let seg = self.expect_identifier_name("expected a property name after `.`")?;
3383 end = seg.span.end;
3384 path.push(self.type_name(seg.span)?);
3385 }
3386 TypeAnnotation {
3387 kind: TypeAnnotationKind::TypeOf { path },
3388 span: self.span(kw.span.start, end),
3389 }
3390 }
3391 TokenKind::Identifier | TokenKind::Void => {
3392 let leading_is_identifier = matches!(self.peek().kind, TokenKind::Identifier);
3394 let tok = self.advance();
3395 let first_span = tok.span;
3396 let mut path_end = first_span.end;
3397 let first_name = self.type_name(first_span)?;
3398 let mut path = vec![first_name.clone()];
3399 if leading_is_identifier {
3400 while matches!(self.peek().kind, TokenKind::Dot) {
3401 self.advance();
3402 if !matches!(self.peek().kind, TokenKind::Identifier) {
3403 self.error_at_peek("expected identifier after `.` in type name");
3404 return None;
3405 }
3406 let seg = self.advance();
3407 path_end = seg.span.end;
3408 path.push(self.type_name(seg.span)?);
3409 }
3410 }
3411 let (args, end) = if matches!(self.peek().kind, TokenKind::LessThan)
3413 && !self.line_break_before_peek()
3414 {
3415 self.parse_type_argument_list()?
3416 } else {
3417 (Vec::new(), path_end)
3418 };
3419 let outer_span = self.span(first_span.start, end);
3420 let kind = if path.len() == 1 {
3421 if first_name.name == "any" {
3422 self.error_at_with_help(
3423 first_span,
3424 "`any` is not supported",
3425 vec![
3426 "annotate a concrete type instead — an object shape \
3427 like `{ id: string }`, a named `interface`, or \
3428 `unknown` (a safe dynamic type you narrow before use)"
3429 .to_string(),
3430 ],
3431 );
3432 }
3433 TypeAnnotationKind::Name {
3434 name: first_name,
3435 args,
3436 }
3437 } else {
3438 TypeAnnotationKind::Qualified { path, args }
3439 };
3440 TypeAnnotation {
3441 kind,
3442 span: outer_span,
3443 }
3444 }
3445 TokenKind::NullLiteral => {
3447 let tok = self.advance();
3448 TypeAnnotation {
3449 kind: TypeAnnotationKind::Name {
3450 name: self.type_name(tok.span)?,
3451 args: Vec::new(),
3452 },
3453 span: tok.span,
3454 }
3455 }
3456 TokenKind::StringLiteral(_) => {
3457 let tok = self.advance();
3458 let span = tok.span;
3459 let TokenKind::StringLiteral(s) = tok.kind else {
3460 unreachable!("advance preserves the matched StringLiteral variant");
3462 };
3463 TypeAnnotation {
3464 kind: TypeAnnotationKind::StringLiteral(s),
3465 span,
3466 }
3467 }
3468 TokenKind::NumberLiteral(_) => {
3469 let tok = self.advance();
3470 let span = tok.span;
3471 let TokenKind::NumberLiteral(v) = tok.kind else {
3472 unreachable!("advance preserves the matched NumberLiteral variant");
3474 };
3475 let canonical = if v == 0.0 { 0.0 } else { v };
3477 TypeAnnotation {
3478 kind: TypeAnnotationKind::NumberLiteral(crate::types::LiteralF64(canonical)),
3479 span,
3480 }
3481 }
3482 TokenKind::BigIntLiteral(_) => {
3483 let tok = self.advance();
3484 let TokenKind::BigIntLiteral(digits) = tok.kind else {
3485 unreachable!("advance preserves the matched BigIntLiteral variant");
3487 };
3488 TypeAnnotation {
3489 kind: TypeAnnotationKind::BigIntLiteral(digits),
3490 span: tok.span,
3491 }
3492 }
3493 TokenKind::Minus
3494 if matches!(
3495 self.peek_at(1).kind,
3496 TokenKind::NumberLiteral(_) | TokenKind::BigIntLiteral(_)
3497 ) =>
3498 {
3499 let minus = self.advance().span;
3500 let tok = self.advance();
3501 let span = self.span(minus.start, tok.span.end);
3502 let kind =
3503 match tok.kind {
3504 TokenKind::NumberLiteral(v) => TypeAnnotationKind::NumberLiteral(
3505 crate::types::LiteralF64(if v == 0.0 { 0.0 } else { -v }),
3506 ),
3507 TokenKind::BigIntLiteral(digits) => TypeAnnotationKind::BigIntLiteral(
3508 crate::types::negate_bigint_digits(&digits),
3509 ),
3510 _ => unreachable!("guarded to a numeric literal after `-`"),
3512 };
3513 TypeAnnotation { kind, span }
3514 }
3515 TokenKind::BooleanLiteral(value) => {
3516 let span = self.advance().span;
3517 TypeAnnotation {
3518 kind: TypeAnnotationKind::BooleanLiteral(value),
3519 span,
3520 }
3521 }
3522 TokenKind::LeftBrace => self.parse_object_type_annotation()?,
3523 TokenKind::LeftParen if self.paren_at_opens_function_type(self.pos, type_pos) => {
3524 self.parse_function_type_annotation()?
3525 }
3526 TokenKind::LeftParen => self.parse_grouped_or_function_type()?,
3527 TokenKind::LeftBracket => self.parse_tuple_type_annotation()?,
3529 _ => {
3530 self.error_at_peek("expected type");
3531 return None;
3532 }
3533 };
3534 let mut array_suffixes = 0usize;
3537 while matches!(self.peek().kind, TokenKind::LeftBracket) && !self.line_break_before_peek() {
3540 array_suffixes += 1;
3541 if self.recursion_depth.saturating_add(array_suffixes) >= MAX_PARSE_DEPTH {
3542 self.stop_at_recursion_limit();
3543 return None;
3544 }
3545 let open = self.advance();
3546 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3547 self.error_at(open.span, "expected `]` to close array type");
3548 return None;
3549 }
3550 let close = self.advance();
3551 let span = self.span(ty.span.start, close.span.end);
3552 ty = TypeAnnotation {
3553 kind: TypeAnnotationKind::Array(Box::new(ty)),
3554 span,
3555 };
3556 }
3557 Some(ty)
3558 }
3559
3560 fn parse_readonly_type(&mut self, type_pos: TypePos) -> Option<TypeAnnotation> {
3564 let kw = self.advance();
3565 let opens_group = matches!(self.peek().kind, TokenKind::LeftParen);
3566 let operand = self.parse_type_array(type_pos)?;
3567 let grouped = opens_group
3570 && matches!(
3571 self.pos
3572 .checked_sub(1)
3573 .and_then(|index| self.tokens.get(index))
3574 .map(|token| &token.kind),
3575 Some(TokenKind::RightParen)
3576 );
3577 if grouped
3578 || !matches!(
3579 operand.kind,
3580 TypeAnnotationKind::Array(_) | TypeAnnotationKind::Tuple(_)
3581 )
3582 {
3583 self.error_at_with_help(
3584 kw.span,
3585 "`readonly` only applies to array and tuple types",
3586 vec![
3587 "write `readonly T[]` or `readonly [A, B]`; to protect an object's \
3588 properties, mark each one `readonly` instead"
3589 .to_string(),
3590 ],
3591 );
3592 return Some(operand);
3593 }
3594 let span = self.span(kw.span.start, operand.span.end);
3595 Some(TypeAnnotation {
3596 kind: TypeAnnotationKind::Readonly(Box::new(operand)),
3597 span,
3598 })
3599 }
3600
3601 fn paren_at_opens_function_type(&self, index: usize, type_pos: TypePos) -> bool {
3612 let after = |offset: usize| self.tokens.get(index + offset).map(|t| &t.kind);
3613 match (after(1), after(2)) {
3614 (Some(TokenKind::RightParen | TokenKind::DotDotDot), _) => true,
3616 (Some(TokenKind::Identifier), Some(TokenKind::Colon | TokenKind::Question)) => true,
3618 (Some(TokenKind::Identifier), Some(TokenKind::Comma | TokenKind::RightParen)) => {
3627 type_pos == TypePos::Anywhere
3628 && self.index_after_matching_paren(index).is_some_and(|i| {
3629 matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow))
3630 })
3631 }
3632 _ => false,
3634 }
3635 }
3636
3637 fn parse_grouped_or_function_type(&mut self) -> Option<TypeAnnotation> {
3642 let open = self.pos;
3643 let diagnostics_before = self.diagnostics.len();
3644 if let Some(ty) = self.parse_grouped_type_annotation() {
3645 return Some(ty);
3646 }
3647 let arrow_follows = self
3648 .index_after_matching_paren(open)
3649 .is_some_and(|i| matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)));
3650 if !arrow_follows {
3651 return None;
3652 }
3653 self.pos = open;
3654 self.diagnostics.truncate(diagnostics_before);
3655 self.parse_function_type_annotation()
3656 }
3657
3658 fn parse_grouped_type_annotation(&mut self) -> Option<TypeAnnotation> {
3661 let open = self.advance();
3662 let inner = self.parse_type_annotation()?;
3663 if !matches!(self.peek().kind, TokenKind::RightParen) {
3664 self.error_at_peek("expected `)` to close a parenthesized type");
3665 return None;
3666 }
3667 let close = self.advance();
3668 Some(TypeAnnotation {
3669 span: self.span(open.span.start, close.span.end),
3670 ..inner
3671 })
3672 }
3673
3674 fn parse_function_type_annotation(&mut self) -> Option<TypeAnnotation> {
3676 let open = self.advance();
3677 let params = self.parse_function_type_params()?;
3678 if !matches!(self.peek().kind, TokenKind::Arrow) {
3679 self.error_at_peek("expected `=>` in function-type annotation");
3680 return None;
3681 }
3682 self.advance();
3683 let return_type = self.parse_type_annotation()?;
3684 let end = return_type.span.end;
3685 Some(TypeAnnotation {
3686 kind: TypeAnnotationKind::Function {
3687 params,
3688 return_type: Box::new(return_type),
3689 },
3690 span: self.span(open.span.start, end),
3691 })
3692 }
3693
3694 fn parse_function_type_params(&mut self) -> Option<Vec<TypeAnnotationField>> {
3698 let mut params: Vec<TypeAnnotationField> = Vec::new();
3699 if !matches!(self.peek().kind, TokenKind::RightParen) {
3700 loop {
3701 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
3702 self.advance();
3703 true
3704 } else {
3705 false
3706 };
3707 let name_tok = self.expect_binding_identifier(if rest {
3708 "expected identifier after `...` in function-type annotation"
3709 } else {
3710 "expected parameter name in function-type annotation"
3711 })?;
3712 let name = self.ident_from_token(&name_tok);
3713 let optional = self.parse_parameter_optional(rest, false)?;
3714 if !matches!(self.peek().kind, TokenKind::Colon) {
3715 if rest {
3716 self.error_at_peek_with_help(
3717 "rest parameter requires a type annotation",
3718 vec!["(...args: T[]) => R".to_string()],
3719 );
3720 } else {
3721 self.error_at_peek(
3722 "expected `:` after parameter name (function-type params \
3723 require named annotations in v1)",
3724 );
3725 }
3726 return None;
3727 }
3728 self.advance();
3729 let ty = self.parse_type_annotation()?;
3730 if rest && !is_rest_array_annotation(&ty) {
3731 self.error_at(ty.span, "rest parameter type must be an array");
3732 return None;
3733 }
3734 if matches!(self.peek().kind, TokenKind::Equals) {
3735 if rest {
3736 self.error_rest_parameter_default();
3737 } else {
3738 self.error_at_peek(
3739 "default values in function-type annotations are not yet supported",
3740 );
3741 }
3742 return None;
3743 }
3744 params.push(TypeAnnotationField {
3745 name,
3746 ty,
3747 optional,
3748 readonly: false,
3749 rest,
3750 method: false,
3751 });
3752 match self.peek().kind {
3753 TokenKind::Comma => {
3754 if rest {
3755 let comma = self.advance();
3756 self.error_at(comma.span, "rest parameter must be the last parameter");
3757 return None;
3758 }
3759 self.advance();
3760 if matches!(self.peek().kind, TokenKind::RightParen) {
3761 break;
3762 }
3763 }
3764 TokenKind::RightParen => break,
3765 _ => {
3766 self.error_at_peek("expected `,` or `)`");
3767 return None;
3768 }
3769 }
3770 }
3771 self.check_parameter_order(params.iter().map(ParamOrder::of_field));
3772 }
3773 if !matches!(self.peek().kind, TokenKind::RightParen) {
3774 self.error_at_peek("expected `)`");
3775 return None;
3776 }
3777 self.advance();
3778 Some(params)
3779 }
3780
3781 fn parse_tuple_type_annotation(&mut self) -> Option<TypeAnnotation> {
3782 let open = self.advance();
3783 if matches!(self.peek().kind, TokenKind::RightBracket) {
3784 let close = self.peek().span;
3785 self.error_at(
3786 self.span(open.span.start, close.end),
3787 "tuple types must have at least one element",
3788 );
3789 return None;
3790 }
3791 let mut elements: Vec<TypeAnnotation> = Vec::new();
3792 loop {
3793 if matches!(self.peek().kind, TokenKind::DotDotDot) {
3794 self.error_at_with_help(
3795 self.peek().span,
3796 "rest elements in tuple types are not supported",
3797 vec!["use an array type `T[]` for a list of varying length".to_string()],
3798 );
3799 return None;
3800 }
3801 let label = self.skip_tuple_element_label();
3804 let mut ty = self.parse_type_annotation()?;
3805 let suffix_optional = matches!(self.peek().kind, TokenKind::Question);
3806 if suffix_optional && label != TupleElementLabel::None {
3807 self.error_at_peek_with_help(
3808 "put the optional marker after the tuple element label",
3809 vec!["write `name?: T` instead of `name: T?`".to_string()],
3810 );
3811 return None;
3812 }
3813 let optional = label == TupleElementLabel::Optional || suffix_optional;
3814 if optional {
3815 let end = if suffix_optional {
3816 self.advance().span.end
3817 } else {
3818 ty.span.end
3819 };
3820 let span = self.span(ty.span.start, end);
3821 ty = TypeAnnotation {
3822 kind: TypeAnnotationKind::Optional(Box::new(ty)),
3823 span,
3824 };
3825 } else if elements
3826 .iter()
3827 .any(|element| matches!(element.kind, TypeAnnotationKind::Optional(_)))
3828 {
3829 self.error_at(
3830 ty.span,
3831 "a required tuple element cannot follow an optional element",
3832 );
3833 return None;
3834 }
3835 elements.push(ty);
3836 match self.peek().kind {
3837 TokenKind::Comma => {
3838 self.advance();
3839 if matches!(self.peek().kind, TokenKind::RightBracket) {
3840 break;
3841 }
3842 }
3843 TokenKind::RightBracket => break,
3844 _ => {
3845 self.error_at_peek("expected `,` or `]`");
3846 return None;
3847 }
3848 }
3849 }
3850 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3851 self.error_at_peek("expected `]`");
3852 return None;
3853 }
3854 let close = self.advance();
3855 Some(TypeAnnotation {
3856 kind: TypeAnnotationKind::Tuple(elements),
3857 span: self.span(open.span.start, close.span.end),
3858 })
3859 }
3860
3861 fn skip_tuple_element_label(&mut self) -> TupleElementLabel {
3863 if !is_property_name(&self.peek().kind) {
3864 return TupleElementLabel::None;
3865 }
3866 match self.peek_at(1).kind {
3867 TokenKind::Colon => {
3868 self.advance();
3869 self.advance();
3870 TupleElementLabel::Required
3871 }
3872 TokenKind::Question if matches!(self.peek_at(2).kind, TokenKind::Colon) => {
3873 self.advance();
3874 self.advance();
3875 self.advance();
3876 TupleElementLabel::Optional
3877 }
3878 _ => TupleElementLabel::None,
3879 }
3880 }
3881
3882 fn peek_is_parameterless_index_signature(&self) -> bool {
3885 matches!(self.peek().kind, TokenKind::LeftBracket)
3886 && matches!(self.peek_at(1).kind, TokenKind::RightBracket)
3887 }
3888
3889 fn skip_parameterless_index_signature(&mut self, member_start: u32) -> Option<()> {
3893 self.advance();
3894 let close = self.advance();
3895 self.error_at_with_help(
3896 self.span(member_start, close.span.end),
3897 "an index signature must declare exactly one parameter",
3898 vec!["write `[key: string]: V`".to_string()],
3899 );
3900 if matches!(self.peek().kind, TokenKind::Colon) {
3901 self.advance();
3902 self.parse_type_annotation()?;
3903 }
3904 Some(())
3905 }
3906
3907 fn parse_index_signature(&mut self, readonly: bool) -> Option<crate::IndexSignatureAnnotation> {
3908 let open = self.advance();
3909 let parameter = self.expect_property_ident("expected index parameter name")?;
3910 self.reject_eval_or_arguments(parameter.span);
3911 if !matches!(self.peek().kind, TokenKind::Colon) {
3912 self.error_at_peek("expected `:` after index parameter name");
3913 return None;
3914 }
3915 self.advance();
3916 if !self.peek_identifier_text_is("string") {
3917 self.error_at_peek("index signatures require string keys; use `[key: string]: V`");
3918 return None;
3919 }
3920 self.advance();
3921 if !matches!(self.peek().kind, TokenKind::RightBracket) {
3922 self.error_at_peek("expected `]` after string index key type");
3923 return None;
3924 }
3925 self.advance();
3926 if !matches!(self.peek().kind, TokenKind::Colon) {
3927 self.error_at_peek("expected `:` before index value type");
3928 return None;
3929 }
3930 self.advance();
3931 let value = self.parse_type_annotation()?;
3932 Some(crate::IndexSignatureAnnotation {
3933 span: self.span(open.span.start, value.span.end),
3934 value,
3935 readonly,
3936 })
3937 }
3938
3939 fn parse_object_type_annotation(&mut self) -> Option<TypeAnnotation> {
3940 let open = self.advance();
3941 let mut fields: Vec<TypeAnnotationField> = Vec::new();
3942 let mut index = None;
3943 while !matches!(self.peek().kind, TokenKind::RightBrace | TokenKind::Eof)
3944 && !self.peek_starts_top_level_item()
3945 {
3946 let member_start = self.peek().span.start;
3947 let readonly = self.eat_readonly_property_modifier();
3948 if self.peek_is_parameterless_index_signature() {
3949 self.skip_parameterless_index_signature(member_start)?;
3950 } else if self.peek_is_construct_signature() {
3951 self.reject_construct_signature()?;
3952 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
3953 let signature = self.parse_index_signature(readonly)?;
3954 if index.is_some() {
3955 self.error_at_with_help(
3956 signature.span,
3957 "duplicate string index signature",
3958 vec![],
3959 );
3960 }
3961 index = Some(Box::new(signature));
3962 } else {
3963 let field = self.parse_object_type_field(readonly, member_start)?;
3964 if let Some(existing) = fields.iter().find(|f| f.name.name == field.name.name) {
3965 self.diagnostics.push(Diagnostic {
3966 severity: Severity::Error,
3967 span: field.name.span,
3968 message: format!("duplicate field `{}` in object type", field.name.name),
3969 help: vec![],
3970 notes: vec![(existing.name.span, "first defined here".into())],
3971 });
3972 }
3973 fields.push(field);
3974 }
3975 self.finish_type_member(self.prev_token_end(), "expected `;`, `,`, or `}`")?;
3976 }
3977 if !matches!(self.peek().kind, TokenKind::RightBrace) {
3978 self.error_at_peek("expected `}`");
3979 return None;
3980 }
3981 let close = self.advance();
3982 Some(TypeAnnotation {
3983 kind: TypeAnnotationKind::Object { index, fields },
3984 span: self.span(open.span.start, close.span.end),
3985 })
3986 }
3987
3988 fn peek_starts_top_level_item(&self) -> bool {
3991 let import_statement = matches!(self.peek().kind, TokenKind::Import)
3992 && matches!(
3993 self.peek_at(1).kind,
3994 TokenKind::LeftBrace
3995 | TokenKind::Star
3996 | TokenKind::Identifier
3997 | TokenKind::StringLiteral(_)
3998 );
3999 import_statement || self.peek_starts_declaration()
4000 }
4001
4002 fn peek_starts_declaration(&self) -> bool {
4009 if matches!(self.peek().kind, TokenKind::Export)
4010 && matches!(
4011 self.peek_at(1).kind,
4012 TokenKind::Default | TokenKind::LeftBrace | TokenKind::Star
4013 )
4014 {
4015 return true;
4016 }
4017 let mut offset = 0;
4018 while self.peek_at_is_declaration_modifier(offset) {
4019 offset += 1;
4020 }
4021 let declaration_keyword = matches!(
4022 self.peek_at(offset).kind,
4023 TokenKind::Function
4024 | TokenKind::Class
4025 | TokenKind::Let
4026 | TokenKind::Const
4027 | TokenKind::Enum
4028 | TokenKind::Interface
4029 ) || self.peek_at_is_word(offset, "type");
4030 declaration_keyword && matches!(self.peek_at(offset + 1).kind, TokenKind::Identifier)
4031 }
4032
4033 fn peek_at_is_declaration_modifier(&self, offset: usize) -> bool {
4035 matches!(self.peek_at(offset).kind, TokenKind::Export)
4036 || ["declare", "async", "abstract"]
4037 .iter()
4038 .any(|word| self.peek_at_is_word(offset, word))
4039 }
4040
4041 fn peek_is_construct_signature(&self) -> bool {
4046 matches!(self.peek().kind, TokenKind::New)
4047 && matches!(
4048 self.peek_at(1).kind,
4049 TokenKind::LeftParen | TokenKind::LessThan
4050 )
4051 }
4052
4053 fn reject_construct_signature(&mut self) -> Option<()> {
4057 let keyword = self.advance();
4058 self.error_at_with_help(
4059 keyword.span,
4060 "construct signatures are not supported in object types",
4061 vec![
4062 "declare an `interface` with the `new (…)` signature, or pass a factory \
4063 function such as `() => T`"
4064 .to_string(),
4065 ],
4066 );
4067 if matches!(self.peek().kind, TokenKind::LessThan) {
4068 self.parse_generic_param_list()?;
4069 }
4070 if !matches!(self.peek().kind, TokenKind::LeftParen) {
4071 self.error_at_peek("expected `(` after construct signature type parameters");
4072 return None;
4073 }
4074 self.parse_object_type_method_signature(keyword.span.start)?;
4075 Some(())
4076 }
4077
4078 fn parse_object_type_field(
4080 &mut self,
4081 readonly: bool,
4082 member_start: u32,
4083 ) -> Option<TypeAnnotationField> {
4084 let name = self.expect_property_ident("expected field name in object type")?;
4085 let optional = matches!(self.peek().kind, TokenKind::Question);
4087 if optional {
4088 self.advance();
4089 }
4090 let method = matches!(self.peek().kind, TokenKind::LeftParen);
4091 let ty = if method {
4092 if readonly {
4093 self.reject_readonly_modifier(self.readonly_modifier_span(member_start));
4094 }
4095 self.parse_object_type_method_signature(name.span.start)?
4096 } else {
4097 if !matches!(self.peek().kind, TokenKind::Colon) {
4098 self.error_at_peek("expected `:` after field name");
4099 return None;
4100 }
4101 self.advance();
4102 self.parse_type_annotation()?
4103 };
4104 Some(TypeAnnotationField {
4105 name,
4106 ty,
4107 optional,
4108 readonly,
4109 rest: false,
4110 method,
4111 })
4112 }
4113
4114 fn parse_object_type_method_signature(&mut self, start: u32) -> Option<TypeAnnotation> {
4119 self.advance();
4120 let params = self.parse_function_type_params()?;
4121 if !matches!(self.peek().kind, TokenKind::Colon) {
4122 self.error_at_peek("expected `:` and return type after method parameters");
4123 return None;
4124 }
4125 self.advance();
4126 let return_type = self.parse_type_annotation()?;
4127 let end = return_type.span.end;
4128 Some(TypeAnnotation {
4129 kind: TypeAnnotationKind::Function {
4130 params,
4131 return_type: Box::new(return_type),
4132 },
4133 span: self.span(start, end),
4134 })
4135 }
4136
4137 fn expect_identifier(&mut self, message: &str) -> Option<Token> {
4140 let tok = self.expect_identifier_name(message)?;
4141 self.reject_strict_mode_reserved_word(&tok);
4142 Some(tok)
4143 }
4144
4145 fn expect_binding_identifier(&mut self, message: &str) -> Option<Token> {
4149 let tok = self.expect_identifier(message)?;
4150 self.reject_eval_or_arguments(tok.span);
4151 Some(tok)
4152 }
4153
4154 fn expect_identifier_name(&mut self, message: &str) -> Option<Token> {
4157 if matches!(self.peek().kind, TokenKind::Identifier) {
4158 return Some(self.advance());
4159 }
4160 if is_reserved_identifier_word(&self.peek().kind) {
4161 let tok = self.advance();
4162 let keyword = &self.source[tok.span.start as usize..tok.span.end as usize];
4163 self.error_at_with_help(
4164 tok.span,
4165 format!("`{keyword}` is a reserved keyword and can't be used as a name"),
4166 vec![format!(
4167 "rename it, for example: `{}`",
4168 reserved_keyword_rename_example(keyword)
4169 )],
4170 );
4171 return None;
4172 }
4173 self.error_at_peek(message.to_string());
4174 None
4175 }
4176
4177 fn reject_strict_mode_reserved_word(&mut self, tok: &Token) {
4180 let word = &self.source[tok.span.start as usize..tok.span.end as usize];
4181 if !STRICT_MODE_RESERVED_WORDS.contains(&word) {
4182 return;
4183 }
4184 self.error_at_with_help(
4185 tok.span,
4186 format!("`{word}` is a reserved word in strict mode and can't be used as a name"),
4187 vec![format!(
4188 "rename it, for example: `{}`",
4189 reserved_keyword_rename_example(word)
4190 )],
4191 );
4192 }
4193
4194 fn reject_eval_or_arguments(&mut self, span: Span) {
4197 let word = &self.source[span.start as usize..span.end as usize];
4198 if !matches!(word, "eval" | "arguments") {
4199 return;
4200 }
4201 self.error_at_with_help(
4202 span,
4203 format!("`{word}` can't name a binding in strict mode"),
4204 vec![format!(
4205 "rename it, for example: `{}`",
4206 reserved_keyword_rename_example(word)
4207 )],
4208 );
4209 }
4210
4211 fn ident_from_token(&self, tok: &Token) -> Ident {
4212 Ident {
4213 name: self.source[tok.span.start as usize..tok.span.end as usize].to_string(),
4214 span: tok.span,
4215 }
4216 }
4217
4218 fn property_ident_from_token(&self, tok: &Token) -> Ident {
4219 if let TokenKind::StringLiteral(s) = &tok.kind {
4220 Ident {
4221 name: s.clone(),
4222 span: tok.span,
4223 }
4224 } else {
4225 self.ident_from_token(tok)
4226 }
4227 }
4228
4229 fn expect_property_name(&mut self, message: &str) -> Option<Token> {
4230 if !is_property_name(&self.peek().kind) {
4231 self.error_at_peek(message.to_string());
4232 return None;
4233 }
4234 Some(self.advance())
4235 }
4236
4237 fn expect_property_ident(&mut self, message: &str) -> Option<Ident> {
4238 if !is_property_name(&self.peek().kind)
4239 && !matches!(self.peek().kind, TokenKind::StringLiteral(_))
4240 {
4241 self.error_at_peek(message.to_string());
4242 return None;
4243 }
4244 let tok = self.advance();
4245 Some(self.property_ident_from_token(&tok))
4246 }
4247
4248 fn readonly_modifier_span(&self, member_start: u32) -> Span {
4251 self.span(member_start, member_start + "readonly".len() as u32)
4252 }
4253
4254 fn reject_readonly_modifier(&mut self, span: Span) {
4257 self.error_at_with_help(
4258 span,
4259 "`readonly` can only modify a property or index signature",
4260 vec!["remove `readonly`".to_string()],
4261 );
4262 }
4263
4264 fn eat_readonly_property_modifier(&mut self) -> bool {
4265 if self.peek_identifier_text_is("readonly")
4266 && !self.line_break_after_peek()
4267 && (self.peek_starts_property_after_readonly()
4268 || matches!(self.peek_at(1).kind, TokenKind::LeftBracket))
4269 {
4270 self.advance();
4271 return true;
4272 }
4273 false
4274 }
4275
4276 fn peek_starts_property_after_readonly(&self) -> bool {
4277 let name = &self.peek_at(1).kind;
4278 let after_name = &self.peek_at(2).kind;
4279 let after_optional = &self.peek_at(3).kind;
4280 let starts_member_type =
4283 |kind: &TokenKind| matches!(kind, TokenKind::Colon | TokenKind::LeftParen);
4284 (is_property_name(name) || matches!(name, TokenKind::StringLiteral(_)))
4285 && (starts_member_type(after_name)
4286 || matches!(after_name, TokenKind::Question) && starts_member_type(after_optional))
4287 }
4288
4289 fn peek_at_is_binary_word(&self, offset: usize) -> bool {
4291 self.peek_at_is_word(offset, "as") || self.peek_at_is_word(offset, "satisfies")
4292 }
4293
4294 fn peek_identifier_text_is(&self, expected: &str) -> bool {
4295 self.token_is_word(self.peek(), expected)
4296 }
4297
4298 fn peek_at_is_word(&self, offset: usize, expected: &str) -> bool {
4299 self.token_is_word(self.peek_at(offset), expected)
4300 }
4301
4302 fn token_is_word(&self, tok: &Token, expected: &str) -> bool {
4303 matches!(tok.kind, TokenKind::Identifier)
4304 && &self.source[tok.span.start as usize..tok.span.end as usize] == expected
4305 }
4306
4307 fn at_delete_operator(&self) -> bool {
4314 if !self.peek_identifier_text_is("delete") {
4315 return false;
4316 }
4317 if !matches!(
4318 self.peek_at(1).kind,
4319 TokenKind::Identifier | TokenKind::This
4320 ) {
4321 return false;
4322 }
4323 let next_is_cast_keyword =
4324 self.peek_at_is_word(1, "as") || self.peek_at_is_word(1, "satisfies");
4325 !next_is_cast_keyword || cannot_start_type(&self.peek_at(2).kind)
4326 }
4327
4328 fn at_type_alias_head(&self) -> bool {
4332 self.peek_identifier_text_is("type")
4333 && matches!(self.peek_at(1).kind, TokenKind::Identifier)
4334 }
4335
4336 fn parse_expression(&mut self) -> Option<ExprId> {
4339 self.with_recursion_limit(Self::parse_expression_inner)
4340 }
4341
4342 fn parse_expression_inner(&mut self) -> Option<ExprId> {
4343 let written = self.parse_conditional()?;
4344 let shift_assignment = self.peek_shift().filter(|shift| shift.assignment);
4345 if !is_assign_lookahead(&self.peek().kind) && shift_assignment.is_none() {
4346 return Some(written);
4347 }
4348 let target_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4349 let target = self.assignment_target(written)?;
4350 let op_tok = self.advance();
4351 let mut op_span = op_tok.span;
4352 let op = if let Some(ShiftToken { op, tokens, .. }) = shift_assignment {
4353 for _ in 1..tokens {
4354 op_span.end = self.advance().span.end;
4355 }
4356 Some(op)
4357 } else {
4358 compound_op_for_token(&op_tok.kind)
4359 };
4360 let value = self.parse_expression()?;
4361 let value_span = parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
4362 parse_arena_result(
4363 self.ast.try_push_expr(Expr {
4364 kind: ExprKind::Assign {
4365 target,
4366 op,
4367 op_span,
4368 value,
4369 },
4370 span: self.span(target_span.start, value_span.end),
4371 }),
4372 &mut self.fatal,
4373 )
4374 }
4375
4376 fn assignment_target(&mut self, written: ExprId) -> Option<ExprId> {
4379 let mut target = written;
4380 while let ExprKind::Paren(inner) =
4381 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind
4382 {
4383 target = inner;
4384 }
4385 if matches!(
4386 parse_arena_result(self.ast.try_expr(target), &mut self.fatal)?.kind,
4387 ExprKind::Identifier(_) | ExprKind::FieldAccess { .. } | ExprKind::IndexAccess { .. }
4388 ) {
4389 return Some(target);
4390 }
4391 let error_span = parse_arena_result(self.ast.try_expr(written), &mut self.fatal)?.span;
4392 self.error_at_with_help(
4393 error_span,
4394 "invalid assignment target",
4395 vec![
4396 "assign to a variable, a field (`o.f = …`), or an element (`a[i] = …`)".to_string(),
4397 ],
4398 );
4399 None
4400 }
4401
4402 fn parse_conditional(&mut self) -> Option<ExprId> {
4403 self.reject_async_modifier();
4404 if self.is_arrow_start() {
4405 return self.parse_arrow();
4406 }
4407 let cond = self.parse_binary(0)?;
4408 if !matches!(self.peek().kind, TokenKind::Question) {
4410 return Some(cond);
4411 }
4412 self.advance();
4413 let then_ = self.parse_expression()?;
4414 if !matches!(self.peek().kind, TokenKind::Colon) {
4415 self.error_at_peek_with_help(
4416 "expected `:` to complete ternary expression",
4417 vec!["const x = cond ? then : else;".to_string()],
4418 );
4419 return None;
4420 }
4421 self.advance();
4422 let else_ = self.parse_expression()?;
4423 let cond_span = parse_arena_result(self.ast.try_expr(cond), &mut self.fatal)?.span;
4424 let else_span = parse_arena_result(self.ast.try_expr(else_), &mut self.fatal)?.span;
4425 parse_arena_result(
4426 self.ast.try_push_expr(Expr {
4427 kind: ExprKind::Ternary { cond, then_, else_ },
4428 span: self.span(cond_span.start, else_span.end),
4429 }),
4430 &mut self.fatal,
4431 )
4432 }
4433
4434 fn is_arrow_start(&self) -> bool {
4435 match self.peek().kind {
4436 TokenKind::Identifier => matches!(self.peek_at(1).kind, TokenKind::Arrow),
4437 TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos),
4438 _ => false,
4439 }
4440 }
4441
4442 fn reject_async_modifier(&mut self) {
4445 if !self.peek_identifier_text_is("async") {
4446 return;
4447 }
4448 let modifies = match self.peek_at(1).kind {
4449 TokenKind::Function => true,
4450 TokenKind::LeftParen => self.find_arrow_after_matching_paren(self.pos + 1),
4452 ref name
4456 if is_property_name(name)
4457 && !matches!(name, TokenKind::In)
4458 && !self.peek_at_is_binary_word(1) =>
4459 {
4460 matches!(
4461 self.peek_at(2).kind,
4462 TokenKind::Arrow | TokenKind::LeftParen | TokenKind::LessThan
4463 )
4464 }
4465 _ => false,
4466 };
4467 if modifies {
4468 self.report_async_modifier();
4469 }
4470 }
4471
4472 fn reject_async_member_modifier(&mut self) {
4477 if !self.peek_identifier_text_is("async") || self.line_break_after_peek() {
4478 return;
4479 }
4480 let modifies = match self.peek_at(1).kind {
4481 TokenKind::Star
4482 | TokenKind::LeftBracket
4483 | TokenKind::StringLiteral(_)
4484 | TokenKind::NumberLiteral(_) => true,
4485 ref name if is_property_name(name) => {
4486 matches!(
4487 self.peek_at(2).kind,
4488 TokenKind::LeftParen | TokenKind::LessThan
4489 )
4490 }
4491 _ => false,
4492 };
4493 if modifies {
4494 self.report_async_modifier();
4495 }
4496 }
4497
4498 fn report_async_modifier(&mut self) {
4502 let tok = self.advance();
4503 self.error_at_with_help(
4504 tok.span,
4505 "`async` functions are not supported",
4506 vec![
4507 "calls return their value directly: remove `async`, write the return type \
4508 `T` instead of `Promise<T>`, and drop `await`"
4509 .to_string(),
4510 ],
4511 );
4512 }
4513
4514 fn reject_await_operator(&mut self) -> bool {
4520 if !self.peek_identifier_text_is("await") {
4521 return false;
4522 }
4523 let operand_starts = matches!(
4524 self.peek_at(1).kind,
4525 TokenKind::Identifier
4526 | TokenKind::This
4527 | TokenKind::Super
4528 | TokenKind::New
4529 | TokenKind::Function
4530 | TokenKind::StringLiteral(_)
4531 | TokenKind::NumberLiteral(_)
4532 | TokenKind::NullLiteral
4533 | TokenKind::BooleanLiteral(_)
4534 | TokenKind::TemplateHead(_)
4537 | TokenKind::TemplateNoSubstitution(_)
4538 ) && !self.peek_at_is_binary_word(1);
4539 if !operand_starts || self.line_break_after_peek() {
4540 return false;
4541 }
4542 let tok = self.advance();
4543 self.error_at_with_help(
4544 tok.span,
4545 "`await` is not supported",
4546 vec!["calls return their value directly: remove `await`".to_string()],
4547 );
4548 true
4549 }
4550
4551 fn index_after_matching_paren(&self, index: usize) -> Option<usize> {
4554 if !matches!(
4555 self.tokens.get(index).map(|t| &t.kind),
4556 Some(TokenKind::LeftParen)
4557 ) {
4558 return None;
4559 }
4560 self.scan_past_balanced(
4561 index,
4562 |k| matches!(k, TokenKind::LeftParen),
4563 |k| matches!(k, TokenKind::RightParen),
4564 )
4565 }
4566
4567 fn find_arrow_after_matching_paren(&self, open: usize) -> bool {
4568 let Some(i) = self.index_after_matching_paren(open) else {
4569 return false;
4570 };
4571 let after_type = if matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Colon)) {
4572 match self.scan_past_type_annotation(i + 1, TypePos::ArrowReturn) {
4573 Some(p) => p,
4574 None => return false,
4575 }
4576 } else {
4577 i
4578 };
4579 matches!(
4580 self.tokens.get(after_type).map(|t| &t.kind),
4581 Some(TokenKind::Arrow),
4582 )
4583 }
4584
4585 fn scan_past_type_annotation(&self, start: usize, type_pos: TypePos) -> Option<usize> {
4591 self.scan_past_type_annotation_at_depth(start, type_pos, 0)
4592 }
4593
4594 fn scan_past_type_annotation_at_depth(
4595 &self,
4596 start: usize,
4597 type_pos: TypePos,
4598 depth: usize,
4599 ) -> Option<usize> {
4600 if depth >= MAX_PARSE_DEPTH {
4601 return None;
4602 }
4603 if matches!(
4604 self.tokens.get(start).map(|t| &t.kind),
4605 Some(TokenKind::Identifier)
4606 ) && self
4607 .tokens
4608 .get(start + 1)
4609 .is_some_and(|t| self.token_is_word(t, "is"))
4610 {
4611 return self.scan_past_type_annotation_at_depth(start + 2, type_pos, depth + 1);
4612 }
4613 let start = if matches!(
4615 self.tokens.get(start).map(|t| &t.kind),
4616 Some(TokenKind::Pipe)
4617 ) {
4618 start + 1
4619 } else {
4620 start
4621 };
4622 let mut i = self.scan_past_single_type_member(start, type_pos, depth + 1)?;
4623 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Pipe)) {
4624 i = self.scan_past_single_type_member(i + 1, type_pos, depth + 1)?;
4625 }
4626 Some(i)
4627 }
4628
4629 fn scan_past_single_type_member(
4630 &self,
4631 start: usize,
4632 type_pos: TypePos,
4633 depth: usize,
4634 ) -> Option<usize> {
4635 if depth >= MAX_PARSE_DEPTH {
4636 return None;
4637 }
4638 if self.is_contextual_type_operator(start, "keyof")
4639 || self.is_contextual_type_operator(start, "readonly")
4640 {
4641 return self.scan_past_single_type_member(start + 1, type_pos, depth + 1);
4642 }
4643 let mut i = start;
4644 match self.tokens.get(i)?.kind {
4645 TokenKind::Identifier | TokenKind::Void => {
4646 i += 1;
4647 while matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Dot)) {
4648 if !matches!(
4649 self.tokens.get(i + 1).map(|t| &t.kind),
4650 Some(TokenKind::Identifier)
4651 ) {
4652 return None;
4653 }
4654 i += 2;
4655 }
4656 if matches!(
4657 self.tokens.get(i).map(|t| &t.kind),
4658 Some(TokenKind::LessThan)
4659 ) {
4660 i = self.scan_past_balanced(
4661 i,
4662 |k| matches!(k, TokenKind::LessThan),
4663 |k| matches!(k, TokenKind::GreaterThan),
4664 )?;
4665 }
4666 }
4667 TokenKind::NullLiteral
4668 | TokenKind::StringLiteral(_)
4669 | TokenKind::NumberLiteral(_)
4670 | TokenKind::BooleanLiteral(_) => {
4671 i += 1;
4672 }
4673 TokenKind::LeftBrace => {
4674 i = self.scan_past_balanced(
4675 i,
4676 |k| matches!(k, TokenKind::LeftBrace),
4677 |k| matches!(k, TokenKind::RightBrace),
4678 )?;
4679 }
4680 TokenKind::LeftBracket => {
4681 i = self.scan_past_balanced(
4682 i,
4683 |k| matches!(k, TokenKind::LeftBracket),
4684 |k| matches!(k, TokenKind::RightBracket),
4685 )?;
4686 }
4687 TokenKind::LeftParen => {
4692 let is_function_type = self.paren_at_opens_function_type(i, type_pos);
4693 i = self.scan_past_balanced(
4694 i,
4695 |k| matches!(k, TokenKind::LeftParen),
4696 |k| matches!(k, TokenKind::RightParen),
4697 )?;
4698 if is_function_type {
4699 if !matches!(self.tokens.get(i).map(|t| &t.kind), Some(TokenKind::Arrow)) {
4700 return None;
4701 }
4702 return self.scan_past_type_annotation_at_depth(
4703 i + 1,
4704 TypePos::Anywhere,
4705 depth + 1,
4706 );
4707 }
4708 }
4709 _ => return None,
4710 }
4711 while matches!(
4712 self.tokens.get(i).map(|t| &t.kind),
4713 Some(TokenKind::LeftBracket)
4714 ) {
4715 i += 1;
4716 if !matches!(
4717 self.tokens.get(i).map(|t| &t.kind),
4718 Some(TokenKind::RightBracket)
4719 ) {
4720 return None;
4721 }
4722 i += 1;
4723 }
4724 Some(i)
4725 }
4726
4727 fn scan_past_balanced(
4730 &self,
4731 start: usize,
4732 is_open: fn(&TokenKind) -> bool,
4733 is_close: fn(&TokenKind) -> bool,
4734 ) -> Option<usize> {
4735 let mut depth: i32 = 1;
4736 let mut i = start + 1;
4737 while i < self.tokens.len() && depth > 0 {
4738 let token = self.tokens.get(i)?;
4739 let kind = &token.kind;
4740 if is_open(kind) {
4741 depth += 1;
4742 } else if is_close(kind) {
4743 depth -= 1;
4744 } else if matches!(kind, TokenKind::Eof) {
4745 return None;
4746 }
4747 i += 1;
4748 }
4749 if depth == 0 { Some(i) } else { None }
4750 }
4751
4752 fn parse_arrow(&mut self) -> Option<ExprId> {
4753 let start_span = self.peek().span;
4754
4755 let params = match self.peek().kind {
4756 TokenKind::Identifier => {
4757 let name_tok = self.advance();
4758 self.reject_eval_or_arguments(name_tok.span);
4759 let name = self.ident_from_token(&name_tok);
4760 vec![ParamDecl {
4761 name,
4762 ty: None,
4763 default: None,
4764 optional: false,
4765 pattern: None,
4766 rest: false,
4767 modifiers: None,
4768 }]
4769 }
4770 TokenKind::LeftParen => {
4771 self.advance();
4772 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4773 Vec::new()
4774 } else {
4775 self.parse_arrow_param_list()?
4776 };
4777 if !matches!(self.peek().kind, TokenKind::RightParen) {
4778 self.error_at_peek("expected `)`");
4779 return None;
4780 }
4781 self.advance();
4782 params
4783 }
4784 _ => return self.invariant_failure("is_arrow_start guarded the entry"),
4785 };
4786
4787 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4788 self.advance();
4789 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4790 } else {
4791 (None, None)
4792 };
4793
4794 if !matches!(self.peek().kind, TokenKind::Arrow) {
4795 self.error_at_peek("expected `=>` in arrow function");
4796 return None;
4797 }
4798 if self.line_break_before_peek() {
4800 let arrow_span = self.peek().span;
4801 self.error_at(arrow_span, "a line break is not allowed before `=>`");
4802 }
4803 self.advance();
4804
4805 let (body, end_pos) = if matches!(self.peek().kind, TokenKind::LeftBrace) {
4806 let block = self.parse_block()?;
4807 let span = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?.span;
4808 (ArrowBody::Block(block), span.end)
4809 } else {
4810 let expr = self.parse_expression()?;
4811 let span = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?.span;
4812 (ArrowBody::Expr(expr), span.end)
4813 };
4814
4815 parse_arena_result(
4816 self.ast.try_push_expr(Expr {
4817 kind: ExprKind::Arrow {
4818 params,
4819 return_type,
4820 type_predicate,
4821 body,
4822 },
4823 span: self.span(start_span.start, end_pos),
4824 }),
4825 &mut self.fatal,
4826 )
4827 }
4828
4829 fn parse_function_expression(&mut self) -> Option<ExprId> {
4832 let kw = self.advance();
4833
4834 if matches!(self.peek().kind, TokenKind::LessThan) {
4835 self.error_at_peek("generic function expressions are not supported");
4836 return None;
4837 }
4838
4839 let self_name = if matches!(self.peek().kind, TokenKind::Identifier) {
4840 let name_tok = self.advance();
4841 self.reject_eval_or_arguments(name_tok.span);
4842 Some(self.ident_from_token(&name_tok))
4843 } else {
4844 None
4845 };
4846
4847 if !matches!(self.peek().kind, TokenKind::LeftParen) {
4848 self.error_at_peek("expected `(` after `function`");
4849 return None;
4850 }
4851 self.advance();
4852
4853 let this_type = if matches!(self.peek().kind, TokenKind::This) {
4854 self.advance();
4855 if !matches!(self.peek().kind, TokenKind::Colon) {
4856 self.error_at_peek("expected a type annotation after `this`");
4857 return None;
4858 }
4859 self.advance();
4860 let ty = self.parse_type_annotation()?;
4861 if matches!(self.peek().kind, TokenKind::Comma) {
4862 self.advance();
4863 } else if !matches!(self.peek().kind, TokenKind::RightParen) {
4864 self.error_at_peek("expected `,` or `)` after the `this` parameter");
4865 return None;
4866 }
4867 Some(ty)
4868 } else {
4869 None
4870 };
4871 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
4872 Vec::new()
4873 } else {
4874 let saved_class = self.class_member_body_depth;
4875 let saved_function = self.function_expression_body_depth;
4876 self.class_member_body_depth = 0;
4877 self.function_expression_body_depth = 1;
4878 let params = self.parse_arrow_param_list();
4879 self.class_member_body_depth = saved_class;
4880 self.function_expression_body_depth = saved_function;
4881 params?
4882 };
4883 if !matches!(self.peek().kind, TokenKind::RightParen) {
4884 self.error_at_peek("expected `)`");
4885 return None;
4886 }
4887 self.advance();
4888
4889 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
4890 self.advance();
4891 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
4892 } else {
4893 (None, None)
4894 };
4895
4896 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
4897 self.error_at_peek("expected `{` to open the function body");
4898 return None;
4899 }
4900 let saved = self.class_member_body_depth;
4901 self.class_member_body_depth = 0;
4902 self.function_expression_body_depth += 1;
4903 let block = self.parse_block();
4904 self.function_expression_body_depth -= 1;
4905 self.class_member_body_depth = saved;
4906 let block = block?;
4907 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
4908 .span
4909 .end;
4910
4911 let function = parse_arena_result(
4912 self.ast.try_push_expr(Expr {
4913 kind: ExprKind::Arrow {
4914 params,
4915 return_type,
4916 type_predicate,
4917 body: ArrowBody::Block(block),
4918 },
4919 span: self.span(kw.span.start, end),
4920 }),
4921 &mut self.fatal,
4922 )?;
4923 parse_arena_result(
4924 self.ast.try_push_expr(Expr {
4925 kind: ExprKind::FunctionExpression {
4926 name: self_name,
4927 this_type,
4928 function,
4929 },
4930 span: self.span(kw.span.start, end),
4931 }),
4932 &mut self.fatal,
4933 )
4934 }
4935
4936 fn parse_outer_this_boundary<T>(
4939 &mut self,
4940 parse: impl FnOnce(&mut Self) -> Option<T>,
4941 ) -> Option<T> {
4942 let saved = self.class_member_body_depth;
4943 self.class_member_body_depth = 0;
4944 let saved_function = self.function_expression_body_depth;
4945 self.function_expression_body_depth = 0;
4946 let parsed = parse(self);
4947 self.class_member_body_depth = saved;
4948 self.function_expression_body_depth = saved_function;
4949 parsed
4950 }
4951
4952 fn parse_arrow_param_list(&mut self) -> Option<Vec<ParamDecl>> {
4953 let mut params = Vec::new();
4954 loop {
4955 let rest = if matches!(self.peek().kind, TokenKind::DotDotDot) {
4956 self.advance();
4957 true
4958 } else {
4959 false
4960 };
4961 let (name, pattern) = match self.peek().kind {
4964 TokenKind::LeftBrace | TokenKind::LeftBracket => {
4965 if rest {
4966 self.error_at_peek("rest parameter cannot be destructured");
4967 return None;
4968 }
4969 let binding = self.parse_binding()?;
4970 let placeholder = Ident {
4971 name: String::new(),
4972 span: binding.span(),
4973 };
4974 (placeholder, Some(binding))
4975 }
4976 _ => {
4977 let name_tok = self.expect_binding_identifier(if rest {
4978 "expected identifier after `...`"
4979 } else {
4980 "expected parameter name"
4981 })?;
4982 let name = self.ident_from_token(&name_tok);
4983 (name, None)
4984 }
4985 };
4986 let optional = self.parse_parameter_optional(rest, pattern.is_some())?;
4987 let ty = if matches!(self.peek().kind, TokenKind::Colon) {
4988 self.advance();
4989 Some(self.parse_type_annotation()?)
4990 } else {
4991 None
4994 };
4995 let default = self.parse_parameter_default(rest, optional)?;
4996 params.push(ParamDecl {
4997 name,
4998 ty,
4999 default,
5000 optional,
5001 pattern,
5002 rest,
5003 modifiers: None,
5004 });
5005 match self.peek().kind {
5006 TokenKind::Comma => {
5007 if rest {
5008 let comma = self.advance();
5009 self.error_at(comma.span, "rest parameter must be the last parameter");
5010 return None;
5011 }
5012 self.advance();
5013 if matches!(self.peek().kind, TokenKind::RightParen) {
5014 break;
5015 }
5016 }
5017 TokenKind::RightParen => break,
5018 _ => {
5019 self.error_at_peek("expected `,` or `)`");
5020 return None;
5021 }
5022 }
5023 }
5024 self.check_parameter_order(params.iter().map(ParamOrder::of_param));
5025 Some(params)
5026 }
5027
5028 fn parse_binary(&mut self, min_prec: u8) -> Option<ExprId> {
5029 self.with_recursion_limit(|parser| parser.parse_binary_inner(min_prec))
5030 }
5031
5032 fn peek_shift(&self) -> Option<ShiftToken> {
5035 let first = self.peek();
5036 let second = self.peek_at(1);
5037 if first.span.end != second.span.start {
5038 return None;
5039 }
5040 match (&first.kind, &second.kind) {
5041 (TokenKind::LessThan, TokenKind::LessThan) => {
5042 Some(ShiftToken::new(BinOp::Shl, 2, false))
5043 }
5044 (TokenKind::LessThan, TokenKind::LessEquals) => {
5045 Some(ShiftToken::new(BinOp::Shl, 2, true))
5046 }
5047 (TokenKind::GreaterThan, TokenKind::GreaterEquals) => {
5048 Some(ShiftToken::new(BinOp::Shr, 2, true))
5049 }
5050 (TokenKind::GreaterThan, TokenKind::GreaterThan) => {
5051 let third = self.peek_at(2);
5052 if second.span.end == third.span.start {
5053 match third.kind {
5054 TokenKind::GreaterThan => {
5055 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, false));
5056 }
5057 TokenKind::GreaterEquals => {
5058 return Some(ShiftToken::new(BinOp::UnsignedShr, 3, true));
5059 }
5060 _ => {}
5061 }
5062 }
5063 Some(ShiftToken::new(BinOp::Shr, 2, false))
5064 }
5065 _ => None,
5066 }
5067 }
5068
5069 fn peek_binary(&self) -> Option<BinaryToken> {
5070 if let Some(shift) = self.peek_shift() {
5071 return (!shift.assignment).then_some(BinaryToken {
5072 op: shift.op,
5073 precedence: SHIFT_PRECEDENCE,
5074 tokens: shift.tokens,
5075 });
5076 }
5077 peek_binop(&self.peek().kind).map(|(op, precedence)| BinaryToken {
5078 op,
5079 precedence,
5080 tokens: 1,
5081 })
5082 }
5083
5084 fn parse_binary_inner(&mut self, min_prec: u8) -> Option<ExprId> {
5085 let mut lhs = self.parse_cast()?;
5086 let mut last_op: Option<BinOp> = None;
5090 let mut reported_mixing = false;
5091 while let Some(BinaryToken {
5092 op,
5093 precedence: prec,
5094 tokens: count,
5095 }) = self.peek_binary()
5096 {
5097 if prec < min_prec {
5098 break;
5099 }
5100 if !reported_mixing
5101 && let Some(last) = last_op
5102 && mixed_logical(last, op)
5103 {
5104 reported_mixing = true;
5105 let op_span = self.peek().span;
5106 self.error_at_with_help(
5110 op_span,
5111 "mixing `??` with `||` / `&&` requires parentheses",
5112 vec![mixed_logical_help(last, op)],
5113 );
5114 }
5115 if op == BinOp::Pow
5116 && matches!(
5117 parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.kind,
5118 ExprKind::Unary { .. }
5119 | ExprKind::Typeof { .. }
5120 | ExprKind::Delete { .. }
5121 | ExprKind::Void { .. }
5122 )
5123 {
5124 let error_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5125 self.error_at_with_help(
5126 error_span,
5127 "an unparenthesized unary expression cannot be the left operand of `**`",
5128 vec!["choose the grouping explicitly: `(-x) ** 2` or `-(x ** 2)`".into()],
5129 );
5130 return None;
5131 }
5132 let loose = matches!(self.peek().kind, TokenKind::EqEq | TokenKind::BangEq);
5133 for _ in 0..count {
5134 self.advance();
5135 }
5136 let rhs = self.parse_binary(right_operand_min_prec(op, prec))?;
5137 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
5138 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
5139 if loose {
5140 self.reject_loose_nullish_comparison(op, lhs, rhs)?;
5141 }
5142 lhs = parse_arena_result(
5143 self.ast.try_push_expr(Expr {
5144 kind: ExprKind::Binary { op, lhs, rhs },
5145 span: self.span(lhs_span.start, rhs_span.end),
5146 }),
5147 &mut self.fatal,
5148 )?;
5149 last_op = Some(op);
5150 }
5151 Some(lhs)
5152 }
5153
5154 fn parse_cast(&mut self) -> Option<ExprId> {
5156 let mut expr = self.parse_unary()?;
5157 while matches!(self.peek().kind, TokenKind::Instanceof)
5158 || self.peek_identifier_text_is("as")
5159 {
5160 let is_instanceof = matches!(self.peek().kind, TokenKind::Instanceof);
5161 self.advance();
5162 let ty = self.parse_type_annotation()?;
5163 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5164 .span
5165 .start;
5166 let end = ty.span.end;
5167 let kind = if is_instanceof {
5168 ExprKind::InstanceOf { value: expr, ty }
5169 } else {
5170 self.last_as_type_end = Some(end);
5171 ExprKind::As { expr, ty }
5172 };
5173 expr = parse_arena_result(
5174 self.ast.try_push_expr(Expr {
5175 kind,
5176 span: self.span(start, end),
5177 }),
5178 &mut self.fatal,
5179 )?;
5180 }
5181 Some(expr)
5182 }
5183
5184 fn parse_unary(&mut self) -> Option<ExprId> {
5185 self.with_recursion_limit(Self::parse_unary_inner)
5186 }
5187
5188 fn parse_unary_inner(&mut self) -> Option<ExprId> {
5189 if self.reject_await_operator() {
5190 return self.parse_unary();
5191 }
5192 if matches!(self.peek().kind, TokenKind::Typeof) {
5195 return self.parse_prefix_expr(|operand| ExprKind::Typeof { operand });
5196 }
5197 if matches!(self.peek().kind, TokenKind::Void) {
5198 return self.parse_prefix_expr(|operand| ExprKind::Void { operand });
5199 }
5200 if self.at_delete_operator() {
5201 return self.parse_prefix_expr(|operand| ExprKind::Delete { operand });
5202 }
5203 if matches!(self.peek().kind, TokenKind::New) {
5204 let op_tok = self.advance();
5205 let mut callee = self.parse_atom()?;
5206 while matches!(self.peek().kind, TokenKind::Dot) {
5207 self.advance();
5208 if !matches!(self.peek().kind, TokenKind::Identifier) {
5209 self.error_at_peek("expected identifier after `.` in constructor name");
5210 return None;
5211 }
5212 let ident_tok = self.advance();
5213 let name = self.ident_from_token(&ident_tok);
5214 let receiver_span =
5215 parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5216 let combined_span = self.span(receiver_span.start, ident_tok.span.end);
5217 callee = parse_arena_result(
5218 self.ast.try_push_expr(Expr {
5219 kind: ExprKind::FieldAccess {
5220 receiver: callee,
5221 name,
5222 },
5223 span: combined_span,
5224 }),
5225 &mut self.fatal,
5226 )?;
5227 }
5228 let type_args = if matches!(self.peek().kind, TokenKind::LessThan) {
5229 let saved_pos = self.pos;
5230 let saved_diag_len = self.diagnostics.len();
5231 if let Some(args) = self.try_parse_type_args_only() {
5232 Some(args)
5233 } else {
5234 self.pos = saved_pos;
5235 self.diagnostics.truncate(saved_diag_len);
5236 None
5237 }
5238 } else {
5239 None
5240 };
5241 if !matches!(self.peek().kind, TokenKind::LeftParen) {
5242 self.error_at_peek("expected `(` after constructor name in `new` expression");
5243 return None;
5244 }
5245 self.advance();
5246 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5247 Vec::new()
5248 } else {
5249 self.parse_call_args()?
5250 };
5251 if !matches!(self.peek().kind, TokenKind::RightParen) {
5252 self.error_at_peek("expected `)`");
5253 return None;
5254 }
5255 let close = self.advance();
5256 let new_expr = parse_arena_result(
5257 self.ast.try_push_expr(Expr {
5258 kind: ExprKind::New {
5259 callee,
5260 type_args,
5261 args,
5262 },
5263 span: self.span(op_tok.span.start, close.span.end),
5264 }),
5265 &mut self.fatal,
5266 )?;
5267 return self.parse_postfix_from(new_expr);
5270 }
5271 let op = match self.peek().kind {
5272 TokenKind::Bang => UnOp::Not,
5273 TokenKind::Tilde => UnOp::BitNot,
5274 TokenKind::Minus => UnOp::Neg,
5275 TokenKind::Plus => UnOp::Pos,
5276 _ => return self.parse_postfix(),
5277 };
5278 self.parse_prefix_expr(|operand| ExprKind::Unary { op, operand })
5279 }
5280
5281 fn parse_prefix_expr(&mut self, kind: impl FnOnce(ExprId) -> ExprKind) -> Option<ExprId> {
5283 let op_tok = self.advance();
5284 if matches!(op_tok.kind, TokenKind::Void) && self.at_inserted_semicolon() {
5287 self.advance();
5288 }
5289 let operand = self.parse_unary()?;
5290 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
5291 parse_arena_result(
5292 self.ast.try_push_expr(Expr {
5293 kind: kind(operand),
5294 span: self.span(op_tok.span.start, operand_span.end),
5295 }),
5296 &mut self.fatal,
5297 )
5298 }
5299
5300 fn parse_postfix(&mut self) -> Option<ExprId> {
5301 let expr = self.parse_atom()?;
5302 self.parse_postfix_from(expr)
5303 }
5304
5305 fn parse_postfix_from(&mut self, mut expr: ExprId) -> Option<ExprId> {
5306 let mut chain_parts: Option<(ExprId, Vec<crate::ChainPart>)> = None;
5310 loop {
5311 match self.peek().kind {
5312 TokenKind::LeftParen => {
5313 if let Some((_, parts)) = &mut chain_parts {
5314 let part = self.parse_chain_call_tail(false)?;
5315 parts.push(part);
5316 } else {
5317 expr = self.parse_call_tail(expr, None)?;
5318 }
5319 }
5320 TokenKind::Dot => {
5321 if let Some((_, parts)) = &mut chain_parts {
5322 let part = self.parse_chain_field_tail(false)?;
5323 parts.push(part);
5324 } else {
5325 expr = self.parse_field_access_tail(expr)?;
5326 }
5327 }
5328 TokenKind::LeftBracket => {
5329 if let Some((_, parts)) = &mut chain_parts {
5330 let part = self.parse_chain_index_tail(false)?;
5331 parts.push(part);
5332 } else {
5333 expr = self.parse_index_access_tail(expr)?;
5334 }
5335 }
5336 TokenKind::QuestionDot => {
5337 if chain_parts.is_none() {
5338 chain_parts = Some((expr, Vec::new()));
5339 }
5340 self.advance();
5341 let part = match self.peek().kind {
5342 TokenKind::LeftParen => self.parse_chain_call_tail(true)?,
5343 TokenKind::LeftBracket => self.parse_chain_index_tail(true)?,
5344 _ if is_property_name(&self.peek().kind) => {
5345 self.parse_chain_field_tail(true)?
5346 }
5347 _ => {
5348 self.error_at_peek(
5349 "expected field name, `(` for call, or `[` for index after `?.`",
5350 );
5351 return None;
5352 }
5353 };
5354 if let Some((_, parts)) = &mut chain_parts {
5355 parts.push(part);
5356 }
5357 }
5358 TokenKind::LessThan => {
5362 if chain_parts.is_some() {
5363 break;
5365 }
5366 let saved_pos = self.pos;
5367 let saved_diag_len = self.diagnostics.len();
5368 if let Some(call_id) = self.try_parse_generic_call_tail(expr) {
5369 expr = call_id;
5370 } else {
5371 self.pos = saved_pos;
5372 self.diagnostics.truncate(saved_diag_len);
5373 break;
5374 }
5375 }
5376 TokenKind::Bang => {
5377 if let Some((_, parts)) = &mut chain_parts {
5378 let tok = self.advance();
5379 parts.push(crate::ChainPart::NonNull { span: tok.span });
5380 continue;
5381 }
5382 let tok = self.advance();
5383 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5384 .span
5385 .start;
5386 expr = parse_arena_result(
5387 self.ast.try_push_expr(Expr {
5388 kind: ExprKind::PostfixUnary {
5389 op: PostfixOp::NonNullAssert,
5390 operand: expr,
5391 },
5392 span: self.span(start, tok.span.end),
5393 }),
5394 &mut self.fatal,
5395 )?;
5396 }
5397 TokenKind::PlusPlus | TokenKind::MinusMinus => {
5399 if chain_parts.is_some() {
5400 break;
5401 }
5402 let tok = self.advance();
5403 let op = if matches!(tok.kind, TokenKind::PlusPlus) {
5404 PostfixOp::Inc
5405 } else {
5406 PostfixOp::Dec
5407 };
5408 let start = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5409 .span
5410 .start;
5411 expr = parse_arena_result(
5412 self.ast.try_push_expr(Expr {
5413 kind: ExprKind::PostfixUnary { op, operand: expr },
5414 span: self.span(start, tok.span.end),
5415 }),
5416 &mut self.fatal,
5417 )?;
5418 break;
5419 }
5420 _ => break,
5421 }
5422 }
5423 if let Some((base, parts)) = chain_parts {
5424 let base_span = parse_arena_result(self.ast.try_expr(base), &mut self.fatal)?.span;
5425 let end = parts.last().map_or(base_span.end, |p| p.span().end);
5426 Some(parse_arena_result(
5427 self.ast.try_push_expr(Expr {
5428 kind: ExprKind::OptionalChain { base, parts },
5429 span: self.span(base_span.start, end),
5430 }),
5431 &mut self.fatal,
5432 )?)
5433 } else {
5434 Some(expr)
5435 }
5436 }
5437
5438 fn parse_chain_field_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5440 let start_pos = self.peek().span.start;
5441 if !optional {
5442 self.advance();
5443 }
5444 let name_tok = if is_property_name(&self.peek().kind) {
5445 self.advance()
5446 } else {
5447 let what = if optional { "?." } else { "." };
5448 self.error_at_peek(format!("expected field name after `{what}`"));
5449 return None;
5450 };
5451 let name = self.ident_from_token(&name_tok);
5452 let end = name.span.end;
5453 Some(crate::ChainPart::Field {
5454 name,
5455 optional,
5456 span: self.span(start_pos, end),
5457 })
5458 }
5459
5460 fn parse_chain_index_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5461 let open = self.advance();
5462 let idx = self.parse_expression()?;
5463 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5464 self.error_at_peek("expected `]`");
5465 return None;
5466 }
5467 let close = self.advance();
5468 Some(crate::ChainPart::Index {
5469 idx,
5470 optional,
5471 span: self.span(open.span.start, close.span.end),
5472 })
5473 }
5474
5475 fn parse_chain_call_tail(&mut self, optional: bool) -> Option<crate::ChainPart> {
5476 let open = self.advance();
5477 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5478 Vec::new()
5479 } else {
5480 self.parse_call_args()?
5481 };
5482 if !matches!(self.peek().kind, TokenKind::RightParen) {
5483 self.error_at_peek("expected `)`");
5484 return None;
5485 }
5486 let close = self.advance();
5487 Some(crate::ChainPart::Call {
5488 args,
5489 type_args: None,
5490 optional,
5491 span: self.span(open.span.start, close.span.end),
5492 })
5493 }
5494
5495 fn parse_field_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5496 self.advance();
5497 let name_tok = if is_property_name(&self.peek().kind) {
5499 self.advance()
5500 } else {
5501 self.error_at_peek("expected field name after `.`");
5502 return None;
5503 };
5504 let name = self.ident_from_token(&name_tok);
5505 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5506 parse_arena_result(
5507 self.ast.try_push_expr(Expr {
5508 kind: ExprKind::FieldAccess {
5509 receiver,
5510 name: name.clone(),
5511 },
5512 span: self.span(receiver_span.start, name.span.end),
5513 }),
5514 &mut self.fatal,
5515 )
5516 }
5517
5518 fn parse_index_access_tail(&mut self, receiver: ExprId) -> Option<ExprId> {
5519 self.advance();
5520 let index = self.parse_expression()?;
5521 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5522 self.error_at_peek("expected `]`");
5523 return None;
5524 }
5525 let close = self.advance();
5526 let receiver_span = parse_arena_result(self.ast.try_expr(receiver), &mut self.fatal)?.span;
5527 parse_arena_result(
5528 self.ast.try_push_expr(Expr {
5529 kind: ExprKind::IndexAccess { receiver, index },
5530 span: self.span(receiver_span.start, close.span.end),
5531 }),
5532 &mut self.fatal,
5533 )
5534 }
5535
5536 fn parse_call_tail(
5537 &mut self,
5538 callee: ExprId,
5539 type_args: Option<Vec<TypeAnnotation>>,
5540 ) -> Option<ExprId> {
5541 self.advance();
5542 let args = if matches!(self.peek().kind, TokenKind::RightParen) {
5543 Vec::new()
5544 } else {
5545 self.parse_call_args()?
5546 };
5547 if !matches!(self.peek().kind, TokenKind::RightParen) {
5548 self.error_at_peek("expected `)`");
5549 return None;
5550 }
5551 let close = self.advance();
5552
5553 let callee_span = parse_arena_result(self.ast.try_expr(callee), &mut self.fatal)?.span;
5554 parse_arena_result(
5555 self.ast.try_push_expr(Expr {
5556 kind: ExprKind::Call {
5557 callee,
5558 type_args,
5559 args,
5560 },
5561 span: self.span(callee_span.start, close.span.end),
5562 }),
5563 &mut self.fatal,
5564 )
5565 }
5566
5567 fn try_parse_generic_call_tail(&mut self, callee: ExprId) -> Option<ExprId> {
5569 let type_args = self.try_parse_type_args_only()?;
5570 if !matches!(self.peek().kind, TokenKind::LeftParen) {
5571 return None;
5572 }
5573 self.parse_call_tail(callee, Some(type_args))
5574 }
5575
5576 fn try_parse_type_args_only(&mut self) -> Option<Vec<TypeAnnotation>> {
5577 if !matches!(self.peek().kind, TokenKind::LessThan) {
5578 return self.invariant_failure("type argument parser expected `<`");
5579 }
5580 self.advance();
5581
5582 if matches!(self.peek().kind, TokenKind::GreaterThan) {
5583 return None;
5584 }
5585
5586 let mut type_args = Vec::new();
5587 loop {
5588 let ta = self.parse_type_annotation()?;
5589 type_args.push(ta);
5590 match self.peek().kind {
5591 TokenKind::Comma => {
5592 self.advance();
5593 if matches!(self.peek().kind, TokenKind::GreaterThan) {
5594 self.advance();
5595 break;
5596 }
5597 }
5598 TokenKind::GreaterThan => {
5599 self.advance();
5600 break;
5601 }
5602 _ => return None,
5603 }
5604 }
5605 Some(type_args)
5606 }
5607
5608 fn parse_call_args(&mut self) -> Option<Vec<ExprId>> {
5609 let mut args = Vec::new();
5610 loop {
5611 let arg = self.parse_expression()?;
5612 args.push(arg);
5613 match self.peek().kind {
5614 TokenKind::Comma => {
5615 self.advance();
5616 if matches!(self.peek().kind, TokenKind::RightParen) {
5617 return Some(args);
5618 }
5619 }
5620 TokenKind::RightParen => return Some(args),
5621 _ => {
5622 self.error_at_peek("expected `,` or `)`");
5623 return None;
5624 }
5625 }
5626 }
5627 }
5628
5629 fn parse_angle_cast(&mut self) -> Option<ExprId> {
5636 let open = self.advance();
5637 let ty = self.parse_type_annotation()?;
5638 if !matches!(self.peek().kind, TokenKind::GreaterThan) {
5639 self.error_at_peek("expected `>` to close a type assertion");
5640 return None;
5641 }
5642 self.advance();
5643 let expr = self.parse_unary()?;
5644 let end = parse_arena_result(self.ast.try_expr(expr), &mut self.fatal)?
5645 .span
5646 .end;
5647 parse_arena_result(
5648 self.ast.try_push_expr(Expr {
5649 kind: ExprKind::As { expr, ty },
5650 span: self.span(open.span.start, end),
5651 }),
5652 &mut self.fatal,
5653 )
5654 }
5655
5656 fn parse_atom(&mut self) -> Option<ExprId> {
5657 match self.peek().kind {
5658 TokenKind::LessThan => return self.parse_angle_cast(),
5663 TokenKind::LeftParen => return self.parse_paren(),
5664 TokenKind::LeftBrace => return self.parse_object_literal(),
5667 TokenKind::LeftBracket => return self.parse_array_literal(),
5668 TokenKind::TemplateHead(_) => return self.parse_template_literal(),
5669 TokenKind::This => return self.parse_this_or_super(true),
5670 TokenKind::Super => return self.parse_this_or_super(false),
5671 TokenKind::Function => return self.parse_function_expression(),
5672 _ => {}
5673 }
5674 if !matches!(
5675 self.peek().kind,
5676 TokenKind::NumberLiteral(_)
5677 | TokenKind::BigIntLiteral(_)
5678 | TokenKind::StringLiteral(_)
5679 | TokenKind::TemplateNoSubstitution(_)
5680 | TokenKind::BooleanLiteral(_)
5681 | TokenKind::NullLiteral
5682 | TokenKind::Identifier
5683 | TokenKind::RegexLiteral { .. }
5684 ) {
5685 self.error_at_peek("expected expression");
5686 return None;
5687 }
5688 let tok = self.advance();
5689 let span = tok.span;
5690 let kind = match tok.kind {
5691 TokenKind::NumberLiteral(v) => ExprKind::Number(v),
5692 TokenKind::BigIntLiteral(s) => ExprKind::BigInt(s),
5693 TokenKind::StringLiteral(s) => ExprKind::String(s),
5694 TokenKind::TemplateNoSubstitution(s) => ExprKind::String(s),
5695 TokenKind::BooleanLiteral(b) => ExprKind::Boolean(b),
5696 TokenKind::NullLiteral => ExprKind::Null,
5697 TokenKind::Identifier => ExprKind::Identifier(Ident {
5698 name: self.source[span.start as usize..span.end as usize].to_string(),
5699 span,
5700 }),
5701 TokenKind::RegexLiteral { source, flags } => ExprKind::Regex { source, flags },
5702 _ => unreachable!("advance preserves the accepted expression token"),
5704 };
5705 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5706 }
5707
5708 fn parse_this_or_super(&mut self, is_this: bool) -> Option<ExprId> {
5711 let tok = self.advance();
5712 let span = tok.span;
5713 if !is_this && self.class_member_body_depth == 0 {
5714 self.error_at_with_help(
5715 span,
5716 "`super` is only valid inside a class method or constructor body",
5717 vec![
5718 "call `super(...)` in a subclass constructor or `super.method(...)` \
5719 in a subclass method"
5720 .to_string(),
5721 ],
5722 );
5723 }
5724 let kind = if is_this
5725 && self.class_member_body_depth == 0
5726 && self.function_expression_body_depth == 0
5727 {
5728 ExprKind::ThisOutsideReceiver
5729 } else if is_this {
5730 ExprKind::This
5731 } else {
5732 ExprKind::Super
5733 };
5734 parse_arena_result(self.ast.try_push_expr(Expr { kind, span }), &mut self.fatal)
5735 }
5736
5737 fn parse_template_literal(&mut self) -> Option<ExprId> {
5740 let head_tok = self.advance();
5741 let TokenKind::TemplateHead(head) = head_tok.kind else {
5742 unreachable!("template dispatch requires TemplateHead");
5743 };
5744 let start = head_tok.span.start;
5745 let mut parts: Vec<String> = vec![head];
5746 let mut exprs: Vec<ExprId> = Vec::new();
5747 let mut substitution_spans: Vec<Span> = Vec::new();
5748 let mut substitution_start = head_tok.span.end.saturating_sub(2);
5751 loop {
5752 let expr = self.parse_expression()?;
5753 exprs.push(expr);
5754 let closing = self.peek().span.start;
5755 let substitution_span = self.span(substitution_start, closing.saturating_add(1));
5756 match self.peek().kind.clone() {
5757 TokenKind::TemplateMiddle(s) => {
5758 let tok = self.advance();
5759 substitution_spans.push(substitution_span);
5760 substitution_start = tok.span.end.saturating_sub(2);
5761 parts.push(s);
5762 }
5763 TokenKind::TemplateTail(s) => {
5764 let tok = self.advance();
5765 substitution_spans.push(substitution_span);
5766 parts.push(s);
5767 return parse_arena_result(
5768 self.ast.try_push_expr(Expr {
5769 kind: ExprKind::TemplateLiteral {
5770 parts,
5771 exprs,
5772 substitution_spans,
5773 },
5774 span: self.span(start, tok.span.end),
5775 }),
5776 &mut self.fatal,
5777 );
5778 }
5779 _ => {
5780 self.error_at_peek("expected `}` to close template interpolation");
5781 return None;
5782 }
5783 }
5784 }
5785 }
5786
5787 fn parse_object_literal(&mut self) -> Option<ExprId> {
5788 let open = self.advance();
5789
5790 let mut members: Vec<ObjectLiteralMember> = Vec::new();
5791 if !matches!(self.peek().kind, TokenKind::RightBrace) {
5792 loop {
5793 if matches!(self.peek().kind, TokenKind::DotDotDot) {
5794 let dots = self.advance();
5795 let value = self.parse_expression()?;
5796 let value_span =
5797 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5798 members.push(ObjectLiteralMember::Spread {
5799 value,
5800 span: self.span(dots.span.start, value_span.end),
5801 });
5802 } else if matches!(self.peek().kind, TokenKind::LeftBracket) {
5803 self.advance();
5804 let key = self.parse_expression()?;
5805 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5806 self.error_at_peek("expected `]` after computed property key");
5807 return None;
5808 }
5809 self.advance();
5810 if !matches!(self.peek().kind, TokenKind::Colon) {
5811 self.error_at_peek("expected `:` after computed property key; computed methods are not supported");
5812 return None;
5813 }
5814 self.advance();
5815 let value = self.parse_expression()?;
5816 members.push(ObjectLiteralMember::Computed { key, value });
5817 } else {
5818 let field = self.parse_object_literal_field()?;
5819 if let Some(existing) = members.iter().find_map(|m| match m {
5822 ObjectLiteralMember::Field(f) if f.name.name == field.name.name => Some(f),
5823 _ => None,
5824 }) {
5825 self.diagnostics.push(Diagnostic {
5826 severity: Severity::Error,
5827 span: field.name.span,
5828 message: format!(
5829 "duplicate field `{}` in object literal",
5830 field.name.name
5831 ),
5832 help: vec![],
5833 notes: vec![(existing.name.span, "first defined here".into())],
5834 });
5835 }
5837 members.push(ObjectLiteralMember::Field(field));
5838 }
5839 if self.at_inserted_semicolon()
5840 && matches!(self.peek_at(1).kind, TokenKind::RightBrace)
5841 {
5842 self.advance();
5843 }
5844 match self.peek().kind {
5845 TokenKind::Comma => {
5846 self.advance();
5847 if matches!(self.peek().kind, TokenKind::RightBrace) {
5848 break;
5849 }
5850 }
5851 TokenKind::RightBrace => break,
5852 _ => {
5853 self.error_at_peek("expected `,` or `}`");
5854 return None;
5855 }
5856 }
5857 }
5858 }
5859
5860 if !matches!(self.peek().kind, TokenKind::RightBrace) {
5861 self.error_at_peek("expected `}`");
5862 return None;
5863 }
5864 let close = self.advance();
5865
5866 parse_arena_result(
5867 self.ast.try_push_expr(Expr {
5868 kind: ExprKind::ObjectLiteral { members },
5869 span: self.span(open.span.start, close.span.end),
5870 }),
5871 &mut self.fatal,
5872 )
5873 }
5874
5875 fn parse_object_literal_field(&mut self) -> Option<ObjectLiteralField> {
5876 self.reject_async_member_modifier();
5877 let (name, shorthandable) = match self.peek().kind.clone() {
5878 TokenKind::Identifier => {
5879 let tok = self.advance();
5880 (self.ident_from_token(&tok), true)
5881 }
5882 kind if is_property_name(&kind) => {
5883 let tok = self.advance();
5884 (self.ident_from_token(&tok), false)
5885 }
5886 TokenKind::StringLiteral(_) => {
5887 let tok = self.advance();
5888 (self.property_ident_from_token(&tok), false)
5889 }
5890 _ => {
5891 self.error_at_peek("expected field name");
5892 return None;
5893 }
5894 };
5895
5896 if matches!(self.peek().kind, TokenKind::LeftParen) {
5902 let value = self.parse_method_shorthand_body(name.span)?;
5903 return Some(ObjectLiteralField { name, value });
5904 }
5905
5906 if !matches!(self.peek().kind, TokenKind::Colon) {
5907 if shorthandable {
5910 let value = parse_arena_result(
5911 self.ast.try_push_expr(Expr {
5912 kind: ExprKind::Identifier(name.clone()),
5913 span: name.span,
5914 }),
5915 &mut self.fatal,
5916 )?;
5917 return Some(ObjectLiteralField { name, value });
5918 }
5919 self.error_at_peek("expected `:` after field name");
5920 return None;
5921 }
5922 self.advance();
5923
5924 let value = self.parse_expression()?;
5925 Some(ObjectLiteralField { name, value })
5926 }
5927
5928 fn parse_method_shorthand_body(&mut self, name_span: Span) -> Option<ExprId> {
5931 self.advance();
5932
5933 let params = if matches!(self.peek().kind, TokenKind::RightParen) {
5934 Vec::new()
5935 } else {
5936 self.parse_arrow_param_list()?
5937 };
5938 if !matches!(self.peek().kind, TokenKind::RightParen) {
5939 self.error_at_peek("expected `)`");
5940 return None;
5941 }
5942 self.advance();
5943
5944 let (return_type, type_predicate) = if matches!(self.peek().kind, TokenKind::Colon) {
5945 self.advance();
5946 self.parse_predicate_or_return_type(TypePos::ArrowReturn)?
5947 } else {
5948 (None, None)
5949 };
5950
5951 if !matches!(self.peek().kind, TokenKind::LeftBrace) {
5952 self.error_at_peek("expected `{` to open the method body");
5953 return None;
5954 }
5955 let block = self.parse_outer_this_boundary(Self::parse_block)?;
5956 let end = parse_arena_result(self.ast.try_stmt(block), &mut self.fatal)?
5957 .span
5958 .end;
5959
5960 parse_arena_result(
5961 self.ast.try_push_expr(Expr {
5962 kind: ExprKind::Arrow {
5963 params,
5964 return_type,
5965 type_predicate,
5966 body: ArrowBody::Block(block),
5967 },
5968 span: self.span(name_span.start, end),
5969 }),
5970 &mut self.fatal,
5971 )
5972 }
5973
5974 fn parse_array_literal(&mut self) -> Option<ExprId> {
5975 let open = self.advance();
5976
5977 let mut elements: Vec<ArrayLiteralElement> = Vec::new();
5978 if !matches!(self.peek().kind, TokenKind::RightBracket) {
5979 loop {
5980 let element = if matches!(self.peek().kind, TokenKind::DotDotDot) {
5981 let dots = self.advance();
5982 let value = self.parse_expression()?;
5983 let value_span =
5984 parse_arena_result(self.ast.try_expr(value), &mut self.fatal)?.span;
5985 ArrayLiteralElement::Spread {
5986 value,
5987 span: self.span(dots.span.start, value_span.end),
5988 }
5989 } else {
5990 ArrayLiteralElement::Value(self.parse_expression()?)
5991 };
5992 elements.push(element);
5993 match self.peek().kind {
5994 TokenKind::Comma => {
5995 self.advance();
5996 if matches!(self.peek().kind, TokenKind::RightBracket) {
5997 break;
5998 }
5999 }
6000 TokenKind::RightBracket => break,
6001 _ => {
6002 self.error_at_peek("expected `,` or `]`");
6003 return None;
6004 }
6005 }
6006 }
6007 }
6008
6009 if !matches!(self.peek().kind, TokenKind::RightBracket) {
6010 self.error_at_peek("expected `]`");
6011 return None;
6012 }
6013 let close = self.advance();
6014
6015 parse_arena_result(
6016 self.ast.try_push_expr(Expr {
6017 kind: ExprKind::ArrayLiteral { elements },
6018 span: self.span(open.span.start, close.span.end),
6019 }),
6020 &mut self.fatal,
6021 )
6022 }
6023
6024 fn parse_paren(&mut self) -> Option<ExprId> {
6025 let open = self.advance();
6026 let inner = self.parse_expression()?;
6027 if !matches!(self.peek().kind, TokenKind::RightParen) {
6028 self.error_at_peek("expected `)`");
6029 return None;
6030 }
6031 let close = self.advance();
6032 let span = self.span(open.span.start, close.span.end);
6033 parse_arena_result(
6034 self.ast.try_push_expr(Expr {
6035 kind: ExprKind::Paren(inner),
6036 span,
6037 }),
6038 &mut self.fatal,
6039 )
6040 }
6041
6042 fn with_recursion_limit<T>(&mut self, parse: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
6043 if self.fatal.is_some() {
6044 return None;
6045 }
6046 if self.recursion_limit_span.is_some() || self.recursion_depth >= MAX_PARSE_DEPTH {
6047 self.stop_at_recursion_limit();
6048 return None;
6049 }
6050 self.recursion_depth += 1;
6051 let result = parse(self);
6052 self.recursion_depth -= 1;
6053 result
6054 }
6055
6056 fn stop_at_recursion_limit(&mut self) {
6057 self.recursion_limit_span.get_or_insert(self.peek().span);
6058 self.pos = self.tokens.len().saturating_sub(1);
6060 }
6061
6062 fn recover(&mut self) {
6063 while !self.is_at_eof() {
6064 match self.peek().kind {
6065 TokenKind::Semicolon => {
6066 self.advance();
6067 return;
6068 }
6069 TokenKind::RightBrace
6070 | TokenKind::Let
6071 | TokenKind::Const
6072 | TokenKind::Function
6073 | TokenKind::Export
6074 | TokenKind::If
6075 | TokenKind::While
6076 | TokenKind::Return
6077 | TokenKind::Try
6078 | TokenKind::Throw => return,
6079 _ => {
6080 self.advance();
6081 }
6082 }
6083 }
6084 }
6085
6086 fn peek(&self) -> &Token {
6087 self.tokens.get(self.pos).unwrap_or(&self.eof)
6088 }
6089
6090 fn peek_at(&self, offset: usize) -> &Token {
6091 self.pos
6092 .checked_add(offset)
6093 .and_then(|index| self.tokens.get(index))
6094 .unwrap_or(&self.eof)
6095 }
6096
6097 fn at_inserted_semicolon(&self) -> bool {
6099 let token = self.peek();
6100 matches!(token.kind, TokenKind::Semicolon) && token.span.start == token.span.end
6101 }
6102
6103 fn advance(&mut self) -> Token {
6104 let tok = self.peek().clone();
6105 if self.pos < self.tokens.len().saturating_sub(1) {
6106 self.pos += 1;
6107 }
6108 tok
6109 }
6110
6111 fn is_at_eof(&self) -> bool {
6112 self.fatal.is_some() || matches!(self.peek().kind, TokenKind::Eof)
6113 }
6114
6115 fn line_break_before_peek(&self) -> bool {
6117 self.source_has_line_break(self.prev_token_end(), self.peek().span.start)
6118 }
6119
6120 fn line_break_after_peek(&self) -> bool {
6122 self.source_has_line_break(self.peek().span.end, self.peek_at(1).span.start)
6123 }
6124
6125 fn source_has_line_break(&self, start: u32, end: u32) -> bool {
6128 self.source
6129 .get(start as usize..end as usize)
6130 .is_some_and(|gap| gap.contains(['\n', '\r']))
6131 }
6132
6133 fn at_statement_end(&self) -> bool {
6138 matches!(self.peek().kind, TokenKind::Semicolon)
6139 || (self.last_as_type_end == Some(self.prev_token_end())
6140 && self.line_break_before_peek())
6141 }
6142
6143 fn finish_statement(&mut self) -> u32 {
6146 if matches!(self.peek().kind, TokenKind::Semicolon) {
6147 return self.advance().span.end;
6148 }
6149 self.prev_token_end()
6150 }
6151
6152 fn prev_token_end(&self) -> u32 {
6155 self.tokens
6156 .get(self.pos.saturating_sub(1))
6157 .unwrap_or(&self.eof)
6158 .span
6159 .end
6160 }
6161
6162 fn take_leading_doc(&mut self) -> Option<crate::DocComment> {
6163 let parsed = self
6164 .peek()
6165 .leading_doc
6166 .as_ref()
6167 .map(crate::parse_doc_comment)
6168 .transpose();
6169 match parsed {
6170 Ok(doc) => doc,
6171 Err(error) => {
6172 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6173 None
6174 }
6175 }
6176 }
6177
6178 fn parse_doc(&mut self, raw: &crate::RawDoc) -> Option<crate::DocComment> {
6179 match crate::parse_doc_comment(raw) {
6180 Ok(doc) => Some(doc),
6181 Err(error) => {
6182 self.fatal = Some(error.into_compiler_failure(CompilerStage::Parse));
6183 None
6184 }
6185 }
6186 }
6187
6188 fn reject_use_strict_with_non_simple_params(&mut self) {
6192 let mut functions: Vec<(Vec<Span>, StmtId)> = Vec::new();
6193 let non_simple = |param: &ParamDecl| {
6194 (param.default.is_some() || param.pattern.is_some() || param.rest).then(|| {
6195 param
6196 .pattern
6197 .as_ref()
6198 .map_or(param.name.span, Binding::span)
6199 })
6200 };
6201 for expr in self.ast.source_expressions() {
6202 if let ExprKind::Arrow {
6203 params,
6204 body: ArrowBody::Block(body),
6205 ..
6206 } = &expr.kind
6207 {
6208 functions.push((params.iter().filter_map(non_simple).collect(), *body));
6209 }
6210 }
6211 for stmt in self.ast.source_statements() {
6212 match &stmt.kind {
6213 StmtKind::Function { params, body, .. } => {
6214 functions.push((params.iter().filter_map(non_simple).collect(), *body));
6215 }
6216 StmtKind::ClassDecl { members, .. } => {
6217 for member in members {
6218 match member {
6219 ClassMember::Method { params, body, .. }
6220 | ClassMember::Constructor { params, body, .. } => {
6221 functions
6222 .push((params.iter().filter_map(non_simple).collect(), *body));
6223 }
6224 ClassMember::Accessor { param, body, .. } => {
6225 let params = param.as_deref().and_then(non_simple);
6226 functions.push((params.into_iter().collect(), *body));
6227 }
6228 _ => {}
6229 }
6230 }
6231 }
6232 _ => {}
6233 }
6234 }
6235 for (params, body) in functions {
6236 if params.is_empty() {
6237 continue;
6238 }
6239 let Some(directive) = self.use_strict_directive(body) else {
6240 continue;
6241 };
6242 for span in params {
6243 self.error_at(
6244 span,
6245 "this parameter is not allowed with a `\"use strict\"` directive",
6246 );
6247 }
6248 self.error_at_with_help(
6249 directive,
6250 "a `\"use strict\"` directive can't be used with a parameter list that has defaults, patterns, or a rest parameter",
6251 vec!["remove the directive; Submilli code is always strict".into()],
6252 );
6253 }
6254 }
6255
6256 fn use_strict_directive(&self, body: StmtId) -> Option<Span> {
6260 let StmtKind::Block(stmts) = &self.ast.try_stmt(body).ok()?.kind else {
6261 return None;
6262 };
6263 for stmt in stmts {
6264 let stmt = self.ast.try_stmt(*stmt).ok()?;
6265 let StmtKind::Expr(expr) = stmt.kind else {
6266 return None;
6267 };
6268 let expr = self.ast.try_expr(expr).ok()?;
6269 let ExprKind::String(value) = &expr.kind else {
6270 return None;
6271 };
6272 if self.source.as_bytes().get(expr.span.start as usize) == Some(&b'`') {
6273 return None;
6274 }
6275 if value == "use strict" {
6276 return Some(stmt.span);
6277 }
6278 }
6279 None
6280 }
6281
6282 fn error_at(&mut self, span: Span, message: impl Into<String>) {
6283 self.diagnostics.push(Diagnostic {
6284 severity: Severity::Error,
6285 span,
6286 message: message.into(),
6287 help: vec![],
6288 notes: vec![],
6289 });
6290 }
6291
6292 fn reject_loose_nullish_comparison(
6295 &mut self,
6296 op: BinOp,
6297 lhs: ExprId,
6298 rhs: ExprId,
6299 ) -> Option<()> {
6300 let lhs_span = parse_arena_result(self.ast.try_expr(lhs), &mut self.fatal)?.span;
6301 let rhs_span = parse_arena_result(self.ast.try_expr(rhs), &mut self.fatal)?.span;
6302 let (nullish, operand, nullish_first) =
6303 match (self.nullish_operand(lhs)?, self.nullish_operand(rhs)?) {
6304 (Some(left), Some(right)) => {
6305 self.reject_nullish_against_nullish(op, lhs_span, rhs_span, [left, right]);
6306 return Some(());
6307 }
6308 (None, Some(nullish)) => (nullish, lhs, false),
6309 (Some(nullish), None) => (nullish, rhs, true),
6310 (None, None) => return Some(()),
6311 };
6312 let comparison = LooseComparison {
6313 nullish,
6314 nullish_first,
6315 negated: op == BinOp::NotEq,
6316 };
6317 let operand_span = parse_arena_result(self.ast.try_expr(operand), &mut self.fatal)?.span;
6318 let x = self.single_line_source(operand_span).unwrap_or("x");
6319 let strict = self.strict_suggestion(&comparison, x);
6320 let either = self.either_suggestion(&comparison, operand, x)?;
6321 let literal = comparison.nullish.literal.text();
6322 let written = self
6324 .single_line_source(self.span(lhs_span.start, rhs_span.end))
6325 .map_or_else(
6326 || {
6327 let written_op = if comparison.negated { "!=" } else { "==" };
6328 comparison.ordered(x, written_op, literal)
6329 },
6330 str::to_string,
6331 );
6332 self.error_at_with_help(
6333 self.span(lhs_span.start, rhs_span.end),
6334 format!(
6335 "`{written}` does not also match `{}`: `==` compares like `===`",
6336 comparison.nullish.literal.other().text()
6337 ),
6338 vec![
6339 either,
6340 format!("to test for `{literal}` alone, write `{strict}`"),
6341 ],
6342 );
6343 Some(())
6344 }
6345
6346 fn reject_nullish_against_nullish(
6350 &mut self,
6351 op: BinOp,
6352 lhs: Span,
6353 rhs: Span,
6354 sides: [NullishOperand; 2],
6355 ) {
6356 let span = self.span(lhs.start, rhs.end);
6357 let subject = self.single_line_source(span).map_or_else(
6360 || "this comparison".to_string(),
6361 |written| format!("`{written}`"),
6362 );
6363 let value = if op == BinOp::NotEq { "false" } else { "true" };
6364 let effects: Vec<String> = sides
6365 .iter()
6366 .filter_map(|side| side.effect)
6367 .map(|effect| {
6368 self.single_line_source(effect.operand).map_or_else(
6369 || "the `void` operand's expression".to_string(),
6370 |statement| format!("`{statement};`"),
6371 )
6372 })
6373 .collect();
6374 let help = if effects.is_empty() {
6375 format!("in TypeScript this is always `{value}`; write `{value}`")
6376 } else {
6377 format!(
6378 "in TypeScript this is always `{value}`; run {} {}, then use `{value}`",
6379 effects.join(" and "),
6380 if effects.len() > 1 {
6381 "each as its own statement"
6382 } else {
6383 "as its own statement"
6384 }
6385 )
6386 };
6387 self.error_at_with_help(
6388 span,
6389 format!("{subject} compares two nullish values: `==` compares like `===`"),
6390 vec![help],
6391 );
6392 }
6393
6394 fn strict_suggestion(&self, comparison: &LooseComparison, x: &str) -> String {
6397 let eq = comparison.strict_op();
6398 match comparison.nullish.effect {
6399 Some(effect) => {
6400 let void = self.single_line_source(effect.void).unwrap_or("void expr");
6401 comparison.ordered(x, eq, void)
6402 }
6403 None => format!("{x} {eq} {}", comparison.nullish.literal.text()),
6404 }
6405 }
6406
6407 fn either_suggestion(
6411 &mut self,
6412 comparison: &LooseComparison,
6413 operand: ExprId,
6414 x: &str,
6415 ) -> Option<String> {
6416 let mut steps = Vec::new();
6417 let tested = if self.repeats_without_effect(operand)? {
6418 x
6419 } else {
6420 steps.push(format!("bind `{x}` to a `const` (`const value = {x};`)"));
6421 "value"
6422 };
6423 if let Some(effect) = comparison.nullish.effect {
6424 let statement = self.single_line_source(effect.operand).unwrap_or("expr");
6425 let step = format!("run `{statement};` as its own statement");
6426 if comparison.nullish_first {
6427 steps.insert(0, step);
6428 } else {
6429 steps.push(step);
6430 }
6431 }
6432 let eq = comparison.strict_op();
6433 let joiner = if comparison.negated { "&&" } else { "||" };
6434 let both = format!("{tested} {eq} null {joiner} {tested} {eq} undefined");
6435 Some(if steps.is_empty() {
6436 format!("to test for either, write `{both}`")
6437 } else {
6438 format!(
6439 "to test for either, first {}, then write `{both}`",
6440 steps.join(", then ")
6441 )
6442 })
6443 }
6444
6445 fn repeats_without_effect(&mut self, id: ExprId) -> Option<bool> {
6448 let unwrapped = self.unparenthesized(id)?;
6449 let expr = parse_arena_result(self.ast.try_expr(unwrapped), &mut self.fatal)?;
6450 if is_literal(&expr.kind) {
6451 return Some(true);
6452 }
6453 if self
6454 .nullish_operand(unwrapped)?
6455 .is_some_and(|nullish| nullish.effect.is_none())
6456 {
6457 return Some(true);
6458 }
6459 self.is_reference_path(id)
6460 }
6461
6462 fn nullish_operand(&mut self, id: ExprId) -> Option<Option<NullishOperand>> {
6464 let id = self.unparenthesized(id)?;
6465 let expr = parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?;
6466 let span = expr.span;
6467 Some(match &expr.kind {
6468 ExprKind::Null => Some(NullishOperand {
6469 literal: Nullish::Null,
6470 effect: None,
6471 }),
6472 ExprKind::Identifier(ident) if ident.name == "undefined" => Some(NullishOperand {
6473 literal: Nullish::Undefined,
6474 effect: None,
6475 }),
6476 ExprKind::Void { operand } => {
6477 let operand_id = self.unparenthesized(*operand)?;
6478 let operand =
6479 parse_arena_result(self.ast.try_expr(operand_id), &mut self.fatal)?.clone();
6480 let effect_free =
6482 is_literal(&operand.kind) || self.is_reference_path(operand_id)?;
6483 let effect = (!effect_free).then_some(VoidEffect {
6484 void: span,
6485 operand: operand.span,
6486 });
6487 Some(NullishOperand {
6488 literal: Nullish::Undefined,
6489 effect,
6490 })
6491 }
6492 _ => None,
6493 })
6494 }
6495
6496 fn is_reference_path(&mut self, id: ExprId) -> Option<bool> {
6499 let mut id = id;
6500 loop {
6501 match &parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6502 ExprKind::Identifier(_) | ExprKind::This => return Some(true),
6503 ExprKind::FieldAccess { receiver, .. } => id = *receiver,
6504 ExprKind::Paren(inner) => id = *inner,
6505 _ => return Some(false),
6506 }
6507 }
6508 }
6509
6510 fn unparenthesized(&mut self, id: ExprId) -> Option<ExprId> {
6511 let mut id = id;
6512 loop {
6513 match parse_arena_result(self.ast.try_expr(id), &mut self.fatal)?.kind {
6514 ExprKind::Paren(inner) => id = inner,
6515 _ => return Some(id),
6516 }
6517 }
6518 }
6519
6520 fn single_line_source(&self, span: Span) -> Option<&'a str> {
6521 self.source
6522 .get(span.start as usize..span.end as usize)
6523 .filter(|text| !text.contains('\n'))
6524 }
6525
6526 fn error_at_with_help(&mut self, span: Span, message: impl Into<String>, help: Vec<String>) {
6527 self.diagnostics.push(Diagnostic {
6528 severity: Severity::Error,
6529 span,
6530 message: message.into(),
6531 help,
6532 notes: vec![],
6533 });
6534 }
6535
6536 fn invariant_failure<T>(&mut self, message: &str) -> Option<T> {
6537 self.fatal.get_or_insert_with(|| CompilerFailure::Internal {
6538 stage: CompilerStage::Parse,
6539 span: None,
6540 message: message.into(),
6541 });
6542 self.pos = self.tokens.len();
6543 None
6544 }
6545
6546 fn error_at_peek(&mut self, message: impl Into<String>) {
6547 let span = self.peek().span;
6548 self.error_at(span, message);
6549 }
6550
6551 fn error_at_peek_with_help(&mut self, message: impl Into<String>, help: Vec<String>) {
6552 let span = self.peek().span;
6553 self.error_at_with_help(span, message, help);
6554 }
6555
6556 fn error_count(&self) -> usize {
6557 self.diagnostics
6558 .iter()
6559 .filter(|d| d.severity == Severity::Error)
6560 .count()
6561 }
6562}
6563
6564pub(crate) fn is_property_name(kind: &TokenKind) -> bool {
6565 matches!(
6566 kind,
6567 TokenKind::Identifier
6568 | TokenKind::BooleanLiteral(_)
6569 | TokenKind::NullLiteral
6570 | TokenKind::Let
6571 | TokenKind::Const
6572 | TokenKind::Function
6573 | TokenKind::If
6574 | TokenKind::Else
6575 | TokenKind::While
6576 | TokenKind::Do
6577 | TokenKind::For
6578 | TokenKind::Break
6579 | TokenKind::Continue
6580 | TokenKind::Return
6581 | TokenKind::Switch
6582 | TokenKind::Case
6583 | TokenKind::Default
6584 | TokenKind::Void
6585 | TokenKind::Interface
6586 | TokenKind::Enum
6587 | TokenKind::In
6588 | TokenKind::Typeof
6589 | TokenKind::Import
6590 | TokenKind::Export
6591 | TokenKind::New
6592 | TokenKind::Try
6593 | TokenKind::Catch
6594 | TokenKind::Finally
6595 | TokenKind::Throw
6596 | TokenKind::Class
6597 | TokenKind::Extends
6598 | TokenKind::Implements
6599 | TokenKind::Super
6600 | TokenKind::This
6601 )
6602}
6603
6604fn is_reserved_identifier_word(kind: &TokenKind) -> bool {
6605 matches!(
6606 kind,
6607 TokenKind::BooleanLiteral(_)
6608 | TokenKind::NullLiteral
6609 | TokenKind::Let
6610 | TokenKind::Const
6611 | TokenKind::Function
6612 | TokenKind::If
6613 | TokenKind::Else
6614 | TokenKind::While
6615 | TokenKind::Do
6616 | TokenKind::For
6617 | TokenKind::Break
6618 | TokenKind::Continue
6619 | TokenKind::Return
6620 | TokenKind::Switch
6621 | TokenKind::Case
6622 | TokenKind::Default
6623 | TokenKind::Export
6624 | TokenKind::Void
6625 | TokenKind::Interface
6626 | TokenKind::Enum
6627 | TokenKind::In
6628 | TokenKind::Typeof
6629 | TokenKind::Import
6630 | TokenKind::New
6631 | TokenKind::Try
6632 | TokenKind::Catch
6633 | TokenKind::Finally
6634 | TokenKind::Throw
6635 | TokenKind::Class
6636 | TokenKind::Extends
6637 | TokenKind::Implements
6638 | TokenKind::Super
6639 | TokenKind::This
6640 )
6641}
6642
6643#[derive(Clone, Copy, PartialEq, Eq)]
6647enum SpecifierList {
6648 Import,
6649 Export,
6650}
6651
6652impl SpecifierList {
6653 fn noun(self) -> &'static str {
6654 match self {
6655 SpecifierList::Import => "import",
6656 SpecifierList::Export => "export",
6657 }
6658 }
6659}
6660
6661const STRICT_MODE_RESERVED_WORDS: [&str; 6] = [
6665 "package",
6666 "private",
6667 "protected",
6668 "public",
6669 "static",
6670 "yield",
6671];
6672
6673fn reserved_keyword_rename_example(keyword: &str) -> &'static str {
6674 match keyword {
6675 "default" => "defaultValue",
6676 _ => "valueName",
6677 }
6678}
6679
6680fn mixed_logical(prev: BinOp, next: BinOp) -> bool {
6681 let prev_logical = matches!(prev, BinOp::Or | BinOp::And);
6682 let next_logical = matches!(next, BinOp::Or | BinOp::And);
6683 let prev_nullish = matches!(prev, BinOp::NullishCoalesce);
6684 let next_nullish = matches!(next, BinOp::NullishCoalesce);
6685 (prev_logical && next_nullish) || (prev_nullish && next_logical)
6686}
6687
6688fn mixed_logical_help(prev: BinOp, next: BinOp) -> String {
6691 let symbol = |op: BinOp| if op == BinOp::And { "&&" } else { "||" };
6692 let (first, second) = if prev == BinOp::NullishCoalesce {
6693 ("??", symbol(next))
6694 } else {
6695 (symbol(prev), "??")
6696 };
6697 format!(
6698 "add parentheses to choose the grouping: `(a {first} b) {second} c` or `a {first} (b {second} c)`"
6699 )
6700}
6701
6702const LOGICAL_AND_PREC: u8 = 2;
6703
6704#[derive(Clone, Copy)]
6706struct NullishOperand {
6707 literal: Nullish,
6708 effect: Option<VoidEffect>,
6710}
6711
6712#[derive(Clone, Copy, PartialEq, Eq)]
6713enum Nullish {
6714 Null,
6715 Undefined,
6716}
6717
6718impl Nullish {
6719 fn text(self) -> &'static str {
6720 match self {
6721 Nullish::Null => "null",
6722 Nullish::Undefined => "undefined",
6723 }
6724 }
6725
6726 fn other(self) -> Self {
6728 match self {
6729 Nullish::Null => Nullish::Undefined,
6730 Nullish::Undefined => Nullish::Null,
6731 }
6732 }
6733}
6734
6735struct LooseComparison {
6737 nullish: NullishOperand,
6738 nullish_first: bool,
6740 negated: bool,
6742}
6743
6744impl LooseComparison {
6745 fn strict_op(&self) -> &'static str {
6746 if self.negated { "!==" } else { "===" }
6747 }
6748
6749 fn ordered(&self, x: &str, op: &str, nullish: &str) -> String {
6751 if self.nullish_first {
6752 format!("{nullish} {op} {x}")
6753 } else {
6754 format!("{x} {op} {nullish}")
6755 }
6756 }
6757}
6758
6759#[derive(Clone, Copy)]
6760struct VoidEffect {
6761 void: Span,
6763 operand: Span,
6765}
6766
6767fn is_literal(kind: &ExprKind) -> bool {
6768 matches!(
6769 kind,
6770 ExprKind::Number(_)
6771 | ExprKind::BigInt(_)
6772 | ExprKind::String(_)
6773 | ExprKind::Boolean(_)
6774 | ExprKind::Null
6775 )
6776}
6777
6778struct ParamOrder<'p> {
6780 optional: bool,
6781 required: bool,
6783 name: Option<&'p str>,
6785 span: Span,
6786}
6787
6788impl<'p> ParamOrder<'p> {
6789 fn of_param(param: &'p ParamDecl) -> Self {
6790 let (name, span) = match ¶m.pattern {
6791 Some(binding) => (None, binding.span()),
6792 None => (Some(param.name.name.as_str()), param.name.span),
6793 };
6794 Self {
6795 optional: param.optional,
6796 required: !param.is_omittable() && !param.rest,
6797 name,
6798 span,
6799 }
6800 }
6801
6802 fn of_field(field: &'p TypeAnnotationField) -> Self {
6803 Self {
6804 optional: field.optional,
6805 required: !field.optional && !field.rest,
6806 name: Some(field.name.name.as_str()),
6807 span: field.name.span,
6808 }
6809 }
6810}
6811
6812struct BinaryToken {
6814 op: BinOp,
6815 precedence: u8,
6816 tokens: usize,
6817}
6818
6819struct ShiftToken {
6821 op: BinOp,
6822 tokens: usize,
6823 assignment: bool,
6825}
6826
6827impl ShiftToken {
6828 fn new(op: BinOp, tokens: usize, assignment: bool) -> Self {
6829 Self {
6830 op,
6831 tokens,
6832 assignment,
6833 }
6834 }
6835}
6836
6837const SHIFT_PRECEDENCE: u8 = 8;
6839
6840#[derive(Clone, Copy, PartialEq)]
6841enum TupleElementLabel {
6842 None,
6843 Required,
6845 Optional,
6847}
6848
6849fn peek_binop(kind: &TokenKind) -> Option<(BinOp, u8)> {
6850 Some(match kind {
6851 TokenKind::QuestionQuestion => (BinOp::NullishCoalesce, 0),
6852 TokenKind::PipePipe => (BinOp::Or, 1),
6853 TokenKind::AmpAmp => (BinOp::And, LOGICAL_AND_PREC),
6854 TokenKind::Pipe => (BinOp::BitOr, 3),
6855 TokenKind::Caret => (BinOp::BitXor, 4),
6856 TokenKind::Amp => (BinOp::BitAnd, 5),
6857 TokenKind::EqEqEq | TokenKind::EqEq => (BinOp::Eq, 6),
6858 TokenKind::BangEqEq | TokenKind::BangEq => (BinOp::NotEq, 6),
6859 TokenKind::LessThan => (BinOp::Lt, 7),
6860 TokenKind::GreaterThan => (BinOp::Gt, 7),
6861 TokenKind::LessEquals => (BinOp::Le, 7),
6862 TokenKind::GreaterEquals => (BinOp::Ge, 7),
6863 TokenKind::In => (BinOp::In, 7),
6864 TokenKind::Plus => (BinOp::Add, 9),
6867 TokenKind::Minus => (BinOp::Sub, 9),
6868 TokenKind::Star => (BinOp::Mul, 10),
6869 TokenKind::Slash => (BinOp::Div, 10),
6870 TokenKind::Percent => (BinOp::Rem, 10),
6871 TokenKind::StarStar => (BinOp::Pow, 11),
6873 _ => return None,
6874 })
6875}
6876
6877fn cannot_start_type(kind: &TokenKind) -> bool {
6881 matches!(
6882 kind,
6883 TokenKind::Dot
6884 | TokenKind::QuestionDot
6885 | TokenKind::LeftBracket
6886 | TokenKind::Bang
6887 | TokenKind::Semicolon
6888 | TokenKind::RightParen
6889 | TokenKind::RightBrace
6890 | TokenKind::Comma
6891 | TokenKind::Eof
6892 )
6893}
6894
6895fn right_operand_min_prec(op: BinOp, prec: u8) -> u8 {
6899 match op {
6900 BinOp::Pow => prec,
6901 BinOp::NullishCoalesce => LOGICAL_AND_PREC + 1,
6902 _ => prec + 1,
6903 }
6904}
6905
6906fn compound_op_for_token(kind: &TokenKind) -> Option<BinOp> {
6907 Some(match kind {
6908 TokenKind::PlusEquals => BinOp::Add,
6909 TokenKind::MinusEquals => BinOp::Sub,
6910 TokenKind::StarEquals => BinOp::Mul,
6911 TokenKind::SlashEquals => BinOp::Div,
6912 TokenKind::PercentEquals => BinOp::Rem,
6913 TokenKind::StarStarEquals => BinOp::Pow,
6914 TokenKind::AmpEquals => BinOp::BitAnd,
6915 TokenKind::PipeEquals => BinOp::BitOr,
6916 TokenKind::CaretEquals => BinOp::BitXor,
6917 _ => return None,
6918 })
6919}
6920
6921fn is_assign_lookahead(kind: &TokenKind) -> bool {
6922 matches!(kind, TokenKind::Equals) || compound_op_for_token(kind).is_some()
6923}
6924
6925fn parse_arena_result<T>(
6928 result: Result<T, crate::arena::ArenaError>,
6929 fatal: &mut Option<CompilerFailure>,
6930) -> Option<T> {
6931 match result {
6932 Ok(value) => Some(value),
6933 Err(error) => {
6934 fatal.get_or_insert_with(|| error.into_compiler_failure(CompilerStage::Parse));
6935 None
6936 }
6937 }
6938}
6939
6940fn is_rest_array_annotation(ty: &crate::TypeAnnotation) -> bool {
6942 use crate::ast::TypeAnnotationKind;
6943 match &ty.kind {
6944 TypeAnnotationKind::Array(_) => true,
6945 TypeAnnotationKind::Readonly(inner) => matches!(inner.kind, TypeAnnotationKind::Array(_)),
6946 _ => false,
6947 }
6948}
6949
6950#[cfg(test)]
6951mod tests {
6952 use super::{MAX_ERRORS, MAX_PARSE_DEPTH, Parser, parse};
6953 use crate::source::Sources;
6954 use crate::{Asi, Ast, ExprKind, FileId, ImportKind, StmtKind, Token, TokenKind, diagnostics};
6955
6956 const F: FileId = FileId(0);
6957
6958 #[test]
6959 fn malformed_direct_token_streams_return_typed_failures() {
6960 use crate::compiler_error::CompilerFailure;
6961 for tokens in [
6962 Vec::new(),
6963 vec![Token::new(
6964 TokenKind::Identifier,
6965 crate::Span::new(F, 0, 2).unwrap(),
6966 )],
6967 vec![
6968 Token::new(
6969 TokenKind::Identifier,
6970 crate::Span {
6971 file: F,
6972 start: 1,
6973 end: 2,
6974 },
6975 ),
6976 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
6977 ],
6978 vec![
6979 Token::new(TokenKind::Eof, crate::Span::at(F)),
6980 Token::new(TokenKind::Eof, crate::Span::at(F)),
6981 ],
6982 vec![
6983 Token::new(
6984 TokenKind::Identifier,
6985 crate::Span {
6986 file: F,
6987 start: 2,
6988 end: 0,
6989 },
6990 ),
6991 Token::new(TokenKind::Eof, crate::Span::new(F, 2, 2).unwrap()),
6992 ],
6993 vec![Token::new(TokenKind::Eof, crate::Span::at(FileId(9)))],
6994 ] {
6995 let error = super::parse_checked("é", tokens, F).expect_err("invalid token metadata");
6996 assert!(matches!(
6997 error.fatal,
6998 Some(CompilerFailure::Internal { .. })
6999 ));
7000 }
7001 }
7002
7003 #[test]
7004 fn malformed_documentation_metadata_returns_a_fatal_error() {
7005 let source = "/** hello */ function main(): void {}";
7006 let valid = tokens_of(source);
7007 for doc in [
7008 crate::RawDoc {
7009 text: "/** hi */".into(),
7010 span: crate::Span {
7011 file: F,
7012 start: u32::MAX,
7013 end: u32::MAX,
7014 },
7015 },
7016 crate::RawDoc {
7017 text: "/** different */".into(),
7018 span: crate::Span::new(F, 0, 12).unwrap(),
7019 },
7020 crate::RawDoc {
7021 text: "/** hello */".into(),
7022 span: crate::Span::new(FileId(9), 0, 12).unwrap(),
7023 },
7024 ] {
7025 let mut tokens = valid.clone();
7026 tokens[0].leading_doc = Some(doc);
7027 let error = super::parse_checked(source, tokens, F).expect_err("invalid doc metadata");
7028 assert!(error.to_string().contains("documentation metadata"));
7029 }
7030 assert!(super::parse_checked(source, valid, F).is_ok());
7031 }
7032
7033 #[test]
7034 fn invalid_parser_dispatch_survives_diagnostic_rollback() {
7035 let mut parser = parser_from_source("let x = 1;");
7036 assert!(parser.try_parse_type_args_only().is_none());
7037 parser.diagnostics.clear();
7038 assert!(parser.fatal.is_some());
7039 assert!(parser.parse_expression().is_none());
7040 }
7041
7042 #[test]
7043 fn invalid_arena_read_stops_parser_and_survives_diagnostic_rollback() {
7044 use crate::{
7045 ExprId,
7046 compiler_error::{CompilerFailure, CompilerStage},
7047 };
7048 let mut parser = parser_from_source("let x = 1;");
7049 assert!(parser.statement_kind_for(ExprId(u32::MAX)).is_none());
7050 assert!(matches!(
7051 parser.fatal,
7052 Some(CompilerFailure::Internal {
7053 stage: CompilerStage::Parse,
7054 span: None,
7055 ..
7056 })
7057 ));
7058 parser.diagnostics.clear();
7059 let position = parser.pos;
7060 assert!(parser.parse_statement().is_none());
7061 parser.recover();
7062 assert_eq!(parser.pos, position);
7063 assert!(parser.ast.expr_ids().unwrap().next().is_none());
7064 }
7065
7066 fn tokens_of(source: &str) -> Vec<Token> {
7067 let mut asi = Asi::new(source, crate::FileId(0));
7068 let mut tokens = Vec::new();
7069 loop {
7070 let tok = asi.next_token();
7071 let is_eof = matches!(tok.kind, TokenKind::Eof);
7072 tokens.push(tok);
7073 if is_eof {
7074 break;
7075 }
7076 }
7077 let diags = asi.into_diagnostics();
7078 assert!(diags.is_empty(), "unexpected lexer diagnostics: {diags:?}");
7079 tokens
7080 }
7081
7082 fn parse_str(source: &str) -> (Ast, Vec<crate::Diagnostic>) {
7083 parse(source, tokens_of(source), crate::FileId(0))
7084 }
7085
7086 fn parser_from_source(source: &str) -> Parser<'_> {
7087 Parser {
7088 source,
7089 tokens: tokens_of(source),
7090 file: F,
7091 pos: 0,
7092 ast: Ast::new(),
7093 diagnostics: Vec::new(),
7094 block_depth: 0,
7095 class_member_body_depth: 0,
7096 function_expression_body_depth: 0,
7097 recursion_depth: 0,
7098 recursion_limit_span: None,
7099 resume_at_next_member: false,
7100 last_as_type_end: None,
7101 eof: Token::new(
7102 TokenKind::Eof,
7103 crate::Span::new(F, source.len() as u32, source.len() as u32).unwrap(),
7104 ),
7105 fatal: None,
7106 }
7107 }
7108
7109 #[test]
7110 fn recursion_budget_accepts_boundary_and_restores_depth() {
7111 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH - 1));
7112 let mut parser = parser_from_source(&source);
7113 assert!(parser.parse_unary().is_some());
7114 assert_eq!(parser.recursion_depth, 0);
7115 assert!(parser.recursion_limit_span.is_none());
7116
7117 let source = format!("{}true", "!".repeat(MAX_PARSE_DEPTH));
7118 let mut parser = parser_from_source(&source);
7119 assert!(parser.parse_unary().is_none());
7120 assert_eq!(parser.recursion_depth, 0);
7121 assert!(parser.recursion_limit_span.is_some());
7122 }
7123
7124 #[test]
7125 fn parse_empty_input() {
7126 let (ast, diags) = parse_str("");
7127 assert!(diags.is_empty());
7128 assert!(ast.top_level.is_empty());
7129 }
7130
7131 #[test]
7132 fn any_type_is_rejected() {
7133 for src in [
7136 "function main(): void { const x: any = 1; }",
7137 "function main(): void { const x = 1 as any; }",
7138 "function main(): void { const x: any[] = []; }",
7139 ] {
7140 let (_ast, diags) = parse_str(src);
7141 assert!(
7142 diags
7143 .iter()
7144 .any(|d| d.message.contains("`any` is not supported")),
7145 "expected an `any` rejection for {src:?}, got: {diags:?}",
7146 );
7147 assert!(
7148 diags
7149 .iter()
7150 .any(|d| d.help.iter().any(|h| h.contains("`unknown`"))),
7151 "expected the fix-shape help for {src:?}, got: {diags:?}",
7152 );
7153 }
7154 }
7155
7156 #[test]
7157 fn any_catch_binding_help_suggests_catch_forms() {
7158 let (_ast, diags) = parse_str("function main(): void { try { } catch (e: any) { } }");
7161 let any_diags: Vec<_> = diags
7162 .iter()
7163 .filter(|d| d.message.contains("`any` is not supported"))
7164 .collect();
7165 assert_eq!(any_diags.len(), 1, "got: {diags:?}");
7166 assert!(
7167 any_diags[0].help.iter().any(|h| h.contains("`catch (e)`")),
7168 "got: {diags:?}"
7169 );
7170 assert!(
7171 !any_diags[0].help.iter().any(|h| h.contains("`unknown`")),
7172 "got: {diags:?}"
7173 );
7174 }
7175
7176 #[test]
7177 fn arrow_return_type_scanner_matches_type_grammar() {
7178 for ty in [
7183 "number",
7184 "void",
7185 "null",
7186 "ns.Foo",
7187 "Map<string, number>",
7188 "ns.Foo<string>[]",
7189 "number[]",
7190 "[number, number]",
7191 "[number, [string, boolean]]",
7192 "[number, string][]",
7193 "{ a: number }",
7194 "{ a: number }[]",
7195 "\"on\" | \"off\"",
7196 "1 | 2",
7197 "Point | null",
7198 "| number | string",
7199 "(n: number) => number",
7200 "(n: number) => [number, number]",
7201 "readonly number[]",
7202 "readonly number[][]",
7203 "readonly [number, string]",
7204 "readonly number[] | null",
7205 "[first: number, second: string]",
7206 "[new: number, string]",
7207 "keyof { a: number }",
7208 ] {
7209 let annotation = format!("function f(): void {{ const a: {ty} = x; }}");
7210 let (_ast, diags) = parse_str(&annotation);
7211 assert!(
7212 diags.is_empty(),
7213 "annotation grammar rejected {ty:?}: {diags:?}",
7214 );
7215
7216 let arrow = format!("function f(): void {{ const g = (): {ty} => x; }}");
7217 let (_ast, diags) = parse_str(&arrow);
7218 assert!(
7219 diags.is_empty(),
7220 "arrow lookahead rejected return type {ty:?}: {diags:?}",
7221 );
7222 }
7223
7224 let (_ast, diags) =
7226 parse_str("function f(): void { const g = (v: unknown): v is string => x; }");
7227 assert!(
7228 diags.is_empty(),
7229 "arrow lookahead rejected predicate return: {diags:?}",
7230 );
7231 }
7232
7233 #[test]
7234 fn bare_semicolons_are_silent_empty_statements() {
7235 let (ast, diags) = parse_str(";");
7236 assert!(diags.is_empty());
7237 assert!(ast.top_level.is_empty());
7238 }
7239
7240 #[test]
7241 fn parse_single_non_expression_token_errors() {
7242 let (ast, diags) = parse_str("}");
7243 assert_eq!(diags.len(), 1);
7244 assert_eq!(diags[0].message, "expected expression");
7245 assert!(ast.top_level.is_empty());
7246 }
7247
7248 #[test]
7249 fn recovery_across_many_bad_starts() {
7250 let (ast, diags) = parse_str("} } } x;");
7251 assert_eq!(diags.len(), 3);
7252 for d in &diags {
7253 assert_eq!(d.message, "expected expression");
7254 }
7255 assert_eq!(ast.top_level.len(), 1);
7256 }
7257
7258 #[test]
7259 fn recovery_consumes_semicolon() {
7260 let mut p = parser_from_source("foo ;");
7261 p.error_at_peek("test");
7262 p.recover();
7263 assert!(matches!(
7264 p.peek().kind,
7265 TokenKind::Semicolon | TokenKind::Eof
7266 ));
7267 assert!(matches!(p.peek().kind, TokenKind::Eof));
7268 }
7269
7270 #[test]
7271 fn recovery_stops_at_right_brace() {
7272 let mut p = parser_from_source("foo }");
7273 p.error_at_peek("test");
7274 p.recover();
7275 assert!(matches!(p.peek().kind, TokenKind::RightBrace));
7276 }
7277
7278 #[test]
7279 fn recovery_stops_at_let_keyword() {
7280 let mut p = parser_from_source("foo let x");
7281 p.error_at_peek("test");
7282 p.recover();
7283 assert!(matches!(p.peek().kind, TokenKind::Let));
7284 }
7285
7286 #[test]
7287 fn recovery_stops_at_function_keyword() {
7288 let mut p = parser_from_source("foo function bar");
7289 p.error_at_peek("test");
7290 p.recover();
7291 assert!(matches!(p.peek().kind, TokenKind::Function));
7292 }
7293
7294 #[test]
7295 fn error_cap_at_20() {
7296 let source = "} ".repeat(25);
7297 let (_, diags) = parse_str(&source);
7298 assert_eq!(diags.len(), MAX_ERRORS);
7299 }
7300
7301 #[test]
7302 fn driver_makes_progress_past_unmatched_brace() {
7303 let (ast, diags) = parse_str("} x;");
7304 assert_eq!(diags.len(), 1);
7305 assert_eq!(diags[0].message, "expected expression");
7306 assert_eq!(ast.top_level.len(), 1);
7307 }
7308
7309 #[test]
7310 fn snapshot_multi_error_render() {
7311 let source = "foo bar;\nbaz qux;\nhello world;";
7312 let (_, diags) = parse_str(source);
7313 let (sources, _) = Sources::single("script.subm", source).unwrap();
7314 let rendered: String = diags
7315 .iter()
7316 .map(|d| diagnostics::render(d, &sources))
7317 .collect::<Vec<_>>()
7318 .join("\n");
7319 insta::assert_snapshot!(rendered);
7320 }
7321
7322 fn single_stmt(ast: &Ast) -> &crate::Stmt {
7323 assert_eq!(ast.top_level.len(), 1);
7324 ast.try_stmt(ast.top_level[0]).unwrap()
7325 }
7326
7327 fn expr_of_single_stmt(ast: &Ast) -> &crate::Expr {
7328 let stmt = single_stmt(ast);
7329 match stmt.kind {
7330 crate::StmtKind::Expr(id) => ast.try_expr(id).unwrap(),
7331 _ => panic!("expected expression statement, got {:?}", stmt.kind),
7332 }
7333 }
7334
7335 #[test]
7336 fn parse_number_literal() {
7337 let (ast, diags) = parse_str("42;");
7338 assert!(diags.is_empty());
7339 let expr = expr_of_single_stmt(&ast);
7340 assert_eq!(expr.kind, crate::ExprKind::Number(42.0));
7341 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
7342 }
7343
7344 #[test]
7345 fn parse_string_literal() {
7346 let (ast, diags) = parse_str(r#""s";"#);
7347 assert!(diags.is_empty());
7348 let expr = expr_of_single_stmt(&ast);
7349 assert_eq!(expr.kind, crate::ExprKind::String("s".to_string()));
7350 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7351 }
7352
7353 #[test]
7354 fn parse_boolean_true() {
7355 let (ast, diags) = parse_str("true;");
7356 assert!(diags.is_empty());
7357 let expr = expr_of_single_stmt(&ast);
7358 assert_eq!(expr.kind, crate::ExprKind::Boolean(true));
7359 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7360 }
7361
7362 #[test]
7363 fn parse_boolean_false() {
7364 let (ast, diags) = parse_str("false;");
7365 assert!(diags.is_empty());
7366 let expr = expr_of_single_stmt(&ast);
7367 assert_eq!(expr.kind, crate::ExprKind::Boolean(false));
7368 assert_eq!(expr.span, crate::Span::new(F, 0, 5).unwrap());
7369 }
7370
7371 #[test]
7372 fn parse_null_literal() {
7373 let (ast, diags) = parse_str("null;");
7374 assert!(diags.is_empty());
7375 let expr = expr_of_single_stmt(&ast);
7376 assert_eq!(expr.kind, crate::ExprKind::Null);
7377 assert_eq!(expr.span, crate::Span::new(F, 0, 4).unwrap());
7378 }
7379
7380 #[test]
7381 fn parse_identifier() {
7382 let (ast, diags) = parse_str("x;");
7383 assert!(diags.is_empty());
7384 let expr = expr_of_single_stmt(&ast);
7385 assert!(matches!(expr.kind, crate::ExprKind::Identifier(_)));
7386 assert_eq!(expr.span, crate::Span::new(F, 0, 1).unwrap());
7387 }
7388
7389 #[test]
7390 fn reserved_keywords_remain_invalid_declaration_names() {
7391 let (_ast, diags) = parse_str("function default(): void {}");
7392 assert_eq!(diags.len(), 1, "{diags:#?}");
7393 assert_eq!(
7394 diags[0].message,
7395 "`default` is a reserved keyword and can't be used as a name"
7396 );
7397 assert!(
7398 diags[0]
7399 .help
7400 .iter()
7401 .any(|h| h == "rename it, for example: `defaultValue`"),
7402 "expected rename help, got: {diags:?}"
7403 );
7404 }
7405
7406 #[test]
7407 fn eval_and_arguments_cannot_name_bindings() {
7408 let bindings = [
7409 ("let arguments = 1;", "arguments"),
7410 ("const [eval] = [1];", "eval"),
7411 ("const { a: eval } = { a: 1 };", "eval"),
7412 ("const { eval } = { eval: 1 };", "eval"),
7413 ("const [...arguments] = [1];", "arguments"),
7414 ("function eval(): void {}", "eval"),
7415 ("function f(arguments: number): void {}", "arguments"),
7416 ("const f = arguments => arguments;", "arguments"),
7417 ("const f = (...eval: number[]) => 1;", "eval"),
7418 ("const f = function eval(): void {};", "eval"),
7419 ("for (const eval of [1]) {}", "eval"),
7420 ("try {} catch (arguments) {}", "arguments"),
7421 ("type F = (eval: number) => void;", "eval"),
7422 ("type G = (...arguments: number[]) => void;", "arguments"),
7423 ("type H = { [eval: string]: number };", "eval"),
7424 ];
7425 for (source, word) in bindings {
7426 let (_ast, diags) = parse_str(source);
7427 assert!(
7428 diags
7429 .iter()
7430 .any(|d| d.message == format!("`{word}` can't name a binding in strict mode")),
7431 "{source}: {diags:#?}"
7432 );
7433 }
7434 }
7435
7436 #[test]
7437 fn eval_and_arguments_still_name_types_and_properties() {
7438 for source in [
7439 "class eval {}",
7440 "interface arguments { eval: number }",
7441 "type eval = number;",
7442 "enum arguments { eval }",
7443 "const o = { eval: 1, arguments: 2 };",
7444 "import { eval } from \"./m\";",
7445 ] {
7446 let (_ast, diags) = parse_str(source);
7447 assert!(diags.is_empty(), "{source}: {diags:#?}");
7448 }
7449 }
7450
7451 #[test]
7452 fn reserved_keywords_remain_invalid_parameter_names() {
7453 let (_ast, diags) = parse_str("function f(default: string): void {}");
7454 assert_eq!(diags.len(), 1, "{diags:#?}");
7455 assert_eq!(
7456 diags[0].message,
7457 "`default` is a reserved keyword and can't be used as a name"
7458 );
7459 assert!(
7460 diags[0]
7461 .help
7462 .iter()
7463 .any(|h| h == "rename it, for example: `defaultValue`"),
7464 "expected rename help, got: {diags:?}"
7465 );
7466 }
7467
7468 #[test]
7469 fn contextual_keywords_are_valid_names() {
7470 for src in [
7471 "const type = \"x\";",
7472 "const from = 1;",
7473 "let of = 2;",
7474 "const as = 3;",
7475 "const is = 4;",
7476 "function f(from: string): void {}",
7477 ] {
7478 let (_ast, diags) = parse_str(src);
7479 assert!(
7480 diags.is_empty(),
7481 "unexpected diagnostics for {src:?}: {diags:#?}"
7482 );
7483 }
7484 }
7485
7486 #[test]
7487 fn type_alias_dispatch_table() {
7488 let (ast, diags) = parse_str("type X = number;");
7489 assert!(diags.is_empty(), "{diags:#?}");
7490 assert!(matches!(
7491 ast.try_stmt(ast.top_level[0]).unwrap().kind,
7492 crate::StmtKind::TypeAliasDecl { .. }
7493 ));
7494
7495 for src in ["type = 5;", "type;", "type(1);", "type < 3;", "type.foo;"] {
7497 let (ast, diags) = parse_str(src);
7498 assert!(
7499 diags.is_empty(),
7500 "unexpected diagnostics for {src:?}: {diags:#?}"
7501 );
7502 assert!(
7503 !matches!(
7504 ast.try_stmt(ast.top_level[0]).unwrap().kind,
7505 crate::StmtKind::TypeAliasDecl { .. }
7506 ),
7507 "{src:?} must not parse as a type alias"
7508 );
7509 }
7510
7511 let (_ast, diags) = parse_str("type X;");
7512 assert!(
7513 diags
7514 .iter()
7515 .any(|d| d.message == "expected `=` after type alias name"),
7516 "bare `type X` should commit to the alias diagnostic, got: {diags:#?}"
7517 );
7518 }
7519
7520 #[test]
7521 fn export_type_alias_dispatch() {
7522 let (ast, diags) = parse_str("export type X = number;");
7523 assert!(diags.is_empty(), "{diags:#?}");
7524 assert_eq!(ast.exported_decls.len(), 1);
7525 assert!(matches!(
7526 ast.try_stmt(ast.exported_decls[0].stmt).unwrap().kind,
7527 crate::StmtKind::TypeAliasDecl { .. }
7528 ));
7529
7530 let (_ast, diags) = parse_str("export type = 5;");
7531 assert!(
7532 diags
7533 .iter()
7534 .any(|d| d.message == "expected a declaration after `export`"),
7535 "`export type = 5` must not become an exported assignment, got: {diags:#?}"
7536 );
7537 }
7538
7539 #[test]
7540 fn parse_paren_simple() {
7541 let (ast, diags) = parse_str("(x);");
7542 assert!(diags.is_empty());
7543 let expr = expr_of_single_stmt(&ast);
7544 let crate::ExprKind::Paren(inner_id) = expr.kind else {
7545 panic!("expected Paren, got {:?}", expr.kind);
7546 };
7547 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
7548 let inner = ast.try_expr(inner_id).unwrap();
7549 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7550 assert_eq!(inner.span, crate::Span::new(F, 1, 2).unwrap());
7551 }
7552
7553 #[test]
7554 fn parse_paren_nested() {
7555 let (ast, diags) = parse_str("((x));");
7556 assert!(diags.is_empty());
7557 let outer = expr_of_single_stmt(&ast);
7558 let crate::ExprKind::Paren(mid_id) = outer.kind else {
7559 panic!("expected outer Paren");
7560 };
7561 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7562 let mid = ast.try_expr(mid_id).unwrap();
7563 let crate::ExprKind::Paren(inner_id) = mid.kind else {
7564 panic!("expected inner Paren");
7565 };
7566 assert_eq!(mid.span, crate::Span::new(F, 1, 4).unwrap());
7567 let inner = ast.try_expr(inner_id).unwrap();
7568 assert!(matches!(inner.kind, crate::ExprKind::Identifier(_)));
7569 assert_eq!(inner.span, crate::Span::new(F, 2, 3).unwrap());
7570 }
7571
7572 #[test]
7573 fn parse_sequence_of_atoms() {
7574 let (ast, diags) = parse_str(r#"42; "hi"; true; null; x; (x);"#);
7575 assert!(diags.is_empty());
7576 assert_eq!(ast.top_level.len(), 6);
7577 insta::assert_debug_snapshot!(ast);
7578 }
7579
7580 #[test]
7581 fn missing_semicolon_after_expression() {
7582 let (ast, diags) = parse_str("42 43");
7583 assert_eq!(diags.len(), 1);
7584 assert_eq!(diags[0].message, "expected `;` after expression");
7585 assert!(ast.top_level.is_empty());
7586 }
7587
7588 #[test]
7589 fn empty_paren_diagnoses() {
7590 let (ast, diags) = parse_str("();");
7591 assert_eq!(diags.len(), 1);
7592 assert_eq!(diags[0].message, "expected expression");
7593 assert!(ast.top_level.is_empty());
7594 }
7595
7596 #[test]
7597 fn unterminated_paren_diagnoses() {
7598 let (ast, diags) = parse_str("(x;");
7599 assert_eq!(diags.len(), 1);
7600 assert_eq!(diags[0].message, "expected `)`");
7601 assert!(ast.top_level.is_empty());
7602 }
7603
7604 fn binary(e: &crate::Expr) -> (crate::BinOp, crate::ExprId, crate::ExprId) {
7605 match e.kind {
7606 crate::ExprKind::Binary { op, lhs, rhs } => (op, lhs, rhs),
7607 _ => panic!("expected Binary, got {:?}", e.kind),
7608 }
7609 }
7610
7611 #[test]
7612 fn parse_binary_add() {
7613 let (ast, diags) = parse_str("a + b;");
7614 assert!(diags.is_empty());
7615 let outer = expr_of_single_stmt(&ast);
7616 let (op, lhs, rhs) = binary(outer);
7617 assert_eq!(op, crate::BinOp::Add);
7618 assert!(matches!(
7619 ast.try_expr(lhs).unwrap().kind,
7620 crate::ExprKind::Identifier(_)
7621 ));
7622 assert!(matches!(
7623 ast.try_expr(rhs).unwrap().kind,
7624 crate::ExprKind::Identifier(_)
7625 ));
7626 assert_eq!(outer.span, crate::Span::new(F, 0, 5).unwrap());
7627 }
7628
7629 #[test]
7630 fn bitwise_precedence_and_shift_adjacency() {
7631 let (ast, diags) = parse_str("a | b ^ c & d == e >> f + g;");
7632 assert!(diags.is_empty(), "{diags:?}");
7633 let (op, _, mut rhs) = binary(expr_of_single_stmt(&ast));
7634 assert_eq!(op, crate::BinOp::BitOr);
7635 for expected in [
7636 crate::BinOp::BitXor,
7637 crate::BinOp::BitAnd,
7638 crate::BinOp::Eq,
7639 crate::BinOp::Shr,
7640 crate::BinOp::Add,
7641 ] {
7642 let (op, _, next) = binary(ast.try_expr(rhs).unwrap());
7643 assert_eq!(op, expected);
7644 rhs = next;
7645 }
7646 for source in ["a > > b;", "a >/*gap*/> b;", "a < < b;"] {
7647 let (_, diags) = parse_str(source);
7648 assert!(!diags.is_empty(), "{source}");
7649 }
7650 }
7651
7652 #[test]
7653 fn bitwise_shift_assignment_span() {
7654 let (ast, diags) = parse_str("a >>>= b;");
7655 assert!(diags.is_empty(), "{diags:?}");
7656 let crate::StmtKind::CompoundAssign { op, op_span, .. } = single_stmt(&ast).kind else {
7657 panic!("expected assignment");
7658 };
7659 assert_eq!(op, crate::BinOp::UnsignedShr);
7660 assert_eq!(op_span, crate::Span::new(F, 2, 6).unwrap());
7661 }
7662
7663 #[test]
7664 fn precedence_mul_binds_tighter_than_add() {
7665 let (ast, diags) = parse_str("a + b * c;");
7666 assert!(diags.is_empty());
7667 let outer = expr_of_single_stmt(&ast);
7668 let (op, lhs, rhs) = binary(outer);
7669 assert_eq!(op, crate::BinOp::Add);
7670 assert!(matches!(
7671 ast.try_expr(lhs).unwrap().kind,
7672 crate::ExprKind::Identifier(_)
7673 ));
7674 let (rop, rlhs, rrhs) = binary(ast.try_expr(rhs).unwrap());
7675 assert_eq!(rop, crate::BinOp::Mul);
7676 assert!(matches!(
7677 ast.try_expr(rlhs).unwrap().kind,
7678 crate::ExprKind::Identifier(_)
7679 ));
7680 assert!(matches!(
7681 ast.try_expr(rrhs).unwrap().kind,
7682 crate::ExprKind::Identifier(_)
7683 ));
7684 }
7685
7686 #[test]
7687 fn precedence_mul_left_of_add() {
7688 let (ast, diags) = parse_str("a * b + c;");
7689 assert!(diags.is_empty());
7690 let outer = expr_of_single_stmt(&ast);
7691 let (op, lhs, rhs) = binary(outer);
7692 assert_eq!(op, crate::BinOp::Add);
7693 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7694 assert_eq!(lop, crate::BinOp::Mul);
7695 assert!(matches!(
7696 ast.try_expr(rhs).unwrap().kind,
7697 crate::ExprKind::Identifier(_)
7698 ));
7699 }
7700
7701 #[test]
7702 fn left_associative_same_precedence() {
7703 let (ast, diags) = parse_str("a - b - c;");
7704 assert!(diags.is_empty());
7705 let outer = expr_of_single_stmt(&ast);
7706 let (op, lhs, rhs) = binary(outer);
7707 assert_eq!(op, crate::BinOp::Sub);
7708 let (lop, llhs, lrhs) = binary(ast.try_expr(lhs).unwrap());
7709 assert_eq!(lop, crate::BinOp::Sub);
7710 assert!(matches!(
7711 ast.try_expr(llhs).unwrap().kind,
7712 crate::ExprKind::Identifier(_)
7713 ));
7714 assert!(matches!(
7715 ast.try_expr(lrhs).unwrap().kind,
7716 crate::ExprKind::Identifier(_)
7717 ));
7718 assert!(matches!(
7719 ast.try_expr(rhs).unwrap().kind,
7720 crate::ExprKind::Identifier(_)
7721 ));
7722 }
7723
7724 #[test]
7725 fn equality_inside_logical() {
7726 let (ast, diags) = parse_str("a === b && c === d;");
7727 assert!(diags.is_empty());
7728 let outer = expr_of_single_stmt(&ast);
7729 let (op, lhs, rhs) = binary(outer);
7730 assert_eq!(op, crate::BinOp::And);
7731 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7732 assert_eq!(lop, crate::BinOp::Eq);
7733 let (rop, ..) = binary(ast.try_expr(rhs).unwrap());
7734 assert_eq!(rop, crate::BinOp::Eq);
7735 }
7736
7737 #[test]
7738 fn double_equal_is_same_variant_as_triple() {
7739 let (ast, _) = parse_str("a == b;");
7740 let outer = expr_of_single_stmt(&ast);
7741 let (op, ..) = binary(outer);
7742 assert_eq!(op, crate::BinOp::Eq);
7743 }
7744
7745 #[test]
7746 fn bang_eq_is_not_eq() {
7747 let (ast, _) = parse_str("a != b;");
7748 let outer = expr_of_single_stmt(&ast);
7749 let (op, ..) = binary(outer);
7750 assert_eq!(op, crate::BinOp::NotEq);
7751 }
7752
7753 #[test]
7754 fn comparison_has_higher_prec_than_logical() {
7755 let (ast, _) = parse_str("a < b && c;");
7756 let outer = expr_of_single_stmt(&ast);
7757 let (op, lhs, _) = binary(outer);
7758 assert_eq!(op, crate::BinOp::And);
7759 let (lop, ..) = binary(ast.try_expr(lhs).unwrap());
7760 assert_eq!(lop, crate::BinOp::Lt);
7761 }
7762
7763 #[test]
7764 fn full_precedence_chain_snapshot() {
7765 let (ast, diags) = parse_str("a || b && c === d < e + f * g;");
7766 assert!(diags.is_empty());
7767 insta::assert_debug_snapshot!(ast);
7768 }
7769
7770 #[test]
7771 fn paren_overrides_precedence() {
7772 let (ast, _) = parse_str("(a + b) * c;");
7773 let outer = expr_of_single_stmt(&ast);
7774 let (op, lhs, rhs) = binary(outer);
7775 assert_eq!(op, crate::BinOp::Mul);
7776 let crate::ExprKind::Paren(inner_id) = ast.try_expr(lhs).unwrap().kind else {
7777 panic!("expected Paren on lhs");
7778 };
7779 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7780 assert_eq!(inner_op, crate::BinOp::Add);
7781 assert!(matches!(
7782 ast.try_expr(rhs).unwrap().kind,
7783 crate::ExprKind::Identifier(_)
7784 ));
7785 }
7786
7787 #[test]
7788 fn binary_missing_rhs_diagnoses() {
7789 let (ast, diags) = parse_str("a +;");
7790 assert_eq!(diags.len(), 1);
7791 assert_eq!(diags[0].message, "expected expression");
7792 assert!(ast.top_level.is_empty());
7793 }
7794
7795 fn unary(e: &crate::Expr) -> (crate::UnOp, crate::ExprId) {
7796 match e.kind {
7797 crate::ExprKind::Unary { op, operand } => (op, operand),
7798 _ => panic!("expected Unary, got {:?}", e.kind),
7799 }
7800 }
7801
7802 #[test]
7803 fn parse_unary_not() {
7804 let (ast, _) = parse_str("!x;");
7805 let outer = expr_of_single_stmt(&ast);
7806 let (op, operand) = unary(outer);
7807 assert_eq!(op, crate::UnOp::Not);
7808 assert!(matches!(
7809 ast.try_expr(operand).unwrap().kind,
7810 crate::ExprKind::Identifier(_)
7811 ));
7812 assert_eq!(outer.span, crate::Span::new(F, 0, 2).unwrap());
7813 }
7814
7815 #[test]
7816 fn parse_unary_neg() {
7817 let (ast, _) = parse_str("-x;");
7818 let (op, _) = unary(expr_of_single_stmt(&ast));
7819 assert_eq!(op, crate::UnOp::Neg);
7820 }
7821
7822 #[test]
7823 fn parse_unary_pos() {
7824 let (ast, _) = parse_str("+x;");
7825 let (op, _) = unary(expr_of_single_stmt(&ast));
7826 assert_eq!(op, crate::UnOp::Pos);
7827 }
7828
7829 #[test]
7830 fn repeated_unary_not() {
7831 let (ast, _) = parse_str("!!x;");
7832 let outer = expr_of_single_stmt(&ast);
7833 let (op, operand) = unary(outer);
7834 assert_eq!(op, crate::UnOp::Not);
7835 let (inner_op, inner_operand) = unary(ast.try_expr(operand).unwrap());
7836 assert_eq!(inner_op, crate::UnOp::Not);
7837 assert!(matches!(
7838 ast.try_expr(inner_operand).unwrap().kind,
7839 crate::ExprKind::Identifier(_)
7840 ));
7841 }
7842
7843 #[test]
7844 fn unary_binds_tighter_than_binary() {
7845 let (ast, _) = parse_str("-x + 1;");
7846 let outer = expr_of_single_stmt(&ast);
7847 let (op, lhs, rhs) = binary(outer);
7848 assert_eq!(op, crate::BinOp::Add);
7849 let (unop, _) = unary(ast.try_expr(lhs).unwrap());
7850 assert_eq!(unop, crate::UnOp::Neg);
7851 assert_eq!(
7852 ast.try_expr(rhs).unwrap().kind,
7853 crate::ExprKind::Number(1.0)
7854 );
7855 }
7856
7857 #[test]
7858 fn unary_over_paren() {
7859 let (ast, _) = parse_str("-(x + 1);");
7860 let outer = expr_of_single_stmt(&ast);
7861 let (op, operand) = unary(outer);
7862 assert_eq!(op, crate::UnOp::Neg);
7863 let crate::ExprKind::Paren(inner_id) = ast.try_expr(operand).unwrap().kind else {
7864 panic!("expected Paren");
7865 };
7866 let (inner_op, ..) = binary(ast.try_expr(inner_id).unwrap());
7867 assert_eq!(inner_op, crate::BinOp::Add);
7868 }
7869
7870 #[test]
7871 fn parse_is_as_expression_rejected() {
7872 let (_, diags) = parse_str("x is number;");
7873 assert!(!diags.is_empty(), "expected parse error for `x is T`");
7874 }
7875
7876 fn as_cast(e: &crate::Expr) -> (crate::ExprId, &crate::TypeAnnotation) {
7877 match &e.kind {
7878 crate::ExprKind::As { expr, ty } => (*expr, ty),
7879 other => panic!("expected ExprKind::As, got {other:?}"),
7880 }
7881 }
7882
7883 #[test]
7884 fn parse_instanceof_class() {
7885 let (ast, diags) = parse_str("x instanceof Foo;");
7886 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7887 let outer = expr_of_single_stmt(&ast);
7888 let crate::ExprKind::InstanceOf { value, ty } = &outer.kind else {
7889 panic!("expected ExprKind::InstanceOf, got {:?}", outer.kind);
7890 };
7891 assert!(matches!(
7892 ast.try_expr(*value).unwrap().kind,
7893 crate::ExprKind::Identifier(_)
7894 ));
7895 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7896 }
7897
7898 #[test]
7899 fn parse_instanceof_binds_tighter_than_logical_and() {
7900 let (ast, diags) = parse_str("a instanceof B && c;");
7902 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7903 let outer = expr_of_single_stmt(&ast);
7904 let (op, lhs, rhs) = binary(outer);
7905 assert_eq!(op, crate::BinOp::And);
7906 assert!(matches!(
7907 ast.try_expr(lhs).unwrap().kind,
7908 crate::ExprKind::InstanceOf { .. }
7909 ));
7910 assert!(matches!(
7911 ast.try_expr(rhs).unwrap().kind,
7912 crate::ExprKind::Identifier(_)
7913 ));
7914 }
7915
7916 #[test]
7919 fn parse_angle_cast_lowers_to_the_as_node() {
7920 let (ast, diags) = parse_str("<number>x;");
7921 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7922 let outer = expr_of_single_stmt(&ast);
7923 let (inner, ty) = as_cast(outer);
7924 assert!(matches!(
7925 ast.try_expr(inner).unwrap().kind,
7926 crate::ExprKind::Identifier(_)
7927 ));
7928 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
7929 }
7930
7931 #[test]
7935 fn a_less_than_comparison_is_not_read_as_a_cast() {
7936 let (ast, diags) = parse_str("a < b;");
7937 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7938 let e = expr_of_single_stmt(&ast);
7939 assert!(matches!(e.kind, crate::ExprKind::Binary { .. }));
7940 }
7941
7942 #[test]
7943 fn parse_typeof_in_type_position() {
7944 let (_ast, diags) = parse_str("let a: typeof b = 1;");
7945 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7946 }
7947
7948 #[test]
7949 fn parse_typeof_with_a_dotted_path() {
7950 let (_ast, diags) = parse_str("let a: typeof b.c.d = 1;");
7951 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7952 }
7953
7954 #[test]
7955 fn parse_as_primitive() {
7956 let (ast, diags) = parse_str("x as number;");
7957 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7958 let outer = expr_of_single_stmt(&ast);
7959 let (inner, ty) = as_cast(outer);
7960 assert!(matches!(
7961 ast.try_expr(inner).unwrap().kind,
7962 crate::ExprKind::Identifier(_)
7963 ));
7964 match &ty.kind {
7965 crate::TypeAnnotationKind::Name { args, .. } => {
7966 assert!(args.is_empty());
7967 }
7968 other => panic!("expected Name target, got {other:?}"),
7969 }
7970 assert_eq!(outer.span, crate::Span::new(F, 0, 11).unwrap());
7971 }
7972
7973 #[test]
7974 fn parse_as_object_literal_type() {
7975 let (ast, diags) = parse_str("x as { a: number };");
7976 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7977 let outer = expr_of_single_stmt(&ast);
7978 let (_, ty) = as_cast(outer);
7979 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Object { .. }));
7980 }
7981
7982 #[test]
7983 fn parse_as_array_short() {
7984 let (ast, diags) = parse_str("x as string[];");
7985 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7986 let outer = expr_of_single_stmt(&ast);
7987 let (_, ty) = as_cast(outer);
7988 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
7989 }
7990
7991 #[test]
7992 fn parse_as_array_generic() {
7993 let (ast, diags) = parse_str("x as T[];");
7994 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
7995 let outer = expr_of_single_stmt(&ast);
7996 let (_, ty) = as_cast(outer);
7997 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Array { .. }));
7998 }
7999
8000 #[test]
8001 fn parse_as_generic_instantiation() {
8002 let (ast, diags) = parse_str("x as Foo<Bar>;");
8003 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8004 let outer = expr_of_single_stmt(&ast);
8005 let (_, ty) = as_cast(outer);
8006 match &ty.kind {
8007 crate::TypeAnnotationKind::Name { args, .. } => {
8008 assert_eq!(args.len(), 1, "Foo<Bar> has one type arg");
8009 }
8010 other => panic!("expected Name with type args, got {other:?}"),
8011 }
8012 }
8013
8014 #[test]
8015 fn parse_as_precedence_higher_than_additive() {
8016 let (ast, diags) = parse_str("x as number + 1;");
8017 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8018 let outer = expr_of_single_stmt(&ast);
8019 let (op, lhs, rhs) = binary(outer);
8020 assert_eq!(op, crate::BinOp::Add);
8021 assert!(matches!(
8022 ast.try_expr(lhs).unwrap().kind,
8023 crate::ExprKind::As { .. }
8024 ));
8025 assert!(matches!(
8026 ast.try_expr(rhs).unwrap().kind,
8027 crate::ExprKind::Number(_)
8028 ));
8029 }
8030
8031 #[test]
8032 fn parse_as_precedence_lower_than_unary() {
8033 let (ast, diags) = parse_str("!x as boolean;");
8034 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8035 let outer = expr_of_single_stmt(&ast);
8036 let (inner, _) = as_cast(outer);
8037 assert!(matches!(
8038 ast.try_expr(inner).unwrap().kind,
8039 crate::ExprKind::Unary {
8040 op: crate::UnOp::Not,
8041 ..
8042 }
8043 ));
8044 }
8045
8046 #[test]
8047 fn parse_as_left_associative_chain() {
8048 let (ast, diags) = parse_str("x as A as B;");
8049 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8050 let outer = expr_of_single_stmt(&ast);
8051 let (inner, _outer_ty) = as_cast(outer);
8052 let (innermost, _inner_ty) = as_cast(ast.try_expr(inner).unwrap());
8053 assert!(matches!(
8054 ast.try_expr(innermost).unwrap().kind,
8055 crate::ExprKind::Identifier(_)
8056 ));
8057 }
8058
8059 #[test]
8060 fn parse_as_inside_call_arg() {
8061 let (ast, diags) = parse_str("f(x as T);");
8062 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8063 let outer = expr_of_single_stmt(&ast);
8064 match &outer.kind {
8065 crate::ExprKind::Call { args, .. } => {
8066 assert_eq!(args.len(), 1);
8067 assert!(matches!(
8068 ast.try_expr(args[0]).unwrap().kind,
8069 crate::ExprKind::As { .. }
8070 ));
8071 }
8072 other => panic!("expected Call, got {other:?}"),
8073 }
8074 }
8075
8076 #[test]
8077 fn parse_as_after_field_access() {
8078 let (ast, diags) = parse_str("obj.field as T;");
8079 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8080 let outer = expr_of_single_stmt(&ast);
8081 let (inner, _) = as_cast(outer);
8082 assert!(matches!(
8083 ast.try_expr(inner).unwrap().kind,
8084 crate::ExprKind::FieldAccess { .. }
8085 ));
8086 }
8087
8088 #[test]
8089 fn parse_type_guard_return_annotation_on_function() {
8090 let src = "function isCircle(s: Shape): s is Circle { return true; }";
8091 let (ast, diags) = parse_str(src);
8092 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8093 let stmt = single_stmt(&ast);
8094 match stmt.kind {
8095 crate::StmtKind::Function {
8096 ref return_type,
8097 ref type_predicate,
8098 ..
8099 } => {
8100 assert!(
8101 return_type.is_none(),
8102 "predicate form leaves return_type empty",
8103 );
8104 let tp = type_predicate.as_ref().expect("predicate parsed");
8105 assert_eq!(tp.param.name, "s");
8106 assert!(matches!(
8107 tp.asserted.kind,
8108 crate::TypeAnnotationKind::Name { .. }
8109 ));
8110 assert_eq!(tp.span, crate::Span::new(F, 29, 40).unwrap());
8111 }
8112 _ => panic!("expected Function"),
8113 }
8114 }
8115
8116 #[test]
8117 fn parse_type_guard_return_annotation_on_arrow() {
8118 let src = "const f = (s: Shape): s is Circle => true;";
8119 let (ast, diags) = parse_str(src);
8120 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8121 let value = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
8122 crate::StmtKind::Const { value, .. } => *value,
8123 _ => panic!("expected const"),
8124 };
8125 match &ast.try_expr(value).unwrap().kind {
8126 crate::ExprKind::Arrow {
8127 return_type,
8128 type_predicate,
8129 ..
8130 } => {
8131 assert!(return_type.is_none());
8132 let tp = type_predicate.as_ref().expect("predicate parsed");
8133 assert_eq!(tp.param.name, "s");
8134 }
8135 other => panic!("expected Arrow, got {other:?}"),
8136 }
8137 }
8138
8139 #[test]
8140 fn parse_plain_return_type_unaffected_by_predicate_parser() {
8141 let src = "function f(s: Shape): boolean { return true; }";
8142 let (ast, diags) = parse_str(src);
8143 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8144 let stmt = single_stmt(&ast);
8145 match stmt.kind {
8146 crate::StmtKind::Function {
8147 ref return_type,
8148 ref type_predicate,
8149 ..
8150 } => {
8151 assert!(return_type.is_some(), "plain form has return_type");
8152 assert!(type_predicate.is_none(), "plain form has no type_predicate",);
8153 }
8154 _ => panic!("expected Function"),
8155 }
8156 }
8157
8158 #[test]
8159 fn parse_type_guard_rejects_non_return_position() {
8160 let (_, diags) = parse_str("let x: y is Foo = null;");
8161 assert!(
8162 !diags.is_empty(),
8163 "expected parse error for `is` outside return-type position",
8164 );
8165 }
8166
8167 #[test]
8168 fn parse_typeof_prefix() {
8169 let (ast, diags) = parse_str("typeof x;");
8170 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8171 let outer = expr_of_single_stmt(&ast);
8172 let crate::ExprKind::Typeof { operand } = outer.kind else {
8173 panic!("expected Typeof, got {:?}", outer.kind);
8174 };
8175 assert!(matches!(
8176 ast.try_expr(operand).unwrap().kind,
8177 crate::ExprKind::Identifier(_)
8178 ));
8179 assert_eq!(outer.span, crate::Span::new(F, 0, 8).unwrap());
8180 }
8181
8182 #[test]
8183 fn parse_typeof_in_equality() {
8184 let (ast, diags) = parse_str(r#"typeof x === "number";"#);
8185 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8186 let outer = expr_of_single_stmt(&ast);
8187 let (op, lhs, rhs) = binary(outer);
8188 assert_eq!(op, crate::BinOp::Eq);
8189 assert!(matches!(
8190 ast.try_expr(lhs).unwrap().kind,
8191 crate::ExprKind::Typeof { .. }
8192 ));
8193 assert!(matches!(
8194 ast.try_expr(rhs).unwrap().kind,
8195 crate::ExprKind::String(ref s) if s == "number"
8196 ));
8197 }
8198
8199 #[test]
8200 fn parse_let_without_type() {
8201 let (ast, diags) = parse_str("let x = 1;");
8202 assert!(diags.is_empty());
8203 let stmt = single_stmt(&ast);
8204 assert_eq!(stmt.span, crate::Span::new(F, 0, 10).unwrap());
8205 match stmt.kind {
8206 crate::StmtKind::Let {
8207 ref name,
8208 ref ty,
8209 value,
8210 ..
8211 } => {
8212 assert_eq!(name.name, "x");
8213 assert_eq!(name.span, crate::Span::new(F, 4, 5).unwrap());
8214 assert!(ty.is_none());
8215 assert_eq!(
8216 ast.try_expr(value).unwrap().kind,
8217 crate::ExprKind::Number(1.0)
8218 );
8219 }
8220 _ => panic!("expected Let"),
8221 }
8222 }
8223
8224 #[test]
8225 fn parse_const_with_type_and_string() {
8226 let (ast, diags) = parse_str(r#"const y: string = "hi";"#);
8227 assert!(diags.is_empty());
8228 let stmt = single_stmt(&ast);
8229 match stmt.kind {
8230 crate::StmtKind::Const {
8231 ref name,
8232 ref ty,
8233 value,
8234 ..
8235 } => {
8236 assert_eq!(name.name, "y");
8237 assert_eq!(name.span, crate::Span::new(F, 6, 7).unwrap());
8238 let ty = ty.as_ref().expect("expected type annotation");
8239 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8240 assert_eq!(ty.span, crate::Span::new(F, 9, 15).unwrap());
8241 assert_eq!(
8242 ast.try_expr(value).unwrap().kind,
8243 crate::ExprKind::String("hi".to_string())
8244 );
8245 }
8246 _ => panic!("expected Const"),
8247 }
8248 }
8249
8250 #[test]
8251 fn parse_let_with_type_and_binary_initializer() {
8252 let (ast, diags) = parse_str("let z: number = 1 + 2;");
8253 assert!(diags.is_empty());
8254 let stmt = single_stmt(&ast);
8255 match stmt.kind {
8256 crate::StmtKind::Let { ref ty, value, .. } => {
8257 assert!(ty.is_some());
8258 let (op, ..) = binary(ast.try_expr(value).unwrap());
8259 assert_eq!(op, crate::BinOp::Add);
8260 }
8261 _ => panic!("expected Let"),
8262 }
8263 }
8264
8265 #[test]
8266 fn void_type_annotation_is_accepted_syntactically() {
8267 let (ast, diags) = parse_str("let v: void = null;");
8268 assert!(diags.is_empty());
8269 let stmt = single_stmt(&ast);
8270 match stmt.kind {
8271 crate::StmtKind::Let { ref ty, .. } => {
8272 let ty = ty.as_ref().unwrap();
8273 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
8274 assert_eq!(ty.span, crate::Span::new(F, 7, 11).unwrap());
8275 }
8276 _ => panic!("expected Let"),
8277 }
8278 }
8279
8280 #[test]
8281 fn const_missing_initializer_diagnoses() {
8282 let (ast, diags) = parse_str("const x;");
8283 assert_eq!(diags.len(), 1);
8284 assert_eq!(
8285 diags[0].message,
8286 "`const` declaration requires an initializer"
8287 );
8288 assert!(ast.top_level.is_empty());
8289 }
8290
8291 #[test]
8292 fn parse_const_object_shorthand_pattern() {
8293 let (ast, diags) = parse_str("const { a, b } = obj;");
8294 assert!(diags.is_empty(), "unexpected: {diags:?}");
8295 let stmt = single_stmt(&ast);
8296 match stmt.kind {
8297 crate::StmtKind::ConstPattern {
8298 binding:
8299 crate::Binding::Object {
8300 ref fields,
8301 ref rest,
8302 ..
8303 },
8304 ..
8305 } => {
8306 assert_eq!(fields.len(), 2);
8307 assert_eq!(fields[0].source.name, "a");
8308 assert_eq!(fields[0].local.name, "a");
8309 assert_eq!(fields[1].source.name, "b");
8310 assert_eq!(fields[1].local.name, "b");
8311 assert!(rest.is_none());
8312 }
8313 _ => panic!("expected ConstPattern Object, got {:?}", stmt.kind),
8314 }
8315 }
8316
8317 #[test]
8318 fn parse_const_object_pattern_keyword_source_key() {
8319 let (ast, diags) = parse_str("const { type: statusType, default: fallback } = obj;");
8320 assert!(diags.is_empty(), "unexpected: {diags:?}");
8321 let stmt = single_stmt(&ast);
8322 let crate::StmtKind::ConstPattern {
8323 binding: crate::Binding::Object { ref fields, .. },
8324 ..
8325 } = stmt.kind
8326 else {
8327 panic!("expected object const pattern");
8328 };
8329
8330 let names: Vec<(&str, &str)> = fields
8331 .iter()
8332 .map(|f| (f.source.name.as_str(), f.local.name.as_str()))
8333 .collect();
8334 assert_eq!(names, vec![("type", "statusType"), ("default", "fallback")]);
8335 }
8336
8337 #[test]
8338 fn parse_const_object_pattern_keyword_shorthand_rejected() {
8339 let (_ast, diags) = parse_str("const { default } = obj;");
8340 assert!(
8341 diags
8342 .iter()
8343 .any(|d| d.message == "expected `:` after keyword field name in object pattern"),
8344 "expected keyword-shorthand diagnostic, got: {diags:?}"
8345 );
8346 }
8347
8348 #[test]
8349 fn parse_const_object_renamed_pattern() {
8350 let (ast, diags) = parse_str("const { a: x, b: y } = obj;");
8351 assert!(diags.is_empty(), "unexpected: {diags:?}");
8352 match single_stmt(&ast).kind {
8353 crate::StmtKind::ConstPattern {
8354 binding: crate::Binding::Object { ref fields, .. },
8355 ..
8356 } => {
8357 assert_eq!(fields[0].source.name, "a");
8358 assert_eq!(fields[0].local.name, "x");
8359 assert_eq!(fields[1].source.name, "b");
8360 assert_eq!(fields[1].local.name, "y");
8361 }
8362 ref other => panic!("expected ConstPattern Object, got {other:?}"),
8363 }
8364 }
8365
8366 #[test]
8367 fn parse_const_object_with_rest() {
8368 let (ast, diags) = parse_str("const { a, ...rest } = obj;");
8369 assert!(diags.is_empty(), "unexpected: {diags:?}");
8370 match single_stmt(&ast).kind {
8371 crate::StmtKind::ConstPattern {
8372 binding:
8373 crate::Binding::Object {
8374 ref fields,
8375 ref rest,
8376 ..
8377 },
8378 ..
8379 } => {
8380 assert_eq!(fields.len(), 1);
8381 assert_eq!(fields[0].source.name, "a");
8382 let r = rest.as_ref().expect("expected rest");
8383 assert_eq!(r.name, "rest");
8384 }
8385 ref other => panic!("expected ConstPattern Object, got {other:?}"),
8386 }
8387 }
8388
8389 #[test]
8390 fn parse_const_array_pattern() {
8391 let (ast, diags) = parse_str("const [x, y] = arr;");
8392 assert!(diags.is_empty(), "unexpected: {diags:?}");
8393 match single_stmt(&ast).kind {
8394 crate::StmtKind::ConstPattern {
8395 binding:
8396 crate::Binding::Array {
8397 ref elems,
8398 ref rest,
8399 ..
8400 },
8401 ..
8402 } => {
8403 assert_eq!(elems.len(), 2);
8404 assert_eq!(elems[0].as_ref().unwrap().name, "x");
8405 assert_eq!(elems[1].as_ref().unwrap().name, "y");
8406 assert!(rest.is_none());
8407 }
8408 ref other => panic!("expected ConstPattern Array, got {other:?}"),
8409 }
8410 }
8411
8412 #[test]
8413 fn parse_const_array_with_hole_and_rest() {
8414 let (ast, diags) = parse_str("const [, x, ...rest] = arr;");
8415 assert!(diags.is_empty(), "unexpected: {diags:?}");
8416 match single_stmt(&ast).kind {
8417 crate::StmtKind::ConstPattern {
8418 binding:
8419 crate::Binding::Array {
8420 ref elems,
8421 ref rest,
8422 ..
8423 },
8424 ..
8425 } => {
8426 assert_eq!(elems.len(), 2);
8427 assert!(elems[0].is_none(), "first slot should be a hole");
8428 assert_eq!(elems[1].as_ref().unwrap().name, "x");
8429 assert_eq!(rest.as_ref().unwrap().name, "rest");
8430 }
8431 ref other => panic!("expected ConstPattern Array, got {other:?}"),
8432 }
8433 }
8434
8435 #[test]
8436 fn parse_let_object_pattern() {
8437 let (ast, diags) = parse_str("let { a, b } = obj;");
8438 assert!(diags.is_empty(), "unexpected: {diags:?}");
8439 assert!(matches!(
8440 single_stmt(&ast).kind,
8441 crate::StmtKind::LetPattern {
8442 binding: crate::Binding::Object { .. },
8443 ..
8444 }
8445 ));
8446 }
8447
8448 #[test]
8449 fn parse_function_param_object_pattern() {
8450 let src = "function f({ a, b }: T): void { return; }";
8451 let (ast, diags) = parse_str(src);
8452 assert!(diags.is_empty(), "unexpected: {diags:?}");
8453 match single_stmt(&ast).kind {
8454 crate::StmtKind::Function { ref params, .. } => {
8455 assert_eq!(params.len(), 1);
8456 let p = ¶ms[0];
8457 assert!(p.pattern.is_some(), "expected pattern param");
8458 assert_eq!(p.name.name, "", "pattern placeholder uses empty name");
8459 match p.pattern.as_ref().unwrap() {
8460 crate::Binding::Object { fields, .. } => {
8461 assert_eq!(fields.len(), 2);
8462 assert_eq!(fields[0].source.name, "a");
8463 }
8464 other => panic!("expected Object binding, got {other:?}"),
8465 }
8466 }
8467 ref other => panic!("expected Function, got {other:?}"),
8468 }
8469 }
8470
8471 #[test]
8472 fn parse_arrow_param_array_pattern() {
8473 let src = "const g = ([x, y]: number[]): number => x + y;";
8474 let (_ast, diags) = parse_str(src);
8475 assert!(diags.is_empty(), "unexpected: {diags:?}");
8476 }
8477
8478 #[test]
8479 fn parse_destructure_nested_rejected() {
8480 let (_ast, diags) = parse_str("const { a: { b } } = obj;");
8481 assert!(
8482 diags
8483 .iter()
8484 .any(|d| d.message.contains("nested destructuring")),
8485 "expected nested-destructuring diagnostic, got: {diags:?}",
8486 );
8487 }
8488
8489 #[test]
8490 fn parse_destructure_array_nested_rejected() {
8491 let (_ast, diags) = parse_str("const [[a]] = arr;");
8492 assert!(
8493 diags
8494 .iter()
8495 .any(|d| d.message.contains("nested destructuring")),
8496 "expected nested-destructuring diagnostic, got: {diags:?}",
8497 );
8498 }
8499
8500 #[test]
8501 fn parse_destructure_pattern_defaults() {
8502 let (ast, diags) = parse_str("const { a = 1, b: c = 2 } = obj;");
8503 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8504 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
8505 panic!("expected pattern")
8506 };
8507 let crate::Binding::Object { fields, .. } = binding else {
8508 panic!("expected object")
8509 };
8510 assert_eq!(fields.len(), 2);
8511 assert!(fields.iter().all(|field| field.default.is_some()));
8512 assert_eq!(fields[1].local.name, "c");
8513 }
8514
8515 #[test]
8516 fn parse_destructure_param_default() {
8517 let (ast, diags) = parse_str("function f({ a = 1 }: T = obj): void { return; }");
8518 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8519 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
8520 panic!("expected function")
8521 };
8522 assert!(params[0].default.is_some());
8523 assert!(params[0].pattern.is_some());
8524 }
8525
8526 #[test]
8527 fn parse_destructure_rest_not_last_rejected() {
8528 let (_ast, diags) = parse_str("const { ...rest, a } = obj;");
8529 assert!(
8530 diags
8531 .iter()
8532 .any(|d| d.message.contains("rest element must be the last element")),
8533 "expected rest-not-last diagnostic, got: {diags:?}",
8534 );
8535 }
8536
8537 #[test]
8538 fn parse_destructure_empty_object_rejected() {
8539 let (_ast, diags) = parse_str("const { } = obj;");
8540 assert!(
8541 diags
8542 .iter()
8543 .any(|d| d.message.contains("empty object destructuring pattern")),
8544 "expected empty-pattern diagnostic, got: {diags:?}",
8545 );
8546 }
8547
8548 #[test]
8549 fn parse_ternary_simple() {
8550 let (ast, diags) = parse_str("const x: number = a ? 1 : 2;");
8551 assert!(diags.is_empty(), "unexpected: {diags:?}");
8552 let stmt = single_stmt(&ast);
8553 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8554 panic!("expected Const, got {:?}", stmt.kind);
8555 };
8556 assert!(matches!(
8557 ast.try_expr(value).unwrap().kind,
8558 crate::ExprKind::Ternary { .. }
8559 ));
8560 }
8561
8562 #[test]
8563 fn parse_ternary_nested_right_assoc() {
8564 let (ast, diags) = parse_str("const x: number = a ? 1 : b ? 2 : 3;");
8565 assert!(diags.is_empty(), "unexpected: {diags:?}");
8566 let stmt = single_stmt(&ast);
8567 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8568 panic!("expected Const");
8569 };
8570 let crate::ExprKind::Ternary { else_, .. } = ast.try_expr(value).unwrap().kind else {
8571 panic!("expected outer Ternary");
8572 };
8573 assert!(matches!(
8574 ast.try_expr(else_).unwrap().kind,
8575 crate::ExprKind::Ternary { .. }
8576 ));
8577 }
8578
8579 #[test]
8580 fn parse_ternary_missing_colon_diagnoses() {
8581 let (_, diags) = parse_str("const x: number = a ? 1;");
8582 assert!(
8583 diags
8584 .iter()
8585 .any(|d| d.message.contains("expected `:` to complete ternary")),
8586 "expected colon diagnostic, got: {diags:?}",
8587 );
8588 }
8589
8590 #[test]
8591 fn parse_nullish_coalesce_binop() {
8592 let (ast, diags) = parse_str("const x: number = a ?? 1;");
8593 assert!(diags.is_empty(), "unexpected: {diags:?}");
8594 let stmt = single_stmt(&ast);
8595 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8596 panic!("expected Const");
8597 };
8598 match ast.try_expr(value).unwrap().kind {
8599 crate::ExprKind::Binary { op, .. } => {
8600 assert_eq!(op, crate::BinOp::NullishCoalesce);
8601 }
8602 ref other => panic!("expected Binary NullishCoalesce, got {other:?}"),
8603 }
8604 }
8605
8606 #[test]
8607 fn parse_nullish_left_associative() {
8608 let (ast, diags) = parse_str("const x: number = a ?? b ?? c;");
8609 assert!(diags.is_empty(), "unexpected: {diags:?}");
8610 let stmt = single_stmt(&ast);
8611 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8612 panic!("expected Const");
8613 };
8614 let crate::ExprKind::Binary { lhs, .. } = ast.try_expr(value).unwrap().kind else {
8615 panic!("expected outer Binary");
8616 };
8617 match ast.try_expr(lhs).unwrap().kind {
8618 crate::ExprKind::Binary { op, .. } => {
8619 assert_eq!(op, crate::BinOp::NullishCoalesce);
8620 }
8621 ref other => panic!("expected inner Binary, got {other:?}"),
8622 }
8623 }
8624
8625 #[test]
8626 fn parse_pow_is_right_associative() {
8627 let (ast, diags) = parse_str("const x: number = 2 ** 3 ** 2;");
8628 assert!(diags.is_empty(), "unexpected: {diags:?}");
8629 let stmt = single_stmt(&ast);
8630 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8631 panic!("expected Const");
8632 };
8633 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8634 panic!("expected outer Binary");
8635 };
8636 assert_eq!(op, crate::BinOp::Pow);
8637 match ast.try_expr(rhs).unwrap().kind {
8638 crate::ExprKind::Binary { op: inner_op, .. } => {
8639 assert_eq!(inner_op, crate::BinOp::Pow);
8640 }
8641 ref other => panic!("expected inner Pow Binary on RHS, got {other:?}"),
8642 }
8643 }
8644
8645 #[test]
8646 fn parse_pow_higher_than_multiply() {
8647 let (ast, diags) = parse_str("const x: number = 2 * 3 ** 2;");
8648 assert!(diags.is_empty(), "unexpected: {diags:?}");
8649 let stmt = single_stmt(&ast);
8650 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8651 panic!("expected Const");
8652 };
8653 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8654 panic!("expected outer Binary");
8655 };
8656 assert_eq!(op, crate::BinOp::Mul);
8657 match ast.try_expr(rhs).unwrap().kind {
8658 crate::ExprKind::Binary { op: inner_op, .. } => {
8659 assert_eq!(inner_op, crate::BinOp::Pow);
8660 }
8661 ref other => panic!("expected Pow on the RHS of *, got {other:?}"),
8662 }
8663 }
8664
8665 #[test]
8666 fn parse_compound_assign_ident() {
8667 let (ast, diags) = parse_str("function main(): void { let x: number = 0; x += 5; }");
8668 assert!(diags.is_empty(), "unexpected: {diags:?}");
8669 let func = ast.top_level.iter().find_map(|sid| {
8670 if let crate::StmtKind::Function { body, .. } = &ast.try_stmt(*sid).unwrap().kind {
8671 Some(*body)
8672 } else {
8673 None
8674 }
8675 });
8676 let crate::StmtKind::Block(stmts) = &ast.try_stmt(func.expect("main")).unwrap().kind else {
8677 panic!("expected function body to be a block");
8678 };
8679 let target = stmts
8680 .iter()
8681 .find_map(|sid| match &ast.try_stmt(*sid).unwrap().kind {
8682 crate::StmtKind::CompoundAssign { target, op, .. } => {
8683 Some((target.name.clone(), *op))
8684 }
8685 _ => None,
8686 });
8687 let (name, op) = target.expect("expected a CompoundAssign in the body");
8688 assert_eq!(name, "x");
8689 assert_eq!(op, crate::BinOp::Add);
8690 }
8691
8692 #[test]
8693 fn parse_nullish_mixed_with_or_rejected() {
8694 let (_, diags) = parse_str("const x: number = a || b ?? c;");
8695 assert!(
8696 diags
8697 .iter()
8698 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8699 "expected mixing diagnostic, got: {diags:?}",
8700 );
8701 }
8702
8703 #[test]
8704 fn parse_nullish_mixed_with_and_rejected() {
8705 let (_, diags) = parse_str("const x: number = a && b ?? c;");
8706 assert!(
8707 diags
8708 .iter()
8709 .any(|d| d.message.contains("mixing `??` with `||` / `&&`")),
8710 "expected mixing diagnostic, got: {diags:?}",
8711 );
8712 }
8713
8714 #[test]
8715 fn parse_nullish_followed_by_logical_rejected() {
8716 for (source, help) in [
8717 (
8718 "const x: number = a ?? b || c;",
8719 "`(a ?? b) || c` or `a ?? (b || c)`",
8720 ),
8721 (
8722 "const x: number = a ?? b && c;",
8723 "`(a ?? b) && c` or `a ?? (b && c)`",
8724 ),
8725 (
8726 "const x: number = a ?? b ?? c || d;",
8727 "`(a ?? b) || c` or `a ?? (b || c)`",
8728 ),
8729 (
8731 "const x: number = a || b ?? c || d;",
8732 "`(a || b) ?? c` or `a || (b ?? c)`",
8733 ),
8734 ] {
8735 let (_, diags) = parse_str(source);
8736 assert_eq!(diags.len(), 1, "diags for {source:?}: {diags:?}");
8737 assert!(
8738 diags[0].message.contains("mixing `??` with `||` / `&&`"),
8739 "message for {source:?}: {diags:?}",
8740 );
8741 assert!(
8742 diags[0].help.iter().any(|h| h.contains(help)),
8743 "help for {source:?}: {diags:?}",
8744 );
8745 }
8746 }
8747
8748 #[test]
8749 fn parse_nullish_operand_keeps_tighter_operators() {
8750 let (ast, diags) = parse_str("const x: boolean = a ?? b == c;");
8752 assert!(diags.is_empty(), "unexpected: {diags:?}");
8753 let stmt = single_stmt(&ast);
8754 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8755 panic!("expected Const");
8756 };
8757 let crate::ExprKind::Binary { op, rhs, .. } = ast.try_expr(value).unwrap().kind else {
8758 panic!("expected outer Binary");
8759 };
8760 assert_eq!(op, crate::BinOp::NullishCoalesce);
8761 assert!(matches!(
8762 ast.try_expr(rhs).unwrap().kind,
8763 crate::ExprKind::Binary {
8764 op: crate::BinOp::Eq,
8765 ..
8766 }
8767 ));
8768 }
8769
8770 #[test]
8771 fn parse_nullish_with_parens_ok() {
8772 let (_, diags) = parse_str("const x: number = (a || b) ?? c;");
8773 assert!(diags.is_empty(), "unexpected: {diags:?}");
8774 }
8775
8776 #[test]
8777 fn parse_optional_field_chain() {
8778 let (ast, diags) = parse_str("const x: number = a?.b;");
8779 assert!(diags.is_empty(), "unexpected: {diags:?}");
8780 let stmt = single_stmt(&ast);
8781 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8782 panic!("expected Const");
8783 };
8784 match ast.try_expr(value).unwrap().kind {
8785 crate::ExprKind::OptionalChain { ref parts, .. } => {
8786 assert_eq!(parts.len(), 1);
8787 match &parts[0] {
8788 crate::ChainPart::Field { name, optional, .. } => {
8789 assert_eq!(name.name, "b");
8790 assert!(*optional);
8791 }
8792 other => panic!("expected Field, got {other:?}"),
8793 }
8794 }
8795 ref other => panic!("expected OptionalChain, got {other:?}"),
8796 }
8797 }
8798
8799 #[test]
8800 fn parse_optional_chain_continues_after_first_dot() {
8801 let (ast, diags) = parse_str("const x: number = a?.b.c;");
8802 assert!(diags.is_empty(), "unexpected: {diags:?}");
8803 let stmt = single_stmt(&ast);
8804 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8805 panic!("expected Const");
8806 };
8807 match ast.try_expr(value).unwrap().kind {
8808 crate::ExprKind::OptionalChain { ref parts, .. } => {
8809 assert_eq!(parts.len(), 2);
8810 if let crate::ChainPart::Field { optional, .. } = &parts[0] {
8811 assert!(*optional);
8812 }
8813 if let crate::ChainPart::Field { optional, .. } = &parts[1] {
8814 assert!(!*optional);
8815 }
8816 }
8817 ref other => panic!("expected OptionalChain, got {other:?}"),
8818 }
8819 }
8820
8821 #[test]
8822 fn parse_optional_chain_multiple_short_circuits() {
8823 let (ast, diags) = parse_str("const x: number = a?.b?.c;");
8824 assert!(diags.is_empty(), "unexpected: {diags:?}");
8825 let stmt = single_stmt(&ast);
8826 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8827 panic!("expected Const");
8828 };
8829 let crate::ExprKind::OptionalChain { parts, .. } = &ast.try_expr(value).unwrap().kind
8830 else {
8831 panic!("expected OptionalChain");
8832 };
8833 assert_eq!(parts.len(), 2);
8834 for p in parts {
8835 if let crate::ChainPart::Field { optional, .. } = p {
8836 assert!(*optional, "both parts should be optional");
8837 }
8838 }
8839 }
8840
8841 #[test]
8842 fn parse_optional_call_and_index() {
8843 let (_, diags) = parse_str("const x: number = f?.();");
8844 assert!(diags.is_empty(), "unexpected: {diags:?}");
8845 let (_, diags) = parse_str("const y: number = arr?.[0];");
8846 assert!(diags.is_empty(), "unexpected: {diags:?}");
8847 }
8848
8849 #[test]
8850 fn parse_optional_chain_terminates_at_questionmark_for_ternary() {
8851 let (ast, diags) = parse_str("const x: number = a?.b ? c : d;");
8852 assert!(diags.is_empty(), "unexpected: {diags:?}");
8853 let stmt = single_stmt(&ast);
8854 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8855 panic!("expected Const");
8856 };
8857 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8858 panic!(
8859 "expected top-level Ternary, got {:?}",
8860 ast.try_expr(value).unwrap().kind
8861 );
8862 };
8863 assert!(matches!(
8864 ast.try_expr(cond).unwrap().kind,
8865 crate::ExprKind::OptionalChain { .. }
8866 ));
8867 }
8868
8869 #[test]
8870 fn parse_nullish_below_ternary() {
8871 let (ast, diags) = parse_str("const x: number = a ?? b ? c : d;");
8872 assert!(diags.is_empty(), "unexpected: {diags:?}");
8873 let stmt = single_stmt(&ast);
8874 let crate::StmtKind::Const { value, .. } = stmt.kind else {
8875 panic!("expected Const");
8876 };
8877 let crate::ExprKind::Ternary { cond, .. } = ast.try_expr(value).unwrap().kind else {
8878 panic!("expected top-level Ternary");
8879 };
8880 match ast.try_expr(cond).unwrap().kind {
8881 crate::ExprKind::Binary { op, .. } => {
8882 assert_eq!(op, crate::BinOp::NullishCoalesce);
8883 }
8884 ref other => panic!("expected Binary ??, got {other:?}"),
8885 }
8886 }
8887
8888 #[test]
8889 fn let_missing_initializer_diagnoses() {
8890 let (ast, diags) = parse_str("let x;");
8891 assert_eq!(diags.len(), 1);
8892 assert_eq!(
8893 diags[0].message,
8894 "`let` declaration requires an initializer"
8895 );
8896 assert!(ast.top_level.is_empty());
8897 }
8898
8899 #[test]
8900 fn missing_identifier_after_let_diagnoses() {
8901 let (ast, diags) = parse_str("let = 1;");
8902 assert_eq!(diags.len(), 1);
8903 assert_eq!(diags[0].message, "expected identifier after `let`/`const`");
8904 assert!(ast.top_level.is_empty());
8905 }
8906
8907 #[test]
8908 fn missing_type_after_colon_diagnoses() {
8909 let (ast, diags) = parse_str("let x: = 1;");
8910 assert_eq!(diags.len(), 1);
8911 assert_eq!(diags[0].message, "expected type");
8912 assert!(ast.top_level.is_empty());
8913 }
8914
8915 #[test]
8916 fn missing_semicolon_after_declaration_diagnoses() {
8917 let (ast, diags) = parse_str("let x = 1 y;");
8918 assert_eq!(diags.len(), 1);
8919 assert_eq!(diags[0].message, "expected `;` after declaration");
8920 assert!(ast.top_level.is_empty());
8921 }
8922
8923 #[test]
8924 fn expression_statements_still_parse() {
8925 let (ast, diags) = parse_str("42;");
8926 assert!(diags.is_empty());
8927 assert_eq!(ast.top_level.len(), 1);
8928 }
8929
8930 #[test]
8931 fn snapshot_mixed_declarations() {
8932 let (ast, diags) =
8933 parse_str(r#"let x = 1; const y: string = "hi"; let z: number = 1 + 2;"#);
8934 assert!(diags.is_empty());
8935 assert_eq!(ast.top_level.len(), 3);
8936 insta::assert_debug_snapshot!(ast);
8937 }
8938
8939 #[test]
8940 fn parse_function_typical() {
8941 let (ast, diags) = parse_str("function f(a: number, b: string): boolean { }");
8942 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8943 let stmt = single_stmt(&ast);
8944 match stmt.kind {
8945 crate::StmtKind::Function {
8946 ref name,
8947 ref generics,
8948 ref params,
8949 ref return_type,
8950 body,
8951 ..
8952 } => {
8953 assert_eq!(name.name, "f");
8954 assert_eq!(name.span, crate::Span::new(F, 9, 10).unwrap());
8955 assert!(generics.is_empty());
8956 assert_eq!(params.len(), 2);
8957 assert_eq!(params[0].name.name, "a");
8958 assert_eq!(params[0].name.span, crate::Span::new(F, 11, 12).unwrap());
8959 let p0_ty = params[0]
8960 .ty
8961 .as_ref()
8962 .expect("function-decl param requires annotation");
8963 assert!(matches!(p0_ty.kind, crate::TypeAnnotationKind::Name { .. }));
8964 assert_eq!(p0_ty.span, crate::Span::new(F, 14, 20).unwrap()); assert_eq!(params[1].name.name, "b");
8966 assert_eq!(params[1].name.span, crate::Span::new(F, 22, 23).unwrap());
8967 let p1_ty = params[1]
8968 .ty
8969 .as_ref()
8970 .expect("function-decl param requires annotation");
8971 assert_eq!(p1_ty.span, crate::Span::new(F, 25, 31).unwrap()); let return_type = return_type.as_ref().expect("plain return type");
8973 assert!(matches!(
8974 return_type.kind,
8975 crate::TypeAnnotationKind::Name { .. }
8976 ));
8977 assert_eq!(return_type.span, crate::Span::new(F, 34, 41).unwrap()); let block = ast.try_stmt(body).unwrap();
8979 assert!(matches!(block.kind, crate::StmtKind::Block(ref v) if v.is_empty()));
8980 }
8981 _ => panic!("expected Function"),
8982 }
8983 }
8984
8985 #[test]
8986 fn parse_function_with_doc_comment() {
8987 let (ast, diags) = parse_str(
8988 "/**\n * Sum two numbers.\n * @param a First.\n * @param b Second.\n */\nfunction add(a: number, b: number): number { return a + b; }",
8989 );
8990 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
8991 let stmt = single_stmt(&ast);
8992 match &stmt.kind {
8993 crate::StmtKind::Function { doc, .. } => {
8994 let d = doc.as_ref().expect("doc attached");
8995 assert_eq!(d.summary, "Sum two numbers.");
8996 assert_eq!(d.params.len(), 2);
8997 assert_eq!(d.params[0].name, "a");
8998 assert_eq!(d.params[1].name, "b");
8999 }
9000 _ => panic!("expected Function"),
9001 }
9002 }
9003
9004 #[test]
9005 fn parse_interface_member_docs() {
9006 let (ast, diags) = parse_str(
9007 "interface Foo {\n /** First method. */ first(): void;\n /** A field. */ field: number;\n}",
9008 );
9009 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9010 let stmt = single_stmt(&ast);
9011 match &stmt.kind {
9012 crate::StmtKind::InterfaceDecl { members, .. } => {
9013 assert_eq!(members.len(), 2);
9014 match &members[0] {
9015 crate::InterfaceMember::Method { doc, .. } => {
9016 assert_eq!(doc.as_ref().unwrap().summary, "First method.");
9017 }
9018 _ => panic!("expected Method first"),
9019 }
9020 match &members[1] {
9021 crate::InterfaceMember::Property { doc, .. } => {
9022 assert_eq!(doc.as_ref().unwrap().summary, "A field.");
9023 }
9024 _ => panic!("expected Property second"),
9025 }
9026 }
9027 _ => panic!("expected InterfaceDecl"),
9028 }
9029 }
9030
9031 #[test]
9032 fn parse_numeric_enum_all_implicit() {
9033 let (ast, diags) = parse_str("enum Direction { Up, Down, Left, Right }");
9034 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9035 let stmt = single_stmt(&ast);
9036 match &stmt.kind {
9037 crate::StmtKind::EnumDecl { name, members, doc } => {
9038 assert_eq!(name.name, "Direction");
9039 assert!(doc.is_none());
9040 assert_eq!(members.len(), 4);
9041 for (m, expected) in members.iter().zip(["Up", "Down", "Left", "Right"]) {
9042 assert_eq!(m.name.name, expected);
9043 assert!(m.value.is_none(), "expected no initializer on {expected}");
9044 }
9045 }
9046 _ => panic!("expected EnumDecl"),
9047 }
9048 }
9049
9050 #[test]
9051 fn parse_string_enum_all_explicit() {
9052 let (ast, diags) =
9053 parse_str("enum Status { Active = \"active\", Inactive = \"inactive\" }");
9054 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9055 let stmt = single_stmt(&ast);
9056 match &stmt.kind {
9057 crate::StmtKind::EnumDecl { name, members, .. } => {
9058 assert_eq!(name.name, "Status");
9059 assert_eq!(members.len(), 2);
9060 match &members[0].value {
9061 Some(crate::EnumInitializer::String { value, .. }) => {
9062 assert_eq!(value, "active");
9063 }
9064 other => panic!("expected String init, got {other:?}"),
9065 }
9066 match &members[1].value {
9067 Some(crate::EnumInitializer::String { value, .. }) => {
9068 assert_eq!(value, "inactive");
9069 }
9070 other => panic!("expected String init, got {other:?}"),
9071 }
9072 }
9073 _ => panic!("expected EnumDecl"),
9074 }
9075 }
9076
9077 #[test]
9078 fn parse_numeric_enum_with_negative_init() {
9079 let (ast, diags) = parse_str("enum D { Up = 1, Down = -1 }");
9080 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9081 let stmt = single_stmt(&ast);
9082 match &stmt.kind {
9083 crate::StmtKind::EnumDecl { members, .. } => {
9084 assert_eq!(members.len(), 2);
9085 match &members[0].value {
9086 Some(crate::EnumInitializer::Number { value, .. }) => {
9087 assert_eq!(*value, 1.0);
9088 }
9089 other => panic!("expected Number init, got {other:?}"),
9090 }
9091 match &members[1].value {
9092 Some(crate::EnumInitializer::Number { value, .. }) => {
9093 assert_eq!(*value, -1.0);
9094 }
9095 other => panic!("expected Number init, got {other:?}"),
9096 }
9097 }
9098 _ => panic!("expected EnumDecl"),
9099 }
9100 }
9101
9102 #[test]
9103 fn parse_enum_mixed_kinds_is_parse_error() {
9104 let (_ast, diags) = parse_str("enum Mix { A = 1, B = \"two\" }");
9105 assert!(
9106 diags
9107 .iter()
9108 .any(|d| d.message.contains("mixed numeric and string")),
9109 "expected mixed-kind diagnostic, got: {diags:?}"
9110 );
9111 }
9112
9113 #[test]
9114 fn parse_enum_member_docs() {
9115 let (ast, diags) = parse_str(
9116 "enum D {\n /** The first one. */ A = 1,\n /** The second one. */ B = 2,\n}",
9117 );
9118 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9119 let stmt = single_stmt(&ast);
9120 match &stmt.kind {
9121 crate::StmtKind::EnumDecl { members, .. } => {
9122 assert_eq!(members.len(), 2);
9123 assert_eq!(
9124 members[0].doc.as_ref().expect("first doc").summary,
9125 "The first one."
9126 );
9127 assert_eq!(
9128 members[1].doc.as_ref().expect("second doc").summary,
9129 "The second one."
9130 );
9131 }
9132 _ => panic!("expected EnumDecl"),
9133 }
9134 }
9135
9136 #[test]
9137 fn parse_enum_trailing_comma_ok() {
9138 let (ast, diags) = parse_str("enum D { A, B, }");
9139 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9140 let stmt = single_stmt(&ast);
9141 match &stmt.kind {
9142 crate::StmtKind::EnumDecl { members, .. } => assert_eq!(members.len(), 2),
9143 _ => panic!("expected EnumDecl"),
9144 }
9145 }
9146
9147 #[test]
9148 fn parse_function_empty_params() {
9149 let (ast, diags) = parse_str("function noop(): void { }");
9150 assert!(diags.is_empty());
9151 let stmt = single_stmt(&ast);
9152 match stmt.kind {
9153 crate::StmtKind::Function { ref params, .. } => assert!(params.is_empty()),
9154 _ => panic!("expected Function"),
9155 }
9156 }
9157
9158 #[test]
9159 fn parse_generic_function_single_param() {
9160 let (ast, diags) = parse_str("function identity<T>(x: T): T { return x; }");
9161 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9162 let stmt = single_stmt(&ast);
9163 match stmt.kind {
9164 crate::StmtKind::Function {
9165 ref name,
9166 ref generics,
9167 ref params,
9168 ..
9169 } => {
9170 assert_eq!(name.name, "identity");
9171 assert_eq!(generics.len(), 1);
9172 assert_eq!(generics[0].name, "T");
9173 assert_eq!(params.len(), 1);
9174 assert_eq!(params[0].name.name, "x");
9175 }
9176 _ => panic!("expected Function"),
9177 }
9178 }
9179
9180 #[test]
9181 fn parse_generic_function_multiple_params() {
9182 let (ast, diags) = parse_str("function pair<T, U>(a: T, b: U): T { return a; }");
9183 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9184 let stmt = single_stmt(&ast);
9185 match stmt.kind {
9186 crate::StmtKind::Function { ref generics, .. } => {
9187 assert_eq!(generics.len(), 2);
9188 assert_eq!(generics[0].name, "T");
9189 assert_eq!(generics[1].name, "U");
9190 }
9191 _ => panic!("expected Function"),
9192 }
9193 }
9194
9195 #[test]
9196 fn parse_non_generic_function_has_empty_generics() {
9197 let (ast, diags) = parse_str("function noop(): void { }");
9198 assert!(diags.is_empty());
9199 let stmt = single_stmt(&ast);
9200 match stmt.kind {
9201 crate::StmtKind::Function { ref generics, .. } => assert!(generics.is_empty()),
9202 _ => panic!("expected Function"),
9203 }
9204 }
9205
9206 #[test]
9207 fn parse_empty_generic_list_diagnoses() {
9208 let (_ast, diags) = parse_str("function f<>(): void { }");
9209 assert!(
9210 diags
9211 .iter()
9212 .any(|d| d.message.contains("empty generic parameter list")),
9213 "expected empty-generic-list diagnostic, got: {diags:?}",
9214 );
9215 }
9216
9217 #[test]
9218 fn parse_trailing_comma_in_generic_list_ok() {
9219 let (ast, diags) = parse_str("function f<T,>(): void { }");
9220 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9221 match single_stmt(&ast).kind {
9222 crate::StmtKind::Function { ref generics, .. } => assert_eq!(generics.len(), 1),
9223 _ => panic!("expected Function"),
9224 }
9225 }
9226
9227 #[test]
9228 fn parse_generic_call_single_type_arg() {
9229 let (ast, diags) = parse_str("identity<number>(42);");
9230 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9231 let stmt = single_stmt(&ast);
9232 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9233 panic!("expected Expr stmt");
9234 };
9235 match &ast.try_expr(call_id).unwrap().kind {
9236 crate::ExprKind::Call {
9237 type_args, args, ..
9238 } => {
9239 let ta = type_args.as_ref().expect("type_args set for generic call");
9240 assert_eq!(ta.len(), 1);
9241 assert_eq!(args.len(), 1);
9242 }
9243 other => panic!("expected Call, got {other:?}"),
9244 }
9245 }
9246
9247 #[test]
9248 fn parse_generic_call_trailing_comma_ok() {
9249 let (ast, diags) = parse_str("identity<number,>(42);");
9250 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9251 let stmt = single_stmt(&ast);
9252 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9253 panic!("expected Expr stmt");
9254 };
9255 match &ast.try_expr(call_id).unwrap().kind {
9256 crate::ExprKind::Call {
9257 type_args, args, ..
9258 } => {
9259 assert_eq!(type_args.as_ref().expect("type_args set").len(), 1);
9260 assert_eq!(args.len(), 1);
9261 }
9262 other => panic!("expected Call, got {other:?}"),
9263 }
9264 }
9265
9266 #[test]
9267 fn parse_generic_call_multi_type_args() {
9268 let (ast, diags) = parse_str("pair<number, string>(1, \"x\");");
9269 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9270 let stmt = single_stmt(&ast);
9271 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9272 panic!("expected Expr stmt");
9273 };
9274 match &ast.try_expr(call_id).unwrap().kind {
9275 crate::ExprKind::Call { type_args, .. } => {
9276 let ta = type_args.as_ref().expect("type_args set for generic call");
9277 assert_eq!(ta.len(), 2);
9278 }
9279 other => panic!("expected Call, got {other:?}"),
9280 }
9281 }
9282
9283 #[test]
9284 fn parse_comparison_chain_unaffected_by_generic_lookahead() {
9285 let (ast, diags) = parse_str("let z = a < b > c;");
9286 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9287 assert!(
9288 !ast_contains_call_with_type_args(&ast),
9289 "should not have parsed any generic call",
9290 );
9291 }
9292
9293 #[test]
9294 fn parse_lt_with_paren_rhs_is_comparison_not_generic() {
9295 let (ast, diags) = parse_str("let z = a < (b);");
9296 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9297 assert!(!ast_contains_call_with_type_args(&ast));
9298 }
9299
9300 #[test]
9301 fn parse_generic_call_no_args() {
9302 let (ast, diags) = parse_str("none<number>();");
9303 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9304 let stmt = single_stmt(&ast);
9305 let crate::StmtKind::Expr(call_id) = stmt.kind else {
9306 panic!("expected Expr stmt");
9307 };
9308 match &ast.try_expr(call_id).unwrap().kind {
9309 crate::ExprKind::Call {
9310 type_args, args, ..
9311 } => {
9312 assert_eq!(type_args.as_ref().unwrap().len(), 1);
9313 assert!(args.is_empty());
9314 }
9315 other => panic!("expected Call, got {other:?}"),
9316 }
9317 }
9318
9319 fn ast_contains_call_with_type_args(ast: &crate::Ast) -> bool {
9320 for id in 0..ast.top_level.len() {
9321 let stmt = ast.try_stmt(ast.top_level[id]).unwrap();
9322 if walk_stmt_for_typed_call(ast, stmt) {
9323 return true;
9324 }
9325 }
9326 false
9327 }
9328
9329 fn walk_stmt_for_typed_call(ast: &crate::Ast, stmt: &crate::Stmt) -> bool {
9330 match &stmt.kind {
9331 crate::StmtKind::Let { value, .. } | crate::StmtKind::Const { value, .. } => {
9332 walk_expr_for_typed_call(ast, *value)
9333 }
9334 crate::StmtKind::Expr(e) => walk_expr_for_typed_call(ast, *e),
9335 crate::StmtKind::Block(stmts) => stmts
9336 .iter()
9337 .any(|sid| walk_stmt_for_typed_call(ast, ast.try_stmt(*sid).unwrap())),
9338 _ => false,
9339 }
9340 }
9341
9342 fn walk_expr_for_typed_call(ast: &crate::Ast, id: crate::ExprId) -> bool {
9343 match &ast.try_expr(id).unwrap().kind {
9344 crate::ExprKind::Call {
9345 type_args: Some(_), ..
9346 } => true,
9347 crate::ExprKind::Call { callee, args, .. } => {
9348 walk_expr_for_typed_call(ast, *callee)
9349 || args.iter().any(|a| walk_expr_for_typed_call(ast, *a))
9350 }
9351 crate::ExprKind::Binary { lhs, rhs, .. } => {
9352 walk_expr_for_typed_call(ast, *lhs) || walk_expr_for_typed_call(ast, *rhs)
9353 }
9354 crate::ExprKind::Unary { operand, .. } => walk_expr_for_typed_call(ast, *operand),
9355 crate::ExprKind::Paren(e) => walk_expr_for_typed_call(ast, *e),
9356 crate::ExprKind::FieldAccess { receiver, .. } => {
9357 walk_expr_for_typed_call(ast, *receiver)
9358 }
9359 crate::ExprKind::IndexAccess { receiver, index } => {
9360 walk_expr_for_typed_call(ast, *receiver) || walk_expr_for_typed_call(ast, *index)
9361 }
9362 _ => false,
9363 }
9364 }
9365
9366 #[test]
9367 fn parse_function_body_with_statements() {
9368 let (ast, diags) = parse_str("function g(x: number): number { let y = x; y; }");
9369 assert!(diags.is_empty());
9370 let stmt = single_stmt(&ast);
9371 match stmt.kind {
9372 crate::StmtKind::Function { body, .. } => {
9373 let block = ast.try_stmt(body).unwrap();
9374 match block.kind {
9375 crate::StmtKind::Block(ref v) => assert_eq!(v.len(), 2),
9376 _ => panic!("expected Block"),
9377 }
9378 }
9379 _ => panic!("expected Function"),
9380 }
9381 }
9382
9383 #[test]
9384 fn function_missing_return_type_diagnoses() {
9385 let (ast, diags) = parse_str("function f() { }");
9386 assert!(!diags.is_empty());
9387 assert_eq!(diags[0].message, "expected `:` and return type");
9388 assert!(ast.top_level.is_empty());
9389 }
9390
9391 #[test]
9392 fn method_missing_return_type_still_reads_the_body() {
9393 let (_ast, diags) = parse_str(
9394 "class Box {\n async run() {\n const x = await f();\n return x;\n }\n ok(): void {}\n}",
9395 );
9396 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9397 assert_eq!(
9398 messages,
9399 [
9400 "`async` functions are not supported",
9401 "expected `:` and return type",
9402 "`await` is not supported"
9403 ],
9404 "{diags:?}"
9405 );
9406 assert_eq!(
9407 diags[1].help,
9408 ["write the return type: `run(…): T { … }`, with `void` when it returns nothing"]
9409 );
9410 }
9411
9412 #[test]
9413 fn an_unclosed_method_body_leaves_the_next_declaration_alone() {
9414 let (ast, diags) =
9415 parse_str("class Box {\n a() { return 1;\n}\nfunction main(): number { return 0; }");
9416 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9417 assert_eq!(
9418 messages,
9419 [
9420 "expected `:` and return type",
9421 "expected `}` to close class"
9422 ],
9423 "{diags:?}"
9424 );
9425 assert_eq!(ast.top_level.len(), 1, "`main` is still declared");
9426 }
9427
9428 #[test]
9429 fn every_declaration_after_an_unclosed_method_body_is_kept() {
9430 for next in [
9431 "class B { ok(): number { return 2; } }",
9432 "interface I { a: number; }",
9433 "enum E { A }",
9434 "type X = number;",
9435 "import * as fs from \"submilli:fs\";",
9436 "async function g(): number { return 1; }",
9437 ] {
9438 let source = format!(
9439 "class A {{\n bad() {{\n return 1;\n}}\n{next}\nfunction main(): void {{}}"
9440 );
9441 let (ast, diags) = parse_str(&source);
9442 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9443 let expected_async = next.starts_with("async");
9444 let mut expected = vec![
9445 "expected `:` and return type",
9446 "expected `}` to close class",
9447 ];
9448 if expected_async {
9449 expected.push("`async` functions are not supported");
9450 }
9451 assert_eq!(messages, expected, "{next}: {diags:?}");
9452 assert_eq!(
9453 ast.top_level.len(),
9454 2,
9455 "{next} and `main` are kept: {diags:?}"
9456 );
9457 }
9458 }
9459
9460 #[test]
9461 fn a_failed_statement_leaves_the_next_declaration_to_be_parsed() {
9462 for (source, kept) in [
9463 ("let x = 1 +\nimport { exists } from \"submilli:fs\";", 1),
9464 ("const y =\nclass C {}\nfunction main(): void {}", 2),
9465 (
9466 "interface I {\n a: number;\nimport * as fs from \"submilli:fs\";",
9467 1,
9468 ),
9469 ] {
9470 let (ast, diags) = parse_str(source);
9471 assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9472 assert_eq!(ast.top_level.len(), kept, "{source}: {diags:?}");
9473 }
9474 }
9475
9476 #[test]
9477 fn a_dynamic_import_after_a_failed_statement_reports_once() {
9478 for source in [
9479 "const m = import(\"submilli:fs\");\nclass C {}",
9480 "const d = import.meta.dirname;\nclass C {}",
9481 ] {
9482 let (_ast, diags) = parse_str(source);
9483 assert_eq!(diags.len(), 1, "{source}: {diags:?}");
9484 }
9485 }
9486
9487 #[test]
9488 fn members_named_like_declarations_after_a_bad_method_stay_members() {
9489 let (_ast, diags) = parse_str(
9490 "class A {\n bad() {}\n type: number = 1;\n function(): void {}\n declare(): void {}\n async(): void {}\n}",
9491 );
9492 assert_eq!(diags.len(), 1, "{diags:?}");
9493 }
9494
9495 #[test]
9496 fn every_member_after_a_bad_method_is_still_read() {
9497 let (_ast, diags) = parse_str(
9498 "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}",
9499 );
9500 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9501 assert_eq!(messages, ["expected `:` and return type"; 3], "{diags:?}");
9502 }
9503
9504 #[test]
9505 fn a_member_failing_after_a_type_literal_reports_once() {
9506 let (_ast, diags) = parse_str("class A {\n x: { a: number } 5\n}");
9507 assert_eq!(diags.len(), 1, "{diags:?}");
9508 }
9509
9510 #[test]
9511 fn multiple_declarators_name_the_split() {
9512 let (_ast, diags) = parse_str("let a = 0, b = 0;");
9513 assert_eq!(diags.len(), 1, "{diags:?}");
9514 assert_eq!(diags[0].message, "one declaration declares one variable");
9515 assert_eq!(
9516 diags[0].help,
9517 [
9518 "declare one variable per statement: `let a = …; let b = …;`; in a `for (…)` header, declare `b` before the loop"
9519 ]
9520 );
9521 let (_ast, diags) = parse_str("for (let i = 0, j = 10; i < j; i++) {}");
9522 assert_eq!(
9523 diags[0].message, "one declaration declares one variable",
9524 "{diags:?}"
9525 );
9526 assert!(
9527 diags[0].help[0].ends_with("declare `j` before the loop"),
9528 "{diags:?}"
9529 );
9530 }
9531
9532 #[test]
9533 fn missing_return_type_still_reads_the_body() {
9534 let (_ast, diags) = parse_str(
9535 "async function main() {\n const x = await f();\n if (x) { return 1; }\n return 2;\n}\nfunction g(): void {}",
9536 );
9537 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
9538 assert_eq!(
9539 messages,
9540 [
9541 "`async` functions are not supported",
9542 "expected `:` and return type",
9543 "`await` is not supported"
9544 ],
9545 "{diags:?}"
9546 );
9547 assert_eq!(
9548 diags[1].help,
9549 [
9550 "write the return type: `function main(…): T { … }`, with `void` when it returns nothing"
9551 ]
9552 );
9553 }
9554
9555 #[test]
9556 fn function_missing_param_type_diagnoses() {
9557 let (ast, diags) = parse_str("function f(a): void { }");
9558 assert!(!diags.is_empty());
9559 assert_eq!(diags[0].message, "parameter requires a type annotation");
9560 assert!(ast.top_level.is_empty());
9561 }
9562
9563 #[test]
9564 fn function_missing_name_diagnoses() {
9565 let (ast, diags) = parse_str("function (x: number): void { }");
9566 assert!(!diags.is_empty());
9567 assert_eq!(diags[0].message, "expected function name");
9568 assert!(ast.top_level.is_empty());
9569 }
9570
9571 #[test]
9572 fn function_trailing_param_comma_ok() {
9573 let (ast, diags) = parse_str("function f(a: number,): void { }");
9574 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9575 match single_stmt(&ast).kind {
9576 crate::StmtKind::Function { ref params, .. } => assert_eq!(params.len(), 1),
9577 _ => panic!("expected Function"),
9578 }
9579 }
9580
9581 #[test]
9582 fn function_missing_open_brace_diagnoses() {
9583 let (ast, diags) = parse_str("function f(): void");
9584 assert_eq!(diags.len(), 1);
9585 assert_eq!(diags[0].message, "expected `{`");
9586 assert!(ast.top_level.is_empty());
9587 }
9588
9589 #[test]
9590 fn snapshot_function_declaration() {
9591 let (ast, diags) =
9592 parse_str("function add(a: number, b: number): number { let sum = a + b; sum; }");
9593 assert!(diags.is_empty());
9594 insta::assert_debug_snapshot!(ast);
9595 }
9596
9597 #[test]
9598 fn rest_param_basic_parses() {
9599 let (_, diags) = parse_str("function sum(...nums: number[]): number { return 0; }");
9600 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9601 }
9602
9603 #[test]
9604 fn rest_param_with_fixed_prefix_parses() {
9605 let (_, diags) =
9606 parse_str("function tag(label: string, ...vals: number[]): string { return label; }");
9607 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9608 }
9609
9610 #[test]
9611 fn rest_param_in_arrow_parses() {
9612 let (_, diags) = parse_str("const f = (...n: number[]) => n.length;");
9613 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9614 }
9615
9616 #[test]
9617 fn rest_param_in_function_type_annotation_parses() {
9618 let (_, diags) =
9619 parse_str("let f: (a: number, ...rest: string[]) => void = (a, ...r) => {};");
9620 assert!(diags.is_empty(), "expected clean parse, got {diags:?}");
9621 }
9622
9623 #[test]
9624 fn rest_param_not_last_rejected() {
9625 let (_, diags) = parse_str("function f(...xs: number[], y: number): void {}");
9626 assert!(
9627 diags.iter().any(|d| d
9628 .message
9629 .contains("rest parameter must be the last parameter")),
9630 "expected 'must be last' diagnostic, got {diags:?}",
9631 );
9632 }
9633
9634 #[test]
9635 fn rest_param_with_default_rejected() {
9636 let (_, diags) = parse_str("function f(...xs: number[] = []): void {}");
9637 assert!(
9638 diags.iter().any(|d| d
9639 .message
9640 .contains("rest parameter cannot have a default value")),
9641 "expected default-rejection diagnostic, got {diags:?}",
9642 );
9643 }
9644
9645 #[test]
9646 fn rest_param_without_annotation_rejected() {
9647 let (_, diags) = parse_str("function f(...xs): void {}");
9648 assert!(
9649 diags.iter().any(|d| d
9650 .message
9651 .contains("rest parameter requires a type annotation")),
9652 "expected missing-annotation diagnostic, got {diags:?}",
9653 );
9654 }
9655
9656 #[test]
9657 fn rest_param_destructured_rejected() {
9658 let (_, diags) = parse_str("function f(...{a, b}): void {}");
9659 assert!(
9660 diags
9661 .iter()
9662 .any(|d| d.message.contains("rest parameter cannot be destructured")),
9663 "expected destructured-rejection diagnostic, got {diags:?}",
9664 );
9665 }
9666
9667 #[test]
9668 fn rest_param_in_function_type_with_non_array_rejected() {
9669 let (_, diags) = parse_str("let f: (...xs: number) => void = () => {};");
9670 assert!(
9671 diags
9672 .iter()
9673 .any(|d| d.message.contains("rest parameter type must be an array")),
9674 "expected non-array-type diagnostic, got {diags:?}",
9675 );
9676 }
9677
9678 fn call(e: &crate::Expr) -> (crate::ExprId, &[crate::ExprId]) {
9679 match e.kind {
9680 crate::ExprKind::Call {
9681 callee, ref args, ..
9682 } => (callee, args.as_slice()),
9683 _ => panic!("expected Call, got {:?}", e.kind),
9684 }
9685 }
9686
9687 #[test]
9688 fn parse_call_no_args() {
9689 let (ast, diags) = parse_str("f();");
9690 assert!(diags.is_empty());
9691 let outer = expr_of_single_stmt(&ast);
9692 let (callee, args) = call(outer);
9693 assert!(matches!(
9694 ast.try_expr(callee).unwrap().kind,
9695 crate::ExprKind::Identifier(_)
9696 ));
9697 assert!(args.is_empty());
9698 assert_eq!(outer.span, crate::Span::new(F, 0, 3).unwrap());
9699 }
9700
9701 #[test]
9702 fn parse_call_one_arg() {
9703 let (ast, diags) = parse_str("f(1);");
9704 assert!(diags.is_empty());
9705 let outer = expr_of_single_stmt(&ast);
9706 let (callee, args) = call(outer);
9707 assert!(matches!(
9708 ast.try_expr(callee).unwrap().kind,
9709 crate::ExprKind::Identifier(_)
9710 ));
9711 assert_eq!(args.len(), 1);
9712 assert_eq!(
9713 ast.try_expr(args[0]).unwrap().kind,
9714 crate::ExprKind::Number(1.0)
9715 );
9716 }
9717
9718 #[test]
9719 fn parse_call_three_args() {
9720 let (ast, diags) = parse_str("f(1, 2, 3);");
9721 assert!(diags.is_empty());
9722 let outer = expr_of_single_stmt(&ast);
9723 let (_callee, args) = call(outer);
9724 assert_eq!(args.len(), 3);
9725 assert_eq!(
9726 ast.try_expr(args[0]).unwrap().kind,
9727 crate::ExprKind::Number(1.0)
9728 );
9729 assert_eq!(
9730 ast.try_expr(args[1]).unwrap().kind,
9731 crate::ExprKind::Number(2.0)
9732 );
9733 assert_eq!(
9734 ast.try_expr(args[2]).unwrap().kind,
9735 crate::ExprKind::Number(3.0)
9736 );
9737 }
9738
9739 #[test]
9740 fn parse_call_nested() {
9741 let (ast, diags) = parse_str("f(g(x));");
9742 assert!(diags.is_empty());
9743 let outer = expr_of_single_stmt(&ast);
9744 let (_callee, args) = call(outer);
9745 assert_eq!(args.len(), 1);
9746 let (_inner_callee, inner_args) = call(ast.try_expr(args[0]).unwrap());
9747 assert_eq!(inner_args.len(), 1);
9748 assert!(matches!(
9749 ast.try_expr(inner_args[0]).unwrap().kind,
9750 crate::ExprKind::Identifier(_)
9751 ));
9752 }
9753
9754 #[test]
9755 fn parse_curried_call() {
9756 let (ast, diags) = parse_str("f()(x);");
9757 assert!(diags.is_empty());
9758 let outer = expr_of_single_stmt(&ast);
9759 let (callee, args) = call(outer);
9760 assert_eq!(args.len(), 1);
9761 let (_inner_callee, inner_args) = call(ast.try_expr(callee).unwrap());
9762 assert!(inner_args.is_empty());
9763 }
9764
9765 #[test]
9766 fn unary_binds_looser_than_call() {
9767 let (ast, diags) = parse_str("-f();");
9768 assert!(diags.is_empty());
9769 let outer = expr_of_single_stmt(&ast);
9770 let (op, operand) = unary(outer);
9771 assert_eq!(op, crate::UnOp::Neg);
9772 let (_callee, args) = call(ast.try_expr(operand).unwrap());
9773 assert!(args.is_empty());
9774 }
9775
9776 #[test]
9777 fn call_inside_binary() {
9778 let (ast, diags) = parse_str("f() + 1;");
9779 assert!(diags.is_empty());
9780 let outer = expr_of_single_stmt(&ast);
9781 let (op, lhs, rhs) = binary(outer);
9782 assert_eq!(op, crate::BinOp::Add);
9783 let (_callee, args) = call(ast.try_expr(lhs).unwrap());
9784 assert!(args.is_empty());
9785 assert_eq!(
9786 ast.try_expr(rhs).unwrap().kind,
9787 crate::ExprKind::Number(1.0)
9788 );
9789 }
9790
9791 #[test]
9792 fn call_with_complex_args() {
9793 let (ast, diags) = parse_str("f(1 + 2, g(x));");
9794 assert!(diags.is_empty());
9795 let outer = expr_of_single_stmt(&ast);
9796 let (_callee, args) = call(outer);
9797 assert_eq!(args.len(), 2);
9798 let (op, ..) = binary(ast.try_expr(args[0]).unwrap());
9799 assert_eq!(op, crate::BinOp::Add);
9800 let (_, inner_args) = call(ast.try_expr(args[1]).unwrap());
9801 assert_eq!(inner_args.len(), 1);
9802 }
9803
9804 #[test]
9805 fn call_trailing_comma_ok() {
9806 let (ast, diags) = parse_str("f(1, 2,);");
9807 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
9808 let (_callee, args) = call(expr_of_single_stmt(&ast));
9809 assert_eq!(args.len(), 2);
9810 }
9811
9812 #[test]
9813 fn unclosed_call_diagnoses() {
9814 let (ast, diags) = parse_str("f(1, 2");
9815 assert!(!diags.is_empty());
9816 assert!(diags[0].message == "expected `,` or `)`" || diags[0].message == "expected `)`");
9817 assert!(ast.top_level.is_empty());
9818 }
9819
9820 #[test]
9821 fn call_expression_statement() {
9822 let (ast, diags) = parse_str(r#"greet("world");"#);
9823 assert!(diags.is_empty());
9824 let outer = expr_of_single_stmt(&ast);
9825 let (callee, args) = call(outer);
9826 assert!(matches!(
9827 ast.try_expr(callee).unwrap().kind,
9828 crate::ExprKind::Identifier(_)
9829 ));
9830 assert_eq!(args.len(), 1);
9831 assert_eq!(
9832 ast.try_expr(args[0]).unwrap().kind,
9833 crate::ExprKind::String("world".to_string())
9834 );
9835 }
9836
9837 #[test]
9838 fn snapshot_multi_call_program() {
9839 let (ast, diags) = parse_str("f(); g(1); h(f(2), 3);");
9840 assert!(diags.is_empty());
9841 assert_eq!(ast.top_level.len(), 3);
9842 insta::assert_debug_snapshot!(ast);
9843 }
9844
9845 #[test]
9846 fn parse_if_simple() {
9847 let (ast, diags) = parse_str("if (a) { }");
9848 assert!(diags.is_empty());
9849 let stmt = single_stmt(&ast);
9850 match stmt.kind {
9851 crate::StmtKind::If {
9852 condition,
9853 then_block,
9854 else_block,
9855 } => {
9856 assert!(matches!(
9857 ast.try_expr(condition).unwrap().kind,
9858 crate::ExprKind::Identifier(_)
9859 ));
9860 assert!(matches!(
9861 ast.try_stmt(then_block).unwrap().kind,
9862 crate::StmtKind::Block(ref v) if v.is_empty()
9863 ));
9864 assert!(else_block.is_none());
9865 }
9866 _ => panic!("expected If"),
9867 }
9868 }
9869
9870 #[test]
9871 fn parse_if_else() {
9872 let (ast, diags) = parse_str("if (a) { } else { }");
9873 assert!(diags.is_empty());
9874 let stmt = single_stmt(&ast);
9875 match stmt.kind {
9876 crate::StmtKind::If { else_block, .. } => {
9877 let else_id = else_block.expect("expected else block");
9878 assert!(matches!(
9879 ast.try_stmt(else_id).unwrap().kind,
9880 crate::StmtKind::Block(ref v) if v.is_empty()
9881 ));
9882 }
9883 _ => panic!("expected If"),
9884 }
9885 }
9886
9887 #[test]
9888 fn parse_else_if_chain() {
9889 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
9890 assert!(diags.is_empty());
9891 let stmt = single_stmt(&ast);
9892 let crate::StmtKind::If {
9893 else_block: Some(inner_id),
9894 ..
9895 } = stmt.kind
9896 else {
9897 panic!("expected outer If with else_block");
9898 };
9899 let inner = ast.try_stmt(inner_id).unwrap();
9900 let crate::StmtKind::If {
9901 else_block: Some(tail_id),
9902 ..
9903 } = inner.kind
9904 else {
9905 panic!("expected inner If for `else if`");
9906 };
9907 assert!(matches!(
9908 ast.try_stmt(tail_id).unwrap().kind,
9909 crate::StmtKind::Block(_)
9910 ));
9911 }
9912
9913 #[test]
9914 fn parse_if_with_body_statements() {
9915 let (ast, diags) = parse_str("if (a) { let x = 1; }");
9916 assert!(diags.is_empty());
9917 let stmt = single_stmt(&ast);
9918 match stmt.kind {
9919 crate::StmtKind::If { then_block, .. } => {
9920 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind
9921 else {
9922 panic!("expected Block")
9923 };
9924 assert_eq!(body.len(), 1);
9925 }
9926 _ => panic!("expected If"),
9927 }
9928 }
9929
9930 #[test]
9931 fn parse_if_braceless_body() {
9932 let (ast, diags) = parse_str("if (a) return b;");
9933 assert!(diags.is_empty(), "{diags:?}");
9934 let stmt = single_stmt(&ast);
9935 let crate::StmtKind::If {
9936 then_block,
9937 else_block,
9938 ..
9939 } = stmt.kind
9940 else {
9941 panic!("expected If");
9942 };
9943 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9944 panic!("braceless body should wrap in a Block");
9945 };
9946 assert_eq!(body.len(), 1);
9947 assert!(matches!(
9948 ast.try_stmt(body[0]).unwrap().kind,
9949 crate::StmtKind::Return(_)
9950 ));
9951 assert!(else_block.is_none());
9952 }
9953
9954 #[test]
9955 fn parse_braceless_dangling_else_binds_nearest_if() {
9956 let (ast, diags) = parse_str("if (a) if (b) x(); else y();");
9958 assert!(diags.is_empty(), "{diags:?}");
9959 let crate::StmtKind::If {
9960 then_block,
9961 else_block: outer_else,
9962 ..
9963 } = single_stmt(&ast).kind
9964 else {
9965 panic!("expected outer If");
9966 };
9967 assert!(outer_else.is_none(), "else must bind to the inner if");
9968 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
9969 panic!("expected wrapped Block");
9970 };
9971 let crate::StmtKind::If {
9972 else_block: Some(_),
9973 ..
9974 } = ast.try_stmt(body[0]).unwrap().kind
9975 else {
9976 panic!("inner If should own the else");
9977 };
9978 }
9979
9980 #[test]
9981 fn parse_while_braceless_body() {
9982 let (ast, diags) = parse_str("while (a) x();");
9983 assert!(diags.is_empty(), "{diags:?}");
9984 let crate::StmtKind::While { body, .. } = single_stmt(&ast).kind else {
9985 panic!("expected While");
9986 };
9987 assert!(matches!(
9988 ast.try_stmt(body).unwrap().kind,
9989 crate::StmtKind::Block(ref v) if v.len() == 1
9990 ));
9991 }
9992
9993 #[test]
9994 fn parse_loop_with_empty_body() {
9995 for src in [
9996 "while (a());",
9997 "for (const x of xs);",
9998 "for (let i = 0; i < 3; i++);",
9999 "do ; while (a());",
10000 ] {
10001 let (ast, diags) = parse_str(src);
10002 assert!(diags.is_empty(), "{src}: {diags:?}");
10003 let body = match single_stmt(&ast).kind {
10004 crate::StmtKind::While { body, .. }
10005 | crate::StmtKind::DoWhile { body, .. }
10006 | crate::StmtKind::For { body, .. }
10007 | crate::StmtKind::ForOf { body, .. } => body,
10008 ref other => panic!("{src}: expected a loop, got {other:?}"),
10009 };
10010 assert!(
10011 matches!(ast.try_stmt(body).unwrap().kind, crate::StmtKind::Block(ref v) if v.is_empty()),
10012 "{src}"
10013 );
10014 }
10015 }
10016
10017 #[test]
10018 fn parse_if_with_empty_body_is_an_error() {
10019 let (_ast, diags) = parse_str("if (a);");
10020 assert_eq!(diags[0].message, "`if` has an empty body");
10021 }
10022
10023 #[test]
10024 fn parse_empty_branch_body_reports_once() {
10025 let (_ast, diags) = parse_str("if (a) ; else b();");
10026 assert_eq!(diags.len(), 1, "{diags:?}");
10027 assert_eq!(diags[0].message, "`if` has an empty body");
10028 let (_ast, diags) = parse_str("if (a) b(); else ;");
10029 assert_eq!(diags.len(), 1, "{diags:?}");
10030 assert_eq!(diags[0].message, "`else` has an empty body");
10031 let (_ast, diags) = parse_str("if (a) ; else if (b) ; else ;");
10033 let messages: Vec<_> = diags.iter().map(|d| d.message.as_str()).collect();
10034 assert_eq!(
10035 messages,
10036 [
10037 "`if` has an empty body",
10038 "`if` has an empty body",
10039 "`else` has an empty body"
10040 ]
10041 );
10042 }
10043
10044 #[test]
10045 fn parse_for_of_braceless_body() {
10046 let (ast, diags) = parse_str("for (const x of xs) f(x);");
10047 assert!(diags.is_empty(), "{diags:?}");
10048 let crate::StmtKind::ForOf { body, .. } = single_stmt(&ast).kind else {
10049 panic!("expected ForOf");
10050 };
10051 assert!(matches!(
10052 ast.try_stmt(body).unwrap().kind,
10053 crate::StmtKind::Block(ref v) if v.len() == 1
10054 ));
10055 }
10056
10057 #[test]
10058 fn parse_nested_if() {
10059 let (ast, diags) = parse_str("if (a) { if (b) { } }");
10060 assert!(diags.is_empty());
10061 let outer = single_stmt(&ast);
10062 let crate::StmtKind::If { then_block, .. } = outer.kind else {
10063 panic!("expected outer If");
10064 };
10065 let crate::StmtKind::Block(ref body) = ast.try_stmt(then_block).unwrap().kind else {
10066 panic!("expected Block")
10067 };
10068 assert_eq!(body.len(), 1);
10069 assert!(matches!(
10070 ast.try_stmt(body[0]).unwrap().kind,
10071 crate::StmtKind::If { .. }
10072 ));
10073 }
10074
10075 #[test]
10076 fn if_missing_open_paren_diagnoses() {
10077 let (ast, diags) = parse_str("if a { }");
10078 assert!(!diags.is_empty());
10079 assert_eq!(diags[0].message, "expected `(`");
10080 assert!(ast.top_level.is_empty());
10081 }
10082
10083 #[test]
10084 fn if_missing_close_paren_diagnoses() {
10085 let (ast, diags) = parse_str("if (a { }");
10086 assert!(!diags.is_empty());
10087 assert_eq!(diags[0].message, "expected `)`");
10088 assert!(ast.top_level.is_empty());
10089 }
10090
10091 #[test]
10092 fn if_braceless_body_parses() {
10093 let (ast, diags) = parse_str("if (a) x;");
10094 assert!(diags.is_empty(), "{diags:?}");
10095 let crate::StmtKind::If { then_block, .. } = single_stmt(&ast).kind else {
10096 panic!("expected If");
10097 };
10098 assert!(matches!(
10099 ast.try_stmt(then_block).unwrap().kind,
10100 crate::StmtKind::Block(ref v) if v.len() == 1
10101 ));
10102 }
10103
10104 #[test]
10105 fn snapshot_else_if_chain() {
10106 let (ast, diags) = parse_str("if (a) { } else if (b) { } else { }");
10107 assert!(diags.is_empty());
10108 insta::assert_debug_snapshot!(ast);
10109 }
10110
10111 #[test]
10112 fn parse_while_simple() {
10113 let (ast, diags) = parse_str("while (a) { }");
10114 assert!(diags.is_empty());
10115 let stmt = single_stmt(&ast);
10116 match stmt.kind {
10117 crate::StmtKind::While { condition, body } => {
10118 assert!(matches!(
10119 ast.try_expr(condition).unwrap().kind,
10120 crate::ExprKind::Identifier(_)
10121 ));
10122 assert!(matches!(
10123 ast.try_stmt(body).unwrap().kind,
10124 crate::StmtKind::Block(ref v) if v.is_empty()
10125 ));
10126 }
10127 _ => panic!("expected While"),
10128 }
10129 }
10130
10131 #[test]
10132 fn parse_while_with_body() {
10133 let (ast, diags) = parse_str("while (a) { x; y; }");
10134 assert!(diags.is_empty());
10135 let stmt = single_stmt(&ast);
10136 let crate::StmtKind::While { body, .. } = stmt.kind else {
10137 panic!("expected While");
10138 };
10139 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10140 panic!("expected Block")
10141 };
10142 assert_eq!(stmts.len(), 2);
10143 }
10144
10145 #[test]
10146 fn while_missing_paren_diagnoses() {
10147 let (ast, diags) = parse_str("while a { }");
10148 assert!(!diags.is_empty());
10149 assert_eq!(diags[0].message, "expected `(`");
10150 assert!(ast.top_level.is_empty());
10151 }
10152
10153 #[test]
10154 fn parse_for_all_slots() {
10155 let (ast, diags) = parse_str("for (let i = 0; i < 10; i = i + 1) { x; }");
10156 assert!(diags.is_empty(), "{diags:?}");
10157 let stmt = single_stmt(&ast);
10158 let crate::StmtKind::For {
10159 init,
10160 condition,
10161 update,
10162 body,
10163 } = stmt.kind
10164 else {
10165 panic!("expected For");
10166 };
10167 assert!(init.is_some(), "init missing");
10168 assert!(condition.is_some(), "condition missing");
10169 assert!(update.is_some(), "update missing");
10170 assert!(matches!(
10171 ast.try_stmt(body).unwrap().kind,
10172 crate::StmtKind::Block(_)
10173 ));
10174 }
10175
10176 #[test]
10177 fn parse_for_empty_slots() {
10178 let (ast, diags) = parse_str("for (;;) { break; }");
10179 assert!(diags.is_empty(), "{diags:?}");
10180 let stmt = single_stmt(&ast);
10181 let crate::StmtKind::For {
10182 init,
10183 condition,
10184 update,
10185 ..
10186 } = stmt.kind
10187 else {
10188 panic!("expected For");
10189 };
10190 assert!(init.is_none() && condition.is_none() && update.is_none());
10191 }
10192
10193 #[test]
10194 fn parse_for_of_const_binding() {
10195 let (ast, diags) = parse_str("for (const x of arr) { y; }");
10196 assert!(diags.is_empty(), "{diags:?}");
10197 let stmt = single_stmt(&ast);
10198 let crate::StmtKind::ForOf {
10199 binding_kind,
10200 ref name,
10201 ref ty,
10202 ..
10203 } = stmt.kind
10204 else {
10205 panic!("expected ForOf");
10206 };
10207 assert!(matches!(binding_kind, crate::BindingKind::Const));
10208 assert_eq!(name.name, "x");
10209 assert!(ty.is_none());
10210 }
10211
10212 #[test]
10213 fn parse_for_of_let_with_annotation() {
10214 let (ast, diags) = parse_str("for (let x: number of arr) { y; }");
10215 assert!(diags.is_empty(), "{diags:?}");
10216 let stmt = single_stmt(&ast);
10217 let crate::StmtKind::ForOf {
10218 binding_kind,
10219 ref ty,
10220 ..
10221 } = stmt.kind
10222 else {
10223 panic!("expected ForOf");
10224 };
10225 assert!(matches!(binding_kind, crate::BindingKind::Let));
10226 assert!(ty.is_some());
10227 }
10228
10229 #[test]
10230 fn parse_do_while_simple() {
10231 let (ast, diags) = parse_str("do { x; } while (a);");
10232 assert!(diags.is_empty(), "{diags:?}");
10233 let stmt = single_stmt(&ast);
10234 assert!(matches!(stmt.kind, crate::StmtKind::DoWhile { .. }));
10235 }
10236
10237 #[test]
10238 fn parse_break_and_continue() {
10239 let (ast, diags) = parse_str("while (a) { break; continue; }");
10240 assert!(diags.is_empty(), "{diags:?}");
10241 let stmt = single_stmt(&ast);
10242 let crate::StmtKind::While { body, .. } = stmt.kind else {
10243 panic!("expected While");
10244 };
10245 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10246 panic!("expected Block");
10247 };
10248 assert!(matches!(
10249 ast.try_stmt(stmts[0]).unwrap().kind,
10250 crate::StmtKind::Break
10251 ));
10252 assert!(matches!(
10253 ast.try_stmt(stmts[1]).unwrap().kind,
10254 crate::StmtKind::Continue
10255 ));
10256 }
10257
10258 #[test]
10259 fn parse_bare_return_in_function() {
10260 let (ast, diags) = parse_str("function f(): void { return; }");
10261 assert!(diags.is_empty());
10262 let f = single_stmt(&ast);
10263 let crate::StmtKind::Function { body, .. } = f.kind else {
10264 panic!("expected Function");
10265 };
10266 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10267 panic!("expected Block")
10268 };
10269 assert_eq!(stmts.len(), 1);
10270 match ast.try_stmt(stmts[0]).unwrap().kind {
10271 crate::StmtKind::Return(None) => {}
10272 _ => panic!("expected Return(None)"),
10273 }
10274 }
10275
10276 #[test]
10277 fn parse_return_with_value() {
10278 let (ast, diags) = parse_str("function f(): number { return 1; }");
10279 assert!(diags.is_empty());
10280 let f = single_stmt(&ast);
10281 let crate::StmtKind::Function { body, .. } = f.kind else {
10282 panic!("expected Function");
10283 };
10284 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10285 panic!("expected Block")
10286 };
10287 match ast.try_stmt(stmts[0]).unwrap().kind {
10288 crate::StmtKind::Return(Some(value)) => {
10289 assert_eq!(
10290 ast.try_expr(value).unwrap().kind,
10291 crate::ExprKind::Number(1.0)
10292 );
10293 }
10294 _ => panic!("expected Return(Some)"),
10295 }
10296 }
10297
10298 #[test]
10299 fn parse_return_with_expression() {
10300 let (ast, diags) = parse_str("function f(): number { return 1 + 2; }");
10301 assert!(diags.is_empty());
10302 let f = single_stmt(&ast);
10303 let crate::StmtKind::Function { body, .. } = f.kind else {
10304 panic!("expected Function");
10305 };
10306 let crate::StmtKind::Block(ref stmts) = ast.try_stmt(body).unwrap().kind else {
10307 panic!("expected Block")
10308 };
10309 match ast.try_stmt(stmts[0]).unwrap().kind {
10310 crate::StmtKind::Return(Some(value)) => {
10311 let (op, ..) = binary(ast.try_expr(value).unwrap());
10312 assert_eq!(op, crate::BinOp::Add);
10313 }
10314 _ => panic!("expected Return(Some(Binary))"),
10315 }
10316 }
10317
10318 #[test]
10319 fn return_missing_semicolon_diagnoses() {
10320 let (_, diags) = parse_str("function f(): number { return x x; }");
10321 assert!(!diags.is_empty());
10322 assert_eq!(diags[0].message, "expected `;` after return");
10323 }
10324
10325 #[test]
10326 fn parse_bare_block() {
10327 let (ast, diags) = parse_str("{ x; y; }");
10328 assert!(diags.is_empty());
10329 let stmt = single_stmt(&ast);
10330 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10331 panic!("expected Block");
10332 };
10333 assert_eq!(stmts.len(), 2);
10334 }
10335
10336 #[test]
10337 fn parse_empty_bare_block() {
10338 let (ast, diags) = parse_str("{ }");
10339 assert!(diags.is_empty());
10340 let stmt = single_stmt(&ast);
10341 let crate::StmtKind::Block(ref stmts) = stmt.kind else {
10342 panic!("expected Block");
10343 };
10344 assert!(stmts.is_empty());
10345 }
10346
10347 #[test]
10348 fn parse_nested_bare_blocks() {
10349 let (ast, diags) = parse_str("{ { } }");
10350 assert!(diags.is_empty());
10351 let outer = single_stmt(&ast);
10352 let crate::StmtKind::Block(ref stmts) = outer.kind else {
10353 panic!("expected outer Block");
10354 };
10355 assert_eq!(stmts.len(), 1);
10356 assert!(matches!(
10357 ast.try_stmt(stmts[0]).unwrap().kind,
10358 crate::StmtKind::Block(ref v) if v.is_empty()
10359 ));
10360 }
10361
10362 #[test]
10363 fn snapshot_function_with_if_and_return() {
10364 let source = "function isPositive(n: number): boolean {\n if (n > 0) {\n return true;\n } else {\n return false;\n }\n}";
10365 let (ast, diags) = parse_str(source);
10366 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10367 insta::assert_debug_snapshot!(ast);
10368 }
10369
10370 fn object_literal(e: &crate::Expr) -> &[crate::ObjectLiteralMember] {
10371 match e.kind {
10372 crate::ExprKind::ObjectLiteral { ref members } => members.as_slice(),
10373 _ => panic!("expected ObjectLiteral, got {:?}", e.kind),
10374 }
10375 }
10376
10377 fn object_literal_field(m: &crate::ObjectLiteralMember) -> &crate::ObjectLiteralField {
10378 match m {
10379 crate::ObjectLiteralMember::Field(f) => f,
10380 crate::ObjectLiteralMember::Spread { .. }
10381 | crate::ObjectLiteralMember::Computed { .. } => {
10382 panic!("expected Field member, got Spread")
10383 }
10384 }
10385 }
10386
10387 fn array_literal(e: &crate::Expr) -> &[crate::ArrayLiteralElement] {
10388 match e.kind {
10389 crate::ExprKind::ArrayLiteral { ref elements } => elements.as_slice(),
10390 _ => panic!("expected ArrayLiteral, got {:?}", e.kind),
10391 }
10392 }
10393
10394 fn array_literal_value(el: &crate::ArrayLiteralElement) -> crate::ExprId {
10395 match el {
10396 crate::ArrayLiteralElement::Value(id) => *id,
10397 crate::ArrayLiteralElement::Spread { .. } => {
10398 panic!("expected Value element, got Spread")
10399 }
10400 }
10401 }
10402
10403 #[test]
10404 fn parse_empty_object_literal() {
10405 let (ast, diags) = parse_str("let x = {};");
10406 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10407 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10408 panic!("expected Let");
10409 };
10410 assert!(object_literal(ast.try_expr(value).unwrap()).is_empty());
10411 }
10412
10413 #[test]
10414 fn parse_object_literal_simple() {
10415 let (ast, diags) = parse_str("let p = { x: 1, y: 2 };");
10416 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10417 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10418 panic!("expected Let");
10419 };
10420 let members = object_literal(ast.try_expr(value).unwrap());
10421 assert_eq!(members.len(), 2);
10422 let f0 = object_literal_field(&members[0]);
10423 let f1 = object_literal_field(&members[1]);
10424 assert_eq!(f0.name.name, "x");
10425 assert_eq!(f1.name.name, "y");
10426 assert_eq!(
10427 ast.try_expr(f0.value).unwrap().kind,
10428 crate::ExprKind::Number(1.0)
10429 );
10430 assert_eq!(
10431 ast.try_expr(f1.value).unwrap().kind,
10432 crate::ExprKind::Number(2.0)
10433 );
10434 }
10435
10436 #[test]
10437 fn parse_object_literal_keyword_keys() {
10438 let (ast, diags) = parse_str("let p = { type: 1, default: 2, null: 3, true: 4 };");
10439 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10440 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10441 panic!("expected Let");
10442 };
10443 let names: Vec<&str> = object_literal(ast.try_expr(value).unwrap())
10444 .iter()
10445 .map(object_literal_field)
10446 .map(|f| f.name.name.as_str())
10447 .collect();
10448 assert_eq!(names, vec!["type", "default", "null", "true"]);
10449 }
10450
10451 #[test]
10452 fn parse_object_literal_string_key() {
10453 let (ast, diags) = parse_str(r#"let p = { "hello": 1 };"#);
10454 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10455 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10456 panic!("expected Let");
10457 };
10458 let members = object_literal(ast.try_expr(value).unwrap());
10459 assert_eq!(members.len(), 1);
10460 let f0 = object_literal_field(&members[0]);
10461 assert_eq!(f0.name.name, "hello");
10462 }
10463
10464 #[test]
10465 fn parse_object_literal_string_key_decodes_escape() {
10466 let (ast, diags) = parse_str(r#"let p = { "content\u002dtype": 1 };"#);
10467 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10468 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10469 panic!("expected Let");
10470 };
10471 let members = object_literal(ast.try_expr(value).unwrap());
10472 assert_eq!(members.len(), 1);
10473 let f0 = object_literal_field(&members[0]);
10474 assert_eq!(f0.name.name, "content-type");
10475 }
10476
10477 #[test]
10478 fn parse_object_literal_shorthand() {
10479 let (ast, diags) = parse_str("let p = { x, y: 2 };");
10480 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10481 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10482 panic!("expected Let");
10483 };
10484 let members = object_literal(ast.try_expr(value).unwrap());
10485 assert_eq!(members.len(), 2);
10486 let f0 = object_literal_field(&members[0]);
10487 assert_eq!(f0.name.name, "x");
10488 assert_eq!(
10490 ast.try_expr(f0.value).unwrap().kind,
10491 crate::ExprKind::Identifier(crate::Ident {
10492 name: "x".into(),
10493 span: f0.name.span,
10494 })
10495 );
10496 let f1 = object_literal_field(&members[1]);
10497 assert_eq!(f1.name.name, "y");
10498 assert_eq!(
10499 ast.try_expr(f1.value).unwrap().kind,
10500 crate::ExprKind::Number(2.0)
10501 );
10502 }
10503
10504 #[test]
10505 fn parse_object_literal_string_key_shorthand_rejected() {
10506 let (_ast, diags) = parse_str(r#"let p = { "x" };"#);
10507 assert!(
10508 diags
10509 .iter()
10510 .any(|d| d.message.contains("expected `:` after field name")),
10511 "string-key shorthand should be rejected: {diags:?}"
10512 );
10513 }
10514
10515 #[test]
10516 fn parse_object_literal_trailing_comma() {
10517 let (ast, diags) = parse_str("let p = { x: 1, y: 2, };");
10518 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10519 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10520 panic!("expected Let");
10521 };
10522 assert_eq!(object_literal(ast.try_expr(value).unwrap()).len(), 2);
10523 }
10524
10525 #[test]
10526 fn parse_object_literal_duplicate_key_diagnoses() {
10527 let (_, diags) = parse_str("let p = { x: 1, x: 2 };");
10528 assert_eq!(diags.len(), 1);
10529 assert_eq!(diags[0].message, "duplicate field `x` in object literal");
10530 }
10531
10532 #[test]
10533 fn parse_empty_array_literal() {
10534 let (ast, diags) = parse_str("let xs = [];");
10535 assert!(diags.is_empty());
10536 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10537 panic!("expected Let");
10538 };
10539 assert!(array_literal(ast.try_expr(value).unwrap()).is_empty());
10540 }
10541
10542 #[test]
10543 fn parse_array_literal_simple() {
10544 let (ast, diags) = parse_str("let xs = [1, 2, 3];");
10545 assert!(diags.is_empty());
10546 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10547 panic!("expected Let");
10548 };
10549 let elements = array_literal(ast.try_expr(value).unwrap());
10550 assert_eq!(elements.len(), 3);
10551 assert_eq!(
10552 ast.try_expr(array_literal_value(&elements[0]))
10553 .unwrap()
10554 .kind,
10555 crate::ExprKind::Number(1.0)
10556 );
10557 assert_eq!(
10558 ast.try_expr(array_literal_value(&elements[2]))
10559 .unwrap()
10560 .kind,
10561 crate::ExprKind::Number(3.0)
10562 );
10563 }
10564
10565 #[test]
10566 fn parse_array_literal_trailing_comma() {
10567 let (ast, diags) = parse_str("let xs = [1, 2,];");
10568 assert!(diags.is_empty());
10569 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10570 panic!("expected Let");
10571 };
10572 assert_eq!(array_literal(ast.try_expr(value).unwrap()).len(), 2);
10573 }
10574
10575 #[test]
10576 fn parse_nested_array_in_object() {
10577 let (ast, diags) = parse_str("let p = { items: [1, 2] };");
10578 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10579 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10580 panic!("expected Let");
10581 };
10582 let members = object_literal(ast.try_expr(value).unwrap());
10583 let f0 = object_literal_field(&members[0]);
10584 assert_eq!(f0.name.name, "items");
10585 let inner = array_literal(ast.try_expr(f0.value).unwrap());
10586 assert_eq!(inner.len(), 2);
10587 }
10588
10589 #[test]
10590 fn parse_nested_object_in_array() {
10591 let (ast, diags) = parse_str("let xs = [{ x: 1 }, { x: 2 }];");
10592 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10593 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10594 panic!("expected Let");
10595 };
10596 let elements = array_literal(ast.try_expr(value).unwrap());
10597 assert_eq!(elements.len(), 2);
10598 assert_eq!(
10599 object_literal(ast.try_expr(array_literal_value(&elements[0])).unwrap()).len(),
10600 1
10601 );
10602 assert_eq!(
10603 object_literal(ast.try_expr(array_literal_value(&elements[1])).unwrap()).len(),
10604 1
10605 );
10606 }
10607
10608 #[test]
10609 fn brace_at_statement_start_is_block_not_object() {
10610 let (_, diags) = parse_str("{ x: 1 }");
10611 assert!(
10612 !diags.is_empty(),
10613 "expected a diagnostic for `x:` inside block"
10614 );
10615 }
10616
10617 fn field_access(e: &crate::Expr) -> (crate::ExprId, &str) {
10618 match e.kind {
10619 crate::ExprKind::FieldAccess { receiver, ref name } => (receiver, name.name.as_str()),
10620 _ => panic!("expected FieldAccess, got {:?}", e.kind),
10621 }
10622 }
10623
10624 fn index_access(e: &crate::Expr) -> (crate::ExprId, crate::ExprId) {
10625 match e.kind {
10626 crate::ExprKind::IndexAccess { receiver, index } => (receiver, index),
10627 _ => panic!("expected IndexAccess, got {:?}", e.kind),
10628 }
10629 }
10630
10631 #[test]
10632 fn parse_field_access_simple() {
10633 let (ast, diags) = parse_str("a.b;");
10634 assert!(diags.is_empty());
10635 let outer = expr_of_single_stmt(&ast);
10636 let (receiver, name) = field_access(outer);
10637 assert!(matches!(
10638 ast.try_expr(receiver).unwrap().kind,
10639 crate::ExprKind::Identifier(_)
10640 ));
10641 assert_eq!(name, "b");
10642 }
10643
10644 #[test]
10645 fn parse_field_access_keyword_name() {
10646 let (ast, diags) = parse_str("status.type.default.null;");
10647 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10648 let outer = expr_of_single_stmt(&ast);
10649 let (receiver, name) = field_access(outer);
10650 assert_eq!(name, "null");
10651 let (receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10652 assert_eq!(name, "default");
10653 let (_receiver, name) = field_access(ast.try_expr(receiver).unwrap());
10654 assert_eq!(name, "type");
10655 }
10656
10657 #[test]
10658 fn parse_field_access_chain_left_assoc() {
10659 let (ast, diags) = parse_str("a.b.c;");
10660 assert!(diags.is_empty());
10661 let outer = expr_of_single_stmt(&ast);
10662 let (mid_id, c) = field_access(outer);
10663 assert_eq!(c, "c");
10664 let (a_id, b) = field_access(ast.try_expr(mid_id).unwrap());
10665 assert_eq!(b, "b");
10666 assert!(matches!(
10667 ast.try_expr(a_id).unwrap().kind,
10668 crate::ExprKind::Identifier(_)
10669 ));
10670 }
10671
10672 #[test]
10673 fn parse_index_access_simple() {
10674 let (ast, diags) = parse_str("a[0];");
10675 assert!(diags.is_empty());
10676 let outer = expr_of_single_stmt(&ast);
10677 let (receiver, idx) = index_access(outer);
10678 assert!(matches!(
10679 ast.try_expr(receiver).unwrap().kind,
10680 crate::ExprKind::Identifier(_)
10681 ));
10682 assert_eq!(
10683 ast.try_expr(idx).unwrap().kind,
10684 crate::ExprKind::Number(0.0)
10685 );
10686 }
10687
10688 #[test]
10689 fn parse_index_access_chain() {
10690 let (ast, diags) = parse_str("a[0][1];");
10691 assert!(diags.is_empty());
10692 let outer = expr_of_single_stmt(&ast);
10693 let (mid_id, one) = index_access(outer);
10694 assert_eq!(
10695 ast.try_expr(one).unwrap().kind,
10696 crate::ExprKind::Number(1.0)
10697 );
10698 let (a_id, zero) = index_access(ast.try_expr(mid_id).unwrap());
10699 assert_eq!(
10700 ast.try_expr(zero).unwrap().kind,
10701 crate::ExprKind::Number(0.0)
10702 );
10703 assert!(matches!(
10704 ast.try_expr(a_id).unwrap().kind,
10705 crate::ExprKind::Identifier(_)
10706 ));
10707 }
10708
10709 #[test]
10710 fn parse_mixed_access_and_call_chain() {
10711 let (ast, diags) = parse_str("a.b[c].d();");
10712 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10713 let outer = expr_of_single_stmt(&ast);
10714 let (callee, args) = call(outer);
10715 assert!(args.is_empty());
10716 let (idx_id, d_name) = field_access(ast.try_expr(callee).unwrap());
10717 assert_eq!(d_name, "d");
10718 let (b_id, c_idx) = index_access(ast.try_expr(idx_id).unwrap());
10719 assert!(matches!(
10720 ast.try_expr(c_idx).unwrap().kind,
10721 crate::ExprKind::Identifier(_)
10722 ));
10723 let (a_id, b_name) = field_access(ast.try_expr(b_id).unwrap());
10724 assert_eq!(b_name, "b");
10725 assert!(matches!(
10726 ast.try_expr(a_id).unwrap().kind,
10727 crate::ExprKind::Identifier(_)
10728 ));
10729 }
10730
10731 #[test]
10732 fn parse_call_then_field() {
10733 let (ast, diags) = parse_str("f().x;");
10734 assert!(diags.is_empty());
10735 let outer = expr_of_single_stmt(&ast);
10736 let (call_id, x_name) = field_access(outer);
10737 assert_eq!(x_name, "x");
10738 let (_callee, args) = call(ast.try_expr(call_id).unwrap());
10739 assert!(args.is_empty());
10740 }
10741
10742 #[test]
10743 fn parse_index_with_complex_expression() {
10744 let (ast, diags) = parse_str("a[i + 1];");
10745 assert!(diags.is_empty());
10746 let outer = expr_of_single_stmt(&ast);
10747 let (_, idx) = index_access(outer);
10748 let (op, ..) = binary(ast.try_expr(idx).unwrap());
10749 assert_eq!(op, crate::BinOp::Add);
10750 }
10751
10752 #[test]
10753 fn field_access_missing_name_diagnoses() {
10754 let (_, diags) = parse_str("a.;");
10755 assert!(!diags.is_empty());
10756 assert_eq!(diags[0].message, "expected field name after `.`");
10757 }
10758
10759 #[test]
10760 fn parse_postfix_increment() {
10761 let (ast, diags) = parse_str("x++;");
10762 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10763 let expr = expr_of_single_stmt(&ast);
10764 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10765 panic!("expected PostfixUnary, got {:?}", expr.kind);
10766 };
10767 assert_eq!(op, crate::PostfixOp::Inc);
10768 assert!(matches!(
10769 ast.try_expr(operand).unwrap().kind,
10770 crate::ExprKind::Identifier(_)
10771 ));
10772 assert_eq!(expr.span, crate::Span::new(F, 0, 3).unwrap());
10773 }
10774
10775 #[test]
10776 fn parse_postfix_decrement() {
10777 let (ast, diags) = parse_str("x--;");
10778 assert!(diags.is_empty());
10779 let expr = expr_of_single_stmt(&ast);
10780 let crate::ExprKind::PostfixUnary { op, .. } = expr.kind else {
10781 panic!("expected PostfixUnary");
10782 };
10783 assert_eq!(op, crate::PostfixOp::Dec);
10784 }
10785
10786 #[test]
10787 fn parse_postfix_non_null_assertion() {
10788 let (ast, diags) = parse_str("x!;");
10789 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10790 let expr = expr_of_single_stmt(&ast);
10791 let crate::ExprKind::PostfixUnary { op, operand } = expr.kind else {
10792 panic!("expected PostfixUnary, got {:?}", expr.kind);
10793 };
10794 assert_eq!(op, crate::PostfixOp::NonNullAssert);
10795 assert!(matches!(
10796 ast.try_expr(operand).unwrap().kind,
10797 crate::ExprKind::Identifier(_)
10798 ));
10799 assert_eq!(expr.span, crate::Span::new(F, 0, 2).unwrap());
10800 }
10801
10802 #[test]
10803 fn parse_non_null_assertion_continues_chain() {
10804 let (ast, diags) = parse_str("x!.y;");
10805 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10806 let expr = expr_of_single_stmt(&ast);
10807 let crate::ExprKind::FieldAccess { receiver, name } = &expr.kind else {
10808 panic!("expected FieldAccess, got {:?}", expr.kind);
10809 };
10810 assert_eq!(name.name, "y");
10811 assert!(matches!(
10812 ast.try_expr(*receiver).unwrap().kind,
10813 crate::ExprKind::PostfixUnary {
10814 op: crate::PostfixOp::NonNullAssert,
10815 ..
10816 }
10817 ));
10818 }
10819
10820 #[test]
10821 fn parse_postfix_on_field_access() {
10822 let (ast, diags) = parse_str("o.f++;");
10823 assert!(diags.is_empty());
10824 let expr = expr_of_single_stmt(&ast);
10825 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
10826 panic!("expected PostfixUnary");
10827 };
10828 assert!(matches!(
10829 ast.try_expr(operand).unwrap().kind,
10830 crate::ExprKind::FieldAccess { .. }
10831 ));
10832 }
10833
10834 #[test]
10835 fn parse_postfix_on_index_access() {
10836 let (ast, diags) = parse_str("xs[0]++;");
10837 assert!(diags.is_empty());
10838 let expr = expr_of_single_stmt(&ast);
10839 let crate::ExprKind::PostfixUnary { operand, .. } = expr.kind else {
10840 panic!("expected PostfixUnary");
10841 };
10842 assert!(matches!(
10843 ast.try_expr(operand).unwrap().kind,
10844 crate::ExprKind::IndexAccess { .. }
10845 ));
10846 }
10847
10848 #[test]
10849 fn parse_postfix_terminates_chain() {
10850 let (_, diags) = parse_str("x++.y;");
10851 assert!(!diags.is_empty());
10852 assert_eq!(diags[0].message, "expected `;` after expression");
10853 }
10854
10855 #[test]
10856 fn parse_postfix_in_binary_continues() {
10857 let (ast, diags) = parse_str("let y = x++ + 1;");
10858 assert!(diags.is_empty(), "unexpected: {diags:?}");
10859 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10860 panic!("expected Let");
10861 };
10862 let (op, lhs, _) = binary(ast.try_expr(value).unwrap());
10863 assert_eq!(op, crate::BinOp::Add);
10864 assert!(matches!(
10865 ast.try_expr(lhs).unwrap().kind,
10866 crate::ExprKind::PostfixUnary { .. }
10867 ));
10868 }
10869
10870 #[test]
10871 fn parse_index_assignment() {
10872 let (ast, diags) = parse_str("xs[0] = 1;");
10873 assert!(diags.is_empty(), "unexpected: {diags:?}");
10874 let stmt = single_stmt(&ast);
10875 let crate::StmtKind::AssignIndex {
10876 receiver,
10877 index,
10878 value,
10879 } = stmt.kind
10880 else {
10881 panic!("expected AssignIndex, got {:?}", stmt.kind);
10882 };
10883 assert!(matches!(
10884 ast.try_expr(receiver).unwrap().kind,
10885 crate::ExprKind::Identifier(_)
10886 ));
10887 assert_eq!(
10888 ast.try_expr(index).unwrap().kind,
10889 crate::ExprKind::Number(0.0)
10890 );
10891 assert_eq!(
10892 ast.try_expr(value).unwrap().kind,
10893 crate::ExprKind::Number(1.0)
10894 );
10895 }
10896
10897 #[test]
10898 fn parse_for_update_postfix() {
10899 let (ast, diags) = parse_str("function main(): void { for (let i = 0; i < 3; i++) {} }");
10900 assert!(diags.is_empty(), "unexpected: {diags:?}");
10901 assert_eq!(ast.top_level.len(), 1);
10902 }
10903
10904 #[test]
10905 fn parse_for_update_index_assignment() {
10906 let (ast, diags) =
10907 parse_str("function main(): void { for (let i = 0; i < 3; xs[i] = i) {} }");
10908 assert!(diags.is_empty(), "unexpected: {diags:?}");
10909 assert_eq!(ast.top_level.len(), 1);
10910 }
10911
10912 #[test]
10913 fn index_access_missing_close_bracket_diagnoses() {
10914 let (_, diags) = parse_str("a[1;");
10915 assert!(!diags.is_empty());
10916 assert_eq!(diags[0].message, "expected `]`");
10917 }
10918
10919 #[test]
10920 fn field_access_inside_object_literal_value_does_not_steal_dot() {
10921 let (ast, diags) = parse_str("let x = { a: 1 }.foo;");
10922 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10923 let crate::StmtKind::Let { value, .. } = single_stmt(&ast).kind else {
10924 panic!("expected Let");
10925 };
10926 let (recv_id, name) = field_access(ast.try_expr(value).unwrap());
10927 assert_eq!(name, "foo");
10928 assert_eq!(object_literal(ast.try_expr(recv_id).unwrap()).len(), 1);
10929 }
10930
10931 fn type_of_let(stmt: &crate::Stmt) -> &crate::TypeAnnotation {
10932 match stmt.kind {
10933 crate::StmtKind::Let { ref ty, .. } => ty.as_ref().expect("expected type annotation"),
10934 _ => panic!("expected Let"),
10935 }
10936 }
10937
10938 #[test]
10939 fn parse_array_type_annotation() {
10940 let (ast, diags) = parse_str("let xs: number[] = null;");
10941 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10942 let ty = type_of_let(single_stmt(&ast));
10943 match ty.kind {
10944 crate::TypeAnnotationKind::Array(ref inner) => {
10945 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Name { .. }));
10946 }
10947 _ => panic!("expected Array, got {:?}", ty.kind),
10948 }
10949 }
10950
10951 #[test]
10952 fn parse_nested_array_type_annotation() {
10953 let (ast, diags) = parse_str("let xs: number[][] = null;");
10954 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10955 let ty = type_of_let(single_stmt(&ast));
10956 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
10957 panic!("expected outer Array");
10958 };
10959 let crate::TypeAnnotationKind::Array(ref inner2) = inner.kind else {
10960 panic!("expected inner Array");
10961 };
10962 assert!(matches!(
10963 inner2.kind,
10964 crate::TypeAnnotationKind::Name { .. }
10965 ));
10966 }
10967
10968 #[test]
10969 fn parse_object_type_annotation_simple() {
10970 let (ast, diags) = parse_str("let p: { x: number; y: number } = null;");
10971 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
10972 let ty = type_of_let(single_stmt(&ast));
10973 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
10974 panic!("expected Object, got {:?}", ty.kind);
10975 };
10976 assert_eq!(fields.len(), 2);
10977 assert_eq!(fields[0].name.name, "x");
10978 assert_eq!(fields[1].name.name, "y");
10979 }
10980
10981 #[test]
10982 fn readonly_is_rejected_on_methods_accessors_and_method_signatures() {
10983 let source = "class K {\n\
10984 readonly m(): number { return 1; }\n\
10985 public readonly n<T>(): number { return 1; }\n\
10986 readonly get g(): number { return 1; }\n\
10987 readonly p: number = 1;\n\
10988 }\n\
10989 type T = { readonly r(a: number): boolean; readonly f: () => void };\n\
10990 interface I { readonly s(): void; readonly q: number; }\n";
10991 let (_, diags) = parse_str(source);
10992 let lines: Vec<usize> = diags
10993 .iter()
10994 .map(|d| {
10995 assert_eq!(
10996 d.message,
10997 "`readonly` can only modify a property or index signature"
10998 );
10999 source[..d.span.start as usize].matches('\n').count() + 1
11000 })
11001 .collect();
11002 assert_eq!(lines, vec![2, 3, 4, 7, 8]);
11003 }
11004
11005 #[test]
11006 fn parse_object_type_method_members() {
11007 let (ast, diags) =
11010 parse_str("let p: { m(): number; opt?(): string; r(a: number): boolean } = null;");
11011 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11012 let ty = type_of_let(single_stmt(&ast));
11013 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11014 panic!("expected Object, got {:?}", ty.kind);
11015 };
11016 assert_eq!(fields.len(), 3);
11017 for field in fields {
11018 assert!(
11019 matches!(field.ty.kind, crate::TypeAnnotationKind::Function { .. }),
11020 "member `{}` should be function-typed, got {:?}",
11021 field.name.name,
11022 field.ty.kind,
11023 );
11024 }
11025 assert!(fields[1].optional, "`opt?()` is an optional member");
11026 let crate::TypeAnnotationKind::Function { ref params, .. } = fields[2].ty.kind else {
11027 unreachable!("checked above");
11028 };
11029 assert_eq!(params.len(), 1);
11030 assert_eq!(params[0].name.name, "a");
11031 }
11032
11033 #[test]
11034 fn parse_object_type_annotation_with_commas() {
11035 let (ast, diags) = parse_str("let p: { x: number, y: number } = null;");
11036 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11037 let ty = type_of_let(single_stmt(&ast));
11038 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11039 panic!("expected Object");
11040 };
11041 assert_eq!(fields.len(), 2);
11042 }
11043
11044 #[test]
11045 fn parse_object_type_annotation_empty() {
11046 let (ast, diags) = parse_str("let p: {} = null;");
11047 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11048 let ty = type_of_let(single_stmt(&ast));
11049 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11050 panic!("expected Object");
11051 };
11052 assert!(fields.is_empty());
11053 }
11054
11055 #[test]
11056 fn parse_object_type_with_optional_field() {
11057 let (ast, diags) = parse_str("let p: { id: number; nick?: string } = null;");
11058 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11059 let ty = type_of_let(single_stmt(&ast));
11060 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11061 panic!("expected Object");
11062 };
11063 assert_eq!(fields.len(), 2);
11064 assert_eq!(fields[0].name.name, "id");
11065 assert!(!fields[0].optional);
11066 assert_eq!(fields[1].name.name, "nick");
11067 assert!(fields[1].optional);
11068 }
11069
11070 #[test]
11071 fn parse_object_type_with_readonly_fields() {
11072 let (ast, diags) = parse_str(
11073 "let issue: { readonly id: string; readonly title?: string; body: string } = null;",
11074 );
11075 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11076 let ty = type_of_let(single_stmt(&ast));
11077 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11078 panic!("expected Object");
11079 };
11080 let fields: Vec<(&str, bool, bool)> = fields
11081 .iter()
11082 .map(|field| (field.name.name.as_str(), field.optional, field.readonly))
11083 .collect();
11084 assert_eq!(
11085 fields,
11086 vec![
11087 ("id", false, true),
11088 ("title", true, true),
11089 ("body", false, false),
11090 ]
11091 );
11092 }
11093
11094 #[test]
11095 fn parse_object_type_mixed_optional_and_required() {
11096 let (ast, diags) = parse_str("let p: { a?: number; b: string; c?: boolean } = null;");
11097 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11098 let ty = type_of_let(single_stmt(&ast));
11099 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11100 panic!("expected Object");
11101 };
11102 let opt: Vec<(&str, bool)> = fields
11103 .iter()
11104 .map(|f| (f.name.name.as_str(), f.optional))
11105 .collect();
11106 assert_eq!(opt, vec![("a", true), ("b", false), ("c", true)]);
11107 }
11108
11109 #[test]
11110 fn parse_object_type_keyword_fields() {
11111 let (ast, diags) =
11112 parse_str("let p: { type: string; default?: number; null: boolean } = null;");
11113 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11114 let ty = type_of_let(single_stmt(&ast));
11115 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11116 panic!("expected Object");
11117 };
11118 let opt: Vec<(&str, bool)> = fields
11119 .iter()
11120 .map(|f| (f.name.name.as_str(), f.optional))
11121 .collect();
11122 assert_eq!(
11123 opt,
11124 vec![("type", false), ("default", true), ("null", false)]
11125 );
11126 }
11127
11128 #[test]
11129 fn parse_object_type_quoted_fields() {
11130 let (ast, diags) =
11131 parse_str(r#"let p: { "content-type": string; "x\u002drequest-id"?: string } = null;"#);
11132 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11133 let ty = type_of_let(single_stmt(&ast));
11134 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11135 panic!("expected Object");
11136 };
11137 let opt: Vec<(&str, bool)> = fields
11138 .iter()
11139 .map(|f| (f.name.name.as_str(), f.optional))
11140 .collect();
11141 assert_eq!(opt, vec![("content-type", false), ("x-request-id", true)]);
11142 }
11143
11144 #[test]
11145 fn parse_interface_with_optional_property() {
11146 let (ast, diags) = parse_str("interface User { id: number; deletedAt?: string; }");
11147 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11148 let stmt = single_stmt(&ast);
11149 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11150 panic!("expected interface decl");
11151 };
11152 let opt: Vec<(&str, bool)> = members
11153 .iter()
11154 .map(|m| match m {
11155 crate::InterfaceMember::Property { name, optional, .. } => {
11156 (name.name.as_str(), *optional)
11157 }
11158 _ => panic!("expected property"),
11159 })
11160 .collect();
11161 assert_eq!(opt, vec![("id", false), ("deletedAt", true)]);
11162 }
11163
11164 #[test]
11165 fn parse_interface_with_readonly_properties() {
11166 let (ast, diags) = parse_str(
11167 "interface Issue { readonly id: string; readonly title?: string; body: string; }",
11168 );
11169 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11170 let stmt = single_stmt(&ast);
11171 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11172 panic!("expected interface decl");
11173 };
11174 let opt: Vec<(&str, bool, bool)> = members
11175 .iter()
11176 .map(|member| match member {
11177 crate::InterfaceMember::Property {
11178 name,
11179 optional,
11180 readonly,
11181 ..
11182 } => (name.name.as_str(), *optional, *readonly),
11183 _ => panic!("expected property"),
11184 })
11185 .collect();
11186 assert_eq!(
11187 opt,
11188 vec![
11189 ("id", false, true),
11190 ("title", true, true),
11191 ("body", false, false),
11192 ]
11193 );
11194 }
11195
11196 #[test]
11197 fn parse_readonly_property_name_without_modifier() {
11198 let (ast, diags) = parse_str(
11199 "interface Meta { readonly: boolean; }\nlet meta: { readonly: boolean } = null;",
11200 );
11201 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11202 let crate::StmtKind::InterfaceDecl { ref members, .. } =
11203 ast.try_stmt(ast.top_level[0]).unwrap().kind
11204 else {
11205 panic!("expected interface decl");
11206 };
11207 let crate::InterfaceMember::Property {
11208 ref name, readonly, ..
11209 } = members[0]
11210 else {
11211 panic!("expected property");
11212 };
11213 assert_eq!(name.name, "readonly");
11214 assert!(
11215 !readonly,
11216 "`readonly` here is the property name, not a modifier"
11217 );
11218
11219 let ty = type_of_let(ast.try_stmt(ast.top_level[1]).unwrap());
11220 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11221 panic!("expected Object");
11222 };
11223 assert_eq!(fields[0].name.name, "readonly");
11224 assert!(!fields[0].readonly);
11225 }
11226
11227 #[test]
11228 fn parse_interface_keyword_members() {
11229 let (ast, diags) =
11230 parse_str("interface IssueStatus { type: string; default(): string; null: boolean; }");
11231 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11232 let stmt = single_stmt(&ast);
11233 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11234 panic!("expected interface decl");
11235 };
11236 let names: Vec<&str> = members
11237 .iter()
11238 .map(|m| match m {
11239 crate::InterfaceMember::Method { name, .. }
11240 | crate::InterfaceMember::Property { name, .. } => name.name.as_str(),
11241 crate::InterfaceMember::IndexSignature(_) => panic!("unexpected index signature"),
11242 })
11243 .collect();
11244 assert_eq!(names, vec!["type", "default", "null"]);
11245 }
11246
11247 #[test]
11248 fn parse_interface_quoted_property_members() {
11249 let (ast, diags) = parse_str(
11250 r#"interface Headers { "content-type": string; "x\u002drequest-id"?: string; }"#,
11251 );
11252 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11253 let stmt = single_stmt(&ast);
11254 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11255 panic!("expected interface decl");
11256 };
11257 let names: Vec<(&str, bool)> = members
11258 .iter()
11259 .map(|m| match m {
11260 crate::InterfaceMember::Property { name, optional, .. } => {
11261 (name.name.as_str(), *optional)
11262 }
11263 _ => panic!("expected property"),
11264 })
11265 .collect();
11266 assert_eq!(names, vec![("content-type", false), ("x-request-id", true)]);
11267 }
11268
11269 #[test]
11270 fn parse_param_optional_marker() {
11271 let (ast, diags) = parse_str("function f(x?: number): void {}");
11272 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11273 let crate::StmtKind::Function { ref params, .. } = single_stmt(&ast).kind else {
11274 panic!("expected function")
11275 };
11276 assert!(params[0].optional);
11277 assert!(params[0].default.is_none());
11278 }
11279
11280 #[test]
11281 fn parse_interface_optional_method() {
11282 let (ast, diags) = parse_str("interface F { foo?(x?: number): void; }");
11283 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11284 let crate::StmtKind::InterfaceDecl { ref members, .. } = single_stmt(&ast).kind else {
11285 panic!("expected interface")
11286 };
11287 let crate::InterfaceMember::Property { optional, ty, .. } = &members[0] else {
11288 panic!("expected callable property")
11289 };
11290 assert!(*optional);
11291 let crate::TypeAnnotationKind::Function { params, .. } = &ty.kind else {
11292 panic!("expected function type")
11293 };
11294 assert!(params[0].optional);
11295 }
11296
11297 #[test]
11298 fn parse_interface_with_call_signature() {
11299 let (ast, diags) = parse_str("interface F { (s: string): number; }");
11300 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11301 let stmt = single_stmt(&ast);
11302 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11303 panic!("expected interface decl");
11304 };
11305 assert_eq!(members.len(), 1);
11306 match &members[0] {
11307 crate::InterfaceMember::Method { name, params, .. } => {
11308 assert_eq!(name.name, "@call");
11309 assert_eq!(params.len(), 1);
11310 assert_eq!(params[0].name.name, "s");
11311 }
11312 other => panic!("expected method (call signature), got {other:?}"),
11313 }
11314 }
11315
11316 #[test]
11317 fn parse_interface_mixed_call_signature_and_method() {
11318 let (ast, diags) = parse_str("interface F { greet(): string; (x: number): number; }");
11319 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11320 let stmt = single_stmt(&ast);
11321 let crate::StmtKind::InterfaceDecl { ref members, .. } = stmt.kind else {
11322 panic!("expected interface decl");
11323 };
11324 let names: Vec<&str> = members
11325 .iter()
11326 .map(|m| match m {
11327 crate::InterfaceMember::Method { name, .. } => name.name.as_str(),
11328 _ => "<property>",
11329 })
11330 .collect();
11331 assert_eq!(names, vec!["greet", "@call"]);
11332 }
11333
11334 #[test]
11335 fn parse_interface_rejects_generic_call_signature() {
11336 let (_ast, diags) = parse_str("interface F { <T>(x: T): T; }");
11337 assert!(
11338 diags
11339 .iter()
11340 .any(|d| d.message.contains("generic call signatures")),
11341 "expected generic-call-signature diagnostic, got {diags:?}"
11342 );
11343 }
11344
11345 #[test]
11346 fn parse_object_type_with_array_field() {
11347 let (ast, diags) = parse_str("let p: { xs: number[] } = null;");
11348 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11349 let ty = type_of_let(single_stmt(&ast));
11350 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11351 panic!("expected Object");
11352 };
11353 assert_eq!(fields[0].name.name, "xs");
11354 assert!(matches!(
11355 fields[0].ty.kind,
11356 crate::TypeAnnotationKind::Array(_)
11357 ));
11358 }
11359
11360 #[test]
11361 fn parse_array_of_objects() {
11362 let (ast, diags) = parse_str("let xs: { x: number }[] = null;");
11363 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11364 let ty = type_of_let(single_stmt(&ast));
11365 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11366 panic!("expected Array");
11367 };
11368 assert!(matches!(
11369 inner.kind,
11370 crate::TypeAnnotationKind::Object { .. }
11371 ));
11372 }
11373
11374 #[test]
11375 fn parse_tuple_type_annotation() {
11376 let (ast, diags) = parse_str("let p: [string, number] = null;");
11377 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11378 let ty = type_of_let(single_stmt(&ast));
11379 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11380 panic!("expected Tuple, got {:?}", ty.kind);
11381 };
11382 assert_eq!(elems.len(), 2);
11383 assert!(matches!(
11384 elems[0].kind,
11385 crate::TypeAnnotationKind::Name { .. }
11386 ));
11387 assert!(matches!(
11388 elems[1].kind,
11389 crate::TypeAnnotationKind::Name { .. }
11390 ));
11391 }
11392
11393 #[test]
11394 fn parse_singleton_tuple_type() {
11395 let (ast, diags) = parse_str("let p: [number] = null;");
11396 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11397 let ty = type_of_let(single_stmt(&ast));
11398 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11399 panic!("expected Tuple");
11400 };
11401 assert_eq!(elems.len(), 1);
11402 }
11403
11404 #[test]
11405 fn parse_nested_tuple_type() {
11406 let (ast, diags) = parse_str("let p: [[number, string], boolean] = null;");
11407 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11408 let ty = type_of_let(single_stmt(&ast));
11409 let crate::TypeAnnotationKind::Tuple(ref outer) = ty.kind else {
11410 panic!("expected outer Tuple");
11411 };
11412 assert_eq!(outer.len(), 2);
11413 let crate::TypeAnnotationKind::Tuple(ref inner) = outer[0].kind else {
11414 panic!("expected inner Tuple");
11415 };
11416 assert_eq!(inner.len(), 2);
11417 }
11418
11419 #[test]
11420 fn parse_array_of_tuple_type() {
11421 let (ast, diags) = parse_str("let xs: [string, number][] = null;");
11422 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11423 let ty = type_of_let(single_stmt(&ast));
11424 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11425 panic!("expected outer Array, got {:?}", ty.kind);
11426 };
11427 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Tuple(_)));
11428 }
11429
11430 #[test]
11431 fn parse_trailing_comma_tuple_type() {
11432 let (ast, diags) = parse_str("let p: [number, string,] = null;");
11433 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11434 let ty = type_of_let(single_stmt(&ast));
11435 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11436 panic!("expected Tuple");
11437 };
11438 assert_eq!(elems.len(), 2);
11439 }
11440
11441 #[test]
11442 fn parse_empty_tuple_type_rejected() {
11443 let (_ast, diags) = parse_str("let p: [] = null;");
11444 assert!(
11445 diags.iter().any(|d| d
11446 .message
11447 .contains("tuple types must have at least one element")),
11448 "expected empty-tuple diagnostic, got: {diags:?}"
11449 );
11450 }
11451
11452 #[test]
11453 fn parse_labeled_tuple_drops_labels() {
11454 let (ast, diags) = parse_str("let p: [first: number, string] = null;");
11455 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11456 let ty = type_of_let(single_stmt(&ast));
11457 let crate::TypeAnnotationKind::Tuple(ref elems) = ty.kind else {
11458 panic!("expected Tuple, got {:?}", ty.kind);
11459 };
11460 assert_eq!(elems.len(), 2);
11461 assert!(
11462 elems
11463 .iter()
11464 .all(|e| matches!(e.kind, crate::TypeAnnotationKind::Name { .. }))
11465 );
11466 }
11467
11468 #[test]
11469 fn parse_labeled_optional_tuple_element() {
11470 let (ast, diags) = parse_str("let p: [first: number, second?: string] = null;");
11471 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11472 let ty = type_of_let(single_stmt(&ast));
11473 let crate::TypeAnnotationKind::Tuple(elements) = &ty.kind else {
11474 panic!("expected tuple")
11475 };
11476 assert!(matches!(
11477 elements[1].kind,
11478 crate::TypeAnnotationKind::Optional(_)
11479 ));
11480 }
11481
11482 #[test]
11483 fn parse_readonly_binds_to_the_postfix_type() {
11484 let (ast, diags) = parse_str("let p: readonly number[][] | null = null;");
11485 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11486 let ty = type_of_let(single_stmt(&ast));
11487 let crate::TypeAnnotationKind::Union(ref members) = ty.kind else {
11488 panic!("expected Union, got {:?}", ty.kind);
11489 };
11490 let crate::TypeAnnotationKind::Readonly(ref operand) = members[0].kind else {
11491 panic!("expected Readonly, got {:?}", members[0].kind);
11492 };
11493 let crate::TypeAnnotationKind::Array(ref inner) = operand.kind else {
11494 panic!("expected Array, got {:?}", operand.kind);
11495 };
11496 assert!(matches!(inner.kind, crate::TypeAnnotationKind::Array(_)));
11497 }
11498
11499 #[test]
11500 fn parse_readonly_as_a_type_name_is_not_the_operator() {
11501 let (ast, diags) = parse_str("let p: readonly = null;");
11502 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11503 let ty = type_of_let(single_stmt(&ast));
11504 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11505 }
11506
11507 #[test]
11508 fn parse_generic_one_arg() {
11509 let (ast, diags) = parse_str("let x: Foo<number> = null;");
11510 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11511 let ty = type_of_let(single_stmt(&ast));
11512 let crate::TypeAnnotationKind::Name { ref name, ref args } = ty.kind else {
11513 panic!("expected Name, got {:?}", ty.kind);
11514 };
11515 assert_eq!(name.span, crate::Span::new(F, 7, 10).unwrap());
11516 assert_eq!(ty.span, crate::Span::new(F, 7, 18).unwrap());
11517 assert_eq!(args.len(), 1);
11518 assert!(matches!(
11519 args[0].kind,
11520 crate::TypeAnnotationKind::Name { .. }
11521 ));
11522 }
11523
11524 #[test]
11525 fn parse_generic_two_args() {
11526 let (ast, diags) = parse_str("let m: Map<string, number> = null;");
11527 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11528 let ty = type_of_let(single_stmt(&ast));
11529 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11530 panic!("expected Name");
11531 };
11532 assert_eq!(args.len(), 2);
11533 }
11534
11535 #[test]
11536 fn parse_generic_nested() {
11537 let (ast, diags) = parse_str("let b: Box<Box<T>> = null;");
11538 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11539 let ty = type_of_let(single_stmt(&ast));
11540 let crate::TypeAnnotationKind::Name { ref args, .. } = ty.kind else {
11541 panic!("expected outer Name");
11542 };
11543 assert_eq!(args.len(), 1);
11544 let crate::TypeAnnotationKind::Name {
11545 args: ref inner, ..
11546 } = args[0].kind
11547 else {
11548 panic!("expected inner Name");
11549 };
11550 assert_eq!(inner.len(), 1);
11551 }
11552
11553 #[test]
11554 fn parse_generic_array_postfix() {
11555 let (ast, diags) = parse_str("let xs: Foo<number>[] = null;");
11556 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11557 let ty = type_of_let(single_stmt(&ast));
11558 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11559 panic!("expected Array");
11560 };
11561 assert!(
11562 matches!(inner.kind, crate::TypeAnnotationKind::Name { ref args, .. } if args.len() == 1)
11563 );
11564 }
11565
11566 #[test]
11567 fn parse_generic_empty_diagnoses() {
11568 let (_ast, diags) = parse_str("let x: Foo<> = null;");
11569 assert!(
11570 diags
11571 .iter()
11572 .any(|d| d.message.contains("empty generic argument list")),
11573 "expected empty-generic-args diagnostic, got: {diags:?}"
11574 );
11575 }
11576
11577 #[test]
11578 fn parse_generic_trailing_comma_ok() {
11579 let (_ast, diags) = parse_str("let x: Foo<number,> = null;");
11580 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11581 }
11582
11583 #[test]
11584 fn parse_generic_missing_close_diagnoses() {
11585 let (_ast, diags) = parse_str("let x: Foo<number = null;");
11586 assert!(
11587 diags
11588 .iter()
11589 .any(|d| d.message.contains("expected `,` or `>`")),
11590 "expected unterminated-generic diagnostic, got: {diags:?}"
11591 );
11592 }
11593
11594 #[test]
11595 fn parse_qualified_two_segments() {
11596 let (ast, diags) = parse_str("let x: Foo.Bar = null;");
11597 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11598 let ty = type_of_let(single_stmt(&ast));
11599 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11600 panic!("expected Qualified, got {:?}", ty.kind);
11601 };
11602 assert_eq!(path.len(), 2);
11603 assert_eq!(path[0].span, crate::Span::new(F, 7, 10).unwrap());
11605 assert_eq!(path[1].span, crate::Span::new(F, 11, 14).unwrap());
11606 assert!(args.is_empty());
11607 assert_eq!(ty.span, crate::Span::new(F, 7, 14).unwrap());
11608 }
11609
11610 #[test]
11611 fn parse_qualified_three_segments() {
11612 let (ast, diags) = parse_str("let x: Foo.Bar.Baz = null;");
11613 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11614 let ty = type_of_let(single_stmt(&ast));
11615 let crate::TypeAnnotationKind::Qualified { ref path, .. } = ty.kind else {
11616 panic!("expected Qualified, got {:?}", ty.kind);
11617 };
11618 assert_eq!(path.len(), 3);
11619 }
11620
11621 #[test]
11622 fn parse_qualified_with_generic_args() {
11623 let (ast, diags) = parse_str("let x: Foo.Box<number> = null;");
11624 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11625 let ty = type_of_let(single_stmt(&ast));
11626 let crate::TypeAnnotationKind::Qualified { ref path, ref args } = ty.kind else {
11627 panic!("expected Qualified, got {:?}", ty.kind);
11628 };
11629 assert_eq!(path.len(), 2);
11630 assert_eq!(args.len(), 1);
11631 assert!(matches!(
11632 args[0].kind,
11633 crate::TypeAnnotationKind::Name { .. }
11634 ));
11635 }
11636
11637 #[test]
11638 fn parse_qualified_with_array_postfix() {
11639 let (ast, diags) = parse_str("let xs: Foo.Bar[] = null;");
11640 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11641 let ty = type_of_let(single_stmt(&ast));
11642 let crate::TypeAnnotationKind::Array(ref inner) = ty.kind else {
11643 panic!("expected Array, got {:?}", ty.kind);
11644 };
11645 assert!(matches!(
11646 inner.kind,
11647 crate::TypeAnnotationKind::Qualified { .. }
11648 ));
11649 }
11650
11651 #[test]
11652 fn parse_qualified_trailing_dot_diagnoses() {
11653 let (_ast, diags) = parse_str("let x: Foo. = null;");
11654 assert!(
11655 diags.iter().any(|d| d
11656 .message
11657 .contains("expected identifier after `.` in type name")),
11658 "expected dotted-name diagnostic, got: {diags:?}"
11659 );
11660 }
11661
11662 #[test]
11663 fn parse_qualified_void_root_rejected_dot() {
11664 let (ast, diags) = parse_str("function f(): void.Bar { }");
11665 assert!(!diags.is_empty(), "expected a diagnostic for `void.Bar`");
11666 let _ = ast;
11667 }
11668
11669 fn union_members(ty: &crate::TypeAnnotation) -> &[crate::TypeAnnotation] {
11670 match &ty.kind {
11671 crate::TypeAnnotationKind::Union(members) => members,
11672 other => panic!("expected Union, got {other:?}"),
11673 }
11674 }
11675
11676 #[test]
11677 fn parse_union_two_members() {
11678 let (ast, diags) = parse_str("let x: number | string = null;");
11679 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11680 let ty = type_of_let(single_stmt(&ast));
11681 let members = union_members(ty);
11682 assert_eq!(members.len(), 2);
11683 assert!(matches!(
11684 members[0].kind,
11685 crate::TypeAnnotationKind::Name { .. }
11686 ));
11687 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
11689 members[1].kind,
11690 crate::TypeAnnotationKind::Name { .. }
11691 ));
11692 assert_eq!(members[1].span, crate::Span::new(F, 16, 22).unwrap()); assert_eq!(ty.span, crate::Span::new(F, 7, 22).unwrap());
11694 }
11695
11696 #[test]
11697 fn parse_union_three_members() {
11698 let (ast, diags) = parse_str("let x: A | B | C = null;");
11699 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11700 let ty = type_of_let(single_stmt(&ast));
11701 let members = union_members(ty);
11702 assert_eq!(members.len(), 3);
11703 for m in members {
11704 assert!(matches!(m.kind, crate::TypeAnnotationKind::Name { .. }));
11705 }
11706 }
11707
11708 #[test]
11709 fn parse_union_lower_than_array() {
11710 let (ast, diags) = parse_str("let x: number[] | string = null;");
11711 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11712 let ty = type_of_let(single_stmt(&ast));
11713 let members = union_members(ty);
11714 assert_eq!(members.len(), 2);
11715 assert!(
11716 matches!(members[0].kind, crate::TypeAnnotationKind::Array(_)),
11717 "expected first member to be Array, got {:?}",
11718 members[0].kind
11719 );
11720 assert!(matches!(
11721 members[1].kind,
11722 crate::TypeAnnotationKind::Name { .. }
11723 ));
11724 }
11725
11726 #[test]
11727 fn parse_union_with_null_member() {
11728 let (ast, diags) = parse_str("let x: number | null = null;");
11729 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11730 let ty = type_of_let(single_stmt(&ast));
11731 let members = union_members(ty);
11732 assert_eq!(members.len(), 2);
11733 assert_eq!(members[0].span, crate::Span::new(F, 7, 13).unwrap()); assert!(matches!(
11735 members[1].kind,
11736 crate::TypeAnnotationKind::Name { .. }
11737 ));
11738 assert_eq!(members[1].span, crate::Span::new(F, 16, 20).unwrap()); }
11740
11741 #[test]
11742 fn parse_union_in_function_return_type() {
11743 let (ast, diags) = parse_str("function f(): number | string { }");
11744 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11745 let stmt = single_stmt(&ast);
11746 match stmt.kind {
11747 crate::StmtKind::Function {
11748 ref return_type, ..
11749 } => {
11750 let return_type = return_type.as_ref().expect("plain return type");
11751 let members = union_members(return_type);
11752 assert_eq!(members.len(), 2);
11753 }
11754 _ => panic!("expected Function"),
11755 }
11756 }
11757
11758 #[test]
11759 fn parse_union_in_function_param_type() {
11760 let (ast, diags) = parse_str("function f(x: number | string): void { }");
11761 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11762 let stmt = single_stmt(&ast);
11763 match stmt.kind {
11764 crate::StmtKind::Function { ref params, .. } => {
11765 let p_ty = params[0].ty.as_ref().expect("param type");
11766 let members = union_members(p_ty);
11767 assert_eq!(members.len(), 2);
11768 }
11769 _ => panic!("expected Function"),
11770 }
11771 }
11772
11773 #[test]
11774 fn parse_union_in_object_field_type() {
11775 let (ast, diags) = parse_str("let p: { x: number | string } = null;");
11776 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11777 let ty = type_of_let(single_stmt(&ast));
11778 let crate::TypeAnnotationKind::Object { ref fields, .. } = ty.kind else {
11779 panic!("expected Object, got {:?}", ty.kind);
11780 };
11781 assert_eq!(fields.len(), 1);
11782 let members = union_members(&fields[0].ty);
11783 assert_eq!(members.len(), 2);
11784 }
11785
11786 #[test]
11787 fn parse_union_leading_pipe_is_forgiven() {
11788 let (ast, diags) = parse_str("let x: | number | string = null;");
11789 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11790 let ty = type_of_let(single_stmt(&ast));
11791 let members = union_members(ty);
11792 assert_eq!(members.len(), 2);
11793 }
11794
11795 #[test]
11796 fn parse_union_leading_pipe_single_member_unwraps() {
11797 let (ast, diags) = parse_str("let x: | number = null;");
11798 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11799 let ty = type_of_let(single_stmt(&ast));
11800 assert!(matches!(ty.kind, crate::TypeAnnotationKind::Name { .. }));
11801 }
11802
11803 #[test]
11804 fn parse_union_trailing_pipe_diagnoses() {
11805 let (_, diags) = parse_str("let x: number | = null;");
11806 assert!(!diags.is_empty());
11807 assert_eq!(diags[0].message, "expected type");
11808 }
11809
11810 #[test]
11811 fn snapshot_union_type_mix() {
11812 let (ast, diags) = parse_str("let xs: number[] | string | null = null;");
11813 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11814 insta::assert_debug_snapshot!(ast);
11815 }
11816
11817 #[test]
11818 fn parse_object_type_duplicate_field_diagnoses() {
11819 let (_, diags) = parse_str("let p: { readonly x: number; x: string } = null;");
11820 assert!(!diags.is_empty());
11821 assert!(
11822 diags
11823 .iter()
11824 .any(|d| d.message == "duplicate field `x` in object type"),
11825 "expected duplicate-field diagnostic, got: {diags:?}"
11826 );
11827 }
11828
11829 #[test]
11830 fn snapshot_object_and_array_literals() {
11831 let (ast, diags) = parse_str(r#"let mix = [{ a: 1, b: "hi" }, {}];"#);
11832 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11833 insta::assert_debug_snapshot!(ast);
11834 }
11835
11836 #[test]
11837 fn snapshot_function_with_while() {
11838 let source = "function loop(n: number): void {\n while (n > 0) {\n log(n);\n }\n}";
11839 let (ast, diags) = parse_str(source);
11840 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11841 insta::assert_debug_snapshot!(ast);
11842 }
11843
11844 fn parse_arrow_const(
11845 source: &str,
11846 ) -> (
11847 Ast,
11848 Vec<crate::ParamDecl>,
11849 Option<crate::TypeAnnotation>,
11850 crate::ArrowBody,
11851 ) {
11852 let (ast, diags) = parse_str(source);
11853 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11854 assert_eq!(ast.top_level.len(), 1);
11855 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11856 let value = match &stmt.kind {
11857 StmtKind::Const { value, .. } => *value,
11858 _ => panic!("expected const declaration"),
11859 };
11860 let value = match ast.try_expr(value).unwrap().kind {
11861 ExprKind::FunctionExpression { function, .. } => function,
11862 _ => value,
11863 };
11864 let expr = ast.try_expr(value).unwrap().clone();
11865 let (params, return_type, body) = match expr.kind {
11866 ExprKind::Arrow {
11867 params,
11868 return_type,
11869 body,
11870 ..
11871 } => (params, return_type, body),
11872 other => panic!("expected Arrow, got {other:?}"),
11873 };
11874 (ast, params, return_type, body)
11875 }
11876
11877 #[test]
11878 fn parses_anonymous_function_expression_as_arrow() {
11879 let (ast, params, return_type, body) =
11880 parse_arrow_const("const f = function (x: number): number { return x + 1; };");
11881 assert_eq!(params.len(), 1);
11882 assert_eq!(params[0].name.name, "x");
11883 assert!(return_type.is_some());
11884 assert!(matches!(body, crate::ArrowBody::Block(_)));
11885 let _ = ast;
11886 }
11887
11888 #[test]
11889 fn parses_function_expression_with_no_params() {
11890 let (_ast, params, _return_type, body) =
11891 parse_arrow_const("const f = function (): void {};");
11892 assert!(params.is_empty());
11893 assert!(matches!(body, crate::ArrowBody::Block(_)));
11894 }
11895
11896 #[test]
11898 fn parses_named_function_expression_signature() {
11899 let (_ast, params, _return_type, _body) =
11900 parse_arrow_const("const f = function named(x: number): number { return x + 1; };");
11901 assert_eq!(params.len(), 1);
11902 }
11903
11904 #[test]
11906 fn accepts_a_named_function_expression_that_calls_itself() {
11907 let (_ast, diags) =
11908 parse_str("const f = function bar(x: number): number { return bar(x); };");
11909 assert!(diags.is_empty(), "{diags:?}");
11910 }
11911
11912 #[test]
11914 fn accepts_a_named_function_expression_using_the_name_as_a_property() {
11915 let (_ast, diags) = parse_str("const f = function bar(o: O): number { return o.bar; };");
11916 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11917 }
11918
11919 #[test]
11921 fn accepts_a_named_function_expression_using_the_name_as_an_object_key() {
11922 let (_ast, diags) = parse_str(
11923 "const f = function bar(x: number): number { const o = { bar: 1 }; return o.bar + x; };",
11924 );
11925 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11926 }
11927
11928 #[test]
11930 fn accepts_a_named_function_expression_with_its_name_in_a_string() {
11931 let (_ast, diags) =
11932 parse_str("const f = function bar(x: number): string { return \"bar\"; };");
11933 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11934 }
11935
11936 #[test]
11938 fn accepts_a_self_call_inside_a_template_substitution() {
11939 let (_ast, diags) =
11940 parse_str("const f = function bar(x: number): string { return `${bar(0)}`; };");
11941 assert!(diags.is_empty(), "{diags:?}");
11942 }
11943
11944 #[test]
11945 fn rejects_a_generic_function_expression() {
11946 let (_ast, diags) = parse_str("const f = function <T>(x: T): T { return x; };");
11947 assert!(
11948 diags
11949 .iter()
11950 .any(|d| d.message.contains("generic function expressions")),
11951 "generic function expression should be rejected: {diags:?}"
11952 );
11953 }
11954
11955 #[test]
11956 fn accepts_this_inside_a_function_expression_in_a_class_method() {
11957 let (_ast, diags) = parse_str(
11958 "class C { x: number = 1; m(): number { \
11959 const f = function (this: { x: number }): number { return this.x; }; return f(); } }",
11960 );
11961 assert!(diags.is_empty(), "{diags:?}");
11962 }
11963
11964 #[test]
11965 fn retains_this_boundary_inside_method_shorthand() {
11966 let (ast, diags) = parse_str(
11967 "class C { x: number = 1; m(): number { \
11968 const o = { x: 2, g(): number { return this.x; } }; return o.g(); } }",
11969 );
11970 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11971 assert!((0..ast.exprs_len()).any(|index| matches!(
11972 ast.try_expr(crate::ExprId(index as u32)).unwrap().kind,
11973 ExprKind::ThisOutsideReceiver
11974 )));
11975 }
11976
11977 #[test]
11978 fn this_still_parses_in_an_ordinary_class_method() {
11979 let (_ast, diags) = parse_str("class C { x: number = 1; m(): number { return this.x; } }");
11980 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11981 }
11982
11983 #[test]
11985 fn function_keyword_still_parses_as_an_object_key() {
11986 let (_ast, diags) = parse_str("let o = { function: 1 };");
11987 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11988 }
11989
11990 fn object_literal_member_values(source: &str) -> (Ast, Vec<crate::ExprId>) {
11991 let (ast, diags) = parse_str(source);
11992 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
11993 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
11994 let value = match &stmt.kind {
11995 StmtKind::Const { value, .. } => *value,
11996 other => panic!("expected a const declaration, got {other:?}"),
11997 };
11998 let members = match &ast.try_expr(value).unwrap().kind {
11999 ExprKind::ObjectLiteral { members } => members
12000 .iter()
12001 .map(crate::ObjectLiteralMember::value)
12002 .collect::<Vec<_>>(),
12003 other => panic!("expected ObjectLiteral, got {other:?}"),
12004 };
12005 (ast, members)
12006 }
12007
12008 #[test]
12009 fn parses_object_literal_method_shorthand_as_arrow() {
12010 let (ast, members) =
12011 object_literal_member_values("const o = { m(x: number): number { return x; } };");
12012 assert_eq!(members.len(), 1);
12013 match &ast.try_expr(members[0]).unwrap().kind {
12014 ExprKind::Arrow { params, body, .. } => {
12015 assert_eq!(params.len(), 1);
12016 assert_eq!(params[0].name.name, "x");
12017 assert!(matches!(body, crate::ArrowBody::Block(_)));
12018 }
12019 other => panic!("expected Arrow, got {other:?}"),
12020 }
12021 }
12022
12023 #[test]
12025 fn parses_method_shorthand_with_a_keyword_name() {
12026 let (ast, members) =
12027 object_literal_member_values("const o = { if(x: number): number { return x; } };");
12028 assert!(matches!(
12029 ast.try_expr(members[0]).unwrap().kind,
12030 ExprKind::Arrow { .. }
12031 ));
12032 }
12033
12034 #[test]
12035 fn parses_method_shorthand_alongside_plain_and_shorthand_properties() {
12036 let (ast, members) =
12037 object_literal_member_values("const o = { a: 1, m(): void {}, b: 2 };");
12038 assert_eq!(members.len(), 3);
12039 assert!(matches!(
12040 ast.try_expr(members[1]).unwrap().kind,
12041 ExprKind::Arrow { .. }
12042 ));
12043 }
12044
12045 #[test]
12046 fn parses_bare_ident_arrow_expression_body() {
12047 let (_ast, params, return_type, body) = parse_arrow_const("const f = x => x + 1;");
12048 assert_eq!(params.len(), 1);
12049 assert_eq!(params[0].name.name, "x");
12050 assert!(params[0].ty.is_none());
12051 assert!(return_type.is_none());
12052 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12053 }
12054
12055 #[test]
12056 fn parses_zero_param_arrow() {
12057 let (_ast, params, return_type, body) = parse_arrow_const("const f = () => 0;");
12058 assert!(params.is_empty());
12059 assert!(return_type.is_none());
12060 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12061 }
12062
12063 #[test]
12064 fn parses_paren_unannotated_arrow() {
12065 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x) => x;");
12066 assert_eq!(params.len(), 1);
12067 assert_eq!(params[0].name.name, "x");
12068 assert!(params[0].ty.is_none());
12069 }
12070
12071 #[test]
12072 fn parses_typed_param_arrow() {
12073 let (_ast, params, return_type, body) =
12074 parse_arrow_const("const f = (x: number) => x * 2;");
12075 assert_eq!(params.len(), 1);
12076 assert!(params[0].ty.is_some());
12077 assert!(return_type.is_none());
12078 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12079 }
12080
12081 #[test]
12082 fn parses_typed_param_and_return_arrow() {
12083 let (_ast, params, return_type, body) =
12084 parse_arrow_const("const f = (x: number): number => x * 2;");
12085 assert_eq!(params.len(), 1);
12086 assert!(params[0].ty.is_some());
12087 assert!(return_type.is_some());
12088 assert!(matches!(body, crate::ArrowBody::Expr(_)));
12089 }
12090
12091 #[test]
12092 fn parses_multi_param_unannotated_arrow() {
12093 let (_ast, params, _return_type, _body) = parse_arrow_const("const f = (x, y) => x + y;");
12094 assert_eq!(params.len(), 2);
12095 assert_eq!(params[0].name.name, "x");
12096 assert_eq!(params[1].name.name, "y");
12097 assert!(params[0].ty.is_none());
12098 assert!(params[1].ty.is_none());
12099 }
12100
12101 #[test]
12102 fn parses_multi_param_typed_arrow() {
12103 let (_ast, params, _return_type, _body) =
12104 parse_arrow_const("const f = (x: number, y: number) => x + y;");
12105 assert_eq!(params.len(), 2);
12106 assert!(params[0].ty.is_some());
12107 assert!(params[1].ty.is_some());
12108 }
12109
12110 #[test]
12111 fn parses_block_body_arrow() {
12112 let (_ast, params, _return_type, body) =
12113 parse_arrow_const("const f = (x: number) => { return x; };");
12114 assert_eq!(params.len(), 1);
12115 assert!(matches!(body, crate::ArrowBody::Block(_)));
12116 }
12117
12118 #[test]
12119 fn rejects_typed_bare_ident_arrow() {
12120 let (_ast, diags) = parse_str("const f = x: number => x;");
12121 assert!(
12122 !diags.is_empty(),
12123 "expected diagnostics for `x: number => …`",
12124 );
12125 }
12126
12127 #[test]
12128 fn arrow_inside_call_argument_parses() {
12129 let (ast, diags) = parse_str("f((x: number) => x + 1, 2);");
12130 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12131 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12132 let call = match &stmt.kind {
12133 StmtKind::Expr(eid) => ast.try_expr(*eid).unwrap(),
12134 _ => panic!("expected expression statement"),
12135 };
12136 let args = match &call.kind {
12137 ExprKind::Call { args, .. } => args.clone(),
12138 other => panic!("expected Call, got {other:?}"),
12139 };
12140 assert_eq!(args.len(), 2);
12141 assert!(matches!(
12142 ast.try_expr(args[0]).unwrap().kind,
12143 ExprKind::Arrow { .. }
12144 ));
12145 }
12146
12147 #[test]
12148 fn function_decl_param_still_requires_annotation() {
12149 let (_ast, diags) = parse_str("function f(x): number { return x; }");
12150 assert!(
12151 diags.iter().any(|d| d.message.contains("type annotation")),
12152 "expected `parameter requires a type annotation` diagnostic, got {diags:?}",
12153 );
12154 }
12155
12156 #[test]
12157 fn paren_expression_still_parses_when_no_arrow() {
12158 let (ast, diags) = parse_str("const a = (1 + 2);");
12159 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12160 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12161 let value = match &stmt.kind {
12162 StmtKind::Const { value, .. } => *value,
12163 _ => panic!("expected const declaration"),
12164 };
12165 assert!(matches!(
12166 ast.try_expr(value).unwrap().kind,
12167 ExprKind::Paren(_)
12168 ));
12169 }
12170
12171 #[test]
12172 fn parse_named_import_single() {
12173 let (ast, diags) = parse_str("import { v4 } from \"submilli:uuid\";");
12174 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12175 assert_eq!(ast.top_level.len(), 1);
12176 let stmt = ast.try_stmt(ast.top_level[0]).unwrap();
12177 let (module, kind) = match &stmt.kind {
12178 StmtKind::Import { module, kind, .. } => (module, kind),
12179 other => panic!("expected Import, got {other:?}"),
12180 };
12181 assert_eq!(module, "submilli:uuid");
12182 let specs = match kind {
12183 ImportKind::Named(specs) => specs,
12184 other => panic!("expected Named, got {other:?}"),
12185 };
12186 assert_eq!(specs.len(), 1);
12187 assert_eq!(specs[0].imported_name.name, "v4");
12188 assert_eq!(specs[0].local_name.name, "v4");
12189 }
12190
12191 #[test]
12192 fn parse_named_import_multi_and_alias() {
12193 let (ast, diags) =
12194 parse_str("import { v4, v7 as makeId, validate } from \"submilli:uuid\";");
12195 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12196 let specs = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12197 StmtKind::Import {
12198 kind: ImportKind::Named(s),
12199 ..
12200 } => s.clone(),
12201 other => panic!("expected named import, got {other:?}"),
12202 };
12203 assert_eq!(specs.len(), 3);
12204 assert_eq!(specs[0].imported_name.name, "v4");
12205 assert_eq!(specs[0].local_name.name, "v4");
12206 assert_eq!(specs[1].imported_name.name, "v7");
12207 assert_eq!(specs[1].local_name.name, "makeId");
12208 assert_eq!(specs[2].imported_name.name, "validate");
12209 assert_eq!(specs[2].local_name.name, "validate");
12210 }
12211
12212 #[test]
12213 fn parse_namespace_import() {
12214 let (ast, diags) = parse_str("import uuid from \"submilli:uuid\";");
12215 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12216 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12217 StmtKind::Import { kind, .. } => kind.clone(),
12218 other => panic!("expected import, got {other:?}"),
12219 };
12220 let local = match kind {
12221 ImportKind::Namespace { local_name } => local_name,
12222 other => panic!("expected Namespace, got {other:?}"),
12223 };
12224 assert_eq!(local.name, "uuid");
12225 }
12226
12227 #[test]
12228 fn parse_wildcard_namespace_import_synonym() {
12229 let (ast, diags) = parse_str("import * as uuid from \"submilli:uuid\";");
12230 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
12231 let kind = match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
12232 StmtKind::Import { kind, .. } => kind.clone(),
12233 other => panic!("expected import, got {other:?}"),
12234 };
12235 let local = match kind {
12236 ImportKind::Namespace { local_name } => local_name,
12237 other => panic!("expected Namespace, got {other:?}"),
12238 };
12239 assert_eq!(local.name, "uuid");
12240 }
12241
12242 #[test]
12243 fn parse_wildcard_namespace_import_without_as_rejected() {
12244 let (_ast, diags) = parse_str("import * from \"submilli:uuid\";");
12245 assert!(
12246 diags
12247 .iter()
12248 .any(|d| d.message.contains("expected `as <name>`")),
12249 "expected missing-as diagnostic, got: {diags:?}",
12250 );
12251 }
12252
12253 #[test]
12254 fn parse_combined_default_and_named_rejected() {
12255 let (_ast, diags) = parse_str("import uuid, { v4 } from \"submilli:uuid\";");
12256 assert!(
12257 diags
12258 .iter()
12259 .any(|d| d.message.contains("combining default and named")),
12260 "expected combined-import diagnostic, got: {diags:?}",
12261 );
12262 }
12263
12264 #[test]
12265 fn parse_side_effect_import_rejected() {
12266 let (_ast, diags) = parse_str("import \"submilli:uuid\";");
12267 assert!(
12268 diags
12269 .iter()
12270 .any(|d| d.message.contains("side-effect-only imports")),
12271 "expected side-effect-import diagnostic, got: {diags:?}",
12272 );
12273 }
12274
12275 #[test]
12276 fn parse_empty_specifier_list_rejected() {
12277 let (_ast, diags) = parse_str("import {} from \"submilli:uuid\";");
12278 assert!(
12279 diags
12280 .iter()
12281 .any(|d| d.message.contains("empty import specifier list")),
12282 "expected empty-specifier diagnostic, got: {diags:?}",
12283 );
12284 }
12285
12286 #[test]
12287 fn parse_import_inside_function_body_rejected() {
12288 let (_ast, diags) =
12289 parse_str("function main(): void { import { v4 } from \"submilli:uuid\"; }");
12290 assert!(
12291 diags
12292 .iter()
12293 .any(|d| d.message.contains("must appear at the top of the file")),
12294 "expected top-level diagnostic, got: {diags:?}",
12295 );
12296 }
12297
12298 #[test]
12299 fn parse_missing_from_rejected() {
12300 let (_ast, diags) = parse_str("import { v4 } \"submilli:uuid\";");
12301 assert!(
12302 diags.iter().any(|d| d.message.contains("expected `from`")),
12303 "expected missing-from diagnostic, got: {diags:?}",
12304 );
12305 }
12306
12307 #[test]
12308 fn parse_missing_module_specifier_rejected() {
12309 let (_ast, diags) = parse_str("import { v4 } from;");
12310 assert!(
12311 diags
12312 .iter()
12313 .any(|d| d.message.contains("expected module specifier")),
12314 "expected missing-module diagnostic, got: {diags:?}",
12315 );
12316 }
12317
12318 #[test]
12319 fn parse_export_function_marks_exported() {
12320 let (ast, diags) = parse_str("export function main(): void {}");
12323 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12324 let stmt = single_stmt(&ast);
12325 assert!(
12326 matches!(stmt.kind, crate::StmtKind::Function { .. }),
12327 "expected Function decl, got {:?}",
12328 stmt.kind,
12329 );
12330 assert_eq!(ast.exported_decls.len(), 1, "one exported decl");
12331 assert_eq!(ast.exported_decls[0].stmt, ast.top_level[0]);
12332 }
12333
12334 #[test]
12335 fn parse_export_const_type_interface_enum_are_marked() {
12336 for src in [
12337 "export const X: number = 1;",
12338 "export type Id = string;",
12339 "export interface P { x: number; }",
12340 "export enum E { A, B }",
12341 ] {
12342 let (ast, diags) = parse_str(src);
12343 assert!(diags.is_empty(), "unexpected diags for {src:?}: {diags:?}");
12344 assert_eq!(ast.top_level.len(), 1, "one decl for {src:?}");
12345 assert_eq!(ast.exported_decls.len(), 1, "one exported decl for {src:?}");
12346 }
12347 }
12348
12349 #[test]
12350 fn parse_export_default_is_rejected_with_focused_message() {
12351 let (_ast, diags) = parse_str("export default function main(): void {}");
12352 assert!(
12353 diags
12354 .iter()
12355 .any(|d| d.message.contains("`export default` is not supported")),
12356 "expected focused export-default diagnostic, got: {diags:?}",
12357 );
12358 assert!(
12359 !diags.iter().any(|d| d.message.contains("expected `;`")),
12360 "the spurious missing-semicolon error must be gone: {diags:?}",
12361 );
12362 }
12363
12364 #[test]
12365 fn parse_export_list_parses_as_export_from() {
12366 let (ast, diags) = parse_str("function main(): void {}\nexport { main };");
12369 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12370 assert_eq!(ast.top_level.len(), 2, "function + export-from");
12371 let export = ast.try_stmt(ast.top_level[1]).unwrap();
12372 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12373 panic!("expected ExportFrom, got {:?}", export.kind);
12374 };
12375 assert!(source.is_none(), "bare `export {{ x }}` has no source");
12376 assert_eq!(specs.len(), 1);
12377 assert_eq!(specs[0].imported_name.name, "main");
12378 assert_eq!(specs[0].local_name.name, "main");
12379 }
12380
12381 #[test]
12382 fn parse_export_from_with_source_and_alias_parses() {
12383 let (ast, diags) = parse_str("export { foo, bar as baz } from \"./util\";");
12384 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12385 let export = ast.try_stmt(ast.top_level[0]).unwrap();
12386 let crate::StmtKind::ExportFrom { specs, source, .. } = &export.kind else {
12387 panic!("expected ExportFrom, got {:?}", export.kind);
12388 };
12389 let Some((module, _)) = source else {
12390 panic!("expected a `from` source");
12391 };
12392 assert_eq!(module, "./util");
12393 assert_eq!(specs.len(), 2);
12394 assert_eq!(specs[1].imported_name.name, "bar");
12395 assert_eq!(specs[1].local_name.name, "baz");
12396 }
12397
12398 #[test]
12399 fn parse_export_inside_function_body_rejected() {
12400 let (_ast, diags) = parse_str("function main(): void { export const X: number = 1; }");
12401 assert!(
12402 diags
12403 .iter()
12404 .any(|d| d.message.contains("must appear at the top of the file")),
12405 "expected top-level diagnostic, got: {diags:?}",
12406 );
12407 }
12408
12409 #[test]
12410 fn parse_throw_with_expression() {
12411 let (ast, diags) = parse_str(r#"throw new Error("oops");"#);
12412 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12413 let stmt = single_stmt(&ast);
12414 let crate::StmtKind::Throw { value } = stmt.kind else {
12415 panic!("expected Throw, got {:?}", stmt.kind);
12416 };
12417 assert!(matches!(
12418 ast.try_expr(value).unwrap().kind,
12419 crate::ExprKind::New { .. }
12420 ));
12421 }
12422
12423 #[test]
12424 fn parse_new_with_dotted_callee() {
12425 let (ast, diags) = parse_str("new Temporal.Duration({});");
12426 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12427 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12428 panic!("expected expression statement");
12429 };
12430 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12431 panic!("expected New");
12432 };
12433 let crate::ExprKind::FieldAccess { name, .. } = &ast.try_expr(*callee).unwrap().kind else {
12434 panic!("expected FieldAccess callee");
12435 };
12436 assert_eq!(name.name, "Duration");
12437 }
12438
12439 #[test]
12440 fn parse_new_with_three_level_dotted_callee() {
12441 let (ast, diags) = parse_str("new Foo.Bar.Baz();");
12442 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12443 let crate::StmtKind::Expr(expr_id) = single_stmt(&ast).kind else {
12444 panic!("expected expression statement");
12445 };
12446 let crate::ExprKind::New { callee, .. } = &ast.try_expr(expr_id).unwrap().kind else {
12447 panic!("expected New");
12448 };
12449 let crate::ExprKind::FieldAccess { receiver, name } = &ast.try_expr(*callee).unwrap().kind
12450 else {
12451 panic!("expected outer FieldAccess");
12452 };
12453 assert_eq!(name.name, "Baz");
12454 let crate::ExprKind::FieldAccess { name: mid_name, .. } =
12455 &ast.try_expr(*receiver).unwrap().kind
12456 else {
12457 panic!("expected inner FieldAccess");
12458 };
12459 assert_eq!(mid_name.name, "Bar");
12460 }
12461
12462 #[test]
12463 fn parse_new_with_trailing_dot_diagnoses() {
12464 let (_ast, diags) = parse_str("new Foo.;");
12465 assert!(
12466 diags.iter().any(|d| d
12467 .message
12468 .contains("expected identifier after `.` in constructor name")),
12469 "expected trailing-dot diagnostic, got: {diags:?}",
12470 );
12471 }
12472
12473 #[test]
12474 fn throw_without_expression_diagnoses() {
12475 let (_ast, diags) = parse_str("throw;");
12476 assert!(
12477 diags
12478 .iter()
12479 .any(|d| d.message == "expected expression after `throw`"),
12480 "expected bare-throw diagnostic, got: {diags:?}",
12481 );
12482 }
12483
12484 #[test]
12485 fn parse_try_catch() {
12486 let (ast, diags) = parse_str("try { } catch (e: Error) { }");
12487 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12488 let stmt = single_stmt(&ast);
12489 let crate::StmtKind::Try {
12490 body,
12491 ref catches,
12492 ref finally,
12493 } = stmt.kind
12494 else {
12495 panic!("expected Try, got {:?}", stmt.kind);
12496 };
12497 assert!(matches!(
12498 ast.try_stmt(body).unwrap().kind,
12499 crate::StmtKind::Block(ref v) if v.is_empty()
12500 ));
12501 let [clause] = catches.as_slice() else {
12502 panic!("expected one catch clause, got {catches:?}");
12503 };
12504 assert_eq!(clause.binding.name, "e");
12505 assert!(clause.ty.is_some());
12506 assert!(finally.is_none());
12507 }
12508
12509 #[test]
12510 fn parse_try_finally() {
12511 let (ast, diags) = parse_str("try { } finally { }");
12512 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12513 let stmt = single_stmt(&ast);
12514 let crate::StmtKind::Try {
12515 ref catches,
12516 ref finally,
12517 ..
12518 } = stmt.kind
12519 else {
12520 panic!("expected Try");
12521 };
12522 assert!(catches.is_empty());
12523 let finally_id = finally.expect("finally block");
12524 assert!(matches!(
12525 ast.try_stmt(finally_id).unwrap().kind,
12526 crate::StmtKind::Block(_)
12527 ));
12528 }
12529
12530 #[test]
12531 fn parse_try_catch_finally() {
12532 let (ast, diags) = parse_str("try { } catch (e: Error) { } finally { }");
12533 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12534 let stmt = single_stmt(&ast);
12535 let crate::StmtKind::Try {
12536 ref catches,
12537 ref finally,
12538 ..
12539 } = stmt.kind
12540 else {
12541 panic!("expected Try");
12542 };
12543 assert_eq!(catches.len(), 1);
12544 assert!(finally.is_some());
12545 }
12546
12547 #[test]
12548 fn bare_try_diagnoses() {
12549 let (_ast, diags) = parse_str("try { }");
12550 assert!(
12551 diags
12552 .iter()
12553 .any(|d| d.message == "try requires at least one of `catch` or `finally`"),
12554 "expected bare-try diagnostic, got: {diags:?}",
12555 );
12556 }
12557
12558 #[test]
12559 fn parse_untyped_catch() {
12560 let (ast, diags) = parse_str("try { } catch (e) { }");
12561 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12562 let stmt = single_stmt(&ast);
12563 let crate::StmtKind::Try {
12564 ref catches,
12565 ref finally,
12566 ..
12567 } = stmt.kind
12568 else {
12569 panic!("expected Try");
12570 };
12571 let [clause] = catches.as_slice() else {
12572 panic!("expected one catch clause, got {catches:?}");
12573 };
12574 assert_eq!(clause.binding.name, "e");
12575 assert!(clause.ty.is_none());
12576 assert!(finally.is_none());
12577 }
12578
12579 #[test]
12580 fn parse_multiple_catch_clauses() {
12581 let (ast, diags) = parse_str("try { } catch (e: HttpError) { } catch (f) { }");
12582 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12583 let stmt = single_stmt(&ast);
12584 let crate::StmtKind::Try {
12585 ref catches,
12586 ref finally,
12587 ..
12588 } = stmt.kind
12589 else {
12590 panic!("expected Try");
12591 };
12592 let [first, second] = catches.as_slice() else {
12593 panic!("expected two catch clauses, got {catches:?}");
12594 };
12595 assert_eq!(first.binding.name, "e");
12596 assert!(first.ty.is_some());
12597 assert_eq!(second.binding.name, "f");
12598 assert!(second.ty.is_none());
12599 assert!(finally.is_none());
12600 }
12601
12602 #[test]
12603 fn catch_after_finally_diagnoses() {
12604 let (_ast, diags) = parse_str("try { } finally { } catch (e: Error) { }");
12605 assert!(
12606 !diags.is_empty(),
12607 "expected diagnostic for catch-after-finally"
12608 );
12609 }
12610
12611 #[test]
12612 fn catch_missing_open_paren_diagnoses() {
12613 let (_ast, diags) = parse_str("try { } catch e: Error) { }");
12614 assert!(
12615 diags
12616 .iter()
12617 .any(|d| d.message == "expected `(` after `catch`"),
12618 "expected open-paren diagnostic, got: {diags:?}",
12619 );
12620 }
12621
12622 fn class_members(ast: &Ast) -> &[crate::ClassMember] {
12623 match &single_stmt(ast).kind {
12624 StmtKind::ClassDecl { members, .. } => members,
12625 other => panic!("expected ClassDecl, got {other:?}"),
12626 }
12627 }
12628
12629 #[test]
12630 fn parse_animal_class() {
12631 let src = r#"
12632class Animal {
12633 name: string;
12634 private sound: string;
12635 readonly species: string;
12636
12637 constructor(name: string, sound: string, species: string) {
12638 this.name = name;
12639 this.sound = sound;
12640 this.species = species;
12641 }
12642
12643 speak(): string {
12644 return this.name + " says " + this.sound;
12645 }
12646}
12647"#;
12648 let (ast, diags) = parse_str(src);
12649 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12650 let StmtKind::ClassDecl {
12651 name,
12652 generics,
12653 extends,
12654 implements,
12655 members,
12656 ..
12657 } = &single_stmt(&ast).kind
12658 else {
12659 panic!("expected ClassDecl");
12660 };
12661 assert_eq!(name.name, "Animal");
12662 assert!(generics.is_empty());
12663 assert!(extends.is_none());
12664 assert!(implements.is_empty());
12665 assert_eq!(members.len(), 5);
12666
12667 let crate::ClassMember::Field {
12668 name, modifiers, ..
12669 } = &members[0]
12670 else {
12671 panic!("member 0 should be a field");
12672 };
12673 assert_eq!(name.name, "name");
12674 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12675 assert!(modifiers.readonly.is_none());
12676
12677 let crate::ClassMember::Field { modifiers, .. } = &members[1] else {
12678 panic!("member 1 should be a field");
12679 };
12680 assert_eq!(modifiers.visibility, crate::Visibility::Private);
12681
12682 let crate::ClassMember::Field { modifiers, .. } = &members[2] else {
12683 panic!("member 2 should be a field");
12684 };
12685 assert!(modifiers.readonly.is_some());
12686 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12687
12688 assert!(matches!(
12689 &members[3],
12690 crate::ClassMember::Constructor { .. }
12691 ));
12692
12693 let crate::ClassMember::Method { name, .. } = &members[4] else {
12694 panic!("member 4 should be a method");
12695 };
12696 assert_eq!(name.name, "speak");
12697 }
12698
12699 #[test]
12700 fn parse_dog_class_extends_super_and_void_method() {
12701 let src = r#"
12702class Dog extends Animal {
12703 private tricks: string[];
12704
12705 constructor(name: string) {
12706 super(name, "woof", "canis familiaris");
12707 this.tricks = [];
12708 }
12709
12710 learn(trick: string): void {
12711 this.tricks.push(trick);
12712 }
12713}
12714"#;
12715 let (ast, diags) = parse_str(src);
12716 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12717 let StmtKind::ClassDecl {
12718 extends, members, ..
12719 } = &single_stmt(&ast).kind
12720 else {
12721 panic!("expected ClassDecl");
12722 };
12723 assert!(extends.is_some(), "Dog should extend Animal");
12724 assert_eq!(members.len(), 3);
12725 assert!(matches!(&members[0], crate::ClassMember::Field { .. }));
12726 assert!(matches!(
12727 &members[1],
12728 crate::ClassMember::Constructor { .. }
12729 ));
12730 assert!(matches!(&members[2], crate::ClassMember::Method { .. }));
12731 }
12732
12733 #[test]
12734 fn parse_class_implements_list() {
12735 let (ast, diags) = parse_str("class C implements I, J { run(): void {} }");
12736 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12737 let StmtKind::ClassDecl { implements, .. } = &single_stmt(&ast).kind else {
12738 panic!("expected ClassDecl");
12739 };
12740 assert_eq!(implements.len(), 2);
12741 }
12742
12743 #[test]
12744 fn parse_class_extends_and_implements() {
12745 let (ast, diags) = parse_str("class C extends B implements I { run(): void {} }");
12746 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12747 let StmtKind::ClassDecl {
12748 extends,
12749 implements,
12750 ..
12751 } = &single_stmt(&ast).kind
12752 else {
12753 panic!("expected ClassDecl");
12754 };
12755 assert!(extends.is_some());
12756 assert_eq!(implements.len(), 1);
12757 }
12758
12759 #[test]
12760 fn parse_class_generics_optional_field_and_initializer() {
12761 let (ast, diags) = parse_str("class Box<T> { value?: T; items: T[] = []; }");
12762 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12763 let StmtKind::ClassDecl {
12764 generics, members, ..
12765 } = &single_stmt(&ast).kind
12766 else {
12767 panic!("expected ClassDecl");
12768 };
12769 assert_eq!(generics.len(), 1);
12770 assert_eq!(generics[0].name, "T");
12771
12772 let crate::ClassMember::Field {
12773 optional,
12774 initializer,
12775 ..
12776 } = &members[0]
12777 else {
12778 panic!("member 0 should be a field");
12779 };
12780 assert!(*optional);
12781 assert!(initializer.is_none());
12782
12783 let crate::ClassMember::Field { initializer, .. } = &members[1] else {
12784 panic!("member 1 should be a field");
12785 };
12786 assert!(initializer.is_some());
12787 }
12788
12789 #[test]
12790 fn export_class_is_marked() {
12791 let (ast, diags) = parse_str("export class Foo {}");
12792 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12793 assert_eq!(ast.top_level.len(), 1);
12794 assert_eq!(ast.exported_decls.len(), 1);
12795 assert!(matches!(
12796 &single_stmt(&ast).kind,
12797 StmtKind::ClassDecl { .. }
12798 ));
12799 }
12800
12801 #[test]
12802 fn parameterless_index_signature_is_reported_from_its_member_start() {
12803 for (source, expected) in [
12804 ("interface P { readonly []: number }", "readonly []"),
12805 ("let p: { readonly []: number } = {};", "readonly []"),
12806 ("let q: { []: number } = {};", "[]"),
12807 ] {
12808 let (_ast, diags) = parse_str(source);
12809 let diag = diags
12810 .iter()
12811 .find(|d| d.message.contains("must declare exactly one parameter"))
12812 .unwrap_or_else(|| panic!("no diagnostic for {source}"));
12813 let reported = &source[diag.span.start as usize..diag.span.end as usize];
12814 assert_eq!(reported, expected, "{source}");
12815 }
12816 }
12817
12818 #[test]
12819 fn unterminated_object_type_expects_closing_brace() {
12820 let (_ast, diags) = parse_str("let p: { a: number\n");
12821 assert!(
12822 diags.iter().any(|d| d.message == "expected `}`"),
12823 "{diags:?}"
12824 );
12825 }
12826
12827 #[test]
12828 fn conflicting_visibility_modifiers_diagnose() {
12829 let (_ast, diags) = parse_str("class C { public private x: number; }");
12830 assert!(
12831 diags
12832 .iter()
12833 .any(|d| d.message.contains("at most one visibility modifier")),
12834 "got: {diags:?}",
12835 );
12836 }
12837
12838 #[test]
12839 fn duplicate_readonly_modifier_diagnoses() {
12840 let (_ast, diags) = parse_str("class C { readonly readonly x: number; }");
12841 assert!(
12842 diags
12843 .iter()
12844 .any(|d| d.message.contains("duplicate `readonly`")),
12845 "got: {diags:?}",
12846 );
12847 }
12848
12849 #[test]
12850 fn field_named_like_a_modifier_is_allowed() {
12851 let (ast, diags) = parse_str("class C { private: number; }");
12852 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12853 let crate::ClassMember::Field {
12854 name, modifiers, ..
12855 } = &class_members(&ast)[0]
12856 else {
12857 panic!("expected a field");
12858 };
12859 assert_eq!(name.name, "private");
12860 assert_eq!(modifiers.visibility, crate::Visibility::Public);
12861 }
12862
12863 #[test]
12864 fn method_named_get_is_allowed() {
12865 let (ast, diags) = parse_str("class C { get(): number { return 1; } }");
12866 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12867 let crate::ClassMember::Method { name, .. } = &class_members(&ast)[0] else {
12868 panic!("expected a method");
12869 };
12870 assert_eq!(name.name, "get");
12871 }
12872
12873 #[test]
12874 fn static_method_parses() {
12875 let (ast, diags) = parse_str("class C { static f(): number { return 1; } }");
12876 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12877 let crate::ClassMember::Method {
12878 name, modifiers, ..
12879 } = &class_members(&ast)[0]
12880 else {
12881 panic!("expected a method");
12882 };
12883 assert_eq!(name.name, "f");
12884 assert!(modifiers.static_span.is_some());
12885 }
12886
12887 #[test]
12888 fn static_readonly_field_parses() {
12889 let (ast, diags) = parse_str("class C { static readonly x: number = 1; }");
12890 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12891 let crate::ClassMember::Field {
12892 name,
12893 modifiers,
12894 initializer,
12895 ..
12896 } = &class_members(&ast)[0]
12897 else {
12898 panic!("expected a field");
12899 };
12900 assert_eq!(name.name, "x");
12901 assert!(modifiers.static_span.is_some());
12902 assert!(modifiers.readonly.is_some());
12903 assert!(initializer.is_some());
12904 }
12905
12906 #[test]
12907 fn private_static_method_parses() {
12908 let (ast, diags) = parse_str("class C { private static f(): void {} }");
12909 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12910 let crate::ClassMember::Method { modifiers, .. } = &class_members(&ast)[0] else {
12911 panic!("expected a method");
12912 };
12913 assert_eq!(modifiers.visibility, crate::Visibility::Private);
12914 assert!(modifiers.static_span.is_some());
12915 }
12916
12917 #[test]
12918 fn method_named_static_is_allowed() {
12919 let (ast, diags) = parse_str("class C { static(): number { return 1; } }");
12920 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
12921 let crate::ClassMember::Method {
12922 name, modifiers, ..
12923 } = &class_members(&ast)[0]
12924 else {
12925 panic!("expected a method");
12926 };
12927 assert_eq!(name.name, "static");
12928 assert!(modifiers.static_span.is_none());
12929 }
12930
12931 #[test]
12932 fn visibility_after_static_diagnoses() {
12933 let (_ast, diags) = parse_str("class C { static private f(): void {} }");
12934 assert!(
12935 diags
12936 .iter()
12937 .any(|d| d.message.contains("`private` must come before `static`")),
12938 "got: {diags:?}",
12939 );
12940 }
12941
12942 #[test]
12943 fn duplicate_static_modifier_diagnoses() {
12944 let (_ast, diags) = parse_str("class C { static static f(): void {} }");
12945 assert!(
12946 diags
12947 .iter()
12948 .any(|d| d.message.contains("duplicate `static` modifier")),
12949 "got: {diags:?}",
12950 );
12951 }
12952
12953 #[test]
12954 fn static_accessor_is_rejected() {
12955 let (_ast, diags) = parse_str("class C { static get x(): number { return 1; } }");
12956 assert!(
12957 diags
12958 .iter()
12959 .any(|d| d.message.contains("static accessors are not supported")),
12960 "got: {diags:?}",
12961 );
12962 }
12963
12964 #[test]
12965 fn static_constructor_is_rejected() {
12966 let (_ast, diags) = parse_str("class C { static constructor() {} }");
12967 assert!(
12968 diags
12969 .iter()
12970 .any(|d| d.message.contains("a constructor cannot be `static`")),
12971 "got: {diags:?}",
12972 );
12973 }
12974
12975 #[test]
12976 fn optional_static_field_is_rejected() {
12977 let (_ast, diags) = parse_str("class C { static x?: number; }");
12978 assert!(
12979 diags
12980 .iter()
12981 .any(|d| d.message.contains("a static field cannot be optional")),
12982 "got: {diags:?}",
12983 );
12984 }
12985
12986 #[test]
12987 fn async_function_names_the_synchronous_rewrite() {
12988 let (_ast, diags) = parse_str(
12989 "async function main(): Promise<string> {\n const x = await f();\n return x;\n}",
12990 );
12991 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
12992 assert_eq!(
12993 messages,
12994 [
12995 "`async` functions are not supported",
12996 "`await` is not supported"
12997 ],
12998 "{diags:?}"
12999 );
13000 assert_eq!(
13001 diags[0].help,
13002 [
13003 "calls return their value directly: remove `async`, write the return type `T` \
13004 instead of `Promise<T>`, and drop `await`"
13005 ]
13006 );
13007 assert_eq!(
13008 diags[1].help,
13009 ["calls return their value directly: remove `await`"]
13010 );
13011 }
13012
13013 #[test]
13014 fn async_is_rejected_on_every_function_form() {
13015 for source in [
13016 "export async function f(): void {}",
13017 "const f = async (): void => {};",
13018 "const f = async x => x;",
13019 "const f = async function (): void {};",
13020 "class C { async run(): void {} }",
13021 "class C { static async run(): void {} }",
13022 ] {
13023 let (_ast, diags) = parse_str(source);
13024 assert_eq!(
13025 diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13026 ["`async` functions are not supported"],
13027 "{source}: {diags:?}"
13028 );
13029 }
13030 }
13031
13032 #[test]
13033 fn await_before_a_template_is_reported() {
13034 for source in ["const a = await `hi`;", "const a = await `a${1}b`;"] {
13035 let (_ast, diags) = parse_str(source);
13036 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13037 assert_eq!(
13038 messages,
13039 ["`await` is not supported"],
13040 "{source}: {diags:?}"
13041 );
13042 }
13043 }
13044
13045 #[test]
13046 fn async_and_await_stay_ordinary_names() {
13047 for source in [
13048 "const async = 1; const y = async + 1;",
13049 "function async(n: number): number { return n; } async(1);",
13050 "const await = 2; const z = await * 2;",
13051 "const await = (n: number): number => n; const a = await(1);",
13052 "const await = [1, 2]; const b = await[0];",
13053 "const async = { k: 1 }; const c = \"k\" in async; const d = async in ({ o: 1 });",
13054 "const async: unknown = 1; const n = async as (number);",
13055 "const await: unknown = 1; const m = await as number;",
13056 "const await = 1; const t = `${await}`;",
13057 "class C { async(): void {} async: number = 1; }",
13058 ] {
13059 let (_ast, diags) = parse_str(source);
13060 assert!(diags.is_empty(), "{source}: {diags:?}");
13061 }
13062 }
13063
13064 #[test]
13065 fn an_async_object_literal_method_names_the_rewrite_once() {
13066 assert_eq!(
13067 crate::typechecker::rules::test_util::compile_messages(
13068 "function main(): string {\n const o = { async run(): string { return \"a\"; } };\n return o.run();\n}\n"
13069 ),
13070 ["`async` functions are not supported"]
13071 );
13072 let (_ast, diags) = parse_str("const o = { async run(): string { return \"a\"; } };");
13073 assert_eq!(
13074 diags[0].help,
13075 [
13076 "calls return their value directly: remove `async`, write the return type `T` \
13077 instead of `Promise<T>`, and drop `await`"
13078 ]
13079 );
13080 }
13081
13082 #[test]
13083 fn await_in_an_async_object_literal_method_is_reported_too() {
13084 let (_ast, diags) =
13085 parse_str("const o = { async run(): string { const x = await f(); return x; } };");
13086 let messages: Vec<&str> = diags.iter().map(|d| d.message.as_str()).collect();
13087 assert_eq!(
13088 messages,
13089 [
13090 "`async` functions are not supported",
13091 "`await` is not supported"
13092 ],
13093 "{diags:?}"
13094 );
13095 }
13096
13097 #[test]
13098 fn every_async_member_form_reports_async() {
13099 for source in [
13100 "const o = { async *run(): void {} };",
13101 "class C { async *run(): void {} }",
13102 "const o = { async [k](): void {} };",
13103 "const o = { async in(): void {} };",
13104 "class C { async as(): void {} }",
13105 ] {
13106 let (_ast, diags) = parse_str(source);
13107 assert_eq!(
13108 diags[0].message, "`async` functions are not supported",
13109 "{source}: {diags:?}"
13110 );
13111 }
13112 }
13113
13114 #[test]
13115 fn async_string_key_methods_report_only_async() {
13116 for source in [
13117 "const o = { async \"s\"(): string { return \"a\"; } };",
13118 "class C { async \"s\"(): string { return \"a\"; } }",
13119 ] {
13120 let (_ast, diags) = parse_str(source);
13121 assert_eq!(
13122 diags.iter().map(|d| d.message.as_str()).collect::<Vec<_>>(),
13123 ["`async` functions are not supported"],
13124 "{source}: {diags:?}"
13125 );
13126 }
13127 }
13128
13129 #[test]
13130 fn async_before_a_line_break_is_a_member_of_its_own() {
13131 for source in [
13132 "class C {\n async\n run(): void {}\n}",
13133 "const o = { async\n run(): void {} };",
13134 ] {
13135 let (_ast, diags) = parse_str(source);
13136 assert!(
13137 diags
13138 .iter()
13139 .all(|d| d.message != "`async` functions are not supported"),
13140 "{source}: {diags:?}"
13141 );
13142 }
13143 }
13144
13145 #[test]
13146 fn async_stays_an_object_literal_key() {
13147 let messages = crate::typechecker::rules::test_util::compile_messages(
13148 "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",
13149 );
13150 assert!(messages.is_empty(), "{messages:?}");
13151 }
13152
13153 #[test]
13154 fn for_await_names_the_rewrite_once() {
13155 assert_eq!(
13156 crate::typechecker::rules::test_util::compile_messages(
13157 "import * as fs from \"submilli:fs\";\nfunction main(): string {\n for await (const e of fs.list(\".\", false)) { }\n return \"x\";\n}\n"
13158 ),
13159 ["`for await` is not supported"]
13160 );
13161 let (ast, diags) = parse_str("for await (const e of [1]) { }");
13162 assert_eq!(
13163 diags[0].help,
13164 ["calls return their values directly: remove `await` and write `for (const e of …)`"]
13165 );
13166 assert_eq!(ast.top_level.len(), 1, "the loop is still read");
13167 }
13168
13169 #[test]
13170 fn a_broken_switch_leaves_later_declarations_at_the_top_level() {
13171 assert_eq!(
13172 crate::typechecker::rules::test_util::compile_messages(
13173 "function g(): void { switch (1) { case 1 } }\nclass C {}\nfunction main() { return \"x\"; }\n"
13174 ),
13175 [
13176 "expected `:` after `case` label",
13177 "expected expression",
13178 "expected `:` and return type"
13179 ]
13180 );
13181 }
13182
13183 #[test]
13184 fn every_switch_arm_error_restores_the_nesting() {
13185 for switch in [
13186 "switch (1) { case 1 }",
13187 "switch (1) { case : break; }",
13188 "switch (1) { default: break; default: break; }",
13189 "switch (1) { default }",
13190 "switch (1) { x; }",
13191 "switch (1) { case 1: }",
13192 ] {
13193 let source = format!("function g(): void {{ {switch} }}\nclass C {{}}\n");
13194 let (_ast, diags) = parse_str(&source);
13195 assert!(
13196 diags.iter().all(|d| !d.message.contains("top level")),
13197 "{switch}: {diags:?}"
13198 );
13199 }
13200 }
13201
13202 #[test]
13203 fn protected_member_is_rejected() {
13204 let (_ast, diags) = parse_str("class C { protected x: number; }");
13205 assert!(
13206 diags
13207 .iter()
13208 .any(|d| d.message.contains("`protected` is not supported")),
13209 "got: {diags:?}",
13210 );
13211 }
13212
13213 #[test]
13214 fn abstract_member_is_rejected() {
13215 let (_ast, diags) = parse_str("class C { abstract run(): void; }");
13216 assert!(
13217 diags
13218 .iter()
13219 .any(|d| d.message.contains("abstract classes are not supported")),
13220 "got: {diags:?}",
13221 );
13222 }
13223
13224 #[test]
13225 fn getter_parses_as_accessor() {
13226 let (ast, diags) = parse_str("class C { get value(): number { return 1; } }");
13227 assert!(diags.is_empty(), "got: {diags:?}");
13228 let crate::ClassMember::Accessor { kind, name, .. } = &class_members(&ast)[0] else {
13229 panic!("expected an accessor member");
13230 };
13231 assert_eq!(*kind, crate::AccessorKind::Get);
13232 assert_eq!(name.name, "value");
13233 }
13234
13235 #[test]
13236 fn getter_without_return_type_is_rejected() {
13237 let (ast, diags) = parse_str("class C { get value() { return 1; } }");
13238 assert_eq!(diags.len(), 1, "got: {diags:?}");
13239 assert_eq!(diags[0].message, "expected `:` and return type");
13240 assert_eq!(diags[0].help, vec!["get name(): T { … }".to_string()]);
13241 assert!(
13242 matches!(
13243 &class_members(&ast)[0],
13244 crate::ClassMember::Accessor {
13245 return_type: None,
13246 ..
13247 }
13248 ),
13249 "the getter is still recovered as a member"
13250 );
13251 }
13252
13253 #[test]
13254 fn setter_parses_as_accessor() {
13255 let (ast, diags) = parse_str("class C { set value(v: number) {} }");
13256 assert!(diags.is_empty(), "got: {diags:?}");
13257 let crate::ClassMember::Accessor { kind, param, .. } = &class_members(&ast)[0] else {
13258 panic!("expected an accessor member");
13259 };
13260 assert_eq!(*kind, crate::AccessorKind::Set);
13261 assert!(param.is_some(), "setter should have a parameter");
13262 }
13263
13264 #[test]
13265 fn param_property_modifier_outside_constructor_is_rejected() {
13266 let (_ast, diags) = parse_str("function f(public x: number): void {}");
13267 assert!(
13268 diags.iter().any(|d| d
13269 .message
13270 .contains("parameter properties are only allowed in a constructor")),
13271 "got: {diags:?}",
13272 );
13273 }
13274
13275 #[test]
13276 fn constructor_return_type_is_rejected() {
13277 let (_ast, diags) = parse_str("class C { constructor(): void {} }");
13278 assert!(
13279 diags.iter().any(|d| d
13280 .message
13281 .contains("constructor cannot declare a return type")),
13282 "got: {diags:?}",
13283 );
13284 }
13285
13286 #[test]
13287 fn field_initializer_without_type_is_rejected() {
13288 let (_ast, diags) = parse_str("class C { x = 1; }");
13289 assert!(
13290 diags
13291 .iter()
13292 .any(|d| d.message.contains("class fields require a type annotation")),
13293 "got: {diags:?}",
13294 );
13295 }
13296
13297 #[test]
13298 fn this_outside_method_is_left_to_typechecker() {
13299 let (_ast, diags) = parse_str("function f(): string { return this.name; }");
13300 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13301 }
13302
13303 #[test]
13304 fn super_outside_method_is_rejected() {
13305 let (_ast, diags) = parse_str("function f(): void { super(1); }");
13306 assert!(
13307 diags.iter().any(|d| d
13308 .message
13309 .contains("`super` is only valid inside a class method or constructor body")),
13310 "got: {diags:?}",
13311 );
13312 }
13313
13314 #[test]
13315 fn this_inside_method_is_accepted_by_parser() {
13316 let (_ast, diags) = parse_str("class C { x: number; read(): number { return this.x; } }");
13317 assert!(diags.is_empty(), "unexpected diags: {diags:?}");
13318 }
13319 #[test]
13320 fn parse_void_expression_preserves_operand_and_precedence() {
13321 let (ast, diags) = parse_str("void effect() === undefined;");
13322 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13323 let expression = expr_of_single_stmt(&ast);
13324 let crate::ExprKind::Binary { lhs, rhs, .. } = expression.kind else {
13325 panic!("expected equality")
13326 };
13327 let crate::ExprKind::Void { operand } = ast.try_expr(lhs).unwrap().kind else {
13328 panic!("expected void")
13329 };
13330 assert!(matches!(
13331 ast.try_expr(operand).unwrap().kind,
13332 crate::ExprKind::Call { .. }
13333 ));
13334 assert!(matches!(
13335 ast.try_expr(rhs).unwrap().kind,
13336 crate::ExprKind::Identifier(_)
13337 ));
13338 }
13339
13340 #[test]
13341 fn parse_void_newline_and_object_operands() {
13342 for source in [
13343 "void\n0;",
13344 "void { value: 1 };",
13345 "void\n{ value: 1 };",
13346 "void /x/;",
13347 "void void 0;",
13348 ] {
13349 let (_, diags) = parse_str(source);
13350 assert!(diags.is_empty(), "{source}: {diags:?}");
13351 }
13352 let (_, diags) = parse_str("void; 0;");
13353 assert!(
13354 !diags.is_empty(),
13355 "an explicit semicolon cannot split void from its operand"
13356 );
13357 }
13358
13359 #[test]
13360 fn parse_optional_parameters_and_defaults_in_callable_forms() {
13361 for source in [
13362 "function f(x = 1): number { return x; }",
13363 "function f(x: number = 1, y: number): number { return y; }",
13364 "function f({ x } = { x: 1 }): number { return x; }",
13365 "const f = (x?: number) => x;",
13366 "const f = (x: number = effect()) => x;",
13367 "const f = function(x: number = effect()) { return x; };",
13368 "const f = { m(x: number = effect()): number { return x; } };",
13369 "const f = ([x = 1]: number[] = values) => x;",
13370 "let f: (x?: number, ...rest: string[]) => void = value;",
13371 "let f: { method?(x?: number): void } = value;",
13372 "function f(x: number = 1, y?: number): void {}",
13373 ] {
13374 let (_, diags) = parse_str(source);
13375 assert!(diags.is_empty(), "{source}: {diags:?}");
13376 }
13377 }
13378
13379 #[test]
13380 fn parse_invalid_optional_parameters() {
13381 for source in [
13382 "function f(x?: number, y: number): void {}",
13383 "const f = (x?: number, y: number) => y;",
13384 "let f: (x?: number, y: number) => void = value;",
13385 "function f(...x?: number[]): void {}",
13386 "function f(x?: number = 1): void {}",
13387 "const f = (x?: number = 1) => x;",
13388 "function f({x}?: T): void {}",
13389 ] {
13390 let (_, diags) = parse_str(source);
13391 assert!(!diags.is_empty(), "expected diagnostic for {source}");
13392 }
13393 }
13394
13395 #[test]
13396 fn parse_optional_tuple_forms_and_order() {
13397 for source in [
13398 "let x: [number, string?] = value;",
13399 "let x: [x?: number, y?: string] = value;",
13400 "let x: readonly [(number | null)?, string?] = value;",
13401 ] {
13402 let (_, diags) = parse_str(source);
13403 assert!(diags.is_empty(), "{source}: {diags:?}");
13404 }
13405 for source in [
13406 "let x: [number?, string] = value;",
13407 "let x: [x: number?] = value;",
13408 ] {
13409 let (_, diags) = parse_str(source);
13410 assert!(!diags.is_empty(), "expected diagnostic for {source}");
13411 }
13412 }
13413
13414 #[test]
13415 fn parse_array_pattern_defaults_preserve_holes() {
13416 let (ast, diags) = parse_str("const [, a = effect(), b] = values;");
13417 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13418 let crate::StmtKind::ConstPattern { ref binding, .. } = single_stmt(&ast).kind else {
13419 panic!("expected pattern")
13420 };
13421 let crate::Binding::Array {
13422 elems, defaults, ..
13423 } = binding
13424 else {
13425 panic!("expected array")
13426 };
13427 assert_eq!(elems.len(), 3);
13428 assert_eq!(defaults.len(), elems.len());
13429 assert!(elems[0].is_none());
13430 assert!(defaults[0].is_none());
13431 assert!(defaults[1].is_some());
13432 assert!(defaults[2].is_none());
13433 }
13434
13435 #[test]
13436 fn parse_optional_class_method_forms() {
13437 let (ast, diags) =
13438 parse_str("class C { declared?(x?: number): void; implemented?(): void\n{} }");
13439 assert!(diags.is_empty(), "unexpected diagnostics: {diags:?}");
13440 let members = class_members(&ast);
13441 assert!(matches!(
13442 &members[0],
13443 crate::ClassMember::Field {
13444 optional: true,
13445 initializer: None,
13446 ty: crate::TypeAnnotation {
13447 kind: crate::TypeAnnotationKind::Function { .. },
13448 ..
13449 },
13450 ..
13451 }
13452 ));
13453 assert!(matches!(
13454 &members[1],
13455 crate::ClassMember::Method { optional: true, .. }
13456 ));
13457 }
13458 #[test]
13459 fn defaults_retain_their_callable_receiver_scope() {
13460 for source in [
13461 "class C { value: number = 1; method(value: number = this.value): number { return value; } }",
13462 "class C { constructor(value: number = this.value) {} value: number = 1; }",
13463 "const f = function(this: { value: number }, value: number = this.value): number { return value; };",
13464 ] {
13465 let (ast, diagnostics) = parse_str(source);
13466 assert!(diagnostics.is_empty(), "{source}: {diagnostics:?}");
13467 assert!(
13468 ast.source_expressions()
13469 .iter()
13470 .any(|expr| matches!(expr.kind, ExprKind::This))
13471 );
13472 assert!(
13473 !ast.source_expressions()
13474 .iter()
13475 .any(|expr| matches!(expr.kind, ExprKind::ThisOutsideReceiver))
13476 );
13477 }
13478 }
13479}