1use rowan::GreenNodeBuilder;
45
46use crate::diagnostics::{Diagnostic, DiagnosticCode};
47use crate::lexer::{self, LexError, Token};
48use crate::syntax::{SyntaxKind, SyntaxNode, TextRange};
49
50pub const DEFAULT_MAX_DEPTH: usize = 128;
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62#[non_exhaustive]
63pub struct ParseOptions {
64 pub max_depth: usize,
68}
69
70impl Default for ParseOptions {
71 #[inline]
72 fn default() -> Self {
73 Self {
74 max_depth: DEFAULT_MAX_DEPTH,
75 }
76 }
77}
78
79impl ParseOptions {
80 #[inline]
82 #[must_use]
83 pub fn with_max_depth(mut self, max_depth: usize) -> Self {
84 self.max_depth = max_depth;
85 self
86 }
87}
88
89#[derive(Clone)]
97pub struct CstDocument {
98 green: rowan::GreenNode,
99 diagnostics: Vec<Diagnostic>,
100 source_len: usize,
101}
102
103impl CstDocument {
104 #[must_use]
106 pub fn root(&self) -> SyntaxNode {
107 SyntaxNode::new_root(self.green.clone())
108 }
109
110 #[must_use]
116 pub fn diagnostics(&self) -> &[Diagnostic] {
117 &self.diagnostics
118 }
119
120 #[must_use]
122 pub fn source_len(&self) -> usize {
123 self.source_len
124 }
125
126 #[inline]
133 pub(crate) fn from_green_for_edit(green: rowan::GreenNode) -> Self {
134 let source_len = usize::from(green.text_len());
135 Self {
136 green,
137 diagnostics: Vec::new(),
138 source_len,
139 }
140 }
141}
142
143impl std::fmt::Debug for CstDocument {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 f.debug_struct("CstDocument")
146 .field("root", &self.root())
147 .field("diagnostics", &self.diagnostics)
148 .field("source_len", &self.source_len)
149 .finish()
150 }
151}
152
153#[must_use]
156pub fn parse(src: &str) -> CstDocument {
157 parse_with_options(src, ParseOptions::default())
158}
159
160#[must_use]
163pub fn parse_with_options(src: &str, options: ParseOptions) -> CstDocument {
164 let tokens = lexer::tokenize(src);
165 Parser::new(src.len(), tokens, options).parse_document()
166}
167
168pub fn parse_bytes(bytes: &[u8]) -> Result<CstDocument, LexError> {
176 let src = lexer::validate_utf8(bytes)?;
177 Ok(parse(src))
178}
179
180struct Spanned<'a> {
183 tok: Token<'a>,
184 start: usize,
186}
187
188struct Parser<'a> {
189 tokens: Vec<Spanned<'a>>,
190 cursor: usize,
192 builder: GreenNodeBuilder<'static>,
193 source_len: usize,
194 diagnostics: Vec<Diagnostic>,
195 options: ParseOptions,
196}
197
198impl<'a> Parser<'a> {
199 fn new(source_len: usize, tokens: Vec<Token<'a>>, options: ParseOptions) -> Self {
200 let mut offset = 0usize;
203 let spanned = tokens
204 .into_iter()
205 .map(|tok| {
206 let start = offset;
207 offset += tok.text.len();
208 Spanned { tok, start }
209 })
210 .collect();
211 Self {
212 tokens: spanned,
213 cursor: 0,
214 builder: GreenNodeBuilder::new(),
215 source_len,
216 diagnostics: Vec::new(),
217 options,
218 }
219 }
220
221 fn parse_document(mut self) -> CstDocument {
222 self.builder.start_node(rowan_kind(SyntaxKind::Root));
223
224 self.eat_trivia();
227
228 while self.at(SyntaxKind::Hash) {
230 self.parse_extension_attr();
231 self.eat_trivia();
232 }
233
234 if !self.at_eof() {
236 self.parse_value(0);
237 }
238
239 self.eat_trivia();
244 if !self.at_eof() {
245 let start = self.current_offset();
246 let end = self.source_len;
247 self.push_diagnostic(
248 DiagnosticCode::UnexpectedToken,
249 TextRange::new(start, end),
250 "unexpected trailing tokens after the top-level value",
251 );
252 while !self.at_eof() {
253 self.bump_into_error();
254 self.eat_trivia();
255 }
256 }
257
258 self.builder.finish_node(); let green = self.builder.finish();
260 CstDocument {
261 green,
262 diagnostics: self.diagnostics,
263 source_len: self.source_len,
264 }
265 }
266
267 fn parse_value(&mut self, depth: usize) {
273 self.eat_trivia();
274 let Some(kind) = self.peek_kind() else {
275 return;
276 };
277 match kind {
278 SyntaxKind::LParen | SyntaxKind::LBracket | SyntaxKind::LBrace
279 if depth >= self.options.max_depth =>
280 {
281 self.recover_depth_limit();
284 }
285 SyntaxKind::LParen => self.parse_tuple_or_struct(None, depth),
286 SyntaxKind::LBracket => self.parse_list(depth),
287 SyntaxKind::LBrace => self.parse_map(depth),
288 SyntaxKind::Ident => self.parse_ident_led(depth),
289 SyntaxKind::TrueKw | SyntaxKind::FalseKw => self.parse_literal(),
290 SyntaxKind::Integer
291 | SyntaxKind::Float
292 | SyntaxKind::String
293 | SyntaxKind::RawString
294 | SyntaxKind::Char => self.parse_literal(),
295 _ => {
298 let range = self.current_token_range();
299 self.push_diagnostic(DiagnosticCode::UnexpectedToken, range, "expected a value");
300 self.bump_into_error();
301 }
302 }
303 }
304
305 fn parse_ident_led(&mut self, depth: usize) {
309 let next_sig = self.peek_kind_after_first_significant();
311 match next_sig {
312 Some(SyntaxKind::LParen) => {
313 let name_checkpoint = self.builder.checkpoint();
317 self.bump(); self.parse_tuple_or_struct(Some(name_checkpoint), depth);
319 }
320 Some(SyntaxKind::LBrace) => {
321 self.builder.start_node(rowan_kind(SyntaxKind::EnumVariant));
323 self.bump(); self.eat_trivia();
325 self.parse_map_like_braces(depth);
326 self.builder.finish_node();
327 }
328 _ => {
329 self.builder.start_node(rowan_kind(SyntaxKind::EnumVariant));
332 self.bump(); self.builder.finish_node();
334 }
335 }
336 }
337
338 fn parse_tuple_or_struct(&mut self, name_checkpoint: Option<rowan::Checkpoint>, depth: usize) {
343 let is_struct = self.parens_contain_struct_fields();
345
346 let kind = if is_struct {
347 SyntaxKind::Struct
348 } else {
349 if self.parens_are_empty() {
351 SyntaxKind::Unit
352 } else {
353 SyntaxKind::Tuple
354 }
355 };
356
357 match name_checkpoint {
358 Some(cp) => self.builder.start_node_at(cp, rowan_kind(kind)),
359 None => self.builder.start_node(rowan_kind(kind)),
360 }
361
362 self.eat_trivia();
363 let open = self.current_offset();
364 self.expect_bump(SyntaxKind::LParen);
365 self.eat_trivia();
366
367 while !self.at_eof() && !self.at(SyntaxKind::RParen) {
368 let before = self.cursor;
369 if is_struct {
370 self.parse_struct_field(depth);
371 } else {
372 self.parse_value(depth + 1);
373 }
374 self.eat_trivia();
375 if self.at(SyntaxKind::Comma) {
376 self.bump();
377 self.eat_trivia();
378 } else if self.at(SyntaxKind::RParen) || self.at_eof() {
379 break;
380 } else {
381 self.recover_unexpected_in_group();
384 }
385 if self.cursor == before {
388 self.recover_unexpected_in_group();
389 }
390 }
391
392 self.eat_trivia();
393 self.expect_close(SyntaxKind::RParen, open, "(");
394 self.builder.finish_node();
395 }
396
397 fn parse_struct_field(&mut self, depth: usize) {
398 self.builder.start_node(rowan_kind(SyntaxKind::StructField));
399 self.eat_trivia();
400 if self.at(SyntaxKind::Ident) {
402 self.bump();
403 }
404 self.eat_trivia();
405 if self.at(SyntaxKind::Colon) {
406 self.bump();
407 }
408 self.eat_trivia();
413 if !self.at(SyntaxKind::Comma) && !self.at(SyntaxKind::RParen) && !self.at_eof() {
414 self.parse_value(depth + 1);
415 }
416 self.builder.finish_node();
417 }
418
419 fn parse_list(&mut self, depth: usize) {
420 self.builder.start_node(rowan_kind(SyntaxKind::List));
421 self.eat_trivia();
422 let open = self.current_offset();
423 self.expect_bump(SyntaxKind::LBracket);
424 self.eat_trivia();
425 while !self.at_eof() && !self.at(SyntaxKind::RBracket) {
426 let before = self.cursor;
427 self.parse_value(depth + 1);
428 self.eat_trivia();
429 if self.at(SyntaxKind::Comma) {
430 self.bump();
431 self.eat_trivia();
432 } else if self.at(SyntaxKind::RBracket) || self.at_eof() {
433 break;
434 } else {
435 self.recover_unexpected_in_group();
436 }
437 if self.cursor == before {
438 self.recover_unexpected_in_group();
439 }
440 }
441 self.eat_trivia();
442 self.expect_close(SyntaxKind::RBracket, open, "[");
443 self.builder.finish_node();
444 }
445
446 fn parse_map(&mut self, depth: usize) {
447 self.builder.start_node(rowan_kind(SyntaxKind::Map));
448 self.parse_map_like_braces(depth);
449 self.builder.finish_node();
450 }
451
452 fn parse_map_like_braces(&mut self, depth: usize) {
455 self.eat_trivia();
456 let open = self.current_offset();
457 self.expect_bump(SyntaxKind::LBrace);
458 self.eat_trivia();
459 while !self.at_eof() && !self.at(SyntaxKind::RBrace) {
460 let before = self.cursor;
461 self.parse_map_entry(depth);
462 self.eat_trivia();
463 if self.at(SyntaxKind::Comma) {
464 self.bump();
465 self.eat_trivia();
466 } else if self.at(SyntaxKind::RBrace) || self.at_eof() {
467 break;
468 } else {
469 self.recover_unexpected_in_group();
470 }
471 if self.cursor == before {
472 self.recover_unexpected_in_group();
473 }
474 }
475 self.eat_trivia();
476 self.expect_close(SyntaxKind::RBrace, open, "{");
477 }
478
479 fn parse_map_entry(&mut self, depth: usize) {
480 self.builder.start_node(rowan_kind(SyntaxKind::MapEntry));
481 self.eat_trivia();
482 if !self.at(SyntaxKind::Colon) && !self.at(SyntaxKind::RBrace) && !self.at_eof() {
484 self.parse_value(depth + 1);
485 }
486 self.eat_trivia();
487 if self.at(SyntaxKind::Colon) {
488 self.bump();
489 }
490 self.eat_trivia();
491 if !self.at(SyntaxKind::Comma) && !self.at(SyntaxKind::RBrace) && !self.at_eof() {
492 self.parse_value(depth + 1);
493 }
494 self.builder.finish_node();
495 }
496
497 fn parse_literal(&mut self) {
498 self.builder.start_node(rowan_kind(SyntaxKind::Literal));
499 self.bump(); self.builder.finish_node();
501 }
502
503 fn parse_extension_attr(&mut self) {
504 self.builder
505 .start_node(rowan_kind(SyntaxKind::ExtensionAttr));
506 self.expect_bump(SyntaxKind::Hash);
509 self.eat_trivia();
510 if self.at(SyntaxKind::Bang) {
511 self.bump();
512 }
513 self.eat_trivia();
514 if self.at(SyntaxKind::LBracket) {
515 self.bump();
516 self.eat_trivia();
517 let mut depth = 1usize;
519 while !self.at_eof() && depth > 0 {
520 match self.peek_kind() {
521 Some(SyntaxKind::LBracket) => {
522 depth += 1;
523 self.bump();
524 }
525 Some(SyntaxKind::RBracket) => {
526 depth -= 1;
527 self.bump();
528 }
529 Some(_) => self.bump(),
530 None => break,
531 }
532 if depth > 0 {
533 self.eat_trivia();
534 }
535 }
536 }
537 self.builder.finish_node();
538 }
539
540 fn recover_unexpected_in_group(&mut self) {
546 self.eat_trivia();
547 if self.at_eof() {
548 return;
549 }
550 let range = self.current_token_range();
551 self.push_diagnostic(
552 DiagnosticCode::UnexpectedToken,
553 range,
554 "unexpected token in delimited group",
555 );
556 self.bump_into_error();
557 }
558
559 fn recover_depth_limit(&mut self) {
564 self.eat_trivia();
565 let start = self.current_offset();
566 self.push_diagnostic(
567 DiagnosticCode::NestingDepthExceeded,
568 TextRange::new(start, self.source_len),
569 "nesting depth exceeds the configured limit",
570 );
571 while !self.at_eof() {
574 self.bump_into_error();
575 self.eat_trivia();
576 }
577 }
578
579 fn peek_kind(&self) -> Option<SyntaxKind> {
583 self.tokens.get(self.cursor).map(|t| t.tok.kind)
584 }
585
586 fn at(&self, kind: SyntaxKind) -> bool {
588 self.peek_significant() == Some(kind)
589 }
590
591 fn peek_significant(&self) -> Option<SyntaxKind> {
593 self.tokens[self.cursor..]
594 .iter()
595 .map(|t| t.tok.kind)
596 .find(|k| !k.is_trivia())
597 }
598
599 fn peek_kind_after_first_significant(&self) -> Option<SyntaxKind> {
602 let mut sig_seen = 0;
603 for t in &self.tokens[self.cursor..] {
604 if t.tok.kind.is_trivia() {
605 continue;
606 }
607 sig_seen += 1;
608 if sig_seen == 2 {
609 return Some(t.tok.kind);
610 }
611 }
612 None
613 }
614
615 fn at_eof(&self) -> bool {
617 self.peek_significant().is_none()
618 }
619
620 fn current_offset(&self) -> usize {
622 self.tokens
623 .get(self.cursor)
624 .map_or(self.source_len, |t| t.start)
625 }
626
627 fn current_token_range(&self) -> TextRange {
630 for t in &self.tokens[self.cursor..] {
631 if !t.tok.kind.is_trivia() {
632 return TextRange::new(t.start, t.start + t.tok.text.len());
633 }
634 }
635 TextRange::new(self.source_len, self.source_len)
636 }
637
638 fn parens_contain_struct_fields(&self) -> bool {
642 let mut i = self.cursor;
643 while i < self.tokens.len() && self.tokens[i].tok.kind != SyntaxKind::LParen {
645 if !self.tokens[i].tok.kind.is_trivia() {
646 return false;
649 }
650 i += 1;
651 }
652 if i >= self.tokens.len() {
653 return false;
654 }
655 i += 1; let mut depth = 1usize;
657 let mut last_significant: Option<SyntaxKind> = None;
658 while i < self.tokens.len() && depth > 0 {
659 let k = self.tokens[i].tok.kind;
660 match k {
661 SyntaxKind::LParen | SyntaxKind::LBracket | SyntaxKind::LBrace => depth += 1,
662 SyntaxKind::RParen | SyntaxKind::RBracket | SyntaxKind::RBrace => depth -= 1,
663 SyntaxKind::Colon
664 if depth == 1
665 && last_significant == Some(SyntaxKind::Ident) =>
668 {
669 return true;
670 }
671 _ => {}
672 }
673 if !k.is_trivia() && depth >= 1 {
674 last_significant = Some(k);
675 }
676 i += 1;
677 }
678 false
679 }
680
681 fn parens_are_empty(&self) -> bool {
683 let mut i = self.cursor;
684 while i < self.tokens.len() && self.tokens[i].tok.kind != SyntaxKind::LParen {
685 i += 1;
686 }
687 if i >= self.tokens.len() {
688 return false;
689 }
690 i += 1; while i < self.tokens.len() {
692 let k = self.tokens[i].tok.kind;
693 if k.is_trivia() {
694 i += 1;
695 continue;
696 }
697 return k == SyntaxKind::RParen;
698 }
699 false
700 }
701
702 fn eat_trivia(&mut self) {
707 while let Some(spanned) = self.tokens.get(self.cursor) {
708 if spanned.tok.kind.is_trivia() {
709 self.builder
710 .token(rowan_kind(spanned.tok.kind), spanned.tok.text);
711 self.cursor += 1;
712 } else {
713 break;
714 }
715 }
716 }
717
718 fn bump(&mut self) {
720 self.eat_trivia();
721 if let Some(spanned) = self.tokens.get(self.cursor) {
722 self.builder
723 .token(rowan_kind(spanned.tok.kind), spanned.tok.text);
724 self.cursor += 1;
725 }
726 }
727
728 fn bump_into_error(&mut self) {
732 self.eat_trivia();
733 if let Some(spanned) = self.tokens.get(self.cursor) {
734 self.builder.start_node(rowan_kind(SyntaxKind::Error));
735 self.builder
736 .token(rowan_kind(spanned.tok.kind), spanned.tok.text);
737 self.builder.finish_node();
738 self.cursor += 1;
739 }
740 }
741
742 fn expect_bump(&mut self, kind: SyntaxKind) {
746 if self.at(kind) {
747 self.bump();
748 }
749 }
750
751 fn expect_close(&mut self, kind: SyntaxKind, open_offset: usize, open: &str) {
756 if self.at(kind) {
757 self.bump();
758 } else {
759 let end = self.current_offset();
760 self.push_diagnostic(
761 DiagnosticCode::UnclosedDelimiter,
762 TextRange::new(open_offset, end),
763 format!("unclosed delimiter `{open}`"),
764 );
765 }
766 }
767
768 fn push_diagnostic(
771 &mut self,
772 code: DiagnosticCode,
773 range: TextRange,
774 message: impl Into<String>,
775 ) {
776 debug_assert!(
777 range.start() <= range.end() && range.end() <= self.source_len,
778 "diagnostic range must lie within [0, source_len)"
779 );
780 self.diagnostics.push(Diagnostic::new(code, range, message));
781 }
782}
783
784#[inline]
785fn rowan_kind(kind: SyntaxKind) -> rowan::SyntaxKind {
786 <crate::syntax::kind::RonLang as rowan::Language>::kind_to_raw(kind)
787}
788
789#[cfg(test)]
790mod tests {
791 use super::*;
792 use crate::diagnostics::Severity;
793
794 fn roundtrip(src: &str) -> String {
796 let doc = parse(src);
797 doc.root()
798 .descendant_tokens()
799 .map(|t| t.text().to_string())
800 .collect()
801 }
802
803 #[test]
804 fn roundtrip_covers_all_constructs() {
805 let inputs = [
806 "",
807 " \n\t",
808 "// comment only\n",
809 "/* block */",
810 "42",
811 "-3.14",
812 "true",
813 "false",
814 "'c'",
815 "\"hello\\nworld\"",
816 "r#\"raw \"q\" str\"#",
817 "()",
818 "Unit",
819 "Some(42)",
820 "Foo(x: 1, y: 2.0)",
821 "(1, 2, 3)",
822 "[1, 2, 3,]",
823 "{ \"a\": 1, \"b\": 2, }",
824 "{ 1: \"one\", 'c': true }",
825 "Point(x: 1.0, y: -2.0)",
826 "Enum::A", "Variant { field: 1 }",
828 "#![enable(implicit_some)]\nSome(5)",
829 "#![enable(unwrap_newtypes)]\n#![enable(implicit_some)]\n[1, 2]",
830 "\u{FEFF}42",
831 "1\r\n2\r\n",
832 " Foo( a : [ 1 , 2 ] , b : { 'x' : 'y' } ) // trailing\n",
833 ];
834 for src in inputs {
835 assert_eq!(roundtrip(src), src, "round-trip failed for {src:?}");
836 }
837 }
838
839 #[test]
841 fn valid_input_has_no_diagnostics() {
842 for src in [
843 "Foo(x: 1, y: 2.0)",
844 "[1, 2, 3,]",
845 "{ \"a\": 1 }",
846 "Some(())",
847 "#![enable(implicit_some)]\nSome(5)",
848 ] {
849 assert!(
850 parse(src).diagnostics().is_empty(),
851 "unexpected diagnostics for {src:?}: {:?}",
852 parse(src).diagnostics()
853 );
854 }
855 }
856
857 #[test]
858 fn parse_bytes_rejects_non_utf8() {
859 let bad = [0xFFu8, 0x00];
860 assert!(parse_bytes(&bad).is_err());
861 }
862
863 #[test]
864 fn parse_bytes_accepts_bom() {
865 let doc = parse_bytes("\u{FEFF}1".as_bytes()).unwrap();
866 let printed: String = doc
867 .root()
868 .descendant_tokens()
869 .map(|t| t.text().to_string())
870 .collect();
871 assert_eq!(printed, "\u{FEFF}1");
872 }
873
874 #[test]
875 fn source_len_matches() {
876 let src = "Foo(x: 1)";
877 let doc = parse(src);
878 assert_eq!(doc.source_len(), src.len());
879 }
880
881 #[test]
882 fn struct_vs_tuple_classification() {
883 let s = parse("Foo(x: 1)");
884 let has_struct = s.root().descendant_tokens().count() > 0
885 && s.root().children().any(|n| n.kind() == SyntaxKind::Struct);
886 assert!(has_struct, "named struct should produce a Struct node");
887
888 let t = parse("(1, 2)");
889 let has_tuple = t.root().children().any(|n| n.kind() == SyntaxKind::Tuple);
890 assert!(has_tuple, "positional parens should produce a Tuple node");
891
892 let u = parse("()");
893 let has_unit = u.root().children().any(|n| n.kind() == SyntaxKind::Unit);
894 assert!(has_unit, "empty parens should produce a Unit node");
895 }
896
897 #[test]
902 fn diagnostic_ranges_are_within_source() {
903 for src in [
904 "[1, 2", "Foo(x: 1", "{ \"a\": 1", "@", "[1 @ 2]", "1 2 3", "Foo(x: 1) extra", ] {
912 let doc = parse(src);
913 for d in doc.diagnostics() {
914 assert!(
915 d.range().start() <= d.range().end(),
916 "range ordered for {src:?}"
917 );
918 assert!(
919 d.range().end() <= doc.source_len(),
920 "range within source for {src:?}: {:?} (len {})",
921 d.range(),
922 doc.source_len()
923 );
924 }
925 }
926 }
927
928 #[test]
930 fn recovery_diagnostic_codes_and_severity() {
931 let unclosed = parse("[1, 2");
932 assert!(unclosed
933 .diagnostics()
934 .iter()
935 .any(|d| d.code() == DiagnosticCode::UnclosedDelimiter
936 && d.severity() == Severity::Error));
937
938 let stray = parse("@");
939 assert!(stray.diagnostics().iter().any(
940 |d| d.code() == DiagnosticCode::UnexpectedToken && d.severity() == Severity::Error
941 ));
942 }
943
944 #[test]
947 fn one_diagnostic_per_recovery_point() {
948 let doc = parse("[1, 2");
949 let unclosed: Vec<_> = doc
950 .diagnostics()
951 .iter()
952 .filter(|d| d.code() == DiagnosticCode::UnclosedDelimiter)
953 .collect();
954 assert_eq!(
955 unclosed.len(),
956 1,
957 "exactly one unclosed-delimiter diagnostic"
958 );
959
960 let stray = parse("@");
962 assert_eq!(stray.diagnostics().len(), 1);
963 assert_eq!(
964 stray.diagnostics()[0].code(),
965 DiagnosticCode::UnexpectedToken
966 );
967 }
968
969 #[test]
973 fn malformed_input_roundtrips() {
974 for src in [
975 "[1, 2",
976 "Foo(x: 1",
977 "{ \"a\": 1",
978 "Some(",
979 "(((",
980 "}]) ",
981 "@#$%",
982 "Foo(x: 1) trailing garbage",
983 "[1 2 3]", "{a 1, b 2}", "[1, [2, [3", ] {
987 assert_eq!(
988 roundtrip(src),
989 src,
990 "malformed round-trip failed for {src:?}"
991 );
992 }
993 }
994
995 #[test]
998 fn malformed_input_has_error_nodes() {
999 let doc = parse("@ stray");
1000 let has_error = doc
1001 .root()
1002 .descendant_tokens()
1003 .any(|t| t.parent().map(|p| p.kind()) == Some(SyntaxKind::Error));
1004 assert!(
1005 has_error,
1006 "expected an Error node for stray top-level tokens"
1007 );
1008 }
1009
1010 #[test]
1014 fn parsing_is_deterministic() {
1015 for src in ["Foo(x: 1, y: [2, 3", "@ junk ] ) }", "{a: 1, b: 2,"] {
1016 let a = parse(src);
1017 let b = parse(src);
1018 let ta: String = a
1020 .root()
1021 .descendant_tokens()
1022 .map(|t| t.text().to_string())
1023 .collect();
1024 let tb: String = b
1025 .root()
1026 .descendant_tokens()
1027 .map(|t| t.text().to_string())
1028 .collect();
1029 assert_eq!(ta, tb);
1030 assert_eq!(
1032 a.diagnostics(),
1033 b.diagnostics(),
1034 "diagnostics differ for {src:?}"
1035 );
1036 }
1037 }
1038
1039 #[test]
1044 fn depth_guard_trips_at_bound_plus_one() {
1045 let depth = 5usize;
1046 let opts = ParseOptions::default().with_max_depth(depth);
1047 let src = format!("{}{}", "[".repeat(depth + 1), "]".repeat(depth + 1));
1049 let doc = parse_with_options(&src, opts);
1050 let printed: String = doc
1051 .root()
1052 .descendant_tokens()
1053 .map(|t| t.text().to_string())
1054 .collect();
1055 assert_eq!(printed, src, "depth-limited tree must round-trip");
1056 assert!(
1057 doc.diagnostics()
1058 .iter()
1059 .any(|d| d.code() == DiagnosticCode::NestingDepthExceeded),
1060 "expected an over-limit diagnostic"
1061 );
1062 }
1063
1064 #[test]
1066 fn depth_guard_silent_below_bound() {
1067 let opts = ParseOptions::default().with_max_depth(10);
1068 let src = "[[[[1]]]]";
1069 let doc = parse_with_options(src, opts);
1070 assert!(!doc
1071 .diagnostics()
1072 .iter()
1073 .any(|d| d.code() == DiagnosticCode::NestingDepthExceeded));
1074 }
1075}