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