1mod decode;
50mod encode;
51
52use std::collections::HashMap;
53use std::fmt;
54
55pub use decode::decode_scopes;
56pub use encode::encode_scopes;
57
58use srcmap_codec::DecodeError;
59
60const TAG_ORIGINAL_SCOPE_START: u64 = 0x1;
63const TAG_ORIGINAL_SCOPE_END: u64 = 0x2;
64const TAG_ORIGINAL_SCOPE_VARIABLES: u64 = 0x3;
65const TAG_GENERATED_RANGE_START: u64 = 0x4;
66const TAG_GENERATED_RANGE_END: u64 = 0x5;
67const TAG_GENERATED_RANGE_BINDINGS: u64 = 0x6;
68const TAG_GENERATED_RANGE_SUB_RANGE_BINDINGS: u64 = 0x7;
69const TAG_GENERATED_RANGE_CALL_SITE: u64 = 0x8;
70
71const OS_FLAG_HAS_NAME: u64 = 0x1;
75const OS_FLAG_HAS_KIND: u64 = 0x2;
76const OS_FLAG_IS_STACK_FRAME: u64 = 0x4;
77
78const GR_FLAG_HAS_LINE: u64 = 0x1;
80const GR_FLAG_HAS_DEFINITION: u64 = 0x2;
81const GR_FLAG_IS_STACK_FRAME: u64 = 0x4;
82const GR_FLAG_IS_HIDDEN: u64 = 0x8;
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
88pub struct Position {
89 pub line: u32,
90 pub column: u32,
91}
92
93#[derive(Debug, Clone, PartialEq, Eq)]
95pub struct OriginalScope {
96 pub start: Position,
97 pub end: Position,
98 pub name: Option<String>,
100 pub kind: Option<String>,
102 pub is_stack_frame: bool,
104 pub variables: Vec<String>,
106 pub children: Vec<OriginalScope>,
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
112pub enum Binding {
113 Expression(String),
115 Unavailable,
117 SubRanges(Vec<SubRangeBinding>),
119}
120
121#[derive(Debug, Clone, PartialEq, Eq)]
123pub struct SubRangeBinding {
124 pub expression: Option<String>,
126 pub from: Position,
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub struct CallSite {
133 pub source_index: u32,
134 pub line: u32,
135 pub column: u32,
136}
137
138#[derive(Debug, Clone, PartialEq, Eq)]
140pub struct GeneratedRange {
141 pub start: Position,
142 pub end: Position,
143 pub is_stack_frame: bool,
145 pub is_hidden: bool,
147 pub definition: Option<u32>,
149 pub call_site: Option<CallSite>,
151 pub bindings: Vec<Binding>,
153 pub children: Vec<GeneratedRange>,
155}
156
157#[derive(Debug, Clone, PartialEq, Eq)]
159pub struct ScopeInfo {
160 pub scopes: Vec<Option<OriginalScope>>,
163 pub ranges: Vec<GeneratedRange>,
165}
166
167impl ScopeInfo {
168 pub fn original_scope_for_definition(&self, definition: u32) -> Option<&OriginalScope> {
173 let mut count = 0u32;
174 for scope in self.scopes.iter().flatten() {
175 if let Some(result) = find_nth_scope(scope, definition, &mut count) {
176 return Some(result);
177 }
178 }
179 None
180 }
181}
182
183fn find_nth_scope<'a>(
184 scope: &'a OriginalScope,
185 target: u32,
186 count: &mut u32,
187) -> Option<&'a OriginalScope> {
188 let mut stack: Vec<&'a OriginalScope> = vec![scope];
190
191 while let Some(node) = stack.pop() {
192 if *count == target {
193 return Some(node);
194 }
195 *count += 1;
196 for child in node.children.iter().rev() {
198 stack.push(child);
199 }
200 }
201 None
202}
203
204#[derive(Debug, Clone, PartialEq, Eq)]
208pub enum ScopesError {
209 Vlq(DecodeError),
211 UnmatchedScopeEnd,
213 UnclosedScope,
215 UnmatchedRangeEnd,
217 UnclosedRange,
219 InvalidNameIndex(i64),
221}
222
223impl fmt::Display for ScopesError {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 match self {
226 Self::Vlq(e) => write!(f, "VLQ decode error: {e}"),
227 Self::UnmatchedScopeEnd => write!(f, "scope end without matching start"),
228 Self::UnclosedScope => write!(f, "scope opened but never closed"),
229 Self::UnmatchedRangeEnd => write!(f, "range end without matching start"),
230 Self::UnclosedRange => write!(f, "range opened but never closed"),
231 Self::InvalidNameIndex(idx) => write!(f, "invalid name index: {idx}"),
232 }
233 }
234}
235
236impl std::error::Error for ScopesError {}
237
238impl From<DecodeError> for ScopesError {
239 fn from(e: DecodeError) -> Self {
240 Self::Vlq(e)
241 }
242}
243
244fn resolve_name(names: &[String], index: i64) -> Result<String, ScopesError> {
248 if index < 0 || index as usize >= names.len() {
249 return Err(ScopesError::InvalidNameIndex(index));
250 }
251 Ok(names[index as usize].clone())
252}
253
254fn resolve_binding(names: &[String], index: u64) -> Result<Option<String>, ScopesError> {
256 if index == 0 {
257 return Ok(None);
258 }
259 let actual = (index - 1) as usize;
260 if actual >= names.len() {
261 return Err(ScopesError::InvalidNameIndex(index as i64));
262 }
263 Ok(Some(names[actual].clone()))
264}
265
266fn resolve_or_add_name(
268 name: &str,
269 names: &mut Vec<String>,
270 name_map: &mut HashMap<String, u32>,
271) -> u32 {
272 if let Some(&idx) = name_map.get(name) {
273 return idx;
274 }
275 let idx = names.len() as u32;
276 names.push(name.to_string());
277 name_map.insert(name.to_string(), idx);
278 idx
279}
280
281#[cfg(test)]
284mod tests {
285 use super::*;
286
287 #[test]
288 fn empty_scopes() {
289 let info = decode_scopes("", &[], 0).unwrap();
290 assert!(info.scopes.is_empty());
291 assert!(info.ranges.is_empty());
292 }
293
294 #[test]
295 fn empty_scopes_with_sources() {
296 let info = decode_scopes(",", &[], 2).unwrap();
298 assert_eq!(info.scopes.len(), 2);
299 assert!(info.scopes[0].is_none());
300 assert!(info.scopes[1].is_none());
301 }
302
303 #[test]
304 fn single_global_scope_roundtrip() {
305 let info = ScopeInfo {
306 scopes: vec![Some(OriginalScope {
307 start: Position { line: 0, column: 0 },
308 end: Position { line: 10, column: 0 },
309 name: None,
310 kind: Some("global".to_string()),
311 is_stack_frame: false,
312 variables: vec!["x".to_string(), "y".to_string()],
313 children: vec![],
314 })],
315 ranges: vec![],
316 };
317
318 let mut names = vec![];
319 let encoded = encode_scopes(&info, &mut names);
320 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
321
322 assert_eq!(decoded.scopes.len(), 1);
323 let scope = decoded.scopes[0].as_ref().unwrap();
324 assert_eq!(scope.start, Position { line: 0, column: 0 });
325 assert_eq!(scope.end, Position { line: 10, column: 0 });
326 assert_eq!(scope.kind.as_deref(), Some("global"));
327 assert_eq!(scope.name, None);
328 assert!(!scope.is_stack_frame);
329 assert_eq!(scope.variables, vec!["x", "y"]);
330 }
331
332 #[test]
333 fn nested_scopes_roundtrip() {
334 let info = ScopeInfo {
335 scopes: vec![Some(OriginalScope {
336 start: Position { line: 0, column: 0 },
337 end: Position { line: 10, column: 1 },
338 name: None,
339 kind: Some("global".to_string()),
340 is_stack_frame: false,
341 variables: vec!["z".to_string()],
342 children: vec![OriginalScope {
343 start: Position { line: 1, column: 0 },
344 end: Position { line: 5, column: 1 },
345 name: Some("hello".to_string()),
346 kind: Some("function".to_string()),
347 is_stack_frame: true,
348 variables: vec!["msg".to_string(), "result".to_string()],
349 children: vec![],
350 }],
351 })],
352 ranges: vec![],
353 };
354
355 let mut names = vec![];
356 let encoded = encode_scopes(&info, &mut names);
357 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
358
359 let scope = decoded.scopes[0].as_ref().unwrap();
360 assert_eq!(scope.children.len(), 1);
361 let child = &scope.children[0];
362 assert_eq!(child.start, Position { line: 1, column: 0 });
363 assert_eq!(child.end, Position { line: 5, column: 1 });
364 assert_eq!(child.name.as_deref(), Some("hello"));
365 assert_eq!(child.kind.as_deref(), Some("function"));
366 assert!(child.is_stack_frame);
367 assert_eq!(child.variables, vec!["msg", "result"]);
368 }
369
370 #[test]
371 fn multiple_sources_with_gaps() {
372 let info = ScopeInfo {
373 scopes: vec![
374 Some(OriginalScope {
375 start: Position { line: 0, column: 0 },
376 end: Position { line: 5, column: 0 },
377 name: None,
378 kind: None,
379 is_stack_frame: false,
380 variables: vec![],
381 children: vec![],
382 }),
383 None, Some(OriginalScope {
385 start: Position { line: 0, column: 0 },
386 end: Position { line: 3, column: 0 },
387 name: None,
388 kind: None,
389 is_stack_frame: false,
390 variables: vec![],
391 children: vec![],
392 }),
393 ],
394 ranges: vec![],
395 };
396
397 let mut names = vec![];
398 let encoded = encode_scopes(&info, &mut names);
399 let decoded = decode_scopes(&encoded, &names, 3).unwrap();
400
401 assert_eq!(decoded.scopes.len(), 3);
402 assert!(decoded.scopes[0].is_some());
403 assert!(decoded.scopes[1].is_none());
404 assert!(decoded.scopes[2].is_some());
405 }
406
407 #[test]
408 fn generated_ranges_roundtrip() {
409 let info = ScopeInfo {
410 scopes: vec![Some(OriginalScope {
411 start: Position { line: 0, column: 0 },
412 end: Position { line: 10, column: 0 },
413 name: None,
414 kind: Some("global".to_string()),
415 is_stack_frame: false,
416 variables: vec!["x".to_string()],
417 children: vec![],
418 })],
419 ranges: vec![GeneratedRange {
420 start: Position { line: 0, column: 0 },
421 end: Position { line: 10, column: 0 },
422 is_stack_frame: false,
423 is_hidden: false,
424 definition: Some(0),
425 call_site: None,
426 bindings: vec![Binding::Expression("_x".to_string())],
427 children: vec![],
428 }],
429 };
430
431 let mut names = vec![];
432 let encoded = encode_scopes(&info, &mut names);
433 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
434
435 assert_eq!(decoded.ranges.len(), 1);
436 let range = &decoded.ranges[0];
437 assert_eq!(range.start, Position { line: 0, column: 0 });
438 assert_eq!(range.end, Position { line: 10, column: 0 });
439 assert_eq!(range.definition, Some(0));
440 assert_eq!(range.bindings, vec![Binding::Expression("_x".to_string())]);
441 }
442
443 #[test]
444 fn nested_ranges_with_inlining() {
445 let info = ScopeInfo {
446 scopes: vec![Some(OriginalScope {
447 start: Position { line: 0, column: 0 },
448 end: Position { line: 10, column: 0 },
449 name: None,
450 kind: Some("global".to_string()),
451 is_stack_frame: false,
452 variables: vec!["x".to_string()],
453 children: vec![OriginalScope {
454 start: Position { line: 1, column: 0 },
455 end: Position { line: 5, column: 1 },
456 name: Some("fn1".to_string()),
457 kind: Some("function".to_string()),
458 is_stack_frame: true,
459 variables: vec!["a".to_string()],
460 children: vec![],
461 }],
462 })],
463 ranges: vec![GeneratedRange {
464 start: Position { line: 0, column: 0 },
465 end: Position { line: 10, column: 0 },
466 is_stack_frame: false,
467 is_hidden: false,
468 definition: Some(0),
469 call_site: None,
470 bindings: vec![Binding::Expression("_x".to_string())],
471 children: vec![GeneratedRange {
472 start: Position { line: 6, column: 0 },
473 end: Position { line: 8, column: 20 },
474 is_stack_frame: true,
475 is_hidden: false,
476 definition: Some(1),
477 call_site: Some(CallSite { source_index: 0, line: 7, column: 0 }),
478 bindings: vec![Binding::Expression("\"hello\"".to_string())],
479 children: vec![],
480 }],
481 }],
482 };
483
484 let mut names = vec![];
485 let encoded = encode_scopes(&info, &mut names);
486 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
487
488 assert_eq!(decoded.ranges.len(), 1);
489 let outer = &decoded.ranges[0];
490 assert_eq!(outer.children.len(), 1);
491 let inner = &outer.children[0];
492 assert!(inner.is_stack_frame);
493 assert_eq!(inner.definition, Some(1));
494 assert_eq!(inner.call_site, Some(CallSite { source_index: 0, line: 7, column: 0 }));
495 assert_eq!(inner.bindings, vec![Binding::Expression("\"hello\"".to_string())]);
496 }
497
498 #[test]
499 fn unavailable_bindings() {
500 let info = ScopeInfo {
501 scopes: vec![Some(OriginalScope {
502 start: Position { line: 0, column: 0 },
503 end: Position { line: 5, column: 0 },
504 name: None,
505 kind: None,
506 is_stack_frame: false,
507 variables: vec!["a".to_string(), "b".to_string()],
508 children: vec![],
509 })],
510 ranges: vec![GeneratedRange {
511 start: Position { line: 0, column: 0 },
512 end: Position { line: 5, column: 0 },
513 is_stack_frame: false,
514 is_hidden: false,
515 definition: Some(0),
516 call_site: None,
517 bindings: vec![Binding::Expression("_a".to_string()), Binding::Unavailable],
518 children: vec![],
519 }],
520 };
521
522 let mut names = vec![];
523 let encoded = encode_scopes(&info, &mut names);
524 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
525
526 assert_eq!(
527 decoded.ranges[0].bindings,
528 vec![Binding::Expression("_a".to_string()), Binding::Unavailable,]
529 );
530 }
531
532 #[test]
533 fn sub_range_bindings_roundtrip() {
534 let info = ScopeInfo {
535 scopes: vec![Some(OriginalScope {
536 start: Position { line: 0, column: 0 },
537 end: Position { line: 20, column: 0 },
538 name: None,
539 kind: None,
540 is_stack_frame: false,
541 variables: vec!["x".to_string(), "y".to_string()],
542 children: vec![],
543 })],
544 ranges: vec![GeneratedRange {
545 start: Position { line: 0, column: 0 },
546 end: Position { line: 20, column: 0 },
547 is_stack_frame: false,
548 is_hidden: false,
549 definition: Some(0),
550 call_site: None,
551 bindings: vec![
552 Binding::SubRanges(vec![
553 SubRangeBinding {
554 expression: Some("a".to_string()),
555 from: Position { line: 0, column: 0 },
556 },
557 SubRangeBinding {
558 expression: Some("b".to_string()),
559 from: Position { line: 5, column: 0 },
560 },
561 SubRangeBinding {
562 expression: None,
563 from: Position { line: 10, column: 0 },
564 },
565 ]),
566 Binding::Expression("_y".to_string()),
567 ],
568 children: vec![],
569 }],
570 };
571
572 let mut names = vec![];
573 let encoded = encode_scopes(&info, &mut names);
574 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
575
576 let bindings = &decoded.ranges[0].bindings;
577 assert_eq!(bindings.len(), 2);
578
579 match &bindings[0] {
580 Binding::SubRanges(subs) => {
581 assert_eq!(subs.len(), 3);
582 assert_eq!(subs[0].expression.as_deref(), Some("a"));
583 assert_eq!(subs[0].from, Position { line: 0, column: 0 });
584 assert_eq!(subs[1].expression.as_deref(), Some("b"));
585 assert_eq!(subs[1].from, Position { line: 5, column: 0 });
586 assert_eq!(subs[2].expression, None);
587 assert_eq!(subs[2].from, Position { line: 10, column: 0 });
588 }
589 other => panic!("expected SubRanges, got {other:?}"),
590 }
591 assert_eq!(bindings[1], Binding::Expression("_y".to_string()));
592 }
593
594 #[test]
595 fn hidden_range() {
596 let info = ScopeInfo {
597 scopes: vec![Some(OriginalScope {
598 start: Position { line: 0, column: 0 },
599 end: Position { line: 5, column: 0 },
600 name: None,
601 kind: None,
602 is_stack_frame: false,
603 variables: vec![],
604 children: vec![],
605 })],
606 ranges: vec![GeneratedRange {
607 start: Position { line: 0, column: 0 },
608 end: Position { line: 5, column: 0 },
609 is_stack_frame: true,
610 is_hidden: true,
611 definition: Some(0),
612 call_site: None,
613 bindings: vec![],
614 children: vec![],
615 }],
616 };
617
618 let mut names = vec![];
619 let encoded = encode_scopes(&info, &mut names);
620 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
621
622 assert!(decoded.ranges[0].is_stack_frame);
623 assert!(decoded.ranges[0].is_hidden);
624 }
625
626 #[test]
627 fn definition_resolution() {
628 let info = ScopeInfo {
629 scopes: vec![Some(OriginalScope {
630 start: Position { line: 0, column: 0 },
631 end: Position { line: 10, column: 0 },
632 name: None,
633 kind: Some("global".to_string()),
634 is_stack_frame: false,
635 variables: vec![],
636 children: vec![
637 OriginalScope {
638 start: Position { line: 1, column: 0 },
639 end: Position { line: 4, column: 1 },
640 name: Some("foo".to_string()),
641 kind: Some("function".to_string()),
642 is_stack_frame: true,
643 variables: vec![],
644 children: vec![],
645 },
646 OriginalScope {
647 start: Position { line: 5, column: 0 },
648 end: Position { line: 9, column: 1 },
649 name: Some("bar".to_string()),
650 kind: Some("function".to_string()),
651 is_stack_frame: true,
652 variables: vec![],
653 children: vec![],
654 },
655 ],
656 })],
657 ranges: vec![],
658 };
659
660 let scope0 = info.original_scope_for_definition(0).unwrap();
662 assert_eq!(scope0.kind.as_deref(), Some("global"));
663
664 let scope1 = info.original_scope_for_definition(1).unwrap();
666 assert_eq!(scope1.name.as_deref(), Some("foo"));
667
668 let scope2 = info.original_scope_for_definition(2).unwrap();
670 assert_eq!(scope2.name.as_deref(), Some("bar"));
671
672 assert!(info.original_scope_for_definition(3).is_none());
674 }
675
676 #[test]
677 fn scopes_only_no_ranges() {
678 let info = ScopeInfo {
679 scopes: vec![Some(OriginalScope {
680 start: Position { line: 0, column: 0 },
681 end: Position { line: 5, column: 0 },
682 name: None,
683 kind: None,
684 is_stack_frame: false,
685 variables: vec![],
686 children: vec![],
687 })],
688 ranges: vec![],
689 };
690
691 let mut names = vec![];
692 let encoded = encode_scopes(&info, &mut names);
693 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
694
695 assert_eq!(decoded.scopes.len(), 1);
696 assert!(decoded.scopes[0].is_some());
697 assert!(decoded.ranges.is_empty());
698 }
699
700 #[test]
701 fn ranges_only_no_scopes() {
702 let info = ScopeInfo {
703 scopes: vec![None],
704 ranges: vec![GeneratedRange {
705 start: Position { line: 0, column: 0 },
706 end: Position { line: 5, column: 0 },
707 is_stack_frame: false,
708 is_hidden: false,
709 definition: None,
710 call_site: None,
711 bindings: vec![],
712 children: vec![],
713 }],
714 };
715
716 let mut names = vec![];
717 let encoded = encode_scopes(&info, &mut names);
718 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
719
720 assert_eq!(decoded.scopes.len(), 1);
721 assert!(decoded.scopes[0].is_none());
722 assert_eq!(decoded.ranges.len(), 1);
723 }
724
725 #[test]
726 fn range_no_definition() {
727 let info = ScopeInfo {
728 scopes: vec![],
729 ranges: vec![GeneratedRange {
730 start: Position { line: 0, column: 0 },
731 end: Position { line: 5, column: 0 },
732 is_stack_frame: false,
733 is_hidden: false,
734 definition: None,
735 call_site: None,
736 bindings: vec![],
737 children: vec![],
738 }],
739 };
740
741 let mut names = vec![];
742 let encoded = encode_scopes(&info, &mut names);
743 let decoded = decode_scopes(&encoded, &names, 0).unwrap();
744
745 assert_eq!(decoded.ranges.len(), 1);
746 assert_eq!(decoded.ranges[0].definition, None);
747 }
748
749 #[test]
750 fn scopes_error_display() {
751 let err = ScopesError::UnmatchedScopeEnd;
752 assert_eq!(err.to_string(), "scope end without matching start");
753
754 let err = ScopesError::UnclosedScope;
755 assert_eq!(err.to_string(), "scope opened but never closed");
756
757 let err = ScopesError::UnmatchedRangeEnd;
758 assert_eq!(err.to_string(), "range end without matching start");
759
760 let err = ScopesError::UnclosedRange;
761 assert_eq!(err.to_string(), "range opened but never closed");
762
763 let err = ScopesError::InvalidNameIndex(42);
764 assert_eq!(err.to_string(), "invalid name index: 42");
765
766 let vlq_err = srcmap_codec::DecodeError::UnexpectedEof { offset: 5 };
767 let err = ScopesError::Vlq(vlq_err);
768 assert!(err.to_string().contains("VLQ decode error"));
769 }
770
771 #[test]
772 fn scopes_error_from_decode_error() {
773 let vlq_err = srcmap_codec::DecodeError::UnexpectedEof { offset: 0 };
774 let err: ScopesError = vlq_err.into();
775 assert!(matches!(err, ScopesError::Vlq(_)));
776 }
777
778 #[test]
779 fn invalid_name_index_error() {
780 let info = ScopeInfo {
782 scopes: vec![Some(OriginalScope {
783 start: Position { line: 0, column: 0 },
784 end: Position { line: 5, column: 0 },
785 name: Some("test".to_string()),
786 kind: None,
787 is_stack_frame: false,
788 variables: vec![],
789 children: vec![],
790 })],
791 ranges: vec![],
792 };
793
794 let mut names = vec![];
795 let encoded = encode_scopes(&info, &mut names);
796 let err = decode_scopes(&encoded, &[], 1).unwrap_err();
798 assert!(matches!(err, ScopesError::InvalidNameIndex(_)));
799 }
800
801 #[test]
802 fn invalid_binding_index_error() {
803 let info = ScopeInfo {
805 scopes: vec![Some(OriginalScope {
806 start: Position { line: 0, column: 0 },
807 end: Position { line: 5, column: 0 },
808 name: None,
809 kind: None,
810 is_stack_frame: false,
811 variables: vec!["x".to_string()],
812 children: vec![],
813 })],
814 ranges: vec![GeneratedRange {
815 start: Position { line: 0, column: 0 },
816 end: Position { line: 5, column: 0 },
817 is_stack_frame: false,
818 is_hidden: false,
819 definition: Some(0),
820 call_site: None,
821 bindings: vec![Binding::Expression("_x".to_string())],
822 children: vec![],
823 }],
824 };
825
826 let mut names = vec![];
827 let encoded = encode_scopes(&info, &mut names);
828 let short_names: Vec<String> = names.iter().take(1).cloned().collect();
830 let err = decode_scopes(&encoded, &short_names, 1).unwrap_err();
831 assert!(matches!(err, ScopesError::InvalidNameIndex(_)));
832 }
833
834 #[test]
835 fn scope_same_line_end() {
836 let info = ScopeInfo {
838 scopes: vec![Some(OriginalScope {
839 start: Position { line: 5, column: 10 },
840 end: Position { line: 5, column: 30 },
841 name: None,
842 kind: None,
843 is_stack_frame: false,
844 variables: vec![],
845 children: vec![],
846 })],
847 ranges: vec![],
848 };
849
850 let mut names = vec![];
851 let encoded = encode_scopes(&info, &mut names);
852 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
853
854 let scope = decoded.scopes[0].as_ref().unwrap();
855 assert_eq!(scope.start, Position { line: 5, column: 10 });
856 assert_eq!(scope.end, Position { line: 5, column: 30 });
857 }
858
859 #[test]
860 fn range_same_line() {
861 let info = ScopeInfo {
863 scopes: vec![Some(OriginalScope {
864 start: Position { line: 0, column: 0 },
865 end: Position { line: 10, column: 0 },
866 name: None,
867 kind: None,
868 is_stack_frame: false,
869 variables: vec![],
870 children: vec![],
871 })],
872 ranges: vec![GeneratedRange {
873 start: Position { line: 3, column: 5 },
874 end: Position { line: 3, column: 25 },
875 is_stack_frame: false,
876 is_hidden: false,
877 definition: Some(0),
878 call_site: None,
879 bindings: vec![],
880 children: vec![],
881 }],
882 };
883
884 let mut names = vec![];
885 let encoded = encode_scopes(&info, &mut names);
886 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
887
888 let range = &decoded.ranges[0];
889 assert_eq!(range.start, Position { line: 3, column: 5 });
890 assert_eq!(range.end, Position { line: 3, column: 25 });
891 }
892
893 #[test]
894 fn scopes_first_empty_second_populated() {
895 let info = ScopeInfo {
896 scopes: vec![
897 None, Some(OriginalScope {
899 start: Position { line: 0, column: 0 },
900 end: Position { line: 5, column: 0 },
901 name: None,
902 kind: None,
903 is_stack_frame: false,
904 variables: vec![],
905 children: vec![],
906 }),
907 ],
908 ranges: vec![],
909 };
910
911 let mut names = vec![];
912 let encoded = encode_scopes(&info, &mut names);
913 let decoded = decode_scopes(&encoded, &names, 2).unwrap();
914
915 assert!(decoded.scopes[0].is_none());
916 assert!(decoded.scopes[1].is_some());
917 }
918
919 #[test]
920 fn ranges_only_no_scopes_multi_source() {
921 let info = ScopeInfo {
922 scopes: vec![None, None],
923 ranges: vec![GeneratedRange {
924 start: Position { line: 0, column: 0 },
925 end: Position { line: 5, column: 0 },
926 is_stack_frame: false,
927 is_hidden: false,
928 definition: None,
929 call_site: None,
930 bindings: vec![],
931 children: vec![],
932 }],
933 };
934
935 let mut names = vec![];
936 let encoded = encode_scopes(&info, &mut names);
937 let decoded = decode_scopes(&encoded, &names, 2).unwrap();
938
939 assert_eq!(decoded.scopes.len(), 2);
940 assert!(decoded.scopes[0].is_none());
941 assert!(decoded.scopes[1].is_none());
942 assert_eq!(decoded.ranges.len(), 1);
943 }
944
945 #[test]
946 fn range_no_definition_explicit() {
947 let info = ScopeInfo {
948 scopes: vec![None],
949 ranges: vec![GeneratedRange {
950 start: Position { line: 0, column: 0 },
951 end: Position { line: 5, column: 0 },
952 is_stack_frame: false,
953 is_hidden: false,
954 definition: None,
955 call_site: None,
956 bindings: vec![],
957 children: vec![],
958 }],
959 };
960
961 let mut names = vec![];
962 let encoded = encode_scopes(&info, &mut names);
963 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
964
965 assert_eq!(decoded.ranges[0].definition, None);
966 }
967
968 #[test]
969 fn sub_range_same_line_bindings() {
970 let info = ScopeInfo {
972 scopes: vec![Some(OriginalScope {
973 start: Position { line: 0, column: 0 },
974 end: Position { line: 10, column: 0 },
975 name: None,
976 kind: None,
977 is_stack_frame: false,
978 variables: vec!["x".to_string()],
979 children: vec![],
980 })],
981 ranges: vec![GeneratedRange {
982 start: Position { line: 0, column: 0 },
983 end: Position { line: 10, column: 0 },
984 is_stack_frame: false,
985 is_hidden: false,
986 definition: Some(0),
987 call_site: None,
988 bindings: vec![Binding::SubRanges(vec![
989 SubRangeBinding {
990 expression: Some("a".to_string()),
991 from: Position { line: 0, column: 0 },
992 },
993 SubRangeBinding {
994 expression: Some("b".to_string()),
995 from: Position { line: 0, column: 15 },
996 },
997 ])],
998 children: vec![],
999 }],
1000 };
1001
1002 let mut names = vec![];
1003 let encoded = encode_scopes(&info, &mut names);
1004 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
1005
1006 match &decoded.ranges[0].bindings[0] {
1007 Binding::SubRanges(subs) => {
1008 assert_eq!(subs.len(), 2);
1009 assert_eq!(subs[0].from, Position { line: 0, column: 0 });
1010 assert_eq!(subs[1].from, Position { line: 0, column: 15 });
1011 }
1012 other => panic!("expected SubRanges, got {other:?}"),
1013 }
1014 }
1015
1016 #[test]
1017 fn call_site_with_nonzero_values() {
1018 let info = ScopeInfo {
1019 scopes: vec![Some(OriginalScope {
1020 start: Position { line: 0, column: 0 },
1021 end: Position { line: 20, column: 0 },
1022 name: None,
1023 kind: None,
1024 is_stack_frame: false,
1025 variables: vec![],
1026 children: vec![],
1027 })],
1028 ranges: vec![GeneratedRange {
1029 start: Position { line: 0, column: 0 },
1030 end: Position { line: 20, column: 0 },
1031 is_stack_frame: false,
1032 is_hidden: false,
1033 definition: Some(0),
1034 call_site: Some(CallSite { source_index: 2, line: 15, column: 8 }),
1035 bindings: vec![],
1036 children: vec![],
1037 }],
1038 };
1039
1040 let mut names = vec![];
1041 let encoded = encode_scopes(&info, &mut names);
1042 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
1043
1044 let cs = decoded.ranges[0].call_site.as_ref().unwrap();
1045 assert_eq!(cs.source_index, 2);
1046 assert_eq!(cs.line, 15);
1047 assert_eq!(cs.column, 8);
1048 }
1049
1050 #[test]
1053 fn scope_with_name_and_kind_roundtrip() {
1054 let info = ScopeInfo {
1057 scopes: vec![Some(OriginalScope {
1058 start: Position { line: 2, column: 4 },
1059 end: Position { line: 15, column: 1 },
1060 name: Some("myFunc".to_string()),
1061 kind: Some("function".to_string()),
1062 is_stack_frame: true,
1063 variables: vec!["arg1".to_string(), "arg2".to_string()],
1064 children: vec![OriginalScope {
1065 start: Position { line: 3, column: 8 },
1066 end: Position { line: 14, column: 5 },
1067 name: Some("innerBlock".to_string()),
1068 kind: Some("block".to_string()),
1069 is_stack_frame: false,
1070 variables: vec!["tmp".to_string()],
1071 children: vec![],
1072 }],
1073 })],
1074 ranges: vec![],
1075 };
1076
1077 let mut names = vec![];
1078 let encoded = encode_scopes(&info, &mut names);
1079 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
1080
1081 let scope = decoded.scopes[0].as_ref().unwrap();
1082 assert_eq!(scope.name.as_deref(), Some("myFunc"));
1083 assert_eq!(scope.kind.as_deref(), Some("function"));
1084 assert!(scope.is_stack_frame);
1085 assert_eq!(scope.variables, vec!["arg1", "arg2"]);
1086
1087 let child = &scope.children[0];
1088 assert_eq!(child.name.as_deref(), Some("innerBlock"));
1089 assert_eq!(child.kind.as_deref(), Some("block"));
1090 assert!(!child.is_stack_frame);
1091 assert_eq!(child.variables, vec!["tmp"]);
1092 }
1093
1094 #[test]
1095 fn range_end_multiline_2vlq() {
1096 let info = ScopeInfo {
1100 scopes: vec![],
1101 ranges: vec![GeneratedRange {
1102 start: Position { line: 0, column: 0 },
1103 end: Position { line: 7, column: 15 },
1104 is_stack_frame: false,
1105 is_hidden: false,
1106 definition: None,
1107 call_site: None,
1108 bindings: vec![],
1109 children: vec![GeneratedRange {
1110 start: Position { line: 1, column: 5 },
1111 end: Position { line: 4, column: 10 },
1112 is_stack_frame: false,
1113 is_hidden: false,
1114 definition: None,
1115 call_site: None,
1116 bindings: vec![],
1117 children: vec![],
1118 }],
1119 }],
1120 };
1121
1122 let mut names = vec![];
1123 let encoded = encode_scopes(&info, &mut names);
1124 let decoded = decode_scopes(&encoded, &names, 0).unwrap();
1125
1126 let outer = &decoded.ranges[0];
1127 assert_eq!(outer.end, Position { line: 7, column: 15 });
1128 let inner = &outer.children[0];
1129 assert_eq!(inner.start, Position { line: 1, column: 5 });
1130 assert_eq!(inner.end, Position { line: 4, column: 10 });
1131 }
1132
1133 #[test]
1134 fn binding_unavailable_roundtrip() {
1135 let info = ScopeInfo {
1138 scopes: vec![Some(OriginalScope {
1139 start: Position { line: 0, column: 0 },
1140 end: Position { line: 10, column: 0 },
1141 name: None,
1142 kind: None,
1143 is_stack_frame: false,
1144 variables: vec!["a".to_string(), "b".to_string(), "c".to_string()],
1145 children: vec![],
1146 })],
1147 ranges: vec![GeneratedRange {
1148 start: Position { line: 0, column: 0 },
1149 end: Position { line: 10, column: 0 },
1150 is_stack_frame: false,
1151 is_hidden: false,
1152 definition: Some(0),
1153 call_site: None,
1154 bindings: vec![
1155 Binding::Unavailable,
1156 Binding::Expression("_b".to_string()),
1157 Binding::Unavailable,
1158 ],
1159 children: vec![],
1160 }],
1161 };
1162
1163 let mut names = vec![];
1164 let encoded = encode_scopes(&info, &mut names);
1165 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
1166
1167 assert_eq!(decoded.ranges[0].bindings.len(), 3);
1168 assert_eq!(decoded.ranges[0].bindings[0], Binding::Unavailable);
1169 assert_eq!(decoded.ranges[0].bindings[1], Binding::Expression("_b".to_string()));
1170 assert_eq!(decoded.ranges[0].bindings[2], Binding::Unavailable);
1171 }
1172
1173 #[test]
1174 fn sub_range_with_none_expression() {
1175 let info = ScopeInfo {
1179 scopes: vec![Some(OriginalScope {
1180 start: Position { line: 0, column: 0 },
1181 end: Position { line: 20, column: 0 },
1182 name: None,
1183 kind: None,
1184 is_stack_frame: false,
1185 variables: vec!["x".to_string()],
1186 children: vec![],
1187 })],
1188 ranges: vec![GeneratedRange {
1189 start: Position { line: 0, column: 0 },
1190 end: Position { line: 20, column: 0 },
1191 is_stack_frame: false,
1192 is_hidden: false,
1193 definition: Some(0),
1194 call_site: None,
1195 bindings: vec![Binding::SubRanges(vec![
1196 SubRangeBinding {
1197 expression: Some("a".to_string()),
1198 from: Position { line: 0, column: 0 },
1199 },
1200 SubRangeBinding { expression: None, from: Position { line: 5, column: 0 } },
1201 SubRangeBinding {
1202 expression: Some("c".to_string()),
1203 from: Position { line: 10, column: 0 },
1204 },
1205 ])],
1206 children: vec![],
1207 }],
1208 };
1209
1210 let mut names = vec![];
1211 let encoded = encode_scopes(&info, &mut names);
1212 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
1213
1214 match &decoded.ranges[0].bindings[0] {
1215 Binding::SubRanges(subs) => {
1216 assert_eq!(subs.len(), 3);
1217 assert_eq!(subs[0].expression.as_deref(), Some("a"));
1218 assert_eq!(subs[1].expression, None);
1219 assert_eq!(subs[2].expression.as_deref(), Some("c"));
1220 }
1221 other => panic!("expected SubRanges, got {other:?}"),
1222 }
1223 }
1224
1225 #[test]
1226 fn sub_range_multiple_variables() {
1227 let info = ScopeInfo {
1231 scopes: vec![Some(OriginalScope {
1232 start: Position { line: 0, column: 0 },
1233 end: Position { line: 30, column: 0 },
1234 name: None,
1235 kind: None,
1236 is_stack_frame: false,
1237 variables: vec!["x".to_string(), "y".to_string(), "z".to_string()],
1238 children: vec![],
1239 })],
1240 ranges: vec![GeneratedRange {
1241 start: Position { line: 0, column: 0 },
1242 end: Position { line: 30, column: 0 },
1243 is_stack_frame: false,
1244 is_hidden: false,
1245 definition: Some(0),
1246 call_site: None,
1247 bindings: vec![
1248 Binding::SubRanges(vec![
1250 SubRangeBinding {
1251 expression: Some("_x1".to_string()),
1252 from: Position { line: 0, column: 0 },
1253 },
1254 SubRangeBinding {
1255 expression: Some("_x2".to_string()),
1256 from: Position { line: 10, column: 0 },
1257 },
1258 ]),
1259 Binding::Expression("_y".to_string()),
1261 Binding::SubRanges(vec![
1263 SubRangeBinding {
1264 expression: Some("_z1".to_string()),
1265 from: Position { line: 0, column: 0 },
1266 },
1267 SubRangeBinding {
1268 expression: None,
1269 from: Position { line: 15, column: 5 },
1270 },
1271 SubRangeBinding {
1272 expression: Some("_z3".to_string()),
1273 from: Position { line: 20, column: 0 },
1274 },
1275 ]),
1276 ],
1277 children: vec![],
1278 }],
1279 };
1280
1281 let mut names = vec![];
1282 let encoded = encode_scopes(&info, &mut names);
1283 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
1284
1285 let bindings = &decoded.ranges[0].bindings;
1286 assert_eq!(bindings.len(), 3);
1287
1288 match &bindings[0] {
1290 Binding::SubRanges(subs) => {
1291 assert_eq!(subs.len(), 2);
1292 assert_eq!(subs[0].expression.as_deref(), Some("_x1"));
1293 assert_eq!(subs[1].expression.as_deref(), Some("_x2"));
1294 assert_eq!(subs[1].from, Position { line: 10, column: 0 });
1295 }
1296 other => panic!("expected SubRanges for x, got {other:?}"),
1297 }
1298
1299 assert_eq!(bindings[1], Binding::Expression("_y".to_string()));
1301
1302 match &bindings[2] {
1304 Binding::SubRanges(subs) => {
1305 assert_eq!(subs.len(), 3);
1306 assert_eq!(subs[0].expression.as_deref(), Some("_z1"));
1307 assert_eq!(subs[1].expression, None);
1308 assert_eq!(subs[1].from, Position { line: 15, column: 5 });
1309 assert_eq!(subs[2].expression.as_deref(), Some("_z3"));
1310 }
1311 other => panic!("expected SubRanges for z, got {other:?}"),
1312 }
1313 }
1314
1315 #[test]
1316 fn call_site_on_standalone_range() {
1317 let info = ScopeInfo {
1320 scopes: vec![Some(OriginalScope {
1321 start: Position { line: 0, column: 0 },
1322 end: Position { line: 30, column: 0 },
1323 name: None,
1324 kind: Some("global".to_string()),
1325 is_stack_frame: false,
1326 variables: vec![],
1327 children: vec![OriginalScope {
1328 start: Position { line: 5, column: 0 },
1329 end: Position { line: 10, column: 1 },
1330 name: Some("inlined".to_string()),
1331 kind: Some("function".to_string()),
1332 is_stack_frame: true,
1333 variables: vec!["p".to_string()],
1334 children: vec![],
1335 }],
1336 })],
1337 ranges: vec![GeneratedRange {
1338 start: Position { line: 0, column: 0 },
1339 end: Position { line: 30, column: 0 },
1340 is_stack_frame: false,
1341 is_hidden: false,
1342 definition: Some(0),
1343 call_site: None,
1344 bindings: vec![],
1345 children: vec![GeneratedRange {
1346 start: Position { line: 12, column: 0 },
1347 end: Position { line: 18, column: 0 },
1348 is_stack_frame: true,
1349 is_hidden: false,
1350 definition: Some(1),
1351 call_site: Some(CallSite { source_index: 0, line: 20, column: 4 }),
1352 bindings: vec![Binding::Expression("arg0".to_string())],
1353 children: vec![],
1354 }],
1355 }],
1356 };
1357
1358 let mut names = vec![];
1359 let encoded = encode_scopes(&info, &mut names);
1360 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
1361
1362 let inner = &decoded.ranges[0].children[0];
1363 assert!(inner.is_stack_frame);
1364 assert_eq!(inner.definition, Some(1));
1365 let cs = inner.call_site.as_ref().unwrap();
1366 assert_eq!(cs.source_index, 0);
1367 assert_eq!(cs.line, 20);
1368 assert_eq!(cs.column, 4);
1369 assert_eq!(inner.bindings, vec![Binding::Expression("arg0".to_string())]);
1370 }
1371
1372 #[test]
1373 fn unknown_tag_skipped() {
1374 let info = ScopeInfo {
1380 scopes: vec![Some(OriginalScope {
1381 start: Position { line: 0, column: 0 },
1382 end: Position { line: 5, column: 0 },
1383 name: None,
1384 kind: None,
1385 is_stack_frame: false,
1386 variables: vec![],
1387 children: vec![],
1388 })],
1389 ranges: vec![],
1390 };
1391
1392 let mut names = vec![];
1393 let encoded = encode_scopes(&info, &mut names);
1394
1395 let mut crafted = Vec::new();
1398 srcmap_codec::vlq_encode_unsigned(&mut crafted, 18); srcmap_codec::vlq_encode_unsigned(&mut crafted, 42); srcmap_codec::vlq_encode_unsigned(&mut crafted, 7); crafted.push(b',');
1402 crafted.extend_from_slice(encoded.as_bytes());
1403
1404 let crafted_str = std::str::from_utf8(&crafted).unwrap();
1405 let decoded = decode_scopes(crafted_str, &names, 1).unwrap();
1406
1407 assert_eq!(decoded.scopes.len(), 1);
1408 assert!(decoded.scopes[0].is_some());
1409 let scope = decoded.scopes[0].as_ref().unwrap();
1410 assert_eq!(scope.start, Position { line: 0, column: 0 });
1411 assert_eq!(scope.end, Position { line: 5, column: 0 });
1412 }
1413
1414 #[test]
1415 fn first_source_none_exercises_empty_path() {
1416 let info = ScopeInfo {
1420 scopes: vec![
1421 None,
1422 None,
1423 Some(OriginalScope {
1424 start: Position { line: 0, column: 0 },
1425 end: Position { line: 5, column: 0 },
1426 name: None,
1427 kind: None,
1428 is_stack_frame: false,
1429 variables: vec![],
1430 children: vec![],
1431 }),
1432 ],
1433 ranges: vec![],
1434 };
1435
1436 let mut names = vec![];
1437 let encoded = encode_scopes(&info, &mut names);
1438 let decoded = decode_scopes(&encoded, &names, 3).unwrap();
1439
1440 assert_eq!(decoded.scopes.len(), 3);
1441 assert!(decoded.scopes[0].is_none());
1442 assert!(decoded.scopes[1].is_none());
1443 assert!(decoded.scopes[2].is_some());
1444 }
1445
1446 #[test]
1447 fn scope_end_same_line_as_child_end() {
1448 let info = ScopeInfo {
1452 scopes: vec![Some(OriginalScope {
1453 start: Position { line: 0, column: 0 },
1454 end: Position { line: 10, column: 20 },
1455 name: None,
1456 kind: None,
1457 is_stack_frame: false,
1458 variables: vec![],
1459 children: vec![OriginalScope {
1460 start: Position { line: 5, column: 0 },
1461 end: Position { line: 10, column: 10 },
1462 name: None,
1463 kind: None,
1464 is_stack_frame: false,
1465 variables: vec![],
1466 children: vec![],
1467 }],
1468 })],
1469 ranges: vec![],
1470 };
1471
1472 let mut names = vec![];
1473 let encoded = encode_scopes(&info, &mut names);
1474 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
1475
1476 let scope = decoded.scopes[0].as_ref().unwrap();
1477 assert_eq!(scope.end, Position { line: 10, column: 20 });
1478 assert_eq!(scope.children[0].end, Position { line: 10, column: 10 });
1479 }
1480
1481 #[test]
1482 fn generated_range_has_line_flag() {
1483 let info = ScopeInfo {
1487 scopes: vec![],
1488 ranges: vec![
1489 GeneratedRange {
1490 start: Position { line: 0, column: 5 },
1491 end: Position { line: 0, column: 50 },
1492 is_stack_frame: false,
1493 is_hidden: false,
1494 definition: None,
1495 call_site: None,
1496 bindings: vec![],
1497 children: vec![],
1498 },
1499 GeneratedRange {
1500 start: Position { line: 3, column: 10 },
1501 end: Position { line: 8, column: 20 },
1502 is_stack_frame: false,
1503 is_hidden: false,
1504 definition: None,
1505 call_site: None,
1506 bindings: vec![],
1507 children: vec![],
1508 },
1509 ],
1510 };
1511
1512 let mut names = vec![];
1513 let encoded = encode_scopes(&info, &mut names);
1514 let decoded = decode_scopes(&encoded, &names, 0).unwrap();
1515
1516 assert_eq!(decoded.ranges.len(), 2);
1517 assert_eq!(decoded.ranges[0].start, Position { line: 0, column: 5 });
1518 assert_eq!(decoded.ranges[0].end, Position { line: 0, column: 50 });
1519 assert_eq!(decoded.ranges[1].start, Position { line: 3, column: 10 });
1520 assert_eq!(decoded.ranges[1].end, Position { line: 8, column: 20 });
1521 }
1522
1523 #[test]
1524 fn scope_variables_decode_path() {
1525 let info = ScopeInfo {
1528 scopes: vec![Some(OriginalScope {
1529 start: Position { line: 0, column: 0 },
1530 end: Position { line: 20, column: 0 },
1531 name: None,
1532 kind: None,
1533 is_stack_frame: false,
1534 variables: vec!["alpha".to_string(), "beta".to_string(), "gamma".to_string()],
1535 children: vec![OriginalScope {
1536 start: Position { line: 2, column: 0 },
1537 end: Position { line: 18, column: 0 },
1538 name: None,
1539 kind: None,
1540 is_stack_frame: false,
1541 variables: vec!["delta".to_string(), "epsilon".to_string()],
1542 children: vec![],
1543 }],
1544 })],
1545 ranges: vec![],
1546 };
1547
1548 let mut names = vec![];
1549 let encoded = encode_scopes(&info, &mut names);
1550 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
1551
1552 let scope = decoded.scopes[0].as_ref().unwrap();
1553 assert_eq!(scope.variables, vec!["alpha", "beta", "gamma"]);
1554 assert_eq!(scope.children[0].variables, vec!["delta", "epsilon"]);
1555 }
1556
1557 #[test]
1558 fn sub_range_first_expression_none() {
1559 let info = ScopeInfo {
1562 scopes: vec![Some(OriginalScope {
1563 start: Position { line: 0, column: 0 },
1564 end: Position { line: 20, column: 0 },
1565 name: None,
1566 kind: None,
1567 is_stack_frame: false,
1568 variables: vec!["v".to_string()],
1569 children: vec![],
1570 })],
1571 ranges: vec![GeneratedRange {
1572 start: Position { line: 0, column: 0 },
1573 end: Position { line: 20, column: 0 },
1574 is_stack_frame: false,
1575 is_hidden: false,
1576 definition: Some(0),
1577 call_site: None,
1578 bindings: vec![Binding::SubRanges(vec![
1579 SubRangeBinding { expression: None, from: Position { line: 0, column: 0 } },
1580 SubRangeBinding {
1581 expression: Some("_v".to_string()),
1582 from: Position { line: 8, column: 0 },
1583 },
1584 ])],
1585 children: vec![],
1586 }],
1587 };
1588
1589 let mut names = vec![];
1590 let encoded = encode_scopes(&info, &mut names);
1591 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
1592
1593 match &decoded.ranges[0].bindings[0] {
1594 Binding::SubRanges(subs) => {
1595 assert_eq!(subs.len(), 2);
1596 assert_eq!(subs[0].expression, None);
1597 assert_eq!(subs[0].from, Position { line: 0, column: 0 });
1598 assert_eq!(subs[1].expression.as_deref(), Some("_v"));
1599 assert_eq!(subs[1].from, Position { line: 8, column: 0 });
1600 }
1601 other => panic!("expected SubRanges, got {other:?}"),
1602 }
1603 }
1604
1605 #[test]
1606 fn comprehensive_roundtrip() {
1607 let info = ScopeInfo {
1610 scopes: vec![
1611 Some(OriginalScope {
1612 start: Position { line: 0, column: 0 },
1613 end: Position { line: 50, column: 0 },
1614 name: None,
1615 kind: Some("module".to_string()),
1616 is_stack_frame: false,
1617 variables: vec!["exports".to_string()],
1618 children: vec![
1619 OriginalScope {
1620 start: Position { line: 2, column: 0 },
1621 end: Position { line: 20, column: 1 },
1622 name: Some("add".to_string()),
1623 kind: Some("function".to_string()),
1624 is_stack_frame: true,
1625 variables: vec!["a".to_string(), "b".to_string()],
1626 children: vec![],
1627 },
1628 OriginalScope {
1629 start: Position { line: 22, column: 0 },
1630 end: Position { line: 40, column: 1 },
1631 name: Some("multiply".to_string()),
1632 kind: Some("function".to_string()),
1633 is_stack_frame: true,
1634 variables: vec!["x".to_string(), "y".to_string()],
1635 children: vec![],
1636 },
1637 ],
1638 }),
1639 None, ],
1641 ranges: vec![GeneratedRange {
1642 start: Position { line: 0, column: 0 },
1643 end: Position { line: 25, column: 0 },
1644 is_stack_frame: false,
1645 is_hidden: false,
1646 definition: Some(0),
1647 call_site: None,
1648 bindings: vec![Binding::Expression("module.exports".to_string())],
1649 children: vec![
1650 GeneratedRange {
1651 start: Position { line: 1, column: 0 },
1652 end: Position { line: 10, column: 0 },
1653 is_stack_frame: true,
1654 is_hidden: false,
1655 definition: Some(1),
1656 call_site: Some(CallSite { source_index: 0, line: 45, column: 2 }),
1657 bindings: vec![Binding::Expression("_a".to_string()), Binding::Unavailable],
1658 children: vec![],
1659 },
1660 GeneratedRange {
1661 start: Position { line: 12, column: 0 },
1662 end: Position { line: 20, column: 0 },
1663 is_stack_frame: true,
1664 is_hidden: true,
1665 definition: Some(2),
1666 call_site: Some(CallSite { source_index: 0, line: 46, column: 0 }),
1667 bindings: vec![
1668 Binding::SubRanges(vec![
1669 SubRangeBinding {
1670 expression: Some("p1".to_string()),
1671 from: Position { line: 12, column: 0 },
1672 },
1673 SubRangeBinding {
1674 expression: Some("p2".to_string()),
1675 from: Position { line: 16, column: 0 },
1676 },
1677 ]),
1678 Binding::Expression("_y".to_string()),
1679 ],
1680 children: vec![],
1681 },
1682 ],
1683 }],
1684 };
1685
1686 let mut names = vec![];
1687 let encoded = encode_scopes(&info, &mut names);
1688 let decoded = decode_scopes(&encoded, &names, 2).unwrap();
1689
1690 assert_eq!(decoded.scopes.len(), 2);
1692 assert!(decoded.scopes[1].is_none());
1693 let root = decoded.scopes[0].as_ref().unwrap();
1694 assert_eq!(root.kind.as_deref(), Some("module"));
1695 assert_eq!(root.children.len(), 2);
1696 assert_eq!(root.children[0].name.as_deref(), Some("add"));
1697 assert_eq!(root.children[1].name.as_deref(), Some("multiply"));
1698
1699 assert_eq!(decoded.ranges.len(), 1);
1701 let outer = &decoded.ranges[0];
1702 assert_eq!(outer.children.len(), 2);
1703
1704 let add_range = &outer.children[0];
1706 assert!(add_range.is_stack_frame);
1707 assert!(!add_range.is_hidden);
1708 assert_eq!(add_range.definition, Some(1));
1709 assert_eq!(add_range.call_site, Some(CallSite { source_index: 0, line: 45, column: 2 }));
1710 assert_eq!(add_range.bindings[0], Binding::Expression("_a".to_string()));
1711 assert_eq!(add_range.bindings[1], Binding::Unavailable);
1712
1713 let mul_range = &outer.children[1];
1715 assert!(mul_range.is_stack_frame);
1716 assert!(mul_range.is_hidden);
1717 assert_eq!(mul_range.definition, Some(2));
1718 match &mul_range.bindings[0] {
1719 Binding::SubRanges(subs) => {
1720 assert_eq!(subs.len(), 2);
1721 assert_eq!(subs[0].expression.as_deref(), Some("p1"));
1722 assert_eq!(subs[1].expression.as_deref(), Some("p2"));
1723 }
1724 other => panic!("expected SubRanges, got {other:?}"),
1725 }
1726 assert_eq!(mul_range.bindings[1], Binding::Expression("_y".to_string()));
1727 }
1728
1729 #[test]
1730 fn range_end_column_only_1vlq() {
1731 let info = ScopeInfo {
1735 scopes: vec![],
1736 ranges: vec![GeneratedRange {
1737 start: Position { line: 0, column: 0 },
1738 end: Position { line: 5, column: 50 },
1739 is_stack_frame: false,
1740 is_hidden: false,
1741 definition: None,
1742 call_site: None,
1743 bindings: vec![],
1744 children: vec![GeneratedRange {
1745 start: Position { line: 2, column: 0 },
1746 end: Position { line: 5, column: 30 },
1747 is_stack_frame: false,
1748 is_hidden: false,
1749 definition: None,
1750 call_site: None,
1751 bindings: vec![],
1752 children: vec![],
1753 }],
1754 }],
1755 };
1756
1757 let mut names = vec![];
1758 let encoded = encode_scopes(&info, &mut names);
1759 let decoded = decode_scopes(&encoded, &names, 0).unwrap();
1760
1761 let outer = &decoded.ranges[0];
1762 assert_eq!(outer.end, Position { line: 5, column: 50 });
1763 let inner = &outer.children[0];
1764 assert_eq!(inner.end, Position { line: 5, column: 30 });
1765 }
1766
1767 #[test]
1768 fn all_sources_none_with_ranges() {
1769 let info = ScopeInfo {
1772 scopes: vec![None, None, None],
1773 ranges: vec![GeneratedRange {
1774 start: Position { line: 0, column: 0 },
1775 end: Position { line: 10, column: 0 },
1776 is_stack_frame: false,
1777 is_hidden: false,
1778 definition: None,
1779 call_site: None,
1780 bindings: vec![],
1781 children: vec![],
1782 }],
1783 };
1784
1785 let mut names = vec![];
1786 let encoded = encode_scopes(&info, &mut names);
1787 let decoded = decode_scopes(&encoded, &names, 3).unwrap();
1788
1789 assert_eq!(decoded.scopes.len(), 3);
1790 assert!(decoded.scopes.iter().all(|s| s.is_none()));
1791 assert_eq!(decoded.ranges.len(), 1);
1792 }
1793
1794 #[test]
1795 fn scope_name_only_no_kind() {
1796 let info = ScopeInfo {
1798 scopes: vec![Some(OriginalScope {
1799 start: Position { line: 0, column: 0 },
1800 end: Position { line: 5, column: 0 },
1801 name: Some("myVar".to_string()),
1802 kind: None,
1803 is_stack_frame: false,
1804 variables: vec![],
1805 children: vec![],
1806 })],
1807 ranges: vec![],
1808 };
1809
1810 let mut names = vec![];
1811 let encoded = encode_scopes(&info, &mut names);
1812 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
1813
1814 let scope = decoded.scopes[0].as_ref().unwrap();
1815 assert_eq!(scope.name.as_deref(), Some("myVar"));
1816 assert_eq!(scope.kind, None);
1817 }
1818
1819 #[test]
1820 fn generated_range_with_definition_on_nonzero_line() {
1821 let info = ScopeInfo {
1824 scopes: vec![Some(OriginalScope {
1825 start: Position { line: 0, column: 0 },
1826 end: Position { line: 50, column: 0 },
1827 name: None,
1828 kind: None,
1829 is_stack_frame: false,
1830 variables: vec![],
1831 children: vec![],
1832 })],
1833 ranges: vec![GeneratedRange {
1834 start: Position { line: 10, column: 5 },
1835 end: Position { line: 40, column: 0 },
1836 is_stack_frame: true,
1837 is_hidden: false,
1838 definition: Some(0),
1839 call_site: None,
1840 bindings: vec![],
1841 children: vec![],
1842 }],
1843 };
1844
1845 let mut names = vec![];
1846 let encoded = encode_scopes(&info, &mut names);
1847 let decoded = decode_scopes(&encoded, &names, 1).unwrap();
1848
1849 let range = &decoded.ranges[0];
1850 assert_eq!(range.start, Position { line: 10, column: 5 });
1851 assert_eq!(range.end, Position { line: 40, column: 0 });
1852 assert!(range.is_stack_frame);
1853 assert_eq!(range.definition, Some(0));
1854 }
1855
1856 #[test]
1859 fn decode_unmatched_scope_end() {
1860 let raw = "CAA";
1863 let names: Vec<String> = vec![];
1864 let err = decode_scopes(raw, &names, 1).unwrap_err();
1865 assert!(matches!(err, ScopesError::UnmatchedScopeEnd));
1866 }
1867
1868 #[test]
1869 fn decode_unclosed_scope() {
1870 let raw = "BAAA";
1873 let names: Vec<String> = vec![];
1874 let err = decode_scopes(raw, &names, 1).unwrap_err();
1875 assert!(matches!(err, ScopesError::UnclosedScope));
1876 }
1877
1878 #[test]
1879 fn decode_unmatched_range_end() {
1880 let raw = ",FA";
1884 let names: Vec<String> = vec![];
1885 let err = decode_scopes(raw, &names, 1).unwrap_err();
1886 assert!(matches!(err, ScopesError::UnmatchedRangeEnd));
1887 }
1888
1889 #[test]
1890 fn decode_unclosed_range() {
1891 let raw = ",EAAA";
1894 let names: Vec<String> = vec![];
1895 let err = decode_scopes(raw, &names, 1).unwrap_err();
1896 assert!(matches!(err, ScopesError::UnclosedRange));
1897 }
1898}