1use crate::ast::{
10 ArrowBody, ChainPart, ClassMember, ExprKind, InterfaceMember, ParamDecl, StmtKind,
11 TypeAnnotation, TypeAnnotationKind,
12};
13use crate::compiler_error::{CompilerFailure, CompilerStage};
14use crate::typed_ast::{
15 ClosureBody, PostfixTarget, TypedChainPart, TypedExprKind, TypedObjectFieldSource,
16 TypedStmtKind,
17};
18use std::ops::Range;
19
20use crate::{Ast, ExprId, Span, StmtId, TypedAst};
21
22pub const MAX_SYNTAX_HEIGHT: u32 = 256;
24
25pub const MAX_TYPED_HEIGHT: u32 = 1024;
28
29pub fn check_syntax(ast: &Ast) -> Result<(), CompilerFailure> {
31 measure(
32 &SyntaxTree(ast),
33 MAX_SYNTAX_HEIGHT,
34 CompilerStage::Parse,
35 |span| CompilerFailure::Limit {
36 stage: CompilerStage::Parse,
37 span: Some(span),
38 message: format!(
39 "syntax nesting exceeds the compiler limit of {MAX_SYNTAX_HEIGHT} levels"
40 ),
41 help: vec![
42 "split long operator chains or deeply nested code into intermediate declarations"
43 .into(),
44 ],
45 },
46 )
47}
48
49pub fn check_typed(ta: &TypedAst, stage: CompilerStage) -> Result<(), CompilerFailure> {
51 measure(&TypedTree(ta), MAX_TYPED_HEIGHT, stage, |span| {
52 CompilerFailure::Limit {
53 stage,
54 span: Some(span),
55 message: format!(
56 "lowered expression nesting exceeds the compiler limit of {MAX_TYPED_HEIGHT} levels"
57 ),
58 help: vec![
59 "split long operator chains, template literals or spreads into intermediate declarations"
60 .into(),
61 ],
62 }
63 })
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67enum Node {
68 Expr(ExprId),
69 Stmt(StmtId),
70}
71
72struct NodeInfo {
77 span: Span,
78 children: Vec<Child>,
79 owned_height: u32,
80}
81
82#[derive(Clone, Copy)]
85struct Child {
86 node: Node,
87 offset: u32,
88}
89
90impl Child {
91 fn direct(node: Node) -> Self {
92 Self { node, offset: 0 }
93 }
94}
95
96trait Tree {
97 fn expr_ids(&self) -> Result<Range<u32>, CompilerFailure>;
98 fn stmt_ids(&self) -> Result<Range<u32>, CompilerFailure>;
99 fn node(&self, node: Node) -> Result<NodeInfo, CompilerFailure>;
100}
101
102#[derive(Clone, Copy, PartialEq, Eq)]
103enum Measured {
104 Unvisited,
105 InProgress,
106 Done {
107 height: u32,
108 deepest: Option<Node>,
111 owned_height: u32,
112 },
113}
114
115struct Frame {
116 node: Node,
117 info: NodeInfo,
118 next_child: usize,
119 pending: Option<Child>,
121 child_height: u32,
122 deepest: Option<Node>,
125}
126
127impl Frame {
128 fn include(&mut self, child: Child, height: u32) {
129 let height = height.saturating_add(child.offset);
130 if self.deepest.is_none() || height > self.child_height {
131 self.child_height = height;
132 self.deepest = Some(child.node);
133 }
134 }
135}
136
137struct Heights {
138 exprs: Vec<Measured>,
139 stmts: Vec<Measured>,
140}
141
142impl Heights {
143 fn get(&self, node: Node) -> Option<Measured> {
144 match node {
145 Node::Expr(id) => self.exprs.get(id.0 as usize).copied(),
146 Node::Stmt(id) => self.stmts.get(id.0 as usize).copied(),
147 }
148 }
149
150 fn slot(&mut self, node: Node) -> Option<&mut Measured> {
151 match node {
152 Node::Expr(id) => self.exprs.get_mut(id.0 as usize),
153 Node::Stmt(id) => self.stmts.get_mut(id.0 as usize),
154 }
155 }
156}
157
158fn measure(
161 tree: &impl Tree,
162 limit: u32,
163 stage: CompilerStage,
164 exceeded: impl Fn(Span) -> CompilerFailure,
165) -> Result<(), CompilerFailure> {
166 let expr_ids = tree.expr_ids().map_err(|error| error.with_stage(stage))?;
167 let stmt_ids = tree.stmt_ids().map_err(|error| error.with_stage(stage))?;
168 let mut heights = Heights {
169 exprs: vec![Measured::Unvisited; expr_ids.len()],
170 stmts: vec![Measured::Unvisited; stmt_ids.len()],
171 };
172 let roots = expr_ids
173 .map(|id| Node::Expr(ExprId(id)))
174 .chain(stmt_ids.map(|id| Node::Stmt(StmtId(id))));
175 let mut tallest: Option<(Node, u32)> = None;
181 for root in roots {
182 if heights.get(root) != Some(Measured::Unvisited) {
183 continue;
184 }
185 measure_from(tree, root, &mut heights, stage)?;
186 if let Some(Measured::Done { height, .. }) = heights.get(root)
187 && height > limit
188 && tallest.is_none_or(|(_, tallest_height)| height > tallest_height)
189 {
190 tallest = Some((root, height));
191 }
192 }
193 match tallest {
194 Some((node, _)) => Err(exceeded(reported_span(tree, &heights, node, stage)?)),
195 None => Ok(()),
196 }
197}
198
199fn measure_from(
200 tree: &impl Tree,
201 root: Node,
202 heights: &mut Heights,
203 stage: CompilerStage,
204) -> Result<(), CompilerFailure> {
205 let mut stack = vec![enter(tree, root, heights, stage)?];
206 while let Some(frame) = stack.last_mut() {
207 if let Some(&child) = frame.info.children.get(frame.next_child) {
208 frame.next_child += 1;
209 match heights.get(child.node) {
210 Some(Measured::Unvisited) | None => {
212 let child_frame = enter(tree, child.node, heights, stage)?;
213 frame.pending = Some(child);
214 stack.push(child_frame);
215 }
216 Some(Measured::InProgress) => {
217 return Err(internal(stage, "tree node is its own descendant"));
218 }
219 Some(Measured::Done { height, .. }) => frame.include(child, height),
220 }
221 continue;
222 }
223 let Some(frame) = stack.pop() else {
224 break;
225 };
226 let height = frame
227 .child_height
228 .max(frame.info.owned_height)
229 .saturating_add(1);
230 let Some(stored) = heights.slot(frame.node) else {
231 return Err(internal(stage, "tree node is outside its arena"));
232 };
233 *stored = Measured::Done {
234 height,
235 deepest: frame.deepest,
236 owned_height: frame.info.owned_height,
237 };
238 if let Some(parent) = stack.last_mut() {
239 let child = parent
240 .pending
241 .take()
242 .ok_or_else(|| internal(stage, "tree walk lost its parent's child"))?;
243 parent.include(child, height);
244 }
245 }
246 Ok(())
247}
248
249fn reported_span(
260 tree: &impl Tree,
261 heights: &Heights,
262 over_limit: Node,
263 stage: CompilerStage,
264) -> Result<Span, CompilerFailure> {
265 let span_of = |node| {
266 tree.node(node)
267 .map(|info| info.span)
268 .map_err(|error| error.with_stage(stage))
269 };
270 let mut longest = Longest {
271 span: span_of(over_limit)?,
272 levels: 0,
273 };
274 let mut segment = None;
275 let mut next = Some(over_limit);
276 while let Some(node) = next {
279 let Some(Measured::Done {
280 height,
281 deepest,
282 owned_height,
283 }) = heights.get(node)
284 else {
285 return Err(internal(
286 stage,
287 "a node on the deepest path was not measured",
288 ));
289 };
290 segment = match (node, segment) {
291 (Node::Stmt(_), Some(open)) => {
292 longest.offer(open, height);
293 None
294 }
295 (Node::Stmt(_), None) => None,
296 (Node::Expr(_), open) => {
297 let open = match open {
298 Some(open) => open,
299 None => Segment {
300 expression: span_of(node)?,
301 height,
302 },
303 };
304 if owns_its_height(height, owned_height) {
305 longest.offer(open, 0);
306 None
307 } else {
308 Some(open)
309 }
310 }
311 };
312 next = deepest;
313 }
314 Ok(Span {
315 end: longest.span.start,
316 ..longest.span
317 })
318}
319
320fn owns_its_height(height: u32, owned_height: u32) -> bool {
323 height == owned_height.saturating_add(1)
324}
325
326#[derive(Clone, Copy)]
327struct Segment {
328 expression: Span,
329 height: u32,
330}
331
332struct Longest {
333 span: Span,
334 levels: u32,
335}
336
337impl Longest {
338 fn offer(&mut self, segment: Segment, end_height: u32) {
341 let levels = segment.height.saturating_sub(end_height);
342 if levels > self.levels {
343 self.span = segment.expression;
344 self.levels = levels;
345 }
346 }
347}
348
349fn enter(
350 tree: &impl Tree,
351 node: Node,
352 heights: &mut Heights,
353 stage: CompilerStage,
354) -> Result<Frame, CompilerFailure> {
355 let info = tree.node(node).map_err(|error| error.with_stage(stage))?;
356 let Some(stored) = heights.slot(node) else {
357 return Err(internal(stage, "tree node is outside its arena"));
358 };
359 *stored = Measured::InProgress;
360 Ok(Frame {
361 node,
362 info,
363 next_child: 0,
364 pending: None,
365 child_height: 0,
366 deepest: None,
367 })
368}
369
370fn id_range(mut ids: impl DoubleEndedIterator<Item = u32>) -> Range<u32> {
372 let end = ids.next_back().map_or(0, |last| last.saturating_add(1));
373 0..end
374}
375
376fn syntax_arena_error(error: crate::arena::ArenaError) -> CompilerFailure {
377 error.into_compiler_failure(CompilerStage::Parse)
378}
379
380fn typed_arena_error(error: crate::arena::ArenaError) -> CompilerFailure {
381 error.into_compiler_failure(CompilerStage::Infer)
382}
383
384fn internal(stage: CompilerStage, message: &str) -> CompilerFailure {
385 CompilerFailure::Internal {
386 stage,
387 span: None,
388 message: message.into(),
389 }
390}
391
392struct SyntaxTree<'a>(&'a Ast);
393
394impl Tree for SyntaxTree<'_> {
395 fn expr_ids(&self) -> Result<Range<u32>, CompilerFailure> {
396 let ids = self.0.expr_ids().map_err(syntax_arena_error)?;
397 Ok(id_range(ids.map(|id| id.0)))
398 }
399
400 fn stmt_ids(&self) -> Result<Range<u32>, CompilerFailure> {
401 let ids = self.0.stmt_ids().map_err(syntax_arena_error)?;
402 Ok(id_range(ids.map(|id| id.0)))
403 }
404
405 fn node(&self, node: Node) -> Result<NodeInfo, CompilerFailure> {
406 let mut children = SyntaxChildren::default();
407 let span = match node {
408 Node::Expr(id) => {
409 let expr = self.0.try_expr(id).map_err(syntax_arena_error)?;
410 children.expr_kind(&expr.kind);
411 expr.span
412 }
413 Node::Stmt(id) => {
414 let stmt = self.0.try_stmt(id).map_err(syntax_arena_error)?;
415 children.stmt_kind(&stmt.kind);
416 stmt.span
417 }
418 };
419 if let Some(failure) = children.failure {
420 return Err(failure.with_span(span));
421 }
422 Ok(NodeInfo {
423 span,
424 children: children
425 .nodes
426 .into_iter()
427 .map(Child::direct)
428 .chain(children.behind_owned)
429 .collect(),
430 owned_height: children.owned_height,
431 })
432 }
433}
434
435#[derive(Default)]
436struct SyntaxChildren {
437 failure: Option<CompilerFailure>,
438 nodes: Vec<Node>,
439 behind_owned: Vec<Child>,
441 owned_height: u32,
442}
443
444impl SyntaxChildren {
445 fn expr(&mut self, id: ExprId) {
446 self.nodes.push(Node::Expr(id));
447 }
448
449 fn exprs(&mut self, ids: &[ExprId]) {
450 self.nodes.extend(ids.iter().copied().map(Node::Expr));
451 }
452
453 fn stmt(&mut self, id: StmtId) {
454 self.nodes.push(Node::Stmt(id));
455 }
456
457 fn annotation(&mut self, ty: &TypeAnnotation) {
458 self.annotation_at(ty, 0);
459 }
460
461 fn annotation_at(&mut self, ty: &TypeAnnotation, offset: u32) {
462 if self.failure.is_some() {
463 return;
464 }
465 match annotation_height(ty) {
466 Ok(height) => self.owned_height = self.owned_height.max(height.saturating_add(offset)),
467 Err(failure) => self.failure = Some(failure),
468 }
469 }
470
471 fn annotations<'a>(&mut self, types: impl IntoIterator<Item = &'a TypeAnnotation>) {
472 for ty in types {
473 self.annotation(ty);
474 }
475 }
476
477 fn params(&mut self, params: &[ParamDecl]) {
478 for param in params {
479 self.annotations(¶m.ty);
480 self.nodes.extend(param.default.map(Node::Expr));
481 if let Some(pattern) = ¶m.pattern {
482 self.binding(pattern);
483 }
484 }
485 }
486
487 fn binding(&mut self, binding: &crate::Binding) {
488 match binding {
489 crate::Binding::Object { fields, .. } => {
490 self.nodes.extend(
491 fields
492 .iter()
493 .filter_map(|field| field.default)
494 .map(Node::Expr),
495 );
496 }
497 crate::Binding::Array { defaults, .. } => {
498 self.nodes
499 .extend(defaults.iter().flatten().copied().map(Node::Expr));
500 }
501 }
502 }
503
504 fn chain_parts(&mut self, parts: &[ChainPart]) {
507 let mut depth = 0u32;
508 for part in parts {
509 depth = depth.saturating_add(1);
510 self.owned_height = self.owned_height.max(depth);
511 match part {
512 ChainPart::Field { .. } | ChainPart::NonNull { .. } => {}
513 ChainPart::Index { idx, .. } => self.expr_at(*idx, depth),
514 ChainPart::Call {
515 args, type_args, ..
516 } => {
517 for arg in args {
518 self.expr_at(*arg, depth);
519 }
520 for ty in type_args.iter().flatten() {
521 self.annotation_at(ty, depth);
522 }
523 }
524 }
525 }
526 }
527
528 fn expr_at(&mut self, id: ExprId, offset: u32) {
529 self.behind_owned.push(Child {
530 node: Node::Expr(id),
531 offset,
532 });
533 }
534
535 fn expr_kind(&mut self, kind: &ExprKind) {
536 match kind {
537 ExprKind::Number(_)
538 | ExprKind::BigInt(_)
539 | ExprKind::String(_)
540 | ExprKind::Boolean(_)
541 | ExprKind::Null
542 | ExprKind::Identifier(_)
543 | ExprKind::This
544 | ExprKind::ThisOutsideReceiver
545 | ExprKind::Super
546 | ExprKind::Regex { .. } => {}
547 ExprKind::Binary { lhs, rhs, .. } => self.exprs(&[*lhs, *rhs]),
548 ExprKind::Unary { operand, .. }
549 | ExprKind::Paren(operand)
550 | ExprKind::Typeof { operand }
551 | ExprKind::Void { operand }
552 | ExprKind::Delete { operand }
553 | ExprKind::PostfixUnary { operand, .. } => self.expr(*operand),
554 ExprKind::Call {
555 callee,
556 type_args,
557 args,
558 }
559 | ExprKind::New {
560 callee,
561 type_args,
562 args,
563 } => {
564 self.expr(*callee);
565 self.annotations(type_args.iter().flatten());
566 self.exprs(args);
567 }
568 ExprKind::ObjectLiteral { members } => {
569 for member in members {
570 self.nodes.extend(member.expressions().map(Node::Expr));
571 }
572 }
573 ExprKind::ArrayLiteral { elements } => {
574 self.nodes
575 .extend(elements.iter().map(|element| Node::Expr(element.value())));
576 }
577 ExprKind::FieldAccess { receiver, .. } => self.expr(*receiver),
578 ExprKind::IndexAccess { receiver, index } => self.exprs(&[*receiver, *index]),
579 ExprKind::FunctionExpression {
580 this_type,
581 function,
582 ..
583 } => {
584 self.annotations(this_type);
585 self.expr(*function);
586 }
587 ExprKind::Arrow {
588 params,
589 return_type,
590 type_predicate,
591 body,
592 } => {
593 self.params(params);
594 self.annotations(return_type);
595 self.annotations(type_predicate.iter().map(|predicate| &predicate.asserted));
596 match body {
597 ArrowBody::Expr(id) => self.expr(*id),
598 ArrowBody::Block(id) => self.stmt(*id),
599 }
600 }
601 ExprKind::TemplateLiteral { exprs, .. } => self.exprs(exprs),
602 ExprKind::Ternary { cond, then_, else_ } => self.exprs(&[*cond, *then_, *else_]),
603 ExprKind::OptionalChain { base, parts } => {
604 self.expr(*base);
605 self.chain_parts(parts);
606 }
607 ExprKind::Assign { target, value, .. } => self.exprs(&[*target, *value]),
608 ExprKind::As { expr, ty } => {
609 self.expr(*expr);
610 self.annotation(ty);
611 }
612 ExprKind::InstanceOf { value, ty } => {
613 self.expr(*value);
614 self.annotation(ty);
615 }
616 }
617 }
618
619 fn stmt_kind(&mut self, kind: &StmtKind) {
620 match kind {
621 StmtKind::Let { ty, value, .. }
622 | StmtKind::Const { ty, value, .. }
623 | StmtKind::ObjectRest {
624 ty, source: value, ..
625 } => {
626 self.annotations(ty);
627 self.expr(*value);
628 }
629 StmtKind::LetPattern {
630 ty, value, binding, ..
631 }
632 | StmtKind::ConstPattern {
633 ty, value, binding, ..
634 } => {
635 self.annotations(ty);
636 self.expr(*value);
637 self.binding(binding);
638 }
639 StmtKind::Function {
640 params,
641 return_type,
642 type_predicate,
643 body,
644 ..
645 } => {
646 self.params(params);
647 self.annotations(return_type);
648 self.annotations(type_predicate.iter().map(|predicate| &predicate.asserted));
649 self.stmt(*body);
650 }
651 StmtKind::If {
652 condition,
653 then_block,
654 else_block,
655 } => {
656 self.expr(*condition);
657 self.stmt(*then_block);
658 self.nodes.extend(else_block.map(Node::Stmt));
659 }
660 StmtKind::While { condition, body } | StmtKind::DoWhile { body, condition } => {
661 self.expr(*condition);
662 self.stmt(*body);
663 }
664 StmtKind::For {
665 init,
666 condition,
667 update,
668 body,
669 } => {
670 self.nodes.extend(init.map(Node::Stmt));
671 self.nodes.extend(condition.map(Node::Expr));
672 self.nodes.extend(update.map(Node::Stmt));
673 self.stmt(*body);
674 }
675 StmtKind::ForOf { ty, iter, body, .. } => {
676 self.annotations(ty);
677 self.expr(*iter);
678 self.stmt(*body);
679 }
680 StmtKind::ForOfPattern {
681 ty,
682 iter,
683 body,
684 binding,
685 ..
686 } => {
687 self.annotations(ty);
688 self.expr(*iter);
689 self.stmt(*body);
690 self.binding(binding);
691 }
692 StmtKind::Switch {
693 discriminant,
694 cases,
695 default,
696 } => {
697 self.expr(*discriminant);
698 for case in cases {
699 self.exprs(&case.values);
700 self.stmt(case.body);
701 }
702 self.nodes
703 .extend(default.iter().map(|default| Node::Stmt(default.body)));
704 }
705 StmtKind::Break
706 | StmtKind::Continue
707 | StmtKind::EnumDecl { .. }
708 | StmtKind::Import { .. }
709 | StmtKind::ExportFrom { .. } => {}
710 StmtKind::Return(value) => self.nodes.extend(value.map(Node::Expr)),
711 StmtKind::Throw { value } | StmtKind::Expr(value) => self.expr(*value),
712 StmtKind::Try {
713 body,
714 catches,
715 finally,
716 } => {
717 self.stmt(*body);
718 for catch in catches {
719 self.annotations(&catch.ty);
720 self.stmt(catch.body);
721 }
722 self.nodes.extend(finally.map(Node::Stmt));
723 }
724 StmtKind::Block(stmts) => self.nodes.extend(stmts.iter().copied().map(Node::Stmt)),
725 StmtKind::Assign { value, .. } | StmtKind::CompoundAssign { value, .. } => {
726 self.expr(*value);
727 }
728 StmtKind::AssignField {
729 receiver, value, ..
730 }
731 | StmtKind::CompoundAssignField {
732 receiver, value, ..
733 } => self.exprs(&[*receiver, *value]),
734 StmtKind::AssignIndex {
735 receiver,
736 index,
737 value,
738 }
739 | StmtKind::CompoundAssignIndex {
740 receiver,
741 index,
742 value,
743 ..
744 } => self.exprs(&[*receiver, *index, *value]),
745 StmtKind::InterfaceDecl {
746 extends, members, ..
747 } => {
748 self.annotations(extends);
749 for member in members {
750 match member {
751 InterfaceMember::IndexSignature(signature) => {
752 self.annotation(&signature.value);
753 }
754 InterfaceMember::Method {
755 params,
756 return_type,
757 ..
758 } => {
759 self.params(params);
760 self.annotation(return_type);
761 }
762 InterfaceMember::Property { ty, .. } => self.annotation(ty),
763 }
764 }
765 }
766 StmtKind::ClassDecl {
767 extends,
768 implements,
769 members,
770 ..
771 } => {
772 self.annotations(extends);
773 self.annotations(implements);
774 for member in members {
775 self.class_member(member);
776 }
777 }
778 StmtKind::TypeAliasDecl { ty, .. } => self.annotation(ty),
779 }
780 }
781
782 fn class_member(&mut self, member: &ClassMember) {
783 match member {
784 ClassMember::Field {
785 ty, initializer, ..
786 } => {
787 self.annotation(ty);
788 self.nodes.extend(initializer.map(Node::Expr));
789 }
790 ClassMember::Method {
791 params,
792 return_type,
793 body,
794 ..
795 } => {
796 self.params(params);
797 self.annotation(return_type);
798 self.stmt(*body);
799 }
800 ClassMember::Constructor { params, body, .. } => {
801 self.params(params);
802 self.stmt(*body);
803 }
804 ClassMember::Accessor {
805 param,
806 return_type,
807 body,
808 ..
809 } => {
810 self.params(param.as_deref().map_or(&[], std::slice::from_ref));
811 self.annotations(return_type);
812 self.stmt(*body);
813 }
814 }
815 }
816}
817
818fn annotation_height(root: &TypeAnnotation) -> Result<u32, CompilerFailure> {
821 measure_annotation(root, u64::MAX, u32::MAX).map(|extent| extent.depth)
822}
823
824pub(crate) fn annotation_nodes(
826 root: &TypeAnnotation,
827 max_nodes: u64,
828) -> Result<u64, CompilerFailure> {
829 measure_annotation(root, max_nodes, u32::MAX).map(|extent| extent.nodes)
830}
831
832fn measure_annotation(
833 root: &TypeAnnotation,
834 max_nodes: u64,
835 max_depth: u32,
836) -> Result<crate::type_size::TypeExtent, CompilerFailure> {
837 crate::type_walk::measure(root, max_nodes, max_depth, AnnotationChildren::new).map_err(|_| {
838 CompilerFailure::Internal {
839 stage: CompilerStage::Parse,
840 span: Some(root.span),
841 message: "could not allocate annotation traversal frames".into(),
842 }
843 })
844}
845
846struct AnnotationChildren<'a> {
847 members: std::slice::Iter<'a, TypeAnnotation>,
848 fields: std::slice::Iter<'a, crate::ast::TypeAnnotationField>,
849 trailing: Option<&'a TypeAnnotation>,
850}
851
852impl<'a> AnnotationChildren<'a> {
853 fn new(ty: &'a TypeAnnotation) -> Self {
854 let mut children = Self {
855 members: [].iter(),
856 fields: [].iter(),
857 trailing: None,
858 };
859 match &ty.kind {
860 TypeAnnotationKind::Name { args, .. }
861 | TypeAnnotationKind::Qualified { args, .. }
862 | TypeAnnotationKind::Tuple(args)
863 | TypeAnnotationKind::Union(args) => {
864 children.members = args.iter();
865 }
866 TypeAnnotationKind::StringLiteral(_)
867 | TypeAnnotationKind::NumberLiteral(_)
868 | TypeAnnotationKind::BigIntLiteral(_)
869 | TypeAnnotationKind::BooleanLiteral(_)
870 | TypeAnnotationKind::TypeOf { .. } => {}
871 TypeAnnotationKind::Array(inner)
872 | TypeAnnotationKind::Optional(inner)
873 | TypeAnnotationKind::Readonly(inner)
874 | TypeAnnotationKind::KeyOf(inner) => children.trailing = Some(inner),
875 TypeAnnotationKind::Object { fields, index } => {
876 children.fields = fields.iter();
877 children.trailing = index.as_ref().map(|index| &index.value);
878 }
879 TypeAnnotationKind::Function {
880 params,
881 return_type,
882 } => {
883 children.fields = params.iter();
884 children.trailing = Some(return_type);
885 }
886 }
887 children
888 }
889}
890
891impl<'a> Iterator for AnnotationChildren<'a> {
892 type Item = &'a TypeAnnotation;
893
894 fn next(&mut self) -> Option<Self::Item> {
895 self.trailing
896 .take()
897 .or_else(|| self.fields.next_back().map(|field| &field.ty))
898 .or_else(|| self.members.next_back())
899 }
900}
901
902struct TypedTree<'a>(&'a TypedAst);
903
904impl Tree for TypedTree<'_> {
905 fn expr_ids(&self) -> Result<Range<u32>, CompilerFailure> {
906 let ids = self.0.expr_ids().map_err(typed_arena_error)?;
907 Ok(id_range(ids.map(|id| id.0)))
908 }
909
910 fn stmt_ids(&self) -> Result<Range<u32>, CompilerFailure> {
911 let ids = self.0.stmt_ids().map_err(typed_arena_error)?;
912 Ok(id_range(ids.map(|id| id.0)))
913 }
914
915 fn node(&self, node: Node) -> Result<NodeInfo, CompilerFailure> {
916 let mut children = TypedChildren::default();
917 let span = match node {
918 Node::Expr(id) => {
919 let expr = self.0.try_expr(id).map_err(typed_arena_error)?;
920 children.expr_kind(&expr.kind);
921 expr.span
922 }
923 Node::Stmt(id) => {
924 let stmt = self.0.try_stmt(id).map_err(typed_arena_error)?;
925 children.stmt_kind(&stmt.kind);
926 stmt.span
927 }
928 };
929 Ok(NodeInfo {
930 span,
931 children: children
932 .nodes
933 .into_iter()
934 .map(Child::direct)
935 .chain(children.behind_owned)
936 .collect(),
937 owned_height: children.owned_height,
938 })
939 }
940}
941
942#[derive(Default)]
943struct TypedChildren {
944 nodes: Vec<Node>,
945 behind_owned: Vec<Child>,
947 owned_height: u32,
948}
949
950impl TypedChildren {
951 fn expr(&mut self, id: ExprId) {
952 self.nodes.push(Node::Expr(id));
953 }
954
955 fn exprs(&mut self, ids: &[ExprId]) {
956 self.nodes.extend(ids.iter().copied().map(Node::Expr));
957 }
958
959 fn stmt(&mut self, id: StmtId) {
960 self.nodes.push(Node::Stmt(id));
961 }
962
963 fn chain_parts(&mut self, parts: &[TypedChainPart]) {
966 let mut depth = 0u32;
967 for part in parts {
968 depth = depth.saturating_add(1);
969 self.owned_height = self.owned_height.max(depth);
970 match part {
971 TypedChainPart::Field { .. }
972 | TypedChainPart::InterfaceProperty { .. }
973 | TypedChainPart::NonNull { .. } => {}
974 TypedChainPart::Index { idx, .. } => self.expr_at(*idx, depth),
975 TypedChainPart::Call { args, .. } | TypedChainPart::MethodCall { args, .. } => {
976 for arg in args {
977 self.expr_at(*arg, depth);
978 }
979 }
980 }
981 }
982 }
983
984 fn expr_at(&mut self, id: ExprId, offset: u32) {
985 self.behind_owned.push(Child {
986 node: Node::Expr(id),
987 offset,
988 });
989 }
990
991 fn field_source(&mut self, source: &TypedObjectFieldSource) {
994 let mut chain_height = 0u32;
995 let mut next = Some(source);
996 while let Some(source) = next {
997 chain_height = chain_height.saturating_add(1);
998 next = match source {
999 TypedObjectFieldSource::Literal(id) | TypedObjectFieldSource::Absent(id) => {
1000 self.expr_at(*id, chain_height);
1001 None
1002 }
1003 TypedObjectFieldSource::Spread { fallback, .. } => fallback.as_deref(),
1004 };
1005 }
1006 self.owned_height = self.owned_height.max(chain_height);
1007 }
1008
1009 fn expr_kind(&mut self, kind: &TypedExprKind) {
1010 match kind {
1011 TypedExprKind::Number(_)
1012 | TypedExprKind::BigInt(_)
1013 | TypedExprKind::String(_)
1014 | TypedExprKind::Boolean(_)
1015 | TypedExprKind::Null
1016 | TypedExprKind::Undefined
1017 | TypedExprKind::This
1018 | TypedExprKind::Regex { .. }
1019 | TypedExprKind::LocalRef { .. }
1020 | TypedExprKind::LocalNarrowRef { .. }
1021 | TypedExprKind::GlobalRef { .. }
1022 | TypedExprKind::FunctionRef { .. }
1023 | TypedExprKind::NumberEnumMember { .. }
1024 | TypedExprKind::StringEnumMember { .. } => {}
1025 TypedExprKind::EffectThen { effect, result } => self.exprs(&[*effect, *result]),
1026 TypedExprKind::Sequence { stmts, result } => {
1027 self.nodes.extend(stmts.iter().copied().map(Node::Stmt));
1028 self.expr(*result);
1029 }
1030 TypedExprKind::Binary { lhs, rhs, .. }
1031 | TypedExprKind::NullishCoalesce { lhs, rhs } => self.exprs(&[*lhs, *rhs]),
1032 TypedExprKind::Unary { operand, .. } => self.expr(*operand),
1033 TypedExprKind::Call { args, .. }
1034 | TypedExprKind::McpCall { args, .. }
1035 | TypedExprKind::IntrinsicCall { args, .. }
1036 | TypedExprKind::SuperCtorCall { args, .. }
1037 | TypedExprKind::SuperMethodCall { args, .. } => self.exprs(args),
1038 TypedExprKind::CallClosure { callee, args } => {
1039 self.expr(*callee);
1040 self.exprs(args);
1041 }
1042 TypedExprKind::GenericCall { args, .. } => {
1043 self.nodes
1044 .extend(args.iter().map(|argument| Node::Expr(argument.expr)));
1045 }
1046 TypedExprKind::MethodCall { receiver, args, .. } => {
1047 self.expr(*receiver);
1048 self.exprs(args);
1049 }
1050 TypedExprKind::GenericMethodCall { receiver, args, .. } => {
1051 self.expr(*receiver);
1052 self.nodes
1053 .extend(args.iter().map(|argument| Node::Expr(argument.expr)));
1054 }
1055 TypedExprKind::ObjectLiteral { members, fields } => {
1056 for member in members {
1057 self.nodes.extend(member.expressions().map(Node::Expr));
1058 }
1059 for field in fields {
1060 self.field_source(&field.source);
1061 }
1062 }
1063 TypedExprKind::ArrayLiteral { elements, .. } => {
1064 self.nodes
1065 .extend(elements.iter().map(|element| Node::Expr(element.expr_id())));
1066 }
1067 TypedExprKind::TupleLiteral { elements, .. } => self.exprs(elements),
1068 TypedExprKind::FieldAccess { receiver, .. }
1069 | TypedExprKind::InterfacePropertyAccess { receiver, .. } => self.expr(*receiver),
1070 TypedExprKind::IndexAccess { receiver, index } => self.exprs(&[*receiver, *index]),
1071 TypedExprKind::Closure { body, .. } => match body {
1072 ClosureBody::Expr(id) => self.expr(*id),
1073 ClosureBody::Block(id) => self.stmt(*id),
1074 },
1075 TypedExprKind::TypeofTag { value, .. }
1076 | TypedExprKind::NonNullAssert { value }
1077 | TypedExprKind::Cast { value, .. }
1078 | TypedExprKind::InstanceOf { value, .. } => self.expr(*value),
1079 TypedExprKind::Narrowed { source, inner, .. } => self.exprs(&[*source, *inner]),
1080 TypedExprKind::Ternary { cond, then_, else_ } => self.exprs(&[*cond, *then_, *else_]),
1081 TypedExprKind::OptionalChain { base, parts } => {
1082 self.expr(*base);
1083 self.chain_parts(parts);
1084 }
1085 TypedExprKind::PostfixUnary { target, .. } => match target {
1086 PostfixTarget::Local { .. } | PostfixTarget::Global { .. } => {}
1087 PostfixTarget::Field { receiver, .. } => self.expr(*receiver),
1088 PostfixTarget::Index {
1089 receiver, index, ..
1090 } => self.exprs(&[*receiver, *index]),
1091 },
1092 }
1093 }
1094
1095 fn stmt_kind(&mut self, kind: &TypedStmtKind) {
1096 match kind {
1097 TypedStmtKind::Let { value, .. }
1098 | TypedStmtKind::Const { value, .. }
1099 | TypedStmtKind::Throw { value }
1100 | TypedStmtKind::Expr(value)
1101 | TypedStmtKind::AssignLocal { value, .. }
1102 | TypedStmtKind::AssignGlobal { value, .. } => self.expr(*value),
1103 TypedStmtKind::ReboxLocal { .. } | TypedStmtKind::Break | TypedStmtKind::Continue => {}
1104 TypedStmtKind::If {
1105 condition,
1106 then_block,
1107 else_block,
1108 } => {
1109 self.expr(*condition);
1110 self.stmt(*then_block);
1111 self.nodes.extend(else_block.map(Node::Stmt));
1112 }
1113 TypedStmtKind::While { condition, body }
1114 | TypedStmtKind::DoWhile { body, condition } => {
1115 self.expr(*condition);
1116 self.stmt(*body);
1117 }
1118 TypedStmtKind::For {
1119 init,
1120 condition,
1121 update,
1122 body,
1123 } => {
1124 self.nodes.extend(init.map(Node::Stmt));
1125 self.nodes.extend(condition.map(Node::Expr));
1126 self.nodes.extend(update.map(Node::Stmt));
1127 self.stmt(*body);
1128 }
1129 TypedStmtKind::ForOf { iter, body, .. } => {
1130 self.expr(*iter);
1131 self.stmt(*body);
1132 }
1133 TypedStmtKind::Switch {
1134 discriminant,
1135 cases,
1136 default,
1137 ..
1138 } => {
1139 self.expr(*discriminant);
1140 self.nodes.extend(
1141 cases
1142 .iter()
1143 .flat_map(crate::TypedSwitchCase::label_comparisons)
1144 .map(Node::Expr),
1145 );
1146 self.nodes
1147 .extend(cases.iter().map(|case| Node::Stmt(case.body)));
1148 self.nodes.extend(default.map(Node::Stmt));
1149 }
1150 TypedStmtKind::Return(value) => self.nodes.extend(value.map(Node::Expr)),
1151 TypedStmtKind::Try {
1152 body,
1153 catches,
1154 finally,
1155 } => {
1156 self.stmt(*body);
1157 self.nodes
1158 .extend(catches.iter().map(|catch| Node::Stmt(catch.body)));
1159 self.nodes.extend(finally.map(Node::Stmt));
1160 }
1161 TypedStmtKind::Block(stmts) => self.nodes.extend(stmts.iter().copied().map(Node::Stmt)),
1162 TypedStmtKind::AssignField {
1163 receiver, value, ..
1164 } => self.exprs(&[*receiver, *value]),
1165 TypedStmtKind::AssignIndex {
1166 receiver,
1167 index,
1168 value,
1169 ..
1170 } => self.exprs(&[*receiver, *index, *value]),
1171 TypedStmtKind::NarrowRegion { source, body, .. } => {
1172 self.expr(*source);
1173 self.stmt(*body);
1174 }
1175 }
1176 }
1177}
1178
1179#[cfg(test)]
1180mod tests {
1181 use super::*;
1182 use crate::compile::parse_script;
1183 use crate::{Expr, FileId, Type, TypedExpr};
1184
1185 fn annotation(kind: TypeAnnotationKind) -> TypeAnnotation {
1186 TypeAnnotation {
1187 kind,
1188 span: Span::at(FileId(3)),
1189 }
1190 }
1191
1192 fn annotation_field(ty: TypeAnnotation) -> crate::ast::TypeAnnotationField {
1193 crate::ast::TypeAnnotationField {
1194 name: crate::Ident {
1195 name: "field".into(),
1196 span: ty.span,
1197 },
1198 ty,
1199 optional: false,
1200 readonly: false,
1201 rest: false,
1202 method: false,
1203 }
1204 }
1205
1206 #[test]
1207 fn wide_annotations_use_only_ancestor_frames() {
1208 let root = annotation(TypeAnnotationKind::Tuple(vec![
1209 annotation(
1210 TypeAnnotationKind::BooleanLiteral(true)
1211 );
1212 100_000
1213 ]));
1214 for budget in [0, 1, 7, 100_001] {
1215 let (nodes, observed) =
1216 crate::type_walk::tests::observe(None, || annotation_nodes(&root, budget).unwrap());
1217 assert_eq!(nodes, 100_001.min(budget + 1));
1218 assert!(observed.peak_frames <= 1);
1219 assert!(observed.reservations <= 1);
1220 }
1221 let (height, observed) =
1222 crate::type_walk::tests::observe(None, || annotation_height(&root).unwrap());
1223 assert_eq!(height, 2);
1224 assert_eq!(observed.peak_frames, 1);
1225 }
1226
1227 #[test]
1228 fn mixed_annotations_keep_depth_counts_and_lifo_order() {
1229 let leaf = annotation(TypeAnnotationKind::BooleanLiteral(true));
1230 let deep = (1..20).fold(leaf.clone(), |inner, _| {
1231 annotation(TypeAnnotationKind::Array(Box::new(inner)))
1232 });
1233 let root = annotation(TypeAnnotationKind::Tuple(vec![
1234 annotation(TypeAnnotationKind::Tuple(vec![leaf; 100_000])),
1235 deep,
1236 ]));
1237 let (extent, observed) = crate::type_walk::tests::observe(None, || {
1238 measure_annotation(&root, u64::MAX, u32::MAX).unwrap()
1239 });
1240 assert_eq!(
1241 extent,
1242 crate::type_size::TypeExtent {
1243 nodes: 100_022,
1244 depth: 21
1245 }
1246 );
1247 assert_eq!(observed.peak_frames, 20);
1248 assert_eq!(
1249 measure_annotation(&root, 3, u32::MAX).unwrap(),
1250 crate::type_size::TypeExtent { nodes: 4, depth: 4 }
1251 );
1252 let (failure, _) = crate::type_walk::tests::observe(Some(1), || annotation_height(&root));
1253 assert!(matches!(failure, Err(CompilerFailure::Internal { .. })));
1254 }
1255
1256 #[test]
1257 fn annotation_variants_preserve_children_and_measurement() {
1258 let first = annotation(TypeAnnotationKind::StringLiteral("first".into()));
1259 let second = annotation(TypeAnnotationKind::BooleanLiteral(false));
1260 let pair = vec![first.clone(), second.clone()];
1261 let name = crate::Ident {
1262 name: "T".into(),
1263 span: first.span,
1264 };
1265 let cases = vec![
1266 (
1267 TypeAnnotationKind::Name {
1268 name: name.clone(),
1269 args: pair.clone(),
1270 },
1271 pair.clone(),
1272 ),
1273 (
1274 TypeAnnotationKind::Qualified {
1275 path: vec![name.clone(), name],
1276 args: pair.clone(),
1277 },
1278 pair.clone(),
1279 ),
1280 (TypeAnnotationKind::Tuple(pair.clone()), pair.clone()),
1281 (TypeAnnotationKind::Union(pair.clone()), pair.clone()),
1282 (
1283 TypeAnnotationKind::Array(Box::new(first.clone())),
1284 vec![first.clone()],
1285 ),
1286 (
1287 TypeAnnotationKind::Readonly(Box::new(first.clone())),
1288 vec![first.clone()],
1289 ),
1290 (
1291 TypeAnnotationKind::KeyOf(Box::new(first.clone())),
1292 vec![first.clone()],
1293 ),
1294 (
1295 TypeAnnotationKind::Object {
1296 fields: vec![annotation_field(first.clone())],
1297 index: Some(Box::new(crate::IndexSignatureAnnotation {
1298 value: second.clone(),
1299 readonly: false,
1300 span: second.span,
1301 })),
1302 },
1303 pair.clone(),
1304 ),
1305 (
1306 TypeAnnotationKind::Function {
1307 params: vec![annotation_field(first.clone())],
1308 return_type: Box::new(second.clone()),
1309 },
1310 pair,
1311 ),
1312 (TypeAnnotationKind::TypeOf { path: Vec::new() }, Vec::new()),
1313 (TypeAnnotationKind::StringLiteral("leaf".into()), Vec::new()),
1314 (
1315 TypeAnnotationKind::NumberLiteral(crate::types::LiteralF64(1.0)),
1316 Vec::new(),
1317 ),
1318 (TypeAnnotationKind::BooleanLiteral(true), Vec::new()),
1319 ];
1320 for (kind, mut expected) in cases {
1321 let root = annotation(kind);
1322 let nodes = expected.len() as u64 + 1;
1323 let height = if expected.is_empty() { 1 } else { 2 };
1324 expected.reverse();
1325 assert_eq!(
1326 AnnotationChildren::new(&root).cloned().collect::<Vec<_>>(),
1327 expected
1328 );
1329 assert_eq!(annotation_nodes(&root, u64::MAX).unwrap(), nodes);
1330 assert_eq!(annotation_height(&root).unwrap(), height);
1331 }
1332 }
1333
1334 #[test]
1335 fn annotation_failure_terminates_syntax_validation() {
1336 let root = annotation(TypeAnnotationKind::Array(Box::new(annotation(
1337 TypeAnnotationKind::BooleanLiteral(true),
1338 ))));
1339 let (count, _) = crate::type_walk::tests::observe(Some(0), || annotation_nodes(&root, 10));
1340 assert!(
1341 matches!(count, Err(CompilerFailure::Internal { span: Some(s), .. }) if s == root.span)
1342 );
1343 let source = "type T = [number, string];";
1345 let mut lexer = crate::asi::Asi::new(source, FileId(3));
1346 let mut tokens = Vec::new();
1347 loop {
1348 let token = lexer.next_token();
1349 let eof = matches!(token.kind, crate::TokenKind::Eof);
1350 tokens.push(token);
1351 if eof {
1352 break;
1353 }
1354 }
1355 let (ast, diagnostics) = crate::parser::parse_checked(source, tokens, FileId(3)).unwrap();
1356 assert!(diagnostics.is_empty());
1357 let (result, _) = crate::type_walk::tests::observe(Some(0), || check_syntax(&ast));
1358 assert!(matches!(
1359 result,
1360 Err(CompilerFailure::Internal {
1361 stage: CompilerStage::Parse,
1362 span: Some(_),
1363 ..
1364 })
1365 ));
1366 assert_eq!(check_syntax(&ast), Ok(()));
1367 }
1368
1369 fn syntax_limit(source: &str) -> Option<crate::Diagnostic> {
1371 parse_script(source, FileId(0))
1372 .diagnostics()
1373 .iter()
1374 .find(|d| d.message.contains("syntax nesting"))
1375 .cloned()
1376 }
1377
1378 fn sum(operands: usize) -> String {
1379 format!(
1381 "function main(): number {{ return {}; }}",
1382 vec!["1"; operands].join(" + ")
1383 )
1384 }
1385
1386 #[test]
1387 fn syntax_height_accepts_the_limit_and_rejects_one_more_level() {
1388 let at_limit = MAX_SYNTAX_HEIGHT as usize - 3;
1389 let chain_start = "function main(): number { return ".len() as u32;
1390 assert!(syntax_limit(&sum(at_limit)).is_none());
1391 let diagnostic = syntax_limit(&sum(at_limit + 1)).expect("one level over is rejected");
1394 assert_eq!(diagnostic.span.start, chain_start);
1395 assert!(!diagnostic.help.is_empty());
1396 let diagnostic = syntax_limit(&sum(10 * at_limit)).expect("a long chain is rejected");
1397 assert_eq!(diagnostic.span.start, chain_start);
1398 }
1399
1400 #[test]
1401 fn syntax_height_counts_postfix_chains_and_annotations() {
1402 let calls = format!(
1403 "function main(): number {{ const f = (): unknown => f; f{}; return 1; }}",
1404 "()".repeat(MAX_SYNTAX_HEIGHT as usize)
1405 );
1406 assert!(syntax_limit(&calls).is_some());
1407 let nested_type = format!(
1408 "function main(): number {{ const x: {}number{} | null = null; return 1; }}",
1409 "{ v: ".repeat(40),
1410 " }".repeat(40)
1411 );
1412 let parsed = parse_script(&nested_type, FileId(0));
1413 assert!(!parsed.has_errors(), "{:?}", parsed.diagnostics());
1414 }
1415
1416 fn optional_fields(base: &str, links: usize, tail: &str) -> String {
1418 format!(
1419 "function main(): number {{ const y = {base}?.a{}{tail}; return 1; }}",
1420 ".a".repeat(links.saturating_sub(1))
1421 )
1422 }
1423
1424 fn longest_accepted_field_chain() -> usize {
1425 let longest = (1..=MAX_SYNTAX_HEIGHT as usize)
1426 .take_while(|&links| syntax_limit(&optional_fields("x", links, "")).is_none())
1427 .last()
1428 .expect("a one-link chain is accepted");
1429 assert!(longest < MAX_SYNTAX_HEIGHT as usize - 1);
1430 longest
1431 }
1432
1433 #[test]
1434 fn syntax_height_counts_each_optional_chain_link() {
1435 let longest = longest_accepted_field_chain();
1436 let over = optional_fields("x", longest + 1, "");
1437 let diagnostic = syntax_limit(&over).expect("one more link is rejected");
1438 assert_eq!(diagnostic.span.start as usize, over.find("x?.").unwrap());
1439 assert!(syntax_limit(&optional_fields("x", 20_000, "")).is_some());
1440 }
1441
1442 #[test]
1443 fn optional_chain_operands_sit_below_their_link() {
1444 let longest = longest_accepted_field_chain();
1445 assert!(syntax_limit(&optional_fields("x", longest - 3, ".f(1)")).is_none());
1447 assert!(syntax_limit(&optional_fields("x", longest - 3, ".f(- 1)")).is_some());
1448 assert!(syntax_limit(&optional_fields("x", longest - 2, "[0]")).is_none());
1449 assert!(syntax_limit(&optional_fields("x", longest - 2, "[- 0]")).is_some());
1450 }
1451
1452 #[test]
1453 fn a_long_expression_is_reported_rather_than_a_closure_it_contains() {
1454 let longest = longest_accepted_field_chain();
1455 let closure = "((): number => { const q = 1; return q; })()";
1456 let annotated = "((): number => { const q: number = 1; return q; })()";
1457 let plus_chain = |operands: usize| vec!["1"; operands].join(" + ");
1458 let closure_led_sum = |inner: usize, outer: usize| {
1459 format!(
1460 "function main(): number {{ return ((): number => {{ {{ return {}; }} }})(){}; }}",
1461 plus_chain(inner),
1462 " + 1".repeat(outer)
1463 )
1464 };
1465 let cases = [
1467 (optional_fields(closure, longest + 1, ""), closure),
1469 (optional_fields(closure, 20_000, ""), closure),
1471 (
1473 optional_fields("x", longest + 1, ".f(() => { return 1; })"),
1474 "x?.",
1475 ),
1476 (
1477 optional_fields("x", 20_000, "[(() => { return 1; })()]"),
1478 "x?.",
1479 ),
1480 (
1483 format!(
1484 "function main(): number {{ return {closure}{}; }}",
1485 " + 1".repeat(400)
1486 ),
1487 closure,
1488 ),
1489 (
1490 format!(
1491 "function main(): number {{ return {annotated}{}; }}",
1492 " + 1".repeat(400)
1493 ),
1494 annotated,
1495 ),
1496 (closure_led_sum(130, 10_000), "((): number"),
1498 (closure_led_sum(203, 200), "((): number"),
1501 (closure_led_sum(204, 200), "1 + 1"),
1502 (
1506 optional_fields(
1507 &format!("((): number => {{ {{ return {}; }} }})()", plus_chain(200)),
1508 longest + 1,
1509 "",
1510 ),
1511 "((): number",
1512 ),
1513 (
1516 optional_fields(
1517 &format!(
1518 "((): number => {{ {{ return {}; }} }})()",
1519 plus_chain(longest - 5)
1520 ),
1521 longest + 1,
1522 "",
1523 ),
1524 "((): number",
1525 ),
1526 (
1528 format!(
1529 "function main(): number {{ const a = {0}; const b = {0}; return 1; }}",
1530 plus_chain(300)
1531 ),
1532 "1 + 1",
1533 ),
1534 (
1536 format!(
1537 "function f(): number {{ return {0}; }} function g(): number {{ return {0}; }}",
1538 plus_chain(400)
1539 ),
1540 "1 + 1",
1541 ),
1542 ];
1543 for (case, (source, marker)) in cases.iter().enumerate() {
1544 let diagnostic = syntax_limit(source).expect("the long expression is rejected");
1545 let start = source.find(marker).unwrap();
1546 assert_eq!(diagnostic.span.start as usize, start, "case {case}");
1547 }
1548 }
1549
1550 #[test]
1551 fn syntax_cycles_are_internal_failures() {
1552 let mut ast = Ast::new();
1553 let span = Span::at(FileId(0));
1554 ast.try_push_expr(Expr {
1555 kind: ExprKind::Paren(ExprId(0)),
1556 span,
1557 })
1558 .unwrap();
1559 assert!(matches!(
1560 check_syntax(&ast),
1561 Err(CompilerFailure::Internal {
1562 stage: CompilerStage::Parse,
1563 ..
1564 })
1565 ));
1566 let mut ast = Ast::new();
1567 ast.try_push_expr(Expr {
1568 kind: ExprKind::Paren(ExprId(7)),
1569 span,
1570 })
1571 .unwrap();
1572 assert!(matches!(
1573 check_syntax(&ast),
1574 Err(CompilerFailure::Internal { .. })
1575 ));
1576 }
1577
1578 #[test]
1579 fn typed_height_is_measured_iteratively_and_rejects_cycles() {
1580 let span = Span::at(FileId(0));
1581 let mut ta = TypedAst::new();
1582 let mut previous = ta
1583 .try_push_expr(TypedExpr {
1584 kind: TypedExprKind::Number(1.0),
1585 span,
1586 ty: Type::Number,
1587 })
1588 .unwrap();
1589 for _ in 1..MAX_TYPED_HEIGHT {
1590 previous = ta
1591 .try_push_expr(TypedExpr {
1592 kind: TypedExprKind::Unary {
1593 op: crate::UnOp::Neg,
1594 operand: previous,
1595 },
1596 span,
1597 ty: Type::Number,
1598 })
1599 .unwrap();
1600 }
1601 check_typed(&ta, CompilerStage::Codegen).unwrap();
1602 ta.try_push_expr(TypedExpr {
1603 kind: TypedExprKind::Unary {
1604 op: crate::UnOp::Neg,
1605 operand: previous,
1606 },
1607 span,
1608 ty: Type::Number,
1609 })
1610 .unwrap();
1611 assert!(matches!(
1612 check_typed(&ta, CompilerStage::Codegen),
1613 Err(CompilerFailure::Limit {
1614 stage: CompilerStage::Codegen,
1615 ..
1616 })
1617 ));
1618
1619 let mut ta = TypedAst::new();
1620 ta.try_push_expr(TypedExpr {
1621 kind: TypedExprKind::NonNullAssert { value: ExprId(0) },
1622 span,
1623 ty: Type::Number,
1624 })
1625 .unwrap();
1626 assert!(matches!(
1627 check_typed(&ta, CompilerStage::Infer),
1628 Err(CompilerFailure::Internal {
1629 stage: CompilerStage::Infer,
1630 ..
1631 })
1632 ));
1633 }
1634
1635 fn negations(ta: &mut TypedAst, levels: u32) -> ExprId {
1637 let span = Span::at(FileId(0));
1638 let mut expr = ta
1639 .try_push_expr(TypedExpr {
1640 kind: TypedExprKind::Number(1.0),
1641 span,
1642 ty: Type::Number,
1643 })
1644 .unwrap();
1645 for _ in 1..levels {
1646 expr = ta
1647 .try_push_expr(TypedExpr {
1648 kind: TypedExprKind::Unary {
1649 op: crate::UnOp::Neg,
1650 operand: expr,
1651 },
1652 span,
1653 ty: Type::Number,
1654 })
1655 .unwrap();
1656 }
1657 expr
1658 }
1659
1660 fn object_with_fallback_chain(links: u32, terminal_levels: u32) -> TypedAst {
1661 let mut ta = TypedAst::new();
1662 let terminal = negations(&mut ta, terminal_levels);
1663 let mut source = TypedObjectFieldSource::Literal(terminal);
1664 for _ in 1..links {
1665 source = TypedObjectFieldSource::Spread {
1666 source_index: 0,
1667 field_name: "a".into(),
1668 source_ty: Type::Number,
1669 fallback: Some(Box::new(source)),
1670 };
1671 }
1672 ta.try_push_expr(TypedExpr {
1673 kind: TypedExprKind::ObjectLiteral {
1674 members: Vec::new(),
1675 fields: vec![crate::TypedObjectFieldOrigin {
1676 name: crate::Ident {
1677 name: "a".into(),
1678 span: Span::at(FileId(0)),
1679 },
1680 source,
1681 optional: true,
1682 ty: Type::Number,
1683 }],
1684 },
1685 span: Span::at(FileId(0)),
1686 ty: Type::Unknown,
1687 })
1688 .unwrap();
1689 ta
1690 }
1691
1692 #[test]
1693 fn spread_fallback_links_add_to_the_terminal_expression_height() {
1694 let within = MAX_TYPED_HEIGHT - 1 - 600;
1696 check_typed(
1697 &object_with_fallback_chain(600, within),
1698 CompilerStage::Codegen,
1699 )
1700 .unwrap();
1701 assert!(matches!(
1702 check_typed(
1703 &object_with_fallback_chain(600, within + 1),
1704 CompilerStage::Codegen
1705 ),
1706 Err(CompilerFailure::Limit { .. })
1707 ));
1708 }
1709}