1use std::{
2 borrow::Cow,
3 cmp, fmt,
4 hash::{Hash, Hasher},
5 ops::{Add, Sub},
6 path::PathBuf,
7 sync::{atomic::AtomicU32, Mutex},
8};
9
10use bytes_str::BytesStr;
11use serde::{Deserialize, Serialize};
12use url::Url;
13
14use self::hygiene::MarkData;
15pub use self::hygiene::{Mark, SyntaxContext};
16use crate::{cache::CacheCell, rustc_data_structures::stable_hasher::StableHasher, sync::Lrc};
17
18mod analyze_source_file;
19pub mod hygiene;
20
21#[derive(Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)]
32#[cfg_attr(
33 feature = "encoding-impl",
34 derive(::ast_node::Encode, ::ast_node::Decode)
35)]
36#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
37pub struct Span {
38 #[serde(rename = "start")]
39 pub lo: BytePos,
40 #[serde(rename = "end")]
41 pub hi: BytePos,
42}
43
44impl std::fmt::Debug for Span {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 write!(f, "{}..{}", self.lo.0, self.hi.0,)
47 }
48}
49
50impl From<(BytePos, BytePos)> for Span {
51 #[inline]
52 fn from(sp: (BytePos, BytePos)) -> Self {
53 Span::new(sp.0, sp.1)
54 }
55}
56
57impl From<Span> for (BytePos, BytePos) {
58 #[inline]
59 fn from(sp: Span) -> Self {
60 (sp.lo, sp.hi)
61 }
62}
63
64#[cfg(feature = "arbitrary")]
65#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
66impl<'a> arbitrary::Arbitrary<'a> for Span {
67 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
68 let lo = u.arbitrary::<BytePos>()?;
69 let hi = u.arbitrary::<BytePos>()?;
70
71 Ok(Self::new(lo, hi))
72 }
73}
74
75pub const DUMMY_SP: Span = Span {
78 lo: BytePos::DUMMY,
79 hi: BytePos::DUMMY,
80};
81
82pub const PURE_SP: Span = Span {
84 lo: BytePos::PURE,
85 hi: BytePos::PURE,
86};
87
88pub const PLACEHOLDER_SP: Span = Span {
90 lo: BytePos::PLACEHOLDER,
91 hi: BytePos::PLACEHOLDER,
92};
93
94pub struct Globals {
95 hygiene_data: Mutex<hygiene::HygieneData>,
96 #[allow(unused)]
97 dummy_cnt: AtomicU32,
98 #[allow(unused)]
99 marks: Mutex<Vec<MarkData>>,
100}
101
102const DUMMY_RESERVE: u32 = u32::MAX - 2_u32.pow(16);
103
104impl Default for Globals {
105 fn default() -> Self {
106 Self::new()
107 }
108}
109
110impl Globals {
111 pub fn new() -> Globals {
112 Globals {
113 hygiene_data: Mutex::new(hygiene::HygieneData::new()),
114 marks: Mutex::new(vec![MarkData {
115 parent: Mark::root(),
116 }]),
117 dummy_cnt: AtomicU32::new(DUMMY_RESERVE),
118 }
119 }
120
121 pub fn clone_data(&self) -> Self {
125 Globals {
126 hygiene_data: Mutex::new(self.hygiene_data.lock().unwrap().clone()),
127 marks: Mutex::new(self.marks.lock().unwrap().clone()),
128 dummy_cnt: AtomicU32::new(self.dummy_cnt.load(std::sync::atomic::Ordering::SeqCst)),
129 }
130 }
131}
132
133better_scoped_tls::scoped_tls!(
134
135 pub static GLOBALS: Globals
160);
161
162#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
163pub enum FileName {
164 Real(PathBuf),
165 Macros(String),
168 QuoteExpansion,
170 Anon,
172 MacroExpansion,
174 ProcMacroSourceCode,
175 Url(Url),
176 Internal(String),
177 Custom(String),
179}
180
181#[cfg(feature = "encoding-impl")]
182impl cbor4ii::core::enc::Encode for FileName {
183 #[inline]
184 fn encode<W: cbor4ii::core::enc::Write>(
185 &self,
186 writer: &mut W,
187 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
188 use cbor4ii::core::types::{Array, Nothing, Tag};
189
190 match self {
191 FileName::Real(name) => {
192 let name = name.to_str().unwrap();
193 Tag(1, name).encode(writer)?;
194 }
195 FileName::Macros(name) => Tag(2, name).encode(writer)?,
196 FileName::QuoteExpansion => {
197 Tag(3, Nothing).encode(writer)?;
198 Array::bounded(0, writer)?;
199 }
200 FileName::Anon => {
201 Tag(4, Nothing).encode(writer)?;
202 Array::bounded(0, writer)?;
203 }
204 FileName::MacroExpansion => {
205 Tag(5, Nothing).encode(writer)?;
206 Array::bounded(0, writer)?;
207 }
208 FileName::ProcMacroSourceCode => {
209 Tag(6, Nothing).encode(writer)?;
210 Array::bounded(0, writer)?;
211 }
212 FileName::Url(name) => Tag(7, name.as_str()).encode(writer)?,
213 FileName::Internal(name) => Tag(8, name).encode(writer)?,
214 FileName::Custom(name) => Tag(9, name).encode(writer)?,
215 }
216
217 Ok(())
218 }
219}
220
221#[cfg(feature = "encoding-impl")]
222impl<'de> cbor4ii::core::dec::Decode<'de> for FileName {
223 #[inline]
224 fn decode<R: cbor4ii::core::dec::Read<'de>>(
225 reader: &mut R,
226 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
227 use cbor4ii::core::types::{Array, Tag};
228
229 let tag = Tag::tag(reader)?;
230 match tag {
231 1 => {
232 let name = String::decode(reader)?;
233 Ok(FileName::Real(PathBuf::from(name)))
234 }
235 2 => {
236 let name = String::decode(reader)?;
237 Ok(FileName::Macros(name))
238 }
239 3 => {
240 let n = Array::len(reader)?;
241 debug_assert_eq!(n, Some(0));
242 Ok(FileName::QuoteExpansion)
243 }
244 4 => {
245 let n = Array::len(reader)?;
246 debug_assert_eq!(n, Some(0));
247 Ok(FileName::Anon)
248 }
249 5 => {
250 let n = Array::len(reader)?;
251 debug_assert_eq!(n, Some(0));
252 Ok(FileName::MacroExpansion)
253 }
254 6 => {
255 let n = Array::len(reader)?;
256 debug_assert_eq!(n, Some(0));
257 Ok(FileName::ProcMacroSourceCode)
258 }
259 7 => {
260 let name = <&str>::decode(reader)?;
261 Ok(FileName::Url(Url::parse(name).unwrap()))
262 }
263 8 => {
264 let name = String::decode(reader)?;
265 Ok(FileName::Internal(name))
266 }
267 9 => {
268 let name = String::decode(reader)?;
269 Ok(FileName::Custom(name))
270 }
271 tag => Err(cbor4ii::core::error::DecodeError::Custom {
272 name: &"FileName",
273 num: tag as u32,
274 }),
275 }
276 }
277}
278
279impl std::fmt::Display for FileName {
280 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 match *self {
282 FileName::Real(ref path) => write!(fmt, "{}", path.display()),
283 FileName::Macros(ref name) => write!(fmt, "<{name} macros>"),
284 FileName::QuoteExpansion => write!(fmt, "<quote expansion>"),
285 FileName::MacroExpansion => write!(fmt, "<macro expansion>"),
286 FileName::Anon => write!(fmt, "<anon>"),
287 FileName::ProcMacroSourceCode => write!(fmt, "<proc-macro source code>"),
288 FileName::Url(ref u) => write!(fmt, "{u}"),
289 FileName::Custom(ref s) => {
290 write!(fmt, "{s}")
291 }
292 FileName::Internal(ref s) => write!(fmt, "<{s}>"),
293 }
294 }
295}
296
297impl From<PathBuf> for FileName {
298 fn from(p: PathBuf) -> Self {
299 assert!(!p.to_string_lossy().ends_with('>'));
300 FileName::Real(p)
301 }
302}
303
304impl From<Url> for FileName {
305 fn from(url: Url) -> Self {
306 FileName::Url(url)
307 }
308}
309
310impl FileName {
311 pub fn is_real(&self) -> bool {
312 match *self {
313 FileName::Real(_) => true,
314 FileName::Macros(_)
315 | FileName::Anon
316 | FileName::MacroExpansion
317 | FileName::ProcMacroSourceCode
318 | FileName::Custom(_)
319 | FileName::QuoteExpansion
320 | FileName::Internal(_)
321 | FileName::Url(_) => false,
322 }
323 }
324
325 pub fn is_macros(&self) -> bool {
326 match *self {
327 FileName::Real(_)
328 | FileName::Anon
329 | FileName::MacroExpansion
330 | FileName::ProcMacroSourceCode
331 | FileName::Custom(_)
332 | FileName::QuoteExpansion
333 | FileName::Internal(_)
334 | FileName::Url(_) => false,
335 FileName::Macros(_) => true,
336 }
337 }
338}
339
340#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
341#[cfg_attr(
342 feature = "diagnostic-serde",
343 derive(serde::Serialize, serde::Deserialize)
344)]
345#[cfg_attr(
346 feature = "encoding-impl",
347 derive(::ast_node::Encode, ::ast_node::Decode)
348)]
349pub struct PrimarySpanLabel(pub Span, pub String);
350
351#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
358#[cfg_attr(
359 feature = "diagnostic-serde",
360 derive(serde::Serialize, serde::Deserialize)
361)]
362#[cfg_attr(
363 feature = "encoding-impl",
364 derive(::ast_node::Encode, ::ast_node::Decode)
365)]
366pub struct MultiSpan {
367 primary_spans: Vec<Span>,
368 span_labels: Vec<PrimarySpanLabel>,
369}
370
371#[cfg_attr(target_arch = "wasm32", link(wasm_import_module = "env"))]
372extern "C" {
373 fn __span_dummy_with_cmt_proxy() -> u32;
374}
375
376impl Span {
377 #[inline]
378 pub fn lo(self) -> BytePos {
379 self.lo
380 }
381
382 #[inline]
383 pub fn new(mut lo: BytePos, mut hi: BytePos) -> Self {
384 if lo > hi {
385 std::mem::swap(&mut lo, &mut hi);
386 }
387
388 Span { lo, hi }
389 }
390
391 #[inline]
392 #[track_caller]
393 pub fn new_with_checked(lo: BytePos, hi: BytePos) -> Self {
394 debug_assert!(lo <= hi, "lo: {lo:#?}, hi: {hi:#?}");
395 Span { lo, hi }
396 }
397
398 #[inline]
399 pub fn with_lo(&self, lo: BytePos) -> Span {
400 Span::new(lo, self.hi)
401 }
402
403 #[inline(always)]
404 pub fn hi(self) -> BytePos {
405 self.hi
406 }
407
408 #[inline]
409 pub fn with_hi(&self, hi: BytePos) -> Span {
410 Span::new(self.lo, hi)
411 }
412
413 #[inline]
415 pub fn is_dummy(self) -> bool {
416 self.lo.0 == 0 && self.hi.0 == 0 || self.lo.0 >= DUMMY_RESERVE
417 }
418
419 #[inline]
420 pub fn is_pure(self) -> bool {
421 self.lo.is_pure()
422 }
423
424 #[inline]
425 pub fn is_placeholder(self) -> bool {
426 self.lo.is_placeholder()
427 }
428
429 #[inline]
431 pub fn is_dummy_ignoring_cmt(self) -> bool {
432 self.lo.0 == 0 && self.hi.0 == 0
433 }
434
435 #[inline]
438 pub fn shrink_to_lo(self) -> Span {
439 self.with_hi(self.lo)
440 }
441
442 #[inline]
444 pub fn shrink_to_hi(self) -> Span {
445 self.with_lo(self.hi)
446 }
447
448 pub fn substitute_dummy(self, other: Span) -> Span {
450 if self.is_dummy() {
451 other
452 } else {
453 self
454 }
455 }
456
457 pub fn contains(self, other: Span) -> bool {
459 self.lo <= other.lo && other.hi <= self.hi
460 }
461
462 pub fn source_equal(self, other: Span) -> bool {
467 self.lo == other.lo && self.hi == other.hi
468 }
469
470 pub fn trim_start(self, other: Span) -> Option<Span> {
472 if self.hi > other.hi {
473 Some(self.with_lo(cmp::max(self.lo, other.hi)))
474 } else {
475 None
476 }
477 }
478
479 pub fn to(self, end: Span) -> Span {
481 let span_data = self;
482 let end_data = end;
483 Span::new(
489 cmp::min(span_data.lo, end_data.lo),
490 cmp::max(span_data.hi, end_data.hi),
491 )
492 }
493
494 pub fn between(self, end: Span) -> Span {
496 let span = self;
497 Span::new(span.hi, end.lo)
498 }
499
500 pub fn until(self, end: Span) -> Span {
503 let span = self;
504 Span::new(span.lo, end.lo)
505 }
506
507 pub fn from_inner_byte_pos(self, start: usize, end: usize) -> Span {
508 let span = self;
509 Span::new(
510 span.lo + BytePos::from_usize(start),
511 span.lo + BytePos::from_usize(end),
512 )
513 }
514
515 pub fn dummy_with_cmt() -> Self {
518 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
519 {
520 let lo = BytePos(unsafe { __span_dummy_with_cmt_proxy() });
521
522 return Span { lo, hi: lo };
523 }
524
525 #[cfg(not(all(any(feature = "__plugin_mode"), target_arch = "wasm32")))]
526 return GLOBALS.with(|globals| {
527 let lo = BytePos(
528 globals
529 .dummy_cnt
530 .fetch_add(1, std::sync::atomic::Ordering::SeqCst),
531 );
532 Span { lo, hi: lo }
533 });
534 }
535}
536
537#[derive(Clone, Debug)]
538pub struct SpanLabel {
539 pub span: Span,
541
542 pub is_primary: bool,
545
546 pub label: Option<String>,
548}
549
550impl Default for Span {
551 fn default() -> Self {
552 DUMMY_SP
553 }
554}
555
556impl MultiSpan {
557 #[inline]
558 pub fn new() -> MultiSpan {
559 Self::default()
560 }
561
562 pub fn from_span(primary_span: Span) -> MultiSpan {
563 MultiSpan {
564 primary_spans: vec![primary_span],
565 span_labels: Vec::new(),
566 }
567 }
568
569 pub fn from_spans(vec: Vec<Span>) -> MultiSpan {
570 MultiSpan {
571 primary_spans: vec,
572 span_labels: Vec::new(),
573 }
574 }
575
576 pub fn push_span_label(&mut self, span: Span, label: String) {
577 self.span_labels.push(PrimarySpanLabel(span, label));
578 }
579
580 pub fn primary_span(&self) -> Option<Span> {
582 self.primary_spans.first().cloned()
583 }
584
585 pub fn primary_spans(&self) -> &[Span] {
587 &self.primary_spans
588 }
589
590 pub fn is_dummy(&self) -> bool {
593 let mut is_dummy = true;
594 for span in &self.primary_spans {
595 if !span.is_dummy() {
596 is_dummy = false;
597 }
598 }
599 is_dummy
600 }
601
602 pub fn replace(&mut self, before: Span, after: Span) -> bool {
606 let mut replacements_occurred = false;
607 for primary_span in &mut self.primary_spans {
608 if *primary_span == before {
609 *primary_span = after;
610 replacements_occurred = true;
611 }
612 }
613 for span_label in &mut self.span_labels {
614 if span_label.0 == before {
615 span_label.0 = after;
616 replacements_occurred = true;
617 }
618 }
619 replacements_occurred
620 }
621
622 pub fn span_labels(&self) -> Vec<SpanLabel> {
628 let is_primary = |span| self.primary_spans.contains(&span);
629
630 let mut span_labels = self
631 .span_labels
632 .iter()
633 .map(|&PrimarySpanLabel(span, ref label)| SpanLabel {
634 span,
635 is_primary: is_primary(span),
636 label: Some(label.clone()),
637 })
638 .collect::<Vec<_>>();
639
640 for &span in &self.primary_spans {
641 if !span_labels.iter().any(|sl| sl.span == span) {
642 span_labels.push(SpanLabel {
643 span,
644 is_primary: true,
645 label: None,
646 });
647 }
648 }
649
650 span_labels
651 }
652}
653
654impl From<Span> for MultiSpan {
655 fn from(span: Span) -> MultiSpan {
656 MultiSpan::from_span(span)
657 }
658}
659
660impl From<Vec<Span>> for MultiSpan {
661 fn from(spans: Vec<Span>) -> MultiSpan {
662 MultiSpan::from_spans(spans)
663 }
664}
665
666pub const NO_EXPANSION: SyntaxContext = SyntaxContext::empty();
667
668#[derive(Copy, Clone, Eq, PartialEq, Debug)]
670pub struct MultiByteChar {
671 pub pos: BytePos,
673 pub bytes: u8,
675}
676
677impl MultiByteChar {
678 pub fn byte_to_char_diff(&self) -> u8 {
684 if self.bytes == 4 {
685 2
686 } else {
687 self.bytes - 1
688 }
689 }
690}
691
692#[derive(Copy, Clone, Eq, PartialEq, Debug)]
694pub enum NonNarrowChar {
695 ZeroWidth(BytePos),
697 Wide(BytePos, usize),
699 Tab(BytePos),
702}
703
704impl NonNarrowChar {
705 fn new(pos: BytePos, width: usize) -> Self {
706 match width {
707 0 => NonNarrowChar::ZeroWidth(pos),
708 4 => NonNarrowChar::Tab(pos),
709 w => NonNarrowChar::Wide(pos, w),
710 }
711 }
712
713 pub fn pos(self) -> BytePos {
715 match self {
716 NonNarrowChar::ZeroWidth(p) | NonNarrowChar::Wide(p, _) | NonNarrowChar::Tab(p) => p,
717 }
718 }
719
720 pub fn width(self) -> usize {
722 match self {
723 NonNarrowChar::ZeroWidth(_) => 0,
724 NonNarrowChar::Wide(_, width) => width,
725 NonNarrowChar::Tab(_) => 4,
726 }
727 }
728}
729
730impl Add<BytePos> for NonNarrowChar {
731 type Output = Self;
732
733 fn add(self, rhs: BytePos) -> Self {
734 match self {
735 NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos + rhs),
736 NonNarrowChar::Wide(pos, width) => NonNarrowChar::Wide(pos + rhs, width),
737 NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos + rhs),
738 }
739 }
740}
741
742impl Sub<BytePos> for NonNarrowChar {
743 type Output = Self;
744
745 fn sub(self, rhs: BytePos) -> Self {
746 match self {
747 NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos - rhs),
748 NonNarrowChar::Wide(pos, width) => NonNarrowChar::Wide(pos - rhs, width),
749 NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos - rhs),
750 }
751 }
752}
753
754#[derive(Clone)]
756pub struct SourceFile {
757 pub name: Lrc<FileName>,
761 pub name_was_remapped: bool,
764 pub unmapped_path: Option<Lrc<FileName>>,
767 pub crate_of_origin: u32,
769 pub src: BytesStr,
771 pub start_pos: BytePos,
773 pub end_pos: BytePos,
775
776 lazy: CacheCell<SourceFileAnalysis>,
777}
778
779#[derive(Clone)]
780pub struct SourceFileAnalysis {
781 pub src_hash: u128,
783 pub name_hash: u128,
785 pub lines: Vec<BytePos>,
787 pub multibyte_chars: Vec<MultiByteChar>,
789 pub non_narrow_chars: Vec<NonNarrowChar>,
791}
792
793fn stable_src_hash(src: &BytesStr) -> u128 {
794 let mut hasher = StableHasher::new();
795 hasher.write(src.as_bytes());
796 hasher.finish()
797}
798
799fn stable_name_hash(name: &FileName) -> u128 {
800 let mut hasher = StableHasher::new();
801 name.hash(&mut hasher);
802 hasher.finish()
803}
804
805#[cfg(feature = "encoding-impl")]
806const SOURCE_FILE_WIRE_FIELD_COUNT: usize = 10;
807
808#[cfg(feature = "encoding-impl")]
809impl cbor4ii::core::enc::Encode for SourceFile {
810 #[inline]
811 fn encode<W: cbor4ii::core::enc::Write>(
812 &self,
813 writer: &mut W,
814 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
815 cbor4ii::core::types::Array::<()>::bounded(SOURCE_FILE_WIRE_FIELD_COUNT, writer)?;
826
827 encoding_helper::LrcHelper(&self.name).encode(writer)?;
828 self.name_was_remapped.encode(writer)?;
829 encoding_helper::LrcHelper(&self.unmapped_path).encode(writer)?;
830 self.crate_of_origin.encode(writer)?;
831 encoding_helper::Str(&self.src).encode(writer)?;
832 stable_src_hash(&self.src).encode(writer)?;
833 self.start_pos.encode(writer)?;
834 self.end_pos.encode(writer)?;
835 stable_name_hash(&self.name).encode(writer)?;
836
837 Ok(())
838 }
839}
840
841#[cfg(feature = "encoding-impl")]
842impl<'de> cbor4ii::core::dec::Decode<'de> for SourceFile {
843 #[inline]
844 fn decode<R: cbor4ii::core::dec::Read<'de>>(
845 reader: &mut R,
846 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
847 let len = cbor4ii::core::types::Array::<()>::len(reader)?.unwrap();
848 if len < SOURCE_FILE_WIRE_FIELD_COUNT {
849 return Err(cbor4ii::core::error::DecodeError::Custom {
850 name: &"SourceFile",
851 num: len as u32,
852 });
853 }
854
855 let name = encoding_helper::LrcHelper::<Lrc<FileName>>::decode(reader)?.0;
856 let name_was_remapped = bool::decode(reader)?;
857 let unmapped_path = encoding_helper::LrcHelper::<Option<Lrc<FileName>>>::decode(reader)?.0;
858 let crate_of_origin = u32::decode(reader)?;
859 let src = encoding_helper::Str::<BytesStr>::decode(reader)?.0;
860 let _src_hash = u128::decode(reader)?;
861 let start_pos = BytePos::decode(reader)?;
862 let end_pos = BytePos::decode(reader)?;
863 let _name_hash = u128::decode(reader)?;
864
865 for _ in 0..len - SOURCE_FILE_WIRE_FIELD_COUNT {
866 cbor4ii::core::dec::IgnoredAny::decode(reader)?;
867 }
868
869 Ok(SourceFile {
870 name,
871 name_was_remapped,
872 unmapped_path,
873 crate_of_origin,
874 src,
875 start_pos,
876 end_pos,
877 lazy: CacheCell::new(),
878 })
879 }
880}
881
882impl fmt::Debug for SourceFile {
883 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
884 write!(fmt, "SourceFile({})", self.name)
885 }
886}
887
888impl SourceFile {
889 pub fn new(
891 name: Lrc<FileName>,
892 name_was_remapped: bool,
893 unmapped_path: Lrc<FileName>,
894 src: BytesStr,
895 start_pos: BytePos,
896 ) -> SourceFile {
897 debug_assert_ne!(
898 start_pos,
899 BytePos::DUMMY,
900 "BytePos::DUMMY is reserved and `SourceFile` should not use it"
901 );
902
903 let end_pos = start_pos.to_usize() + src.len();
904
905 SourceFile {
906 name,
907 name_was_remapped,
908 unmapped_path: Some(unmapped_path),
909 crate_of_origin: 0,
910 src,
911 start_pos,
912 end_pos: SmallPos::from_usize(end_pos),
913 lazy: CacheCell::new(),
914 }
915 }
916
917 pub fn src_hash(&self) -> u128 {
919 self.analyze().src_hash
920 }
921
922 pub fn name_hash(&self) -> u128 {
924 self.analyze().name_hash
925 }
926
927 pub fn line_begin_pos(&self, pos: BytePos) -> BytePos {
929 let line_index = self.lookup_line(pos).unwrap();
930 let analysis = self.analyze();
931 analysis.lines[line_index]
932 }
933
934 pub fn get_line(&self, line_number: usize) -> Option<Cow<'_, str>> {
937 fn get_until_newline(src: &str, begin: usize) -> &str {
938 let slice = &src[begin..];
942 match slice.find('\n') {
943 Some(e) => &slice[..e],
944 None => slice,
945 }
946 }
947
948 let begin = {
949 let analysis = self.analyze();
950 let line = analysis.lines.get(line_number)?;
951 let begin: BytePos = *line - self.start_pos;
952 begin.to_usize()
953 };
954
955 Some(Cow::from(get_until_newline(&self.src, begin)))
956 }
957
958 pub fn is_real_file(&self) -> bool {
959 self.name.is_real()
960 }
961
962 pub fn byte_length(&self) -> u32 {
963 self.end_pos.0 - self.start_pos.0
964 }
965
966 pub fn count_lines(&self) -> usize {
967 let analysis = self.analyze();
968 analysis.lines.len()
969 }
970
971 pub fn lookup_line(&self, pos: BytePos) -> Option<usize> {
976 let analysis = self.analyze();
977 if analysis.lines.is_empty() {
978 return None;
979 }
980
981 let line_index = lookup_line(&analysis.lines, pos);
982 assert!(line_index < analysis.lines.len() as isize);
983 if line_index >= 0 {
984 Some(line_index as usize)
985 } else {
986 None
987 }
988 }
989
990 pub fn line_bounds(&self, line_index: usize) -> (BytePos, BytePos) {
991 if self.start_pos == self.end_pos {
992 return (self.start_pos, self.end_pos);
993 }
994
995 let analysis = self.analyze();
996
997 assert!(line_index < analysis.lines.len());
998 if line_index == (analysis.lines.len() - 1) {
999 (analysis.lines[line_index], self.end_pos)
1000 } else {
1001 (analysis.lines[line_index], analysis.lines[line_index + 1])
1002 }
1003 }
1004
1005 #[inline]
1006 pub fn contains(&self, byte_pos: BytePos) -> bool {
1007 byte_pos >= self.start_pos && byte_pos <= self.end_pos
1008 }
1009
1010 pub fn analyze(&self) -> &SourceFileAnalysis {
1011 self.lazy.get_or_init(|| {
1012 let (lines, multibyte_chars, non_narrow_chars) =
1013 analyze_source_file::analyze_source_file(&self.src[..], self.start_pos);
1014 SourceFileAnalysis {
1015 lines,
1016 multibyte_chars,
1017 non_narrow_chars,
1018 src_hash: stable_src_hash(&self.src),
1019 name_hash: stable_name_hash(&self.name),
1020 }
1021 })
1022 }
1023}
1024
1025pub trait SmallPos {
1030 fn from_usize(n: usize) -> Self;
1031 fn to_usize(&self) -> usize;
1032 fn from_u32(n: u32) -> Self;
1033 fn to_u32(&self) -> u32;
1034}
1035
1036#[derive(
1049 Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize, Default,
1050)]
1051#[serde(transparent)]
1052#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1053#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
1054pub struct BytePos(pub u32);
1055
1056#[cfg(feature = "encoding-impl")]
1057impl cbor4ii::core::enc::Encode for BytePos {
1058 #[inline]
1059 fn encode<W: cbor4ii::core::enc::Write>(
1060 &self,
1061 writer: &mut W,
1062 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
1063 self.0.encode(writer)
1064 }
1065}
1066
1067#[cfg(feature = "encoding-impl")]
1068impl<'de> cbor4ii::core::dec::Decode<'de> for BytePos {
1069 #[inline]
1070 fn decode<R: cbor4ii::core::dec::Read<'de>>(
1071 reader: &mut R,
1072 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
1073 u32::decode(reader).map(BytePos)
1074 }
1075}
1076
1077impl BytePos {
1078 pub const DUMMY: Self = BytePos(0);
1080 const MIN_RESERVED: Self = BytePos(DUMMY_RESERVE);
1081 pub const PLACEHOLDER: Self = BytePos(u32::MAX - 2);
1084 pub const PURE: Self = BytePos(u32::MAX - 1);
1086 pub const SYNTHESIZED: Self = BytePos(u32::MAX);
1088
1089 pub const fn is_reserved_for_comments(self) -> bool {
1090 self.0 >= Self::MIN_RESERVED.0 && self.0 != u32::MAX
1091 }
1092
1093 pub const fn is_dummy(self) -> bool {
1096 self.0 == 0
1097 }
1098
1099 pub const fn is_pure(self) -> bool {
1100 self.0 == Self::PURE.0
1101 }
1102
1103 pub const fn is_placeholder(self) -> bool {
1104 self.0 == Self::PLACEHOLDER.0
1105 }
1106
1107 pub const fn can_have_comment(self) -> bool {
1110 self.0 != 0
1111 }
1112}
1113
1114#[cfg_attr(feature = "encoding-impl", derive(crate::Encode, crate::Decode))]
1118#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
1119pub struct CharPos(
1120 #[cfg_attr(feature = "encoding-impl", encoding(with = "encoding_helper::Usize"))] pub usize,
1121);
1122
1123impl SmallPos for BytePos {
1127 #[inline(always)]
1128 fn from_usize(n: usize) -> BytePos {
1129 BytePos(n as u32)
1130 }
1131
1132 #[inline(always)]
1133 fn to_usize(&self) -> usize {
1134 self.0 as usize
1135 }
1136
1137 #[inline(always)]
1138 fn from_u32(n: u32) -> BytePos {
1139 BytePos(n)
1140 }
1141
1142 #[inline(always)]
1143 fn to_u32(&self) -> u32 {
1144 self.0
1145 }
1146}
1147
1148impl Add for BytePos {
1149 type Output = BytePos;
1150
1151 #[inline(always)]
1152 fn add(self, rhs: BytePos) -> BytePos {
1153 BytePos((self.to_usize() + rhs.to_usize()) as u32)
1154 }
1155}
1156
1157impl Sub for BytePos {
1158 type Output = BytePos;
1159
1160 #[inline(always)]
1161 fn sub(self, rhs: BytePos) -> BytePos {
1162 BytePos((self.to_usize() - rhs.to_usize()) as u32)
1163 }
1164}
1165
1166impl SmallPos for CharPos {
1167 #[inline(always)]
1168 fn from_usize(n: usize) -> CharPos {
1169 CharPos(n)
1170 }
1171
1172 #[inline(always)]
1173 fn to_usize(&self) -> usize {
1174 self.0
1175 }
1176
1177 #[inline(always)]
1178 fn from_u32(n: u32) -> CharPos {
1179 CharPos(n as usize)
1180 }
1181
1182 #[inline(always)]
1183 fn to_u32(&self) -> u32 {
1184 self.0 as u32
1185 }
1186}
1187
1188impl Add for CharPos {
1189 type Output = CharPos;
1190
1191 #[inline(always)]
1192 fn add(self, rhs: CharPos) -> CharPos {
1193 CharPos(self.to_usize() + rhs.to_usize())
1194 }
1195}
1196
1197impl Sub for CharPos {
1198 type Output = CharPos;
1199
1200 #[inline(always)]
1201 fn sub(self, rhs: CharPos) -> CharPos {
1202 CharPos(self.to_usize() - rhs.to_usize())
1203 }
1204}
1205
1206#[derive(Debug, Clone)]
1217pub struct Loc {
1218 pub file: Lrc<SourceFile>,
1220 pub line: usize,
1222 pub col: CharPos,
1224 pub col_display: usize,
1226}
1227
1228#[cfg_attr(feature = "encoding-impl", derive(crate::Encode, crate::Decode))]
1231pub struct PartialLoc {
1232 #[cfg_attr(
1233 feature = "encoding-impl",
1234 encoding(with = "encoding_helper::LrcHelper")
1235 )]
1236 pub source_file: Option<Lrc<SourceFile>>,
1237 #[cfg_attr(feature = "encoding-impl", encoding(with = "encoding_helper::Usize"))]
1238 pub line: usize,
1239 #[cfg_attr(feature = "encoding-impl", encoding(with = "encoding_helper::Usize"))]
1240 pub col: usize,
1241 #[cfg_attr(feature = "encoding-impl", encoding(with = "encoding_helper::Usize"))]
1242 pub col_display: usize,
1243}
1244
1245#[derive(Debug)]
1249pub struct LocWithOpt {
1250 pub filename: Lrc<FileName>,
1251 pub line: usize,
1252 pub col: CharPos,
1253 pub file: Option<Lrc<SourceFile>>,
1254}
1255
1256#[derive(Debug)]
1258pub struct SourceFileAndLine {
1259 pub sf: Lrc<SourceFile>,
1260 pub line: usize,
1261}
1262
1263#[cfg_attr(
1264 feature = "encoding-impl",
1265 derive(::ast_node::Encode, ::ast_node::Decode)
1266)]
1267#[derive(Debug)]
1268pub struct SourceFileAndBytePos {
1269 #[cfg_attr(
1270 feature = "encoding-impl",
1271 encoding(with = "encoding_helper::LrcHelper")
1272 )]
1273 pub sf: Lrc<SourceFile>,
1274 pub pos: BytePos,
1275}
1276
1277#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1278#[cfg_attr(feature = "encoding-impl", derive(crate::Encode, crate::Decode))]
1279pub struct LineInfo {
1280 #[cfg_attr(feature = "encoding-impl", encoding(with = "encoding_helper::Usize"))]
1282 pub line_index: usize,
1283
1284 pub start_col: CharPos,
1286
1287 pub end_col: CharPos,
1289}
1290
1291#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1293pub struct LineCol {
1294 pub line: u32,
1296
1297 pub col: u32,
1299}
1300
1301pub struct FileLines {
1308 pub file: Lrc<SourceFile>,
1309 pub lines: Vec<LineInfo>,
1310}
1311
1312#[cfg_attr(
1315 feature = "encoding-impl",
1316 derive(::ast_node::Encode, ::ast_node::Decode)
1317)]
1318pub struct PartialFileLines {
1319 #[cfg_attr(
1320 feature = "encoding-impl",
1321 encoding(with = "encoding_helper::LrcHelper")
1322 )]
1323 pub file: Option<Lrc<SourceFile>>,
1324 pub lines: Vec<LineInfo>,
1325}
1326
1327pub type FileLinesResult = Result<FileLines, Box<SpanLinesError>>;
1333#[cfg(feature = "__plugin")]
1334pub type PartialFileLinesResult = Result<PartialFileLines, Box<SpanLinesError>>;
1335
1336#[derive(Clone, PartialEq, Eq, Debug)]
1337#[cfg_attr(
1338 feature = "encoding-impl",
1339 derive(::ast_node::Encode, ::ast_node::Decode)
1340)]
1341pub enum SpanLinesError {
1342 IllFormedSpan(Span),
1343 DistinctSources(DistinctSources),
1344}
1345
1346#[derive(Clone, PartialEq, Eq, Debug)]
1347pub enum SpanSnippetError {
1348 DummyBytePos,
1349 IllFormedSpan(Span),
1350 DistinctSources(DistinctSources),
1351 MalformedForSourcemap(MalformedSourceMapPositions),
1352 SourceNotAvailable { filename: FileName },
1353 LookupFailed(SourceMapLookupError),
1354}
1355
1356#[cfg(feature = "encoding-impl")]
1357impl cbor4ii::core::enc::Encode for SpanSnippetError {
1358 #[inline]
1359 fn encode<W: cbor4ii::core::enc::Write>(
1360 &self,
1361 writer: &mut W,
1362 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
1363 use cbor4ii::core::types::{Array, Nothing, Tag};
1364
1365 match self {
1366 SpanSnippetError::DummyBytePos => {
1367 Tag(1, Nothing).encode(writer)?;
1368 Array::bounded(0, writer)
1369 }
1370 SpanSnippetError::IllFormedSpan(span) => Tag(2, span).encode(writer),
1371 SpanSnippetError::DistinctSources(src) => Tag(3, src).encode(writer),
1372 SpanSnippetError::MalformedForSourcemap(pos) => Tag(4, pos).encode(writer),
1373 SpanSnippetError::SourceNotAvailable { filename } => Tag(5, filename).encode(writer),
1374 SpanSnippetError::LookupFailed(err) => Tag(6, err).encode(writer),
1375 }
1376 }
1377}
1378
1379#[cfg(feature = "encoding-impl")]
1380impl<'de> cbor4ii::core::dec::Decode<'de> for SpanSnippetError {
1381 #[inline]
1382 fn decode<R: cbor4ii::core::dec::Read<'de>>(
1383 reader: &mut R,
1384 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
1385 use cbor4ii::core::types::{Array, Tag};
1386
1387 let tag = Tag::tag(reader)?;
1388 match tag {
1389 1 => {
1390 let n = Array::len(reader)?;
1391 debug_assert_eq!(n, Some(0));
1392 Ok(SpanSnippetError::DummyBytePos)
1393 }
1394 2 => Span::decode(reader).map(SpanSnippetError::IllFormedSpan),
1395 3 => DistinctSources::decode(reader).map(SpanSnippetError::DistinctSources),
1396 4 => MalformedSourceMapPositions::decode(reader)
1397 .map(SpanSnippetError::MalformedForSourcemap),
1398 5 => FileName::decode(reader)
1399 .map(|filename| SpanSnippetError::SourceNotAvailable { filename }),
1400 6 => SourceMapLookupError::decode(reader).map(SpanSnippetError::LookupFailed),
1401 tag => Err(cbor4ii::core::error::DecodeError::Custom {
1402 name: &"SpanSnippetError",
1403 num: tag as u32,
1404 }),
1405 }
1406 }
1407}
1408
1409#[derive(Clone, PartialEq, Eq, Debug)]
1414#[cfg_attr(
1415 feature = "encoding-impl",
1416 derive(::ast_node::Encode, ::ast_node::Decode)
1417)]
1418pub enum SourceMapLookupError {
1419 NoFileFor(BytePos),
1420}
1421
1422#[derive(Clone, PartialEq, Eq, Debug)]
1423#[cfg_attr(
1424 feature = "encoding-impl",
1425 derive(::ast_node::Encode, ::ast_node::Decode)
1426)]
1427pub struct FilePos(
1428 #[cfg_attr(
1429 feature = "encoding-impl",
1430 encoding(with = "encoding_helper::LrcHelper")
1431 )]
1432 pub Lrc<FileName>,
1433 pub BytePos,
1434);
1435
1436#[derive(Clone, PartialEq, Eq, Debug)]
1437#[cfg_attr(
1438 feature = "encoding-impl",
1439 derive(::ast_node::Encode, ::ast_node::Decode)
1440)]
1441pub struct DistinctSources {
1442 pub begin: FilePos,
1443 pub end: FilePos,
1444}
1445
1446#[derive(Clone, PartialEq, Eq, Debug)]
1447#[cfg_attr(
1448 feature = "encoding-impl",
1449 derive(::ast_node::Encode, ::ast_node::Decode)
1450)]
1451pub struct MalformedSourceMapPositions {
1452 #[cfg_attr(
1453 feature = "encoding-impl",
1454 encoding(with = "encoding_helper::LrcHelper")
1455 )]
1456 pub name: Lrc<FileName>,
1457 #[cfg_attr(feature = "encoding-impl", encoding(with = "encoding_helper::Usize"))]
1458 pub source_len: usize,
1459 pub begin_pos: BytePos,
1460 pub end_pos: BytePos,
1461}
1462
1463fn lookup_line(lines: &[BytePos], pos: BytePos) -> isize {
1467 match lines.binary_search(&pos) {
1468 Ok(line) => line as isize,
1469 Err(line) => line as isize - 1,
1470 }
1471}
1472
1473impl From<SourceMapLookupError> for Box<SpanSnippetError> {
1474 #[cold]
1475 fn from(err: SourceMapLookupError) -> Self {
1476 Box::new(SpanSnippetError::LookupFailed(err))
1477 }
1478}
1479
1480#[cfg(feature = "encoding-impl")]
1481mod encoding_helper {
1482 use super::Lrc;
1483
1484 pub struct LrcHelper<T>(pub T);
1485
1486 impl<T: cbor4ii::core::enc::Encode> cbor4ii::core::enc::Encode for LrcHelper<&'_ Lrc<T>> {
1487 fn encode<W: cbor4ii::core::enc::Write>(
1488 &self,
1489 writer: &mut W,
1490 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
1491 self.0.encode(writer)
1492 }
1493 }
1494
1495 impl<'de, T: cbor4ii::core::dec::Decode<'de>> cbor4ii::core::dec::Decode<'de>
1496 for LrcHelper<Lrc<T>>
1497 {
1498 fn decode<R: cbor4ii::core::dec::Read<'de>>(
1499 reader: &mut R,
1500 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
1501 T::decode(reader).map(Lrc::new).map(LrcHelper)
1502 }
1503 }
1504
1505 impl<T: cbor4ii::core::enc::Encode> cbor4ii::core::enc::Encode for LrcHelper<&'_ Option<Lrc<T>>> {
1506 fn encode<W: cbor4ii::core::enc::Write>(
1507 &self,
1508 writer: &mut W,
1509 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
1510 let v = self.0.as_deref();
1512 cbor4ii::core::types::Array::bounded(v.is_some() as usize, writer)?;
1513 if let Some(v) = v {
1514 v.encode(writer)?;
1515 }
1516 Ok(())
1517 }
1518 }
1519
1520 impl<'de, T: cbor4ii::core::dec::Decode<'de>> cbor4ii::core::dec::Decode<'de>
1521 for LrcHelper<Option<Lrc<T>>>
1522 {
1523 fn decode<R: cbor4ii::core::dec::Read<'de>>(
1524 reader: &mut R,
1525 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
1526 <cbor4ii::core::types::Maybe<Option<T>>>::decode(reader)
1527 .map(|maybe| maybe.0.map(Lrc::new))
1528 .map(LrcHelper)
1529 }
1530 }
1531
1532 pub struct Usize<T>(pub T);
1533
1534 impl cbor4ii::core::enc::Encode for Usize<&'_ usize> {
1535 fn encode<W: cbor4ii::core::enc::Write>(
1536 &self,
1537 writer: &mut W,
1538 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
1539 (*self.0 as u64).encode(writer)
1540 }
1541 }
1542
1543 impl<'de> cbor4ii::core::dec::Decode<'de> for Usize<usize> {
1544 fn decode<R: cbor4ii::core::dec::Read<'de>>(
1545 reader: &mut R,
1546 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
1547 <u64>::decode(reader)
1548 .map(|n| n.try_into().unwrap())
1549 .map(Usize)
1550 }
1551 }
1552
1553 pub struct Str<T>(pub T);
1554
1555 impl cbor4ii::core::enc::Encode for Str<&'_ bytes_str::BytesStr> {
1556 fn encode<W: cbor4ii::core::enc::Write>(
1557 &self,
1558 writer: &mut W,
1559 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
1560 cbor4ii::core::enc::Encode::encode(&self.0.as_str(), writer)
1561 }
1562 }
1563
1564 impl<'de> cbor4ii::core::dec::Decode<'de> for Str<bytes_str::BytesStr> {
1565 fn decode<R: cbor4ii::core::dec::Read<'de>>(
1566 reader: &mut R,
1567 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
1568 String::decode(reader)
1569 .map(bytes_str::BytesStr::from)
1570 .map(Str)
1571 }
1572 }
1573}
1574
1575#[cfg(test)]
1576mod tests {
1577 use super::{lookup_line, BytePos, FileName, SourceFile, Span};
1578 use crate::sync::Lrc;
1579
1580 #[test]
1581 fn test_lookup_line() {
1582 let lines = &[BytePos(3), BytePos(17), BytePos(28)];
1583
1584 assert_eq!(lookup_line(lines, BytePos(0)), -1);
1585 assert_eq!(lookup_line(lines, BytePos(3)), 0);
1586 assert_eq!(lookup_line(lines, BytePos(4)), 0);
1587
1588 assert_eq!(lookup_line(lines, BytePos(16)), 0);
1589 assert_eq!(lookup_line(lines, BytePos(17)), 1);
1590 assert_eq!(lookup_line(lines, BytePos(18)), 1);
1591
1592 assert_eq!(lookup_line(lines, BytePos(28)), 2);
1593 assert_eq!(lookup_line(lines, BytePos(29)), 2);
1594 }
1595
1596 #[test]
1597 fn size_of_span() {
1598 assert_eq!(std::mem::size_of::<Span>(), 8);
1599 }
1600
1601 #[test]
1602 fn source_file_hashes_are_lazy() {
1603 let name = Lrc::new(FileName::Custom("input.js".into()));
1604 let file = SourceFile::new(
1605 name.clone(),
1606 false,
1607 name,
1608 "let answer = 42;".into(),
1609 BytePos(1),
1610 );
1611
1612 assert!(file.lazy.get().is_none());
1613
1614 let src_hash = file.src_hash();
1615 let name_hash = file.name_hash();
1616 let analysis = file.lazy.get().unwrap();
1617
1618 assert_eq!(analysis.src_hash, src_hash);
1619 assert_eq!(analysis.name_hash, name_hash);
1620 assert_eq!(file.src_hash(), src_hash);
1621 assert_eq!(file.name_hash(), name_hash);
1622 }
1623
1624 #[cfg(feature = "encoding-impl")]
1625 #[test]
1626 fn source_file_encoding_includes_hash_slots() {
1627 use cbor4ii::core::{dec::Decode, enc::Encode};
1628
1629 let name = Lrc::new(FileName::Custom("input.js".into()));
1630 let file = SourceFile::new(
1631 name.clone(),
1632 false,
1633 name,
1634 "let answer = 42;".into(),
1635 BytePos(1),
1636 );
1637
1638 let mut writer = cbor4ii::core::utils::BufWriter::new(Vec::new());
1639 file.encode(&mut writer).unwrap();
1640 let bytes = writer.into_inner();
1641 assert!(file.lazy.get().is_none());
1642
1643 let mut reader = cbor4ii::core::utils::SliceReader::new(&bytes);
1644 let len = cbor4ii::core::types::Array::<()>::len(&mut reader)
1645 .unwrap()
1646 .unwrap();
1647 assert_eq!(len, super::SOURCE_FILE_WIRE_FIELD_COUNT);
1648
1649 let decoded_name = FileName::decode(&mut reader).unwrap();
1650 let decoded_name_was_remapped = bool::decode(&mut reader).unwrap();
1651 let decoded_unmapped_path =
1652 cbor4ii::core::types::Maybe::<Option<FileName>>::decode(&mut reader)
1653 .unwrap()
1654 .0;
1655 let decoded_crate_of_origin = u32::decode(&mut reader).unwrap();
1656 let decoded_src = String::decode(&mut reader).unwrap();
1657 let decoded_src_hash = u128::decode(&mut reader).unwrap();
1658 let decoded_start_pos = BytePos::decode(&mut reader).unwrap();
1659 let decoded_end_pos = BytePos::decode(&mut reader).unwrap();
1660 let decoded_name_hash = u128::decode(&mut reader).unwrap();
1661
1662 assert_eq!(decoded_name, FileName::Custom("input.js".into()));
1663 assert!(!decoded_name_was_remapped);
1664 assert_eq!(
1665 decoded_unmapped_path,
1666 Some(FileName::Custom("input.js".into()))
1667 );
1668 assert_eq!(decoded_crate_of_origin, 0);
1669 assert_eq!(decoded_src, "let answer = 42;");
1670 assert_eq!(decoded_src_hash, super::stable_src_hash(&file.src));
1671 assert_eq!(decoded_start_pos, file.start_pos);
1672 assert_eq!(decoded_end_pos, file.end_pos);
1673 assert_eq!(decoded_name_hash, super::stable_name_hash(&file.name));
1674
1675 let mut reader = cbor4ii::core::utils::SliceReader::new(&bytes);
1676 let decoded = SourceFile::decode(&mut reader).unwrap();
1677 assert_eq!(*decoded.name, FileName::Custom("input.js".into()));
1678 assert_eq!(decoded.src, file.src);
1679 assert_eq!(decoded.start_pos, file.start_pos);
1680 assert_eq!(decoded.end_pos, file.end_pos);
1681 assert!(decoded.lazy.get().is_none());
1682 }
1683}