1use crate::compiler_error::{CompilerFailure, CompilerStage};
8use crate::tree_height;
9
10use crate::{
11 Ast, Binding, BindingKind, ClassMember, Expr, ExprId, ExprKind, Ident, ParamDecl,
12 PatternOrigin, Span, Stmt, StmtId, StmtKind,
13};
14
15pub fn lower(mut ast: Ast) -> Result<Ast, CompilerFailure> {
16 tree_height::check_syntax(&ast).map_err(|failure| failure.with_stage(CompilerStage::Infer))?;
19 let mut ctx = LowerCtx { next_tmp: 0 };
20 ctx.lower_arrows(&mut ast)?;
21 ctx.lower_function_params(&mut ast)?;
22 ctx.lower_class_member_params(&mut ast)?;
23 ctx.lower_for_of_patterns(&mut ast)?;
24 ctx.lower_for_pattern_inits(&mut ast)?;
25 ctx.lower_pattern_stmts(&mut ast)?;
26 Ok(ast)
27}
28
29const FRESH_MARKER: &str = "#pattern_";
31
32const PATTERN_PARAM: &str = "p";
34
35pub(crate) fn is_pattern_param(name: &str) -> bool {
39 name.is_empty()
40 || name
41 .strip_prefix(FRESH_MARKER)
42 .and_then(|rest| rest.strip_prefix(PATTERN_PARAM))
43 .is_some_and(|rest| rest.starts_with('_'))
44}
45
46const PATTERN_VALUE: &str = "dst";
48
49pub(crate) fn is_pattern_value(name: &str) -> bool {
52 name.strip_prefix(FRESH_MARKER)
53 .and_then(|rest| rest.strip_prefix(PATTERN_VALUE))
54 .is_some_and(|rest| rest.starts_with('_'))
55}
56
57struct LowerCtx {
58 next_tmp: u32,
59}
60
61#[derive(Clone, Copy)]
63struct PatternRoot {
64 is_const: bool,
65 annotated: bool,
68}
69
70impl LowerCtx {
71 fn fresh(&mut self, prefix: &str, span: Span) -> Result<Ident, CompilerFailure> {
72 let n = self.next_tmp;
73 self.next_tmp = self
74 .next_tmp
75 .checked_add(1)
76 .ok_or_else(|| CompilerFailure::Limit {
77 stage: CompilerStage::Infer,
78 span: None,
79 message: "pattern temporary capacity exceeded".into(),
80 help: vec![],
81 })?;
82 Ok(Ident {
83 name: format!("{FRESH_MARKER}{prefix}_{n}"),
85 span,
86 })
87 }
88
89 fn lower_arrows(&mut self, ast: &mut Ast) -> Result<(), CompilerFailure> {
90 for id in ast
91 .expr_ids()
92 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
93 {
94 let has_pattern = match &ast
95 .try_expr(id)
96 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
97 .kind
98 {
99 ExprKind::Arrow { params, .. } => params.iter().any(|p| p.pattern.is_some()),
100 _ => false,
101 };
102 if has_pattern {
103 self.lower_arrow(ast, id)?;
104 }
105 }
106
107 Ok(())
108 }
109
110 fn lower_arrow(&mut self, ast: &mut Ast, id: ExprId) -> Result<(), CompilerFailure> {
111 let kind = ast
114 .try_expr(id)
115 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
116 .kind
117 .clone();
118 let ExprKind::Arrow {
119 mut params,
120 return_type,
121 type_predicate,
122 body,
123 } = kind
124 else {
125 return Err(lowering_failure(
126 "unexpected node kind during pattern lowering",
127 ));
128 };
129
130 self.materialise_pattern_params(ast, &mut params)?;
131
132 ast.try_expr_mut(id)
133 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
134 .kind = ExprKind::Arrow {
135 params,
136 return_type,
137 type_predicate,
138 body,
139 };
140
141 Ok(())
142 }
143
144 fn lower_function_params(&mut self, ast: &mut Ast) -> Result<(), CompilerFailure> {
145 for id in ast
146 .stmt_ids()
147 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
148 {
149 let has_pattern = match &ast
150 .try_stmt(id)
151 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
152 .kind
153 {
154 StmtKind::Function { params, .. } => params.iter().any(|p| p.pattern.is_some()),
155 _ => false,
156 };
157 if has_pattern {
158 self.lower_function(ast, id)?;
159 }
160 }
161
162 Ok(())
163 }
164
165 fn lower_function(&mut self, ast: &mut Ast, id: StmtId) -> Result<(), CompilerFailure> {
166 let stmt_span = ast
167 .try_stmt(id)
168 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
169 .span;
170 let kind = std::mem::replace(
171 &mut ast
172 .try_stmt_mut(id)
173 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
174 .kind,
175 StmtKind::Block(Vec::new()), );
177 let StmtKind::Function {
178 name,
179 generics,
180 mut params,
181 return_type,
182 type_predicate,
183 body,
184 doc,
185 } = kind
186 else {
187 return Err(lowering_failure(
188 "unexpected node kind during pattern lowering",
189 ));
190 };
191
192 self.materialise_pattern_params(ast, &mut params)?;
193
194 ast.try_stmt_mut(id)
195 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
196 .kind = StmtKind::Function {
197 name,
198 generics,
199 params,
200 return_type,
201 type_predicate,
202 body,
203 doc,
204 };
205 let _ = stmt_span;
207
208 Ok(())
209 }
210
211 fn lower_class_member_params(&mut self, ast: &mut Ast) -> Result<(), CompilerFailure> {
212 for id in ast
213 .stmt_ids()
214 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
215 {
216 let StmtKind::ClassDecl { members, .. } = &mut ast
217 .try_stmt_mut(id)
218 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
219 .kind
220 else {
221 continue;
222 };
223 let mut taken = std::mem::take(members);
224 let lowered = self.lower_member_params(ast, &mut taken);
225 let StmtKind::ClassDecl { members, .. } = &mut ast
228 .try_stmt_mut(id)
229 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
230 .kind
231 else {
232 return Err(lowering_failure(
233 "unexpected node kind during pattern lowering",
234 ));
235 };
236 *members = taken;
237 lowered?;
238 }
239
240 Ok(())
241 }
242
243 fn lower_member_params(
244 &mut self,
245 ast: &mut Ast,
246 members: &mut [ClassMember],
247 ) -> Result<(), CompilerFailure> {
248 for member in members {
249 let params = match member {
250 ClassMember::Method { params, .. } | ClassMember::Constructor { params, .. } => {
251 params.as_mut_slice()
252 }
253 ClassMember::Accessor {
254 param: Some(param), ..
255 } => std::slice::from_mut(param.as_mut()),
256 ClassMember::Accessor { param: None, .. } | ClassMember::Field { .. } => continue,
257 };
258 self.materialise_pattern_params(ast, params)?;
259 }
260
261 Ok(())
262 }
263
264 fn lower_for_of_patterns(&mut self, ast: &mut Ast) -> Result<(), CompilerFailure> {
265 for id in ast
266 .stmt_ids()
267 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
268 {
269 if !matches!(
270 ast.try_stmt(id)
271 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
272 .kind,
273 StmtKind::ForOfPattern { .. }
274 ) {
275 continue;
276 }
277 self.expand_for_of_pattern(ast, id)?;
278 }
279
280 Ok(())
281 }
282
283 fn expand_for_of_pattern(&mut self, ast: &mut Ast, id: StmtId) -> Result<(), CompilerFailure> {
284 let stmt_span = ast
285 .try_stmt(id)
286 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
287 .span;
288 let kind = std::mem::replace(
289 &mut ast
290 .try_stmt_mut(id)
291 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
292 .kind,
293 StmtKind::Block(Vec::new()),
294 );
295 let StmtKind::ForOfPattern {
296 binding_kind,
297 binding,
298 ty,
299 iter,
300 body,
301 } = kind
302 else {
303 return Err(lowering_failure(
304 "unexpected node kind during pattern lowering",
305 ));
306 };
307
308 let pattern_span = binding.span();
309 let dst = self.fresh(PATTERN_VALUE, pattern_span)?;
310 let is_const = matches!(binding_kind, BindingKind::Const);
311 let root = PatternRoot {
312 is_const,
313 annotated: ty.is_some(),
314 };
315 let mut decompose = self.emit_decompose(
316 ast,
317 binding,
318 dst.clone(),
319 root,
320 None,
321 None,
322 )?;
323 let mut source_bindings = Vec::new();
324 for &id in &decompose {
325 match &ast
326 .try_stmt(id)
327 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
328 .kind
329 {
330 StmtKind::Let { name, .. }
331 | StmtKind::Const { name, .. }
332 | StmtKind::ObjectRest { name, .. } => source_bindings.push(name.clone()),
333 _ => {}
334 }
335 }
336 ast.for_of_pattern_bindings.insert(id, source_bindings);
337 decompose.push(body);
339 let body = ast
340 .try_push_stmt(Stmt {
341 kind: StmtKind::Block(decompose),
342 span: ast
343 .try_stmt(body)
344 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
345 .span,
346 })
347 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
348
349 ast.try_stmt_mut(id)
350 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
351 .kind = StmtKind::ForOf {
352 binding_kind,
353 name: dst,
354 ty,
355 iter,
356 body,
357 };
358 let _ = stmt_span;
359
360 Ok(())
361 }
362
363 fn lower_for_pattern_inits(&mut self, ast: &mut Ast) -> Result<(), CompilerFailure> {
364 for id in ast
365 .stmt_ids()
366 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
367 {
368 let StmtKind::For {
369 init: Some(init), ..
370 } = ast
371 .try_stmt(id)
372 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
373 .kind
374 else {
375 continue;
376 };
377 let is_let = match ast
378 .try_stmt(init)
379 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
380 .kind
381 {
382 StmtKind::LetPattern { .. } => true,
383 StmtKind::ConstPattern { .. } => false,
384 _ => continue,
385 };
386 self.hoist_for_pattern_init(ast, id, init, is_let)?;
387 }
388
389 Ok(())
390 }
391
392 fn hoist_for_pattern_init(
398 &mut self,
399 ast: &mut Ast,
400 id: StmtId,
401 init: StmtId,
402 is_let: bool,
403 ) -> Result<(), CompilerFailure> {
404 let mut hoisted = self.expand_pattern_stmt(ast, init)?;
405 let loop_stmt = ast
406 .try_stmt_mut(id)
407 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
408 let span = loop_stmt.span;
409 let kind = std::mem::replace(&mut loop_stmt.kind, StmtKind::Block(Vec::new()));
410 let StmtKind::For {
411 condition,
412 update,
413 body,
414 ..
415 } = kind
416 else {
417 return Err(lowering_failure(
418 "unexpected node kind during pattern lowering",
419 ));
420 };
421 let lowered_loop = ast
422 .try_push_stmt(Stmt {
423 kind: StmtKind::For {
424 init: None,
425 condition,
426 update,
427 body,
428 },
429 span,
430 })
431 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
432 if is_let {
433 let bindings = let_names(ast, &hoisted)?;
434 if !bindings.is_empty() {
435 ast.for_pattern_init_bindings.insert(lowered_loop, bindings);
436 }
437 }
438 hoisted.push(lowered_loop);
439 ast.try_stmt_mut(id)
440 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
441 .kind = StmtKind::Block(hoisted);
442
443 Ok(())
444 }
445
446 fn lower_pattern_stmts(&mut self, ast: &mut Ast) -> Result<(), CompilerFailure> {
447 let top = std::mem::take(&mut ast.top_level);
448 ast.top_level = self.expand_list(ast, top)?;
449
450 for id in ast
451 .stmt_ids()
452 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
453 {
454 let needs = matches!(
455 ast.try_stmt(id)
456 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
457 .kind,
458 StmtKind::Block(_)
459 );
460 if !needs {
461 continue;
462 }
463 let StmtKind::Block(taken) = std::mem::replace(
464 &mut ast
465 .try_stmt_mut(id)
466 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
467 .kind,
468 StmtKind::Block(Vec::new()),
469 ) else {
470 return Err(lowering_failure(
471 "unexpected node kind during pattern lowering",
472 ));
473 };
474 let expanded = self.expand_list(ast, taken)?;
475 ast.try_stmt_mut(id)
476 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
477 .kind = StmtKind::Block(expanded);
478 }
479
480 Ok(())
481 }
482
483 fn expand_list(
484 &mut self,
485 ast: &mut Ast,
486 ids: Vec<StmtId>,
487 ) -> Result<Vec<StmtId>, CompilerFailure> {
488 let mut out = Vec::with_capacity(ids.len());
489 for id in ids {
490 let is_pattern = matches!(
491 ast.try_stmt(id)
492 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
493 .kind,
494 StmtKind::LetPattern { .. } | StmtKind::ConstPattern { .. }
495 );
496 if !is_pattern {
497 out.push(id);
498 continue;
499 }
500 let expanded = self.expand_pattern_stmt(ast, id)?;
501 out.extend(expanded);
502 }
503 Ok(out)
504 }
505
506 fn expand_pattern_stmt(
507 &mut self,
508 ast: &mut Ast,
509 id: StmtId,
510 ) -> Result<Vec<StmtId>, CompilerFailure> {
511 let span = ast
512 .try_stmt(id)
513 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
514 .span;
515 let kind = std::mem::replace(
516 &mut ast
517 .try_stmt_mut(id)
518 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
519 .kind,
520 StmtKind::Block(Vec::new()), );
522 let (is_const, binding, ty, value, doc) = match kind {
523 StmtKind::LetPattern {
524 binding,
525 ty,
526 value,
527 doc,
528 } => (false, binding, ty, value, doc),
529 StmtKind::ConstPattern {
530 binding,
531 ty,
532 value,
533 doc,
534 } => (true, binding, ty, value, doc),
535 _ => {
536 return Err(lowering_failure(
537 "unexpected node kind during pattern lowering",
538 ));
539 }
540 };
541
542 let tuple_len = match ty {
543 None => tuple_source_len(ast, &binding, value)?,
544 Some(_) => None,
545 };
546 if let Some(len) = tuple_len {
547 pad_tuple_source(ast, value, len)?;
548 ast.tuple_pattern_sources.insert(value);
549 }
550 let pattern_span = binding.span();
551 let dst = self.fresh(PATTERN_VALUE, pattern_span)?;
552 let annotated = ty.is_some();
553
554 let dst_stmt = ast
556 .try_push_stmt(Stmt {
557 kind: StmtKind::Const {
558 name: dst.clone(),
559 ty,
560 value,
561 doc: None,
562 },
563 span,
564 })
565 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
566
567 let mut out = vec![dst_stmt];
568 let root = PatternRoot {
569 is_const,
570 annotated,
571 };
572 out.extend(self.emit_decompose(ast, binding, dst, root, doc, tuple_len)?);
573 Ok(out)
574 }
575
576 fn materialise_pattern_params(
577 &mut self,
578 ast: &mut Ast,
579 params: &mut [ParamDecl],
580 ) -> Result<(), CompilerFailure> {
581 for param in params.iter_mut() {
582 let Some(pattern) = param.pattern.take() else {
583 continue;
584 };
585 let fresh = self.fresh(PATTERN_PARAM, pattern.span())?;
586 param.name = fresh.clone();
587 let root = PatternRoot {
588 is_const: false,
589 annotated: param.ty.is_some(),
590 };
591 let statements = self.emit_decompose(
592 ast, pattern, fresh, root, None, None,
593 )?;
594 if ast
595 .parameter_bindings
596 .insert(param.name.span, statements)
597 .is_some()
598 {
599 return Err(lowering_failure(
600 "duplicate parameter binding span during pattern lowering",
601 ));
602 }
603 }
604
605 Ok(())
606 }
607
608 fn emit_decompose(
611 &mut self,
612 ast: &mut Ast,
613 binding: Binding,
614 source: Ident,
615 root: PatternRoot,
616 mut doc: Option<crate::DocComment>,
617 tuple_len: Option<usize>,
618 ) -> Result<Vec<StmtId>, CompilerFailure> {
619 let is_const = root.is_const;
620 let mut out = Vec::new();
621 match binding {
622 Binding::Object {
623 fields,
624 rest,
625 span: _,
626 } => {
627 let bound_names: Vec<Ident> = fields.iter().map(|f| f.source.clone()).collect();
628 for field in fields {
629 let recv = ast
630 .try_push_expr(Expr {
631 kind: ExprKind::Identifier(source.clone()),
632 span: field.span,
633 })
634 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
635 let access = ast
636 .try_push_expr(Expr {
637 kind: ExprKind::FieldAccess {
638 receiver: recv,
639 name: field.source.clone(),
640 },
641 span: field.span,
642 })
643 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
644 let value = self.apply_binding_default(
645 ast,
646 access,
647 field.default,
648 root.annotated,
649 field.span,
650 &mut out,
651 )?;
652 let decl = ast
653 .try_push_stmt(Stmt {
654 kind: make_decl(is_const, field.local, value, doc.take()),
655 span: field.span,
656 })
657 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
658 out.push(decl);
659 }
660 if let Some(rest_ident) = rest {
661 let src_expr = ast
662 .try_push_expr(Expr {
663 kind: ExprKind::Identifier(source.clone()),
664 span: rest_ident.span,
665 })
666 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
667 let decl = ast
668 .try_push_stmt(Stmt {
669 kind: StmtKind::ObjectRest {
670 is_const,
671 name: rest_ident.clone(),
672 source: src_expr,
673 exclude: bound_names,
674 ty: None,
675 doc: doc.take(),
676 },
677 span: rest_ident.span,
678 })
679 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
680 out.push(decl);
681 }
682 }
683 Binding::Array {
684 elems,
685 defaults,
686 rest,
687 span: pattern_span,
688 } => {
689 if elems.len() != defaults.len() {
690 return Err(lowering_failure(
691 "array pattern defaults do not match element positions",
692 ));
693 }
694 let elems_len = elems.len();
695 for (i, (slot, default)) in elems.into_iter().zip(defaults).enumerate() {
696 let Some(local) = slot else {
697 if default.is_some() {
698 return Err(lowering_failure("array pattern hole has a default value"));
699 }
700 continue;
701 };
702 let recv = ast
703 .try_push_expr(Expr {
704 kind: ExprKind::Identifier(source.clone()),
705 span: local.span,
706 })
707 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
708 let idx = ast
709 .try_push_expr(Expr {
710 kind: ExprKind::Number(i as f64),
711 span: local.span,
712 })
713 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
714 let access = ast
715 .try_push_expr(Expr {
716 kind: ExprKind::IndexAccess {
717 receiver: recv,
718 index: idx,
719 },
720 span: local.span,
721 })
722 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
723 ast.pattern_origins.insert(
725 access,
726 PatternOrigin {
727 pattern_span,
728 slot_arity: elems_len,
729 },
730 );
731 let access = if default.is_some() {
732 self.guard_defaulted_array_read(ast, recv, idx, access, local.span)?
733 } else {
734 access
735 };
736 let value = self.apply_binding_default(
737 ast,
738 access,
739 default,
740 root.annotated,
741 local.span,
742 &mut out,
743 )?;
744 let decl = ast
745 .try_push_stmt(Stmt {
746 kind: make_decl(is_const, local.clone(), value, doc.take()),
747 span: local.span,
748 })
749 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
750 out.push(decl);
751 }
752 if let Some(rest_ident) = rest {
753 let rest_value = match tuple_len {
754 Some(len) if len > elems_len => {
755 remaining_elements(ast, &source, elems_len..len, rest_ident.span)?
756 }
757 _ => array_rest(ast, &source, elems_len, rest_ident.span)?,
758 };
759 let decl = ast
760 .try_push_stmt(Stmt {
761 kind: make_decl(is_const, rest_ident.clone(), rest_value, doc.take()),
762 span: rest_ident.span,
763 })
764 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
765 out.push(decl);
766 }
767 }
768 }
769 Ok(out)
770 }
771
772 fn guard_defaulted_array_read(
775 &mut self,
776 ast: &mut Ast,
777 receiver: ExprId,
778 index: ExprId,
779 access: ExprId,
780 span: Span,
781 ) -> Result<ExprId, CompilerFailure> {
782 let length = push_expr(
783 ast,
784 ExprKind::FieldAccess {
785 receiver,
786 name: Ident {
787 name: "length".to_string(),
788 span,
789 },
790 },
791 span,
792 )?;
793 let in_bounds = push_expr(
794 ast,
795 ExprKind::Binary {
796 op: crate::BinOp::Lt,
797 lhs: index,
798 rhs: length,
799 },
800 span,
801 )?;
802 let zero = push_expr(ast, ExprKind::Number(0.0), span)?;
803 let undefined = push_expr(ast, ExprKind::Void { operand: zero }, span)?;
804 push_expr(
805 ast,
806 ExprKind::Ternary {
807 cond: in_bounds,
808 then_: access,
809 else_: undefined,
810 },
811 span,
812 )
813 }
814
815 fn apply_binding_default(
818 &mut self,
819 ast: &mut Ast,
820 access: ExprId,
821 default: Option<ExprId>,
822 annotated: bool,
823 span: Span,
824 out: &mut Vec<StmtId>,
825 ) -> Result<ExprId, CompilerFailure> {
826 let Some(default) = default else {
827 return Ok(access);
828 };
829 let saved = self.fresh("value", span)?;
830 let declaration = ast
831 .try_push_stmt(Stmt {
832 kind: make_decl(true, saved.clone(), access, None),
833 span,
834 })
835 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
836 out.push(declaration);
837 let saved_value = push_expr(ast, ExprKind::Identifier(saved.clone()), span)?;
838 let zero = push_expr(ast, ExprKind::Number(0.0), span)?;
839 let undefined = push_expr(ast, ExprKind::Void { operand: zero }, span)?;
840 let condition = push_expr(
841 ast,
842 ExprKind::Binary {
843 op: crate::BinOp::Eq,
844 lhs: saved_value,
845 rhs: undefined,
846 },
847 span,
848 )?;
849 let selected = push_expr(
850 ast,
851 ExprKind::Ternary {
852 cond: condition,
853 then_: default,
854 else_: saved_value,
855 },
856 span,
857 )?;
858 ast.binding_defaults.insert(
859 selected,
860 crate::ast::BindingDefault {
861 saved,
862 default,
863 annotated,
864 },
865 );
866 Ok(selected)
867 }
868}
869
870fn tuple_source_len(
877 ast: &Ast,
878 binding: &Binding,
879 value: ExprId,
880) -> Result<Option<usize>, CompilerFailure> {
881 let Binding::Array {
882 elems, defaults, ..
883 } = binding
884 else {
885 return Ok(None);
886 };
887 if elems.is_empty() {
888 return Ok(None);
889 }
890 let ExprKind::ArrayLiteral { elements } = &ast
891 .try_expr(value)
892 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
893 .kind
894 else {
895 return Ok(None);
896 };
897 let has_spread = elements
898 .iter()
899 .any(|element| matches!(element, crate::ArrayLiteralElement::Spread { .. }));
900 if has_spread {
901 return Ok(None);
902 }
903 let padded = elems.len() > elements.len()
904 && (elements.len()..elems.len()).all(|slot| {
905 elems.get(slot).is_some_and(Option::is_none)
906 || defaults.get(slot).is_some_and(Option::is_some)
907 });
908 Ok(Some(if padded { elems.len() } else { elements.len() }))
909}
910
911fn pad_tuple_source(ast: &mut Ast, value: ExprId, len: usize) -> Result<(), CompilerFailure> {
915 let expr = ast
916 .try_expr(value)
917 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?;
918 let span = expr.span;
919 let ExprKind::ArrayLiteral { elements } = &expr.kind else {
920 return Ok(());
921 };
922 let missing = len.saturating_sub(elements.len());
923 if missing == 0 {
924 return Ok(());
925 }
926 let mut padding = Vec::with_capacity(missing);
927 for _ in 0..missing {
928 let undefined = Ident {
929 name: "undefined".to_string(),
930 span,
931 };
932 let element = push_expr(ast, ExprKind::Identifier(undefined), span)?;
933 ast.synthetic_undefined.insert(element);
934 padding.push(crate::ArrayLiteralElement::Value(element));
935 }
936 if let ExprKind::ArrayLiteral { elements } = &mut ast
938 .try_expr_mut(value)
939 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
940 .kind
941 {
942 elements.extend(padding);
943 }
944 Ok(())
945}
946
947fn array_rest(
950 ast: &mut Ast,
951 source: &Ident,
952 from: usize,
953 span: Span,
954) -> Result<ExprId, CompilerFailure> {
955 let from_lit = push_expr(ast, ExprKind::Number(from as f64), span)?;
956 let slice_receiver = push_expr(ast, ExprKind::Identifier(source.clone()), span)?;
957 let slice_callee = push_expr(
958 ast,
959 ExprKind::FieldAccess {
960 receiver: slice_receiver,
961 name: Ident {
962 name: "slice".to_string(),
963 span,
964 },
965 },
966 span,
967 )?;
968 let length_receiver = push_expr(ast, ExprKind::Identifier(source.clone()), span)?;
969 let length_access = push_expr(
970 ast,
971 ExprKind::FieldAccess {
972 receiver: length_receiver,
973 name: Ident {
974 name: "length".to_string(),
975 span,
976 },
977 },
978 span,
979 )?;
980 let slice_call = push_expr(
981 ast,
982 ExprKind::Call {
983 callee: slice_callee,
984 type_args: None,
985 args: vec![from_lit, length_access],
986 },
987 span,
988 )?;
989 Ok(slice_call)
990}
991
992fn remaining_elements(
997 ast: &mut Ast,
998 source: &Ident,
999 indices: std::ops::Range<usize>,
1000 span: Span,
1001) -> Result<ExprId, CompilerFailure> {
1002 let count = indices.len();
1003 let mut elements = Vec::with_capacity(count);
1004 for i in indices {
1005 let receiver = push_expr(ast, ExprKind::Identifier(source.clone()), span)?;
1006 let index = push_expr(ast, ExprKind::Number(i as f64), span)?;
1007 let access = push_expr(ast, ExprKind::IndexAccess { receiver, index }, span)?;
1008 elements.push(crate::ArrayLiteralElement::Value(access));
1009 }
1010 let remaining = push_expr(ast, ExprKind::ArrayLiteral { elements }, span)?;
1011 ast.tuple_pattern_sources.insert(remaining);
1012 let slice = push_expr(
1013 ast,
1014 ExprKind::FieldAccess {
1015 receiver: remaining,
1016 name: Ident {
1017 name: "slice".to_string(),
1018 span,
1019 },
1020 },
1021 span,
1022 )?;
1023 let from = push_expr(ast, ExprKind::Number(0.0), span)?;
1024 let to = push_expr(ast, ExprKind::Number(count as f64), span)?;
1025 push_expr(
1026 ast,
1027 ExprKind::Call {
1028 callee: slice,
1029 type_args: None,
1030 args: vec![from, to],
1031 },
1032 span,
1033 )
1034}
1035
1036fn push_expr(ast: &mut Ast, kind: ExprKind, span: Span) -> Result<ExprId, CompilerFailure> {
1037 ast.try_push_expr(Expr { kind, span })
1038 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))
1039}
1040
1041fn make_decl(
1042 is_const: bool,
1043 name: Ident,
1044 value: ExprId,
1045 doc: Option<crate::DocComment>,
1046) -> StmtKind {
1047 if is_const {
1048 StmtKind::Const {
1049 name,
1050 ty: None,
1051 value,
1052 doc,
1053 }
1054 } else {
1055 StmtKind::Let {
1056 name,
1057 ty: None,
1058 value,
1059 doc,
1060 }
1061 }
1062}
1063
1064fn let_names(ast: &Ast, stmts: &[StmtId]) -> Result<Vec<Ident>, CompilerFailure> {
1066 let mut names = Vec::new();
1067 for &stmt in stmts {
1068 if let StmtKind::Let { name, .. } = &ast
1069 .try_stmt(stmt)
1070 .map_err(|error| error.into_compiler_failure(CompilerStage::Infer))?
1071 .kind
1072 {
1073 names.push(name.clone());
1074 }
1075 }
1076 Ok(names)
1077}
1078
1079fn lowering_failure(message: &str) -> CompilerFailure {
1080 CompilerFailure::Internal {
1081 stage: CompilerStage::Infer,
1082 span: None,
1083 message: message.into(),
1084 }
1085}
1086
1087#[cfg(test)]
1088mod tests {
1089 use super::lower;
1090 use crate::{Asi, Binding, Stmt, StmtKind, Token, TokenKind, parse};
1091
1092 fn pipeline(source: &str) -> crate::Ast {
1093 let mut asi = Asi::new(source, crate::FileId(0));
1094 let mut tokens: Vec<Token> = Vec::new();
1095 loop {
1096 let t = asi.next_token();
1097 let is_eof = matches!(t.kind, TokenKind::Eof);
1098 tokens.push(t);
1099 if is_eof {
1100 break;
1101 }
1102 }
1103 let _ = asi.into_diagnostics();
1104 let (mut ast, diags) = parse(source, tokens, crate::FileId(0));
1105 assert!(diags.is_empty(), "unexpected parse diags: {diags:?}");
1106 ast = lower(ast).unwrap();
1107 ast
1108 }
1109
1110 fn no_patterns_left(ast: &crate::Ast) {
1111 for i in 0..ast.stmts_len() {
1112 let s = ast.try_stmt(crate::StmtId(i as u32)).unwrap();
1113 assert!(
1114 !matches!(
1115 s.kind,
1116 StmtKind::LetPattern { .. }
1117 | StmtKind::ConstPattern { .. }
1118 | StmtKind::ForOfPattern { .. }
1119 ),
1120 "found unlowered pattern at stmt #{i}: {:?}",
1121 s.kind,
1122 );
1123 if let StmtKind::Function { params, .. } = &s.kind {
1124 for (pi, p) in params.iter().enumerate() {
1125 assert!(
1126 p.pattern.is_none(),
1127 "function param #{pi} still has a pattern after lowering",
1128 );
1129 }
1130 }
1131 }
1132 for i in 0..ast.exprs_len() {
1133 let e = ast.try_expr(crate::ExprId(i as u32)).unwrap();
1134 if let crate::ExprKind::Arrow { params, .. } = &e.kind {
1135 for (pi, p) in params.iter().enumerate() {
1136 assert!(
1137 p.pattern.is_none(),
1138 "arrow param #{pi} still has a pattern after lowering",
1139 );
1140 }
1141 }
1142 }
1143 }
1144
1145 #[test]
1146 fn lowers_simple_object_destructure() {
1147 let ast = pipeline("const { a, b } = obj;");
1148 no_patterns_left(&ast);
1149 assert_eq!(ast.top_level.len(), 3);
1151 let names: Vec<String> = ast
1152 .top_level
1153 .iter()
1154 .map(|id| match &ast.try_stmt(*id).unwrap().kind {
1155 StmtKind::Const { name, .. } => name.name.clone(),
1156 other => panic!("expected Const, got {other:?}"),
1157 })
1158 .collect();
1159 assert!(names[0].starts_with("#pattern_dst_"));
1160 assert_eq!(names[1], "a");
1161 assert_eq!(names[2], "b");
1162 }
1163
1164 #[test]
1165 fn lowers_renamed_object_destructure() {
1166 let ast = pipeline("const { a: x, b: y } = obj;");
1167 no_patterns_left(&ast);
1168 let names: Vec<String> = ast
1169 .top_level
1170 .iter()
1171 .map(|id| match &ast.try_stmt(*id).unwrap().kind {
1172 StmtKind::Const { name, .. } => name.name.clone(),
1173 other => panic!("expected Const, got {other:?}"),
1174 })
1175 .collect();
1176 assert_eq!(names[1], "x");
1177 assert_eq!(names[2], "y");
1178 }
1179
1180 #[test]
1181 fn lowers_array_destructure() {
1182 let ast = pipeline("const [x, y] = arr;");
1183 no_patterns_left(&ast);
1184 assert_eq!(ast.top_level.len(), 3);
1185 if let StmtKind::Const { value, .. } = &ast.try_stmt(ast.top_level[1]).unwrap().kind {
1186 assert!(matches!(
1187 ast.try_expr(*value).unwrap().kind,
1188 crate::ExprKind::IndexAccess { .. }
1189 ));
1190 }
1191 }
1192
1193 #[test]
1194 fn lowers_array_with_hole_and_rest() {
1195 let ast = pipeline("const [, x, ...rest] = arr;");
1196 no_patterns_left(&ast);
1197 assert_eq!(ast.top_level.len(), 3);
1199 if let StmtKind::Const { name, value, .. } = &ast.try_stmt(ast.top_level[1]).unwrap().kind {
1200 assert_eq!(name.name, "x");
1201 if let crate::ExprKind::IndexAccess { index, .. } = &ast.try_expr(*value).unwrap().kind
1202 {
1203 assert_eq!(
1204 ast.try_expr(*index).unwrap().kind,
1205 crate::ExprKind::Number(1.0)
1206 );
1207 } else {
1208 panic!("expected IndexAccess");
1209 }
1210 }
1211 if let StmtKind::Const { name, value, .. } = &ast.try_stmt(ast.top_level[2]).unwrap().kind {
1212 assert_eq!(name.name, "rest");
1213 match &ast.try_expr(*value).unwrap().kind {
1214 crate::ExprKind::Call { callee, args, .. } => {
1215 assert_eq!(args.len(), 2, "slice takes (start, end)");
1216 assert_eq!(
1217 ast.try_expr(args[0]).unwrap().kind,
1218 crate::ExprKind::Number(2.0)
1219 );
1220 match &ast.try_expr(*callee).unwrap().kind {
1221 crate::ExprKind::FieldAccess { name, .. } => {
1222 assert_eq!(name.name, "slice");
1223 }
1224 other => panic!("expected slice FieldAccess, got {other:?}"),
1225 }
1226 }
1227 other => panic!("expected Call, got {other:?}"),
1228 }
1229 }
1230 }
1231
1232 #[test]
1233 fn lowers_object_rest_to_object_rest() {
1234 let ast = pipeline("const { a, ...rest } = obj;");
1235 no_patterns_left(&ast);
1236 assert_eq!(ast.top_level.len(), 3);
1237 match &ast.try_stmt(ast.top_level[2]).unwrap().kind {
1238 StmtKind::ObjectRest {
1239 is_const,
1240 name,
1241 exclude,
1242 ..
1243 } => {
1244 assert!(*is_const);
1245 assert_eq!(name.name, "rest");
1246 assert_eq!(exclude.len(), 1);
1247 assert_eq!(exclude[0].name, "a");
1248 }
1249 other => panic!("expected ObjectRest, got {other:?}"),
1250 }
1251 }
1252
1253 #[test]
1254 fn lowers_function_param_pattern() {
1255 let src = "function f({ a, b }: T): number { return a + b; }";
1256 let ast = pipeline(src);
1257 no_patterns_left(&ast);
1258 let Stmt {
1259 kind: StmtKind::Function { body, params, .. },
1260 ..
1261 } = ast.try_stmt(ast.top_level[0]).unwrap().clone()
1262 else {
1263 panic!("expected Function");
1264 };
1265 assert_eq!(params.len(), 1);
1266 assert!(params[0].pattern.is_none());
1267 assert!(params[0].name.name.starts_with("#pattern_p_"));
1268 let body_stmts = match &ast.try_stmt(body).unwrap().kind {
1269 StmtKind::Block(s) => s.clone(),
1270 _ => panic!("body is not a Block"),
1271 };
1272 assert_eq!(body_stmts.len(), 1);
1273 let bindings = ast.parameter_bindings.get(¶ms[0].name.span).unwrap();
1274 assert_eq!(bindings.len(), 2);
1275 if let StmtKind::Let { name, .. } = &ast.try_stmt(bindings[0]).unwrap().kind {
1276 assert_eq!(name.name, "a");
1277 } else {
1278 panic!("expected Let a");
1279 }
1280 if let StmtKind::Let { name, .. } = &ast.try_stmt(bindings[1]).unwrap().kind {
1281 assert_eq!(name.name, "b");
1282 } else {
1283 panic!("expected Let b");
1284 }
1285 assert!(matches!(
1286 ast.try_stmt(body_stmts[0]).unwrap().kind,
1287 StmtKind::Return(_)
1288 ));
1289 }
1290
1291 #[test]
1292 fn parameter_object_rest_has_a_mutable_authored_binding() {
1293 let ast = pipeline("function f({ head, ...tail }: T): number { return tail.value; }");
1294 let StmtKind::Function { params, .. } = &ast.try_stmt(ast.top_level[0]).unwrap().kind
1295 else {
1296 panic!("expected function");
1297 };
1298 let bindings = ast.parameter_bindings.get(¶ms[0].name.span).unwrap();
1299 assert_eq!(bindings.len(), 2);
1300 let StmtKind::ObjectRest { is_const, name, .. } = &ast.try_stmt(bindings[1]).unwrap().kind
1301 else {
1302 panic!("expected object rest binding");
1303 };
1304 assert!(!*is_const);
1305 assert_eq!(name.name, "tail");
1306 }
1307
1308 #[test]
1309 fn lowers_arrow_expr_body_with_pattern() {
1310 let src = "const f = ({a, b}: T): number => a + b;";
1311 let ast = pipeline(src);
1312 no_patterns_left(&ast);
1313 let mut found = false;
1314 for i in 0..ast.exprs_len() {
1315 let e = ast.try_expr(crate::ExprId(i as u32)).unwrap();
1316 if let crate::ExprKind::Arrow { params, body, .. } = &e.kind {
1317 assert!(matches!(body, crate::ArrowBody::Expr(_)));
1318 assert_eq!(
1319 ast.parameter_bindings
1320 .get(¶ms[0].name.span)
1321 .unwrap()
1322 .len(),
1323 2
1324 );
1325 found = true;
1326 }
1327 }
1328 assert!(found, "no Arrow found in expr arena");
1329 }
1330
1331 #[test]
1332 fn lowers_for_of_array_pattern() {
1333 let src = "function main(): void { for (const [a, b] of pairs) { use(a, b); } }";
1337 let ast = pipeline(src);
1338 no_patterns_left(&ast);
1339
1340 let Stmt {
1341 kind: StmtKind::Function { body, .. },
1342 ..
1343 } = ast.try_stmt(ast.top_level[0]).unwrap().clone()
1344 else {
1345 panic!("expected Function");
1346 };
1347 let body_stmts = match &ast.try_stmt(body).unwrap().kind {
1348 StmtKind::Block(s) => s.clone(),
1349 _ => panic!("function body is not a Block"),
1350 };
1351 assert_eq!(body_stmts.len(), 1, "main body has just the for-of");
1352
1353 let (loop_name, loop_body) = match &ast.try_stmt(body_stmts[0]).unwrap().kind {
1354 StmtKind::ForOf { name, body, .. } => (name.name.clone(), *body),
1355 other => panic!("expected ForOf, got {other:?}"),
1356 };
1357 assert!(
1358 loop_name.starts_with("#pattern_dst_"),
1359 "loop var was rewritten to a synth temp; got {loop_name}",
1360 );
1361
1362 let loop_body_stmts = match &ast.try_stmt(loop_body).unwrap().kind {
1363 StmtKind::Block(s) => s.clone(),
1364 _ => panic!("for-of body is not a Block"),
1365 };
1366 assert_eq!(loop_body_stmts.len(), 3);
1367 if let StmtKind::Const { name, value, .. } = &ast.try_stmt(loop_body_stmts[0]).unwrap().kind
1368 {
1369 assert_eq!(name.name, "a");
1370 assert!(matches!(
1371 ast.try_expr(*value).unwrap().kind,
1372 crate::ExprKind::IndexAccess { .. }
1373 ));
1374 assert!(ast.pattern_origins.contains_key(value));
1376 } else {
1377 panic!("expected Const a");
1378 }
1379 if let StmtKind::Const { name, .. } = &ast.try_stmt(loop_body_stmts[1]).unwrap().kind {
1380 assert_eq!(name.name, "b");
1381 } else {
1382 panic!("expected Const b");
1383 }
1384 }
1385
1386 #[test]
1387 fn lowers_let_pattern_preserves_mutability() {
1388 let ast = pipeline("let { a } = obj;");
1390 no_patterns_left(&ast);
1391 assert!(matches!(
1393 ast.try_stmt(ast.top_level[0]).unwrap().kind,
1394 StmtKind::Const { .. }
1395 ));
1396 assert!(matches!(
1397 ast.try_stmt(ast.top_level[1]).unwrap().kind,
1398 StmtKind::Let { .. }
1399 ));
1400 }
1401
1402 #[test]
1403 fn no_op_on_plain_program() {
1404 let src = "const x = 1; function main(): void { }";
1405 let before = {
1406 let mut asi = Asi::new(src, crate::FileId(0));
1407 let mut tokens: Vec<Token> = Vec::new();
1408 loop {
1409 let t = asi.next_token();
1410 let is_eof = matches!(t.kind, TokenKind::Eof);
1411 tokens.push(t);
1412 if is_eof {
1413 break;
1414 }
1415 }
1416 let _ = asi.into_diagnostics();
1417 let (ast, _) = parse(src, tokens, crate::FileId(0));
1418 ast
1419 };
1420 let after = pipeline(src);
1421 assert_eq!(format!("{before:?}"), format!("{after:?}"));
1422 }
1423
1424 #[test]
1425 fn lowers_pattern_in_nested_block() {
1426 let src = "function main(): void { const { a } = obj; }";
1427 let ast = pipeline(src);
1428 no_patterns_left(&ast);
1429 }
1430
1431 #[test]
1432 fn idempotent() {
1433 let src = "const { a, b } = obj;";
1434 let mut ast1 = pipeline(src);
1435 let snapshot = format!("{ast1:?}");
1436 ast1 = lower(ast1).unwrap();
1437 assert_eq!(snapshot, format!("{ast1:?}"));
1438 }
1439
1440 #[test]
1441 fn binding_span_preserved() {
1442 let (ast, _) = parse(
1443 "const { a } = obj;",
1444 tokens_of("const { a } = obj;"),
1445 crate::FileId(0),
1446 );
1447 match &ast.try_stmt(ast.top_level[0]).unwrap().kind {
1448 crate::StmtKind::ConstPattern {
1449 binding: Binding::Object { span, .. },
1450 ..
1451 } => {
1452 assert_eq!(*span, crate::Span::new(crate::FileId(0), 6, 11).unwrap());
1454 }
1455 other => panic!("expected ConstPattern Object, got {other:?}"),
1456 }
1457 }
1458
1459 #[test]
1460 fn defaults_save_each_access_once_and_test_undefined() {
1461 for source in [
1462 "const { a = fallback() } = source();",
1463 "const [a = fallback()] = source();",
1464 ] {
1465 let ast = pipeline(source);
1466 assert_eq!(ast.top_level.len(), 3);
1467 let StmtKind::Const {
1468 value: saved_access,
1469 ..
1470 } = ast.try_stmt(ast.top_level[1]).unwrap().kind
1471 else {
1472 panic!("expected saved read")
1473 };
1474 assert!(matches!(
1475 ast.try_expr(saved_access).unwrap().kind,
1476 crate::ExprKind::FieldAccess { .. } | crate::ExprKind::Ternary { .. }
1477 ));
1478 let StmtKind::Const { value, .. } = ast.try_stmt(ast.top_level[2]).unwrap().kind else {
1479 panic!("expected binding")
1480 };
1481 let crate::ExprKind::Ternary { cond, then_, else_ } = ast.try_expr(value).unwrap().kind
1482 else {
1483 panic!("expected lazy default")
1484 };
1485 let crate::ExprKind::Binary {
1486 op: crate::BinOp::Eq,
1487 lhs,
1488 rhs,
1489 } = ast.try_expr(cond).unwrap().kind
1490 else {
1491 panic!("expected undefined comparison")
1492 };
1493 assert_eq!(lhs, else_);
1494 assert!(matches!(
1495 ast.try_expr(rhs).unwrap().kind,
1496 crate::ExprKind::Void { .. }
1497 ));
1498 assert!(matches!(
1499 ast.try_expr(then_).unwrap().kind,
1500 crate::ExprKind::Call { .. }
1501 ));
1502 }
1503 }
1504
1505 #[test]
1506 fn array_defaults_keep_original_indices_and_origin_metadata() {
1507 let ast = pipeline("const [, a = 1] = values;");
1508 let StmtKind::Const { value, .. } = ast.try_stmt(ast.top_level[1]).unwrap().kind else {
1509 panic!("expected saved index read")
1510 };
1511 let crate::ExprKind::Ternary {
1512 cond,
1513 then_: access,
1514 else_,
1515 } = ast.try_expr(value).unwrap().kind
1516 else {
1517 panic!("expected bounds guard")
1518 };
1519 assert!(matches!(
1520 ast.try_expr(cond).unwrap().kind,
1521 crate::ExprKind::Binary {
1522 op: crate::BinOp::Lt,
1523 ..
1524 }
1525 ));
1526 assert!(matches!(
1527 ast.try_expr(else_).unwrap().kind,
1528 crate::ExprKind::Void { .. }
1529 ));
1530 let crate::ExprKind::IndexAccess { index, .. } = ast.try_expr(access).unwrap().kind else {
1531 panic!("expected guarded index")
1532 };
1533 assert!(matches!(
1534 ast.try_expr(index).unwrap().kind,
1535 crate::ExprKind::Number(1.0)
1536 ));
1537 assert_eq!(ast.pattern_origins.get(&access).unwrap().slot_arity, 2);
1538 }
1539
1540 #[test]
1541 fn lowers_class_method_parameter_defaults() {
1542 let ast = pipeline("class C { method({ x = 1 }: T): number { return x; } }");
1543 let StmtKind::ClassDecl { ref members, .. } = ast.try_stmt(ast.top_level[0]).unwrap().kind
1544 else {
1545 panic!("expected class")
1546 };
1547 let crate::ClassMember::Method { params, body, .. } = &members[0] else {
1548 panic!("expected method")
1549 };
1550 assert!(params[0].pattern.is_none());
1551 let StmtKind::Block(ref statements) = ast.try_stmt(*body).unwrap().kind else {
1552 panic!("expected block")
1553 };
1554 assert_eq!(statements.len(), 1);
1555 assert_eq!(
1556 ast.parameter_bindings
1557 .get(¶ms[0].name.span)
1558 .unwrap()
1559 .len(),
1560 2
1561 );
1562 }
1563
1564 fn tokens_of(source: &str) -> Vec<Token> {
1565 let mut asi = Asi::new(source, crate::FileId(0));
1566 let mut tokens = Vec::new();
1567 loop {
1568 let t = asi.next_token();
1569 let is_eof = matches!(t.kind, TokenKind::Eof);
1570 tokens.push(t);
1571 if is_eof {
1572 break;
1573 }
1574 }
1575 let _ = asi.into_diagnostics();
1576 tokens
1577 }
1578}