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 any(feature = "rkyv-impl"),
34 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
35)]
36#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
37#[cfg_attr(feature = "rkyv-impl", repr(C))]
38#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
39pub struct Span {
40 #[serde(rename = "start")]
41 #[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))]
42 pub lo: BytePos,
43 #[serde(rename = "end")]
44 #[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))]
45 pub hi: BytePos,
46}
47
48impl std::fmt::Debug for Span {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 write!(f, "{}..{}", self.lo.0, self.hi.0,)
51 }
52}
53
54impl From<(BytePos, BytePos)> for Span {
55 #[inline]
56 fn from(sp: (BytePos, BytePos)) -> Self {
57 Span::new(sp.0, sp.1)
58 }
59}
60
61impl From<Span> for (BytePos, BytePos) {
62 #[inline]
63 fn from(sp: Span) -> Self {
64 (sp.lo, sp.hi)
65 }
66}
67
68#[cfg(feature = "arbitrary")]
69#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
70impl<'a> arbitrary::Arbitrary<'a> for Span {
71 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
72 let lo = u.arbitrary::<BytePos>()?;
73 let hi = u.arbitrary::<BytePos>()?;
74
75 Ok(Self::new(lo, hi))
76 }
77}
78
79pub const DUMMY_SP: Span = Span {
82 lo: BytePos::DUMMY,
83 hi: BytePos::DUMMY,
84};
85
86pub const PURE_SP: Span = Span {
88 lo: BytePos::PURE,
89 hi: BytePos::PURE,
90};
91
92pub const PLACEHOLDER_SP: Span = Span {
94 lo: BytePos::PLACEHOLDER,
95 hi: BytePos::PLACEHOLDER,
96};
97
98pub struct Globals {
99 hygiene_data: Mutex<hygiene::HygieneData>,
100 #[allow(unused)]
101 dummy_cnt: AtomicU32,
102 #[allow(unused)]
103 marks: Mutex<Vec<MarkData>>,
104}
105
106const DUMMY_RESERVE: u32 = u32::MAX - 2_u32.pow(16);
107
108impl Default for Globals {
109 fn default() -> Self {
110 Self::new()
111 }
112}
113
114impl Globals {
115 pub fn new() -> Globals {
116 Globals {
117 hygiene_data: Mutex::new(hygiene::HygieneData::new()),
118 marks: Mutex::new(vec![MarkData {
119 parent: Mark::root(),
120 }]),
121 dummy_cnt: AtomicU32::new(DUMMY_RESERVE),
122 }
123 }
124
125 pub fn clone_data(&self) -> Self {
129 Globals {
130 hygiene_data: Mutex::new(self.hygiene_data.lock().unwrap().clone()),
131 marks: Mutex::new(self.marks.lock().unwrap().clone()),
132 dummy_cnt: AtomicU32::new(self.dummy_cnt.load(std::sync::atomic::Ordering::SeqCst)),
133 }
134 }
135}
136
137better_scoped_tls::scoped_tls!(
138
139 pub static GLOBALS: Globals
164);
165
166#[cfg_attr(
167 any(feature = "rkyv-impl"),
168 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
169)]
170#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
171#[cfg_attr(feature = "rkyv-impl", repr(u32))]
172#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
173pub enum FileName {
174 Real(
175 #[cfg_attr(
176 any(feature = "rkyv-impl"),
177 rkyv(with = crate::source_map::EncodePathBuf)
178 )]
179 PathBuf,
180 ),
181 Macros(String),
184 QuoteExpansion,
186 Anon,
188 MacroExpansion,
190 ProcMacroSourceCode,
191 Url(#[cfg_attr(any(feature = "rkyv-impl"), rkyv(with = crate::source_map::EncodeUrl))] Url),
192 Internal(String),
193 Custom(String),
195}
196
197#[cfg(feature = "rkyv-impl")]
207#[derive(Debug, Clone, Copy)]
208#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
209#[cfg_attr(feature = "rkyv-impl", repr(C))]
210pub struct EncodePathBuf;
211
212#[cfg(feature = "rkyv-impl")]
213impl rkyv::with::ArchiveWith<PathBuf> for EncodePathBuf {
214 type Archived = rkyv::string::ArchivedString;
215 type Resolver = rkyv::string::StringResolver;
216
217 #[inline]
218 fn resolve_with(field: &PathBuf, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) {
219 rkyv::string::ArchivedString::resolve_from_str(field.to_str().unwrap(), resolver, out);
222 }
223}
224
225#[cfg(feature = "rkyv-impl")]
226impl<S> rkyv::with::SerializeWith<PathBuf, S> for EncodePathBuf
227where
228 S: ?Sized + rancor::Fallible + rkyv::ser::Writer,
229 S::Error: rancor::Source,
230{
231 #[inline]
232 fn serialize_with(field: &PathBuf, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
233 let s = field.to_str().unwrap_or_default();
234 rkyv::string::ArchivedString::serialize_from_str(s, serializer)
235 }
236}
237
238#[cfg(feature = "rkyv-impl")]
239impl<D> rkyv::with::DeserializeWith<rkyv::string::ArchivedString, PathBuf, D> for EncodePathBuf
240where
241 D: ?Sized + rancor::Fallible,
242{
243 #[inline]
244 fn deserialize_with(
245 field: &rkyv::string::ArchivedString,
246 _: &mut D,
247 ) -> Result<PathBuf, D::Error> {
248 Ok(<PathBuf as std::str::FromStr>::from_str(field.as_str()).unwrap())
249 }
250}
251
252#[cfg(feature = "rkyv-impl")]
254#[derive(Debug, Clone, Copy)]
255#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
256#[cfg_attr(feature = "rkyv-impl", repr(C))]
257pub struct EncodeUrl;
258
259#[cfg(feature = "rkyv-impl")]
260impl rkyv::with::ArchiveWith<Url> for EncodeUrl {
261 type Archived = rkyv::string::ArchivedString;
262 type Resolver = rkyv::string::StringResolver;
263
264 #[inline]
265 fn resolve_with(field: &Url, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) {
266 rkyv::string::ArchivedString::resolve_from_str(field.as_str(), resolver, out);
267 }
268}
269
270#[cfg(feature = "rkyv-impl")]
271impl<S> rkyv::with::SerializeWith<Url, S> for EncodeUrl
272where
273 S: ?Sized + rancor::Fallible + rkyv::ser::Writer,
274 S::Error: rancor::Source,
275{
276 #[inline]
277 fn serialize_with(field: &Url, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
278 let field = field.as_str();
279 rkyv::string::ArchivedString::serialize_from_str(field, serializer)
280 }
281}
282
283#[cfg(feature = "rkyv-impl")]
284impl<D> rkyv::with::DeserializeWith<rkyv::Archived<String>, Url, D> for EncodeUrl
285where
286 D: ?Sized + rancor::Fallible,
287{
288 #[inline]
289 fn deserialize_with(field: &rkyv::string::ArchivedString, _: &mut D) -> Result<Url, D::Error> {
290 Ok(Url::parse(field.as_str()).unwrap())
291 }
292}
293
294impl std::fmt::Display for FileName {
295 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296 match *self {
297 FileName::Real(ref path) => write!(fmt, "{}", path.display()),
298 FileName::Macros(ref name) => write!(fmt, "<{name} macros>"),
299 FileName::QuoteExpansion => write!(fmt, "<quote expansion>"),
300 FileName::MacroExpansion => write!(fmt, "<macro expansion>"),
301 FileName::Anon => write!(fmt, "<anon>"),
302 FileName::ProcMacroSourceCode => write!(fmt, "<proc-macro source code>"),
303 FileName::Url(ref u) => write!(fmt, "{u}"),
304 FileName::Custom(ref s) => {
305 write!(fmt, "{s}")
306 }
307 FileName::Internal(ref s) => write!(fmt, "<{s}>"),
308 }
309 }
310}
311
312impl From<PathBuf> for FileName {
313 fn from(p: PathBuf) -> Self {
314 assert!(!p.to_string_lossy().ends_with('>'));
315 FileName::Real(p)
316 }
317}
318
319impl From<Url> for FileName {
320 fn from(url: Url) -> Self {
321 FileName::Url(url)
322 }
323}
324
325impl FileName {
326 pub fn is_real(&self) -> bool {
327 match *self {
328 FileName::Real(_) => true,
329 FileName::Macros(_)
330 | FileName::Anon
331 | FileName::MacroExpansion
332 | FileName::ProcMacroSourceCode
333 | FileName::Custom(_)
334 | FileName::QuoteExpansion
335 | FileName::Internal(_)
336 | FileName::Url(_) => false,
337 }
338 }
339
340 pub fn is_macros(&self) -> bool {
341 match *self {
342 FileName::Real(_)
343 | FileName::Anon
344 | FileName::MacroExpansion
345 | FileName::ProcMacroSourceCode
346 | FileName::Custom(_)
347 | FileName::QuoteExpansion
348 | FileName::Internal(_)
349 | FileName::Url(_) => false,
350 FileName::Macros(_) => true,
351 }
352 }
353}
354
355#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
356#[cfg_attr(
357 feature = "diagnostic-serde",
358 derive(serde::Serialize, serde::Deserialize)
359)]
360#[cfg_attr(
361 any(feature = "rkyv-impl"),
362 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
363)]
364#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
365#[cfg_attr(feature = "rkyv-impl", repr(C))]
366pub struct PrimarySpanLabel(pub Span, pub String);
367
368#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
375#[cfg_attr(
376 feature = "diagnostic-serde",
377 derive(serde::Serialize, serde::Deserialize)
378)]
379#[cfg_attr(
380 any(feature = "rkyv-impl"),
381 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
382)]
383#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
384#[cfg_attr(feature = "rkyv-impl", repr(C))]
385pub struct MultiSpan {
386 primary_spans: Vec<Span>,
387 span_labels: Vec<PrimarySpanLabel>,
388}
389
390extern "C" {
391 fn __span_dummy_with_cmt_proxy() -> u32;
392}
393
394impl Span {
395 #[inline]
396 pub fn lo(self) -> BytePos {
397 self.lo
398 }
399
400 #[inline]
401 pub fn new(mut lo: BytePos, mut hi: BytePos) -> Self {
402 if lo > hi {
403 std::mem::swap(&mut lo, &mut hi);
404 }
405
406 Span { lo, hi }
407 }
408
409 #[inline]
410 #[track_caller]
411 pub fn new_with_checked(lo: BytePos, hi: BytePos) -> Self {
412 debug_assert!(lo <= hi, "lo: {lo:#?}, hi: {hi:#?}");
413 Span { lo, hi }
414 }
415
416 #[inline]
417 pub fn with_lo(&self, lo: BytePos) -> Span {
418 Span::new(lo, self.hi)
419 }
420
421 #[inline(always)]
422 pub fn hi(self) -> BytePos {
423 self.hi
424 }
425
426 #[inline]
427 pub fn with_hi(&self, hi: BytePos) -> Span {
428 Span::new(self.lo, hi)
429 }
430
431 #[inline]
433 pub fn is_dummy(self) -> bool {
434 self.lo.0 == 0 && self.hi.0 == 0 || self.lo.0 >= DUMMY_RESERVE
435 }
436
437 #[inline]
438 pub fn is_pure(self) -> bool {
439 self.lo.is_pure()
440 }
441
442 #[inline]
443 pub fn is_placeholder(self) -> bool {
444 self.lo.is_placeholder()
445 }
446
447 #[inline]
449 pub fn is_dummy_ignoring_cmt(self) -> bool {
450 self.lo.0 == 0 && self.hi.0 == 0
451 }
452
453 #[inline]
456 pub fn shrink_to_lo(self) -> Span {
457 self.with_hi(self.lo)
458 }
459
460 #[inline]
462 pub fn shrink_to_hi(self) -> Span {
463 self.with_lo(self.hi)
464 }
465
466 pub fn substitute_dummy(self, other: Span) -> Span {
468 if self.is_dummy() {
469 other
470 } else {
471 self
472 }
473 }
474
475 pub fn contains(self, other: Span) -> bool {
477 self.lo <= other.lo && other.hi <= self.hi
478 }
479
480 pub fn source_equal(self, other: Span) -> bool {
485 self.lo == other.lo && self.hi == other.hi
486 }
487
488 pub fn trim_start(self, other: Span) -> Option<Span> {
490 if self.hi > other.hi {
491 Some(self.with_lo(cmp::max(self.lo, other.hi)))
492 } else {
493 None
494 }
495 }
496
497 pub fn to(self, end: Span) -> Span {
499 let span_data = self;
500 let end_data = end;
501 Span::new(
507 cmp::min(span_data.lo, end_data.lo),
508 cmp::max(span_data.hi, end_data.hi),
509 )
510 }
511
512 pub fn between(self, end: Span) -> Span {
514 let span = self;
515 Span::new(span.hi, end.lo)
516 }
517
518 pub fn until(self, end: Span) -> Span {
521 let span = self;
522 Span::new(span.lo, end.lo)
523 }
524
525 pub fn from_inner_byte_pos(self, start: usize, end: usize) -> Span {
526 let span = self;
527 Span::new(
528 span.lo + BytePos::from_usize(start),
529 span.lo + BytePos::from_usize(end),
530 )
531 }
532
533 pub fn dummy_with_cmt() -> Self {
536 #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
537 {
538 let lo = BytePos(unsafe { __span_dummy_with_cmt_proxy() });
539
540 return Span { lo, hi: lo };
541 }
542
543 #[cfg(not(all(any(feature = "__plugin_mode"), target_arch = "wasm32")))]
544 return GLOBALS.with(|globals| {
545 let lo = BytePos(
546 globals
547 .dummy_cnt
548 .fetch_add(1, std::sync::atomic::Ordering::SeqCst),
549 );
550 Span { lo, hi: lo }
551 });
552 }
553}
554
555#[derive(Clone, Debug)]
556pub struct SpanLabel {
557 pub span: Span,
559
560 pub is_primary: bool,
563
564 pub label: Option<String>,
566}
567
568impl Default for Span {
569 fn default() -> Self {
570 DUMMY_SP
571 }
572}
573
574impl MultiSpan {
575 #[inline]
576 pub fn new() -> MultiSpan {
577 Self::default()
578 }
579
580 pub fn from_span(primary_span: Span) -> MultiSpan {
581 MultiSpan {
582 primary_spans: vec![primary_span],
583 span_labels: Vec::new(),
584 }
585 }
586
587 pub fn from_spans(vec: Vec<Span>) -> MultiSpan {
588 MultiSpan {
589 primary_spans: vec,
590 span_labels: Vec::new(),
591 }
592 }
593
594 pub fn push_span_label(&mut self, span: Span, label: String) {
595 self.span_labels.push(PrimarySpanLabel(span, label));
596 }
597
598 pub fn primary_span(&self) -> Option<Span> {
600 self.primary_spans.first().cloned()
601 }
602
603 pub fn primary_spans(&self) -> &[Span] {
605 &self.primary_spans
606 }
607
608 pub fn is_dummy(&self) -> bool {
611 let mut is_dummy = true;
612 for span in &self.primary_spans {
613 if !span.is_dummy() {
614 is_dummy = false;
615 }
616 }
617 is_dummy
618 }
619
620 pub fn replace(&mut self, before: Span, after: Span) -> bool {
624 let mut replacements_occurred = false;
625 for primary_span in &mut self.primary_spans {
626 if *primary_span == before {
627 *primary_span = after;
628 replacements_occurred = true;
629 }
630 }
631 for span_label in &mut self.span_labels {
632 if span_label.0 == before {
633 span_label.0 = after;
634 replacements_occurred = true;
635 }
636 }
637 replacements_occurred
638 }
639
640 pub fn span_labels(&self) -> Vec<SpanLabel> {
646 let is_primary = |span| self.primary_spans.contains(&span);
647
648 let mut span_labels = self
649 .span_labels
650 .iter()
651 .map(|&PrimarySpanLabel(span, ref label)| SpanLabel {
652 span,
653 is_primary: is_primary(span),
654 label: Some(label.clone()),
655 })
656 .collect::<Vec<_>>();
657
658 for &span in &self.primary_spans {
659 if !span_labels.iter().any(|sl| sl.span == span) {
660 span_labels.push(SpanLabel {
661 span,
662 is_primary: true,
663 label: None,
664 });
665 }
666 }
667
668 span_labels
669 }
670}
671
672impl From<Span> for MultiSpan {
673 fn from(span: Span) -> MultiSpan {
674 MultiSpan::from_span(span)
675 }
676}
677
678impl From<Vec<Span>> for MultiSpan {
679 fn from(spans: Vec<Span>) -> MultiSpan {
680 MultiSpan::from_spans(spans)
681 }
682}
683
684pub const NO_EXPANSION: SyntaxContext = SyntaxContext::empty();
685
686#[cfg_attr(
688 any(feature = "rkyv-impl"),
689 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
690)]
691#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
692#[cfg_attr(feature = "rkyv-impl", repr(C))]
693#[derive(Copy, Clone, Eq, PartialEq, Debug)]
694pub struct MultiByteChar {
695 pub pos: BytePos,
697 pub bytes: u8,
699}
700
701impl MultiByteChar {
702 pub fn byte_to_char_diff(&self) -> u8 {
708 if self.bytes == 4 {
709 2
710 } else {
711 self.bytes - 1
712 }
713 }
714}
715
716#[cfg_attr(
718 any(feature = "rkyv-impl"),
719 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
720)]
721#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
722#[cfg_attr(feature = "rkyv-impl", repr(u32))]
723#[derive(Copy, Clone, Eq, PartialEq, Debug)]
724pub enum NonNarrowChar {
725 ZeroWidth(BytePos),
727 Wide(BytePos, usize),
729 Tab(BytePos),
732}
733
734impl NonNarrowChar {
735 fn new(pos: BytePos, width: usize) -> Self {
736 match width {
737 0 => NonNarrowChar::ZeroWidth(pos),
738 4 => NonNarrowChar::Tab(pos),
739 w => NonNarrowChar::Wide(pos, w),
740 }
741 }
742
743 pub fn pos(self) -> BytePos {
745 match self {
746 NonNarrowChar::ZeroWidth(p) | NonNarrowChar::Wide(p, _) | NonNarrowChar::Tab(p) => p,
747 }
748 }
749
750 pub fn width(self) -> usize {
752 match self {
753 NonNarrowChar::ZeroWidth(_) => 0,
754 NonNarrowChar::Wide(_, width) => width,
755 NonNarrowChar::Tab(_) => 4,
756 }
757 }
758}
759
760impl Add<BytePos> for NonNarrowChar {
761 type Output = Self;
762
763 fn add(self, rhs: BytePos) -> Self {
764 match self {
765 NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos + rhs),
766 NonNarrowChar::Wide(pos, width) => NonNarrowChar::Wide(pos + rhs, width),
767 NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos + rhs),
768 }
769 }
770}
771
772impl Sub<BytePos> for NonNarrowChar {
773 type Output = Self;
774
775 fn sub(self, rhs: BytePos) -> Self {
776 match self {
777 NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos - rhs),
778 NonNarrowChar::Wide(pos, width) => NonNarrowChar::Wide(pos - rhs, width),
779 NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos - rhs),
780 }
781 }
782}
783
784#[cfg_attr(
786 any(feature = "rkyv-impl"),
787 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
788)]
789#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
790#[cfg_attr(feature = "rkyv-impl", repr(C))]
791#[derive(Clone)]
792pub struct SourceFile {
793 pub name: Lrc<FileName>,
797 pub name_was_remapped: bool,
800 pub unmapped_path: Option<Lrc<FileName>>,
803 pub crate_of_origin: u32,
805 pub src: BytesStr,
807 pub src_hash: u128,
809 pub start_pos: BytePos,
811 pub end_pos: BytePos,
813 pub name_hash: u128,
815
816 lazy: CacheCell<SourceFileAnalysis>,
817}
818
819#[cfg_attr(
820 any(feature = "rkyv-impl"),
821 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
822)]
823#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
824#[cfg_attr(feature = "rkyv-impl", repr(C))]
825#[derive(Clone)]
826pub struct SourceFileAnalysis {
827 pub lines: Vec<BytePos>,
829 pub multibyte_chars: Vec<MultiByteChar>,
831 pub non_narrow_chars: Vec<NonNarrowChar>,
833}
834
835impl fmt::Debug for SourceFile {
836 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
837 write!(fmt, "SourceFile({})", self.name)
838 }
839}
840
841impl SourceFile {
842 pub fn new(
844 name: Lrc<FileName>,
845 name_was_remapped: bool,
846 unmapped_path: Lrc<FileName>,
847 src: BytesStr,
848 start_pos: BytePos,
849 ) -> SourceFile {
850 debug_assert_ne!(
851 start_pos,
852 BytePos::DUMMY,
853 "BytePos::DUMMY is reserved and `SourceFile` should not use it"
854 );
855
856 let src_hash = {
857 let mut hasher: StableHasher = StableHasher::new();
858 hasher.write(src.as_bytes());
859 hasher.finish()
860 };
861 let name_hash = {
862 let mut hasher: StableHasher = StableHasher::new();
863 name.hash(&mut hasher);
864 hasher.finish()
865 };
866 let end_pos = start_pos.to_usize() + src.len();
867
868 SourceFile {
869 name,
870 name_was_remapped,
871 unmapped_path: Some(unmapped_path),
872 crate_of_origin: 0,
873 src,
874 src_hash,
875 start_pos,
876 end_pos: SmallPos::from_usize(end_pos),
877 name_hash,
878 lazy: CacheCell::new(),
879 }
880 }
881
882 pub fn line_begin_pos(&self, pos: BytePos) -> BytePos {
884 let line_index = self.lookup_line(pos).unwrap();
885 let analysis = self.analyze();
886 analysis.lines[line_index]
887 }
888
889 pub fn get_line(&self, line_number: usize) -> Option<Cow<'_, str>> {
892 fn get_until_newline(src: &str, begin: usize) -> &str {
893 let slice = &src[begin..];
897 match slice.find('\n') {
898 Some(e) => &slice[..e],
899 None => slice,
900 }
901 }
902
903 let begin = {
904 let analysis = self.analyze();
905 let line = analysis.lines.get(line_number)?;
906 let begin: BytePos = *line - self.start_pos;
907 begin.to_usize()
908 };
909
910 Some(Cow::from(get_until_newline(&self.src, begin)))
911 }
912
913 pub fn is_real_file(&self) -> bool {
914 self.name.is_real()
915 }
916
917 pub fn byte_length(&self) -> u32 {
918 self.end_pos.0 - self.start_pos.0
919 }
920
921 pub fn count_lines(&self) -> usize {
922 let analysis = self.analyze();
923 analysis.lines.len()
924 }
925
926 pub fn lookup_line(&self, pos: BytePos) -> Option<usize> {
931 let analysis = self.analyze();
932 if analysis.lines.is_empty() {
933 return None;
934 }
935
936 let line_index = lookup_line(&analysis.lines, pos);
937 assert!(line_index < analysis.lines.len() as isize);
938 if line_index >= 0 {
939 Some(line_index as usize)
940 } else {
941 None
942 }
943 }
944
945 pub fn line_bounds(&self, line_index: usize) -> (BytePos, BytePos) {
946 if self.start_pos == self.end_pos {
947 return (self.start_pos, self.end_pos);
948 }
949
950 let analysis = self.analyze();
951
952 assert!(line_index < analysis.lines.len());
953 if line_index == (analysis.lines.len() - 1) {
954 (analysis.lines[line_index], self.end_pos)
955 } else {
956 (analysis.lines[line_index], analysis.lines[line_index + 1])
957 }
958 }
959
960 #[inline]
961 pub fn contains(&self, byte_pos: BytePos) -> bool {
962 byte_pos >= self.start_pos && byte_pos <= self.end_pos
963 }
964
965 pub fn analyze(&self) -> &SourceFileAnalysis {
966 self.lazy.get_or_init(|| {
967 let (lines, multibyte_chars, non_narrow_chars) =
968 analyze_source_file::analyze_source_file(&self.src[..], self.start_pos);
969 SourceFileAnalysis {
970 lines,
971 multibyte_chars,
972 non_narrow_chars,
973 }
974 })
975 }
976}
977
978pub trait SmallPos {
983 fn from_usize(n: usize) -> Self;
984 fn to_usize(&self) -> usize;
985 fn from_u32(n: u32) -> Self;
986 fn to_u32(&self) -> u32;
987}
988
989#[derive(
1002 Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize, Default,
1003)]
1004#[serde(transparent)]
1005#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1006#[cfg_attr(
1007 any(feature = "rkyv-impl"),
1008 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1009)]
1010#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1011#[cfg_attr(feature = "rkyv-impl", repr(C))]
1012#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
1013pub struct BytePos(#[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))] pub u32);
1014
1015impl BytePos {
1016 pub const DUMMY: Self = BytePos(0);
1018 const MIN_RESERVED: Self = BytePos(DUMMY_RESERVE);
1019 pub const PLACEHOLDER: Self = BytePos(u32::MAX - 2);
1022 pub const PURE: Self = BytePos(u32::MAX - 1);
1024 pub const SYNTHESIZED: Self = BytePos(u32::MAX);
1026
1027 pub const fn is_reserved_for_comments(self) -> bool {
1028 self.0 >= Self::MIN_RESERVED.0 && self.0 != u32::MAX
1029 }
1030
1031 pub const fn is_dummy(self) -> bool {
1034 self.0 == 0
1035 }
1036
1037 pub const fn is_pure(self) -> bool {
1038 self.0 == Self::PURE.0
1039 }
1040
1041 pub const fn is_placeholder(self) -> bool {
1042 self.0 == Self::PLACEHOLDER.0
1043 }
1044
1045 pub const fn can_have_comment(self) -> bool {
1048 self.0 != 0
1049 }
1050}
1051
1052#[cfg_attr(
1056 any(feature = "rkyv-impl"),
1057 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1058)]
1059#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1060#[cfg_attr(feature = "rkyv-impl", repr(C))]
1061#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
1062pub struct CharPos(pub usize);
1063
1064impl SmallPos for BytePos {
1068 #[inline(always)]
1069 fn from_usize(n: usize) -> BytePos {
1070 BytePos(n as u32)
1071 }
1072
1073 #[inline(always)]
1074 fn to_usize(&self) -> usize {
1075 self.0 as usize
1076 }
1077
1078 #[inline(always)]
1079 fn from_u32(n: u32) -> BytePos {
1080 BytePos(n)
1081 }
1082
1083 #[inline(always)]
1084 fn to_u32(&self) -> u32 {
1085 self.0
1086 }
1087}
1088
1089impl Add for BytePos {
1090 type Output = BytePos;
1091
1092 #[inline(always)]
1093 fn add(self, rhs: BytePos) -> BytePos {
1094 BytePos((self.to_usize() + rhs.to_usize()) as u32)
1095 }
1096}
1097
1098impl Sub for BytePos {
1099 type Output = BytePos;
1100
1101 #[inline(always)]
1102 fn sub(self, rhs: BytePos) -> BytePos {
1103 BytePos((self.to_usize() - rhs.to_usize()) as u32)
1104 }
1105}
1106
1107impl SmallPos for CharPos {
1108 #[inline(always)]
1109 fn from_usize(n: usize) -> CharPos {
1110 CharPos(n)
1111 }
1112
1113 #[inline(always)]
1114 fn to_usize(&self) -> usize {
1115 self.0
1116 }
1117
1118 #[inline(always)]
1119 fn from_u32(n: u32) -> CharPos {
1120 CharPos(n as usize)
1121 }
1122
1123 #[inline(always)]
1124 fn to_u32(&self) -> u32 {
1125 self.0 as u32
1126 }
1127}
1128
1129impl Add for CharPos {
1130 type Output = CharPos;
1131
1132 #[inline(always)]
1133 fn add(self, rhs: CharPos) -> CharPos {
1134 CharPos(self.to_usize() + rhs.to_usize())
1135 }
1136}
1137
1138impl Sub for CharPos {
1139 type Output = CharPos;
1140
1141 #[inline(always)]
1142 fn sub(self, rhs: CharPos) -> CharPos {
1143 CharPos(self.to_usize() - rhs.to_usize())
1144 }
1145}
1146
1147#[derive(Debug, Clone)]
1158pub struct Loc {
1159 pub file: Lrc<SourceFile>,
1161 pub line: usize,
1163 pub col: CharPos,
1165 pub col_display: usize,
1167}
1168
1169#[cfg_attr(
1172 any(feature = "rkyv-impl"),
1173 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, Clone)
1174)]
1175#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1176#[cfg_attr(feature = "rkyv-impl", repr(C))]
1177pub struct PartialLoc {
1178 pub source_file: Option<Lrc<SourceFile>>,
1179 pub line: usize,
1180 pub col: usize,
1181 pub col_display: usize,
1182}
1183
1184#[derive(Debug)]
1188pub struct LocWithOpt {
1189 pub filename: Lrc<FileName>,
1190 pub line: usize,
1191 pub col: CharPos,
1192 pub file: Option<Lrc<SourceFile>>,
1193}
1194
1195#[derive(Debug)]
1197pub struct SourceFileAndLine {
1198 pub sf: Lrc<SourceFile>,
1199 pub line: usize,
1200}
1201
1202#[cfg_attr(
1203 any(feature = "rkyv-impl"),
1204 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1205)]
1206#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1207#[cfg_attr(feature = "rkyv-impl", repr(C))]
1208#[derive(Debug)]
1209pub struct SourceFileAndBytePos {
1210 pub sf: Lrc<SourceFile>,
1211 pub pos: BytePos,
1212}
1213
1214#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1215#[cfg_attr(
1216 any(feature = "rkyv-impl"),
1217 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1218)]
1219#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1220#[cfg_attr(feature = "rkyv-impl", repr(C))]
1221pub struct LineInfo {
1222 pub line_index: usize,
1224
1225 pub start_col: CharPos,
1227
1228 pub end_col: CharPos,
1230}
1231
1232#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1234pub struct LineCol {
1235 pub line: u32,
1237
1238 pub col: u32,
1240}
1241
1242pub struct FileLines {
1249 pub file: Lrc<SourceFile>,
1250 pub lines: Vec<LineInfo>,
1251}
1252
1253#[cfg_attr(
1256 any(feature = "rkyv-impl"),
1257 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, Clone)
1258)]
1259#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1260#[cfg_attr(feature = "rkyv-impl", repr(C))]
1261pub struct PartialFileLines {
1262 pub file: Option<Lrc<SourceFile>>,
1263 pub lines: Vec<LineInfo>,
1264}
1265
1266pub type FileLinesResult = Result<FileLines, Box<SpanLinesError>>;
1272#[cfg(feature = "__plugin")]
1273pub type PartialFileLinesResult = Result<PartialFileLines, Box<SpanLinesError>>;
1274
1275#[derive(Clone, PartialEq, Eq, Debug)]
1276#[cfg_attr(
1277 any(feature = "rkyv-impl"),
1278 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1279)]
1280#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1281#[cfg_attr(feature = "rkyv-impl", repr(u32))]
1282pub enum SpanLinesError {
1283 IllFormedSpan(Span),
1284 DistinctSources(DistinctSources),
1285}
1286
1287#[derive(Clone, PartialEq, Eq, Debug)]
1288#[cfg_attr(
1289 any(feature = "rkyv-impl"),
1290 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1291)]
1292#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1293#[cfg_attr(feature = "rkyv-impl", repr(u32))]
1294pub enum SpanSnippetError {
1295 DummyBytePos,
1296 IllFormedSpan(Span),
1297 DistinctSources(DistinctSources),
1298 MalformedForSourcemap(MalformedSourceMapPositions),
1299 SourceNotAvailable { filename: FileName },
1300 LookupFailed(SourceMapLookupError),
1301}
1302
1303#[derive(Clone, PartialEq, Eq, Debug)]
1308#[cfg_attr(
1309 any(feature = "rkyv-impl"),
1310 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1311)]
1312#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1313#[cfg_attr(feature = "rkyv-impl", repr(u32))]
1314pub enum SourceMapLookupError {
1315 NoFileFor(BytePos),
1316}
1317
1318#[derive(Clone, PartialEq, Eq, Debug)]
1319#[cfg_attr(
1320 any(feature = "rkyv-impl"),
1321 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1322)]
1323#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1324#[cfg_attr(feature = "rkyv-impl", repr(C))]
1325pub struct FilePos(pub Lrc<FileName>, pub BytePos);
1326
1327#[derive(Clone, PartialEq, Eq, Debug)]
1328#[cfg_attr(
1329 any(feature = "rkyv-impl"),
1330 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1331)]
1332#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1333#[cfg_attr(feature = "rkyv-impl", repr(C))]
1334pub struct DistinctSources {
1335 pub begin: FilePos,
1336 pub end: FilePos,
1337}
1338
1339#[derive(Clone, PartialEq, Eq, Debug)]
1340#[cfg_attr(
1341 any(feature = "rkyv-impl"),
1342 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1343)]
1344#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1345#[cfg_attr(feature = "rkyv-impl", repr(C))]
1346pub struct MalformedSourceMapPositions {
1347 pub name: Lrc<FileName>,
1348 pub source_len: usize,
1349 pub begin_pos: BytePos,
1350 pub end_pos: BytePos,
1351}
1352
1353fn lookup_line(lines: &[BytePos], pos: BytePos) -> isize {
1357 match lines.binary_search(&pos) {
1358 Ok(line) => line as isize,
1359 Err(line) => line as isize - 1,
1360 }
1361}
1362
1363impl From<SourceMapLookupError> for Box<SpanSnippetError> {
1364 #[cold]
1365 fn from(err: SourceMapLookupError) -> Self {
1366 Box::new(SpanSnippetError::LookupFailed(err))
1367 }
1368}
1369
1370#[cfg(test)]
1371mod tests {
1372 use super::{lookup_line, BytePos, Span};
1373
1374 #[test]
1375 fn test_lookup_line() {
1376 let lines = &[BytePos(3), BytePos(17), BytePos(28)];
1377
1378 assert_eq!(lookup_line(lines, BytePos(0)), -1);
1379 assert_eq!(lookup_line(lines, BytePos(3)), 0);
1380 assert_eq!(lookup_line(lines, BytePos(4)), 0);
1381
1382 assert_eq!(lookup_line(lines, BytePos(16)), 0);
1383 assert_eq!(lookup_line(lines, BytePos(17)), 1);
1384 assert_eq!(lookup_line(lines, BytePos(18)), 1);
1385
1386 assert_eq!(lookup_line(lines, BytePos(28)), 2);
1387 assert_eq!(lookup_line(lines, BytePos(29)), 2);
1388 }
1389
1390 #[test]
1391 fn size_of_span() {
1392 assert_eq!(std::mem::size_of::<Span>(), 8);
1393 }
1394}