swc_common/
syntax_pos.rs

1#[cfg(not(feature = "parking_lot"))]
2use std::sync::Mutex;
3use std::{
4    borrow::Cow,
5    cmp, fmt,
6    hash::{Hash, Hasher},
7    ops::{Add, Sub},
8    path::PathBuf,
9    sync::atomic::AtomicU32,
10};
11
12#[cfg(feature = "parking_lot")]
13use parking_lot::Mutex;
14use serde::{Deserialize, Serialize};
15use url::Url;
16
17use self::hygiene::MarkData;
18pub use self::hygiene::{Mark, SyntaxContext};
19use crate::{cache::CacheCell, rustc_data_structures::stable_hasher::StableHasher, sync::Lrc};
20
21mod analyze_source_file;
22pub mod hygiene;
23
24/// Spans represent a region of code, used for error reporting.
25///
26/// Positions in
27/// spans are *absolute* positions from the beginning of the `source_map`, not
28/// positions relative to `SourceFile`s. Methods on the `SourceMap` can be used
29/// to relate spans back to the original source.
30/// You must be careful if the span crosses more than one file - you will not be
31/// able to use many of the functions on spans in `source_map` and you cannot
32/// assume that the length of the `span = hi - lo`; there may be space in the
33/// `BytePos` range between files.
34#[derive(Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)]
35#[cfg_attr(
36    any(feature = "rkyv-impl"),
37    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
38)]
39#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
40#[cfg_attr(feature = "rkyv-impl", repr(C))]
41#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
42pub struct Span {
43    #[serde(rename = "start")]
44    #[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))]
45    pub lo: BytePos,
46    #[serde(rename = "end")]
47    #[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))]
48    pub hi: BytePos,
49}
50
51impl std::fmt::Debug for Span {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        write!(f, "{}..{}", self.lo.0, self.hi.0,)
54    }
55}
56
57impl From<(BytePos, BytePos)> for Span {
58    #[inline]
59    fn from(sp: (BytePos, BytePos)) -> Self {
60        Span::new(sp.0, sp.1)
61    }
62}
63
64impl From<Span> for (BytePos, BytePos) {
65    #[inline]
66    fn from(sp: Span) -> Self {
67        (sp.lo, sp.hi)
68    }
69}
70
71#[cfg(feature = "arbitrary")]
72#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
73impl<'a> arbitrary::Arbitrary<'a> for Span {
74    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
75        let lo = u.arbitrary::<BytePos>()?;
76        let hi = u.arbitrary::<BytePos>()?;
77
78        Ok(Self::new(lo, hi))
79    }
80}
81
82/// Dummy span, both position and length are zero, syntax context is zero as
83/// well.
84pub const DUMMY_SP: Span = Span {
85    lo: BytePos::DUMMY,
86    hi: BytePos::DUMMY,
87};
88
89/// PURE span, will emit `/* #__PURE__ */` comment in codegen.
90pub const PURE_SP: Span = Span {
91    lo: BytePos::PURE,
92    hi: BytePos::PURE,
93};
94
95/// Used for some special cases. e.g. mark the generated AST.
96pub const PLACEHOLDER_SP: Span = Span {
97    lo: BytePos::PLACEHOLDER,
98    hi: BytePos::PLACEHOLDER,
99};
100
101pub struct Globals {
102    hygiene_data: Mutex<hygiene::HygieneData>,
103    #[allow(unused)]
104    dummy_cnt: AtomicU32,
105    #[allow(unused)]
106    marks: Mutex<Vec<MarkData>>,
107}
108
109const DUMMY_RESERVE: u32 = u32::MAX - 2_u32.pow(16);
110
111impl Default for Globals {
112    fn default() -> Self {
113        Self::new()
114    }
115}
116
117impl Globals {
118    pub fn new() -> Globals {
119        Globals {
120            hygiene_data: Mutex::new(hygiene::HygieneData::new()),
121            marks: Mutex::new(vec![MarkData {
122                parent: Mark::root(),
123            }]),
124            dummy_cnt: AtomicU32::new(DUMMY_RESERVE),
125        }
126    }
127}
128
129better_scoped_tls::scoped_tls!(
130
131    /// Storage for span hygiene data.
132    ///
133    /// This variable is used to manage identifiers or to identify nodes.
134    /// Note that it's stored as a thread-local storage, but actually it's shared
135    /// between threads.
136    ///
137    /// # Usages
138    ///
139    /// ## Configuring
140    ///
141    /// ```rust
142    /// use swc_common::GLOBALS;
143    ///
144    /// GLOBALS.set(&Default::default(), || {
145    ///     // Do operations that require span hygiene
146    /// });
147    /// ```
148    ///
149    /// ## Span hygiene
150    ///
151    /// [Mark]s are stored in this variable.
152    ///
153    /// You can see the document how swc uses the span hygiene info at
154    /// https://rustdoc.swc.rs/swc_ecma_transforms_base/resolver/fn.resolver_with_mark.html
155    pub static GLOBALS: Globals
156);
157
158#[cfg_attr(
159    any(feature = "rkyv-impl"),
160    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
161)]
162#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
163#[cfg_attr(feature = "rkyv-impl", repr(u32))]
164#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
165pub enum FileName {
166    Real(
167        #[cfg_attr(
168            any(feature = "rkyv-impl"),
169            rkyv(with = crate::source_map::EncodePathBuf)
170        )]
171        PathBuf,
172    ),
173    /// A macro. This includes the full name of the macro, so that there are no
174    /// clashes.
175    Macros(String),
176    /// call to `quote!`
177    QuoteExpansion,
178    /// Command line
179    Anon,
180    /// Hack in src/libsyntax/parse.rs
181    MacroExpansion,
182    ProcMacroSourceCode,
183    Url(#[cfg_attr(any(feature = "rkyv-impl"), rkyv(with = crate::source_map::EncodeUrl))] Url),
184    Internal(String),
185    /// Custom sources for explicit parser calls from plugins and drivers
186    Custom(String),
187}
188
189/// A wrapper that attempts to convert a type to and from UTF-8.
190///
191/// Types like `OsString` and `PathBuf` aren't guaranteed to be encoded as
192/// UTF-8, but they usually are anyway. Using this wrapper will archive them as
193/// if they were regular `String`s.
194///
195/// There is built-in `AsString` supports PathBuf but it requires custom
196/// serializer wrapper to handle conversion errors. This wrapper is simplified
197/// version accepts errors
198#[cfg(feature = "rkyv-impl")]
199#[derive(Debug, Clone, Copy)]
200#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
201#[cfg_attr(feature = "rkyv-impl", repr(C))]
202pub struct EncodePathBuf;
203
204#[cfg(feature = "rkyv-impl")]
205impl rkyv::with::ArchiveWith<PathBuf> for EncodePathBuf {
206    type Archived = rkyv::string::ArchivedString;
207    type Resolver = rkyv::string::StringResolver;
208
209    #[inline]
210    fn resolve_with(field: &PathBuf, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) {
211        // It's safe to unwrap here because if the OsString wasn't valid UTF-8 it would
212        // have failed to serialize
213        rkyv::string::ArchivedString::resolve_from_str(field.to_str().unwrap(), resolver, out);
214    }
215}
216
217#[cfg(feature = "rkyv-impl")]
218impl<S> rkyv::with::SerializeWith<PathBuf, S> for EncodePathBuf
219where
220    S: ?Sized + rancor::Fallible + rkyv::ser::Writer,
221    S::Error: rancor::Source,
222{
223    #[inline]
224    fn serialize_with(field: &PathBuf, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
225        let s = field.to_str().unwrap_or_default();
226        rkyv::string::ArchivedString::serialize_from_str(s, serializer)
227    }
228}
229
230#[cfg(feature = "rkyv-impl")]
231impl<D> rkyv::with::DeserializeWith<rkyv::string::ArchivedString, PathBuf, D> for EncodePathBuf
232where
233    D: ?Sized + rancor::Fallible,
234{
235    #[inline]
236    fn deserialize_with(
237        field: &rkyv::string::ArchivedString,
238        _: &mut D,
239    ) -> Result<PathBuf, D::Error> {
240        Ok(<PathBuf as std::str::FromStr>::from_str(field.as_str()).unwrap())
241    }
242}
243
244/// A wrapper that attempts to convert a Url to and from String.
245#[cfg(feature = "rkyv-impl")]
246#[derive(Debug, Clone, Copy)]
247#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
248#[cfg_attr(feature = "rkyv-impl", repr(C))]
249pub struct EncodeUrl;
250
251#[cfg(feature = "rkyv-impl")]
252impl rkyv::with::ArchiveWith<Url> for EncodeUrl {
253    type Archived = rkyv::string::ArchivedString;
254    type Resolver = rkyv::string::StringResolver;
255
256    #[inline]
257    fn resolve_with(field: &Url, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) {
258        rkyv::string::ArchivedString::resolve_from_str(field.as_str(), resolver, out);
259    }
260}
261
262#[cfg(feature = "rkyv-impl")]
263impl<S> rkyv::with::SerializeWith<Url, S> for EncodeUrl
264where
265    S: ?Sized + rancor::Fallible + rkyv::ser::Writer,
266    S::Error: rancor::Source,
267{
268    #[inline]
269    fn serialize_with(field: &Url, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
270        let field = field.as_str();
271        rkyv::string::ArchivedString::serialize_from_str(field, serializer)
272    }
273}
274
275#[cfg(feature = "rkyv-impl")]
276impl<D> rkyv::with::DeserializeWith<rkyv::Archived<String>, Url, D> for EncodeUrl
277where
278    D: ?Sized + rancor::Fallible,
279{
280    #[inline]
281    fn deserialize_with(field: &rkyv::string::ArchivedString, _: &mut D) -> Result<Url, D::Error> {
282        Ok(Url::parse(field.as_str()).unwrap())
283    }
284}
285
286impl std::fmt::Display for FileName {
287    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288        match *self {
289            FileName::Real(ref path) => write!(fmt, "{}", path.display()),
290            FileName::Macros(ref name) => write!(fmt, "<{} macros>", name),
291            FileName::QuoteExpansion => write!(fmt, "<quote expansion>"),
292            FileName::MacroExpansion => write!(fmt, "<macro expansion>"),
293            FileName::Anon => write!(fmt, "<anon>"),
294            FileName::ProcMacroSourceCode => write!(fmt, "<proc-macro source code>"),
295            FileName::Url(ref u) => write!(fmt, "{}", u),
296            FileName::Custom(ref s) => {
297                write!(fmt, "{}", s)
298            }
299            FileName::Internal(ref s) => write!(fmt, "<{}>", s),
300        }
301    }
302}
303
304impl From<PathBuf> for FileName {
305    fn from(p: PathBuf) -> Self {
306        assert!(!p.to_string_lossy().ends_with('>'));
307        FileName::Real(p)
308    }
309}
310
311impl From<Url> for FileName {
312    fn from(url: Url) -> Self {
313        FileName::Url(url)
314    }
315}
316
317impl FileName {
318    pub fn is_real(&self) -> bool {
319        match *self {
320            FileName::Real(_) => true,
321            FileName::Macros(_)
322            | FileName::Anon
323            | FileName::MacroExpansion
324            | FileName::ProcMacroSourceCode
325            | FileName::Custom(_)
326            | FileName::QuoteExpansion
327            | FileName::Internal(_)
328            | FileName::Url(_) => false,
329        }
330    }
331
332    pub fn is_macros(&self) -> bool {
333        match *self {
334            FileName::Real(_)
335            | FileName::Anon
336            | FileName::MacroExpansion
337            | FileName::ProcMacroSourceCode
338            | FileName::Custom(_)
339            | FileName::QuoteExpansion
340            | FileName::Internal(_)
341            | FileName::Url(_) => false,
342            FileName::Macros(_) => true,
343        }
344    }
345}
346
347#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
348#[cfg_attr(
349    feature = "diagnostic-serde",
350    derive(serde::Serialize, serde::Deserialize)
351)]
352#[cfg_attr(
353    any(feature = "rkyv-impl"),
354    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
355)]
356#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
357#[cfg_attr(feature = "rkyv-impl", repr(C))]
358pub struct PrimarySpanLabel(pub Span, pub String);
359
360/// A collection of spans. Spans have two orthogonal attributes:
361///
362/// - they can be *primary spans*. In this case they are the locus of the error,
363///   and would be rendered with `^^^`.
364/// - they can have a *label*. In this case, the label is written next to the
365///   mark in the snippet when we render.
366#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
367#[cfg_attr(
368    feature = "diagnostic-serde",
369    derive(serde::Serialize, serde::Deserialize)
370)]
371#[cfg_attr(
372    any(feature = "rkyv-impl"),
373    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
374)]
375#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
376#[cfg_attr(feature = "rkyv-impl", repr(C))]
377pub struct MultiSpan {
378    primary_spans: Vec<Span>,
379    span_labels: Vec<PrimarySpanLabel>,
380}
381
382extern "C" {
383    fn __span_dummy_with_cmt_proxy() -> u32;
384}
385
386impl Span {
387    #[inline]
388    pub fn lo(self) -> BytePos {
389        self.lo
390    }
391
392    #[inline]
393    pub fn new(mut lo: BytePos, mut hi: BytePos) -> Self {
394        if lo > hi {
395            std::mem::swap(&mut lo, &mut hi);
396        }
397
398        Span { lo, hi }
399    }
400
401    #[inline]
402    pub fn with_lo(&self, lo: BytePos) -> Span {
403        Span::new(lo, self.hi)
404    }
405
406    #[inline]
407    pub fn hi(self) -> BytePos {
408        self.hi
409    }
410
411    #[inline]
412    pub fn with_hi(&self, hi: BytePos) -> Span {
413        Span::new(self.lo, hi)
414    }
415
416    /// Returns `true` if this is a dummy span with any hygienic context.
417    #[inline]
418    pub fn is_dummy(self) -> bool {
419        self.lo.0 == 0 && self.hi.0 == 0 || self.lo.0 >= DUMMY_RESERVE
420    }
421
422    #[inline]
423    pub fn is_pure(self) -> bool {
424        self.lo.is_pure()
425    }
426
427    #[inline]
428    pub fn is_placeholder(self) -> bool {
429        self.lo.is_placeholder()
430    }
431
432    /// Returns `true` if this is a dummy span with any hygienic context.
433    #[inline]
434    pub fn is_dummy_ignoring_cmt(self) -> bool {
435        self.lo.0 == 0 && self.hi.0 == 0
436    }
437
438    /// Returns a new span representing an empty span at the beginning of this
439    /// span
440    #[inline]
441    pub fn shrink_to_lo(self) -> Span {
442        self.with_hi(self.lo)
443    }
444
445    /// Returns a new span representing an empty span at the end of this span
446    #[inline]
447    pub fn shrink_to_hi(self) -> Span {
448        self.with_lo(self.hi)
449    }
450
451    /// Returns `self` if `self` is not the dummy span, and `other` otherwise.
452    pub fn substitute_dummy(self, other: Span) -> Span {
453        if self.is_dummy() {
454            other
455        } else {
456            self
457        }
458    }
459
460    /// Return true if `self` fully encloses `other`.
461    pub fn contains(self, other: Span) -> bool {
462        self.lo <= other.lo && other.hi <= self.hi
463    }
464
465    /// Return true if the spans are equal with regards to the source text.
466    ///
467    /// Use this instead of `==` when either span could be generated code,
468    /// and you only care that they point to the same bytes of source text.
469    pub fn source_equal(self, other: Span) -> bool {
470        self.lo == other.lo && self.hi == other.hi
471    }
472
473    /// Returns `Some(span)`, where the start is trimmed by the end of `other`
474    pub fn trim_start(self, other: Span) -> Option<Span> {
475        if self.hi > other.hi {
476            Some(self.with_lo(cmp::max(self.lo, other.hi)))
477        } else {
478            None
479        }
480    }
481
482    /// Return a `Span` that would enclose both `self` and `end`.
483    pub fn to(self, end: Span) -> Span {
484        let span_data = self;
485        let end_data = end;
486        // FIXME(jseyfried): self.ctxt should always equal end.ctxt here (c.f. issue
487        // #23480) Return the macro span on its own to avoid weird diagnostic
488        // output. It is preferable to have an incomplete span than a completely
489        // nonsensical one.
490
491        Span::new(
492            cmp::min(span_data.lo, end_data.lo),
493            cmp::max(span_data.hi, end_data.hi),
494        )
495    }
496
497    /// Return a `Span` between the end of `self` to the beginning of `end`.
498    pub fn between(self, end: Span) -> Span {
499        let span = self;
500        Span::new(span.hi, end.lo)
501    }
502
503    /// Return a `Span` between the beginning of `self` to the beginning of
504    /// `end`.
505    pub fn until(self, end: Span) -> Span {
506        let span = self;
507        Span::new(span.lo, end.lo)
508    }
509
510    pub fn from_inner_byte_pos(self, start: usize, end: usize) -> Span {
511        let span = self;
512        Span::new(
513            span.lo + BytePos::from_usize(start),
514            span.lo + BytePos::from_usize(end),
515        )
516    }
517
518    /// Dummy span, both position are extremely large numbers so they would be
519    /// ignore by sourcemap, but can still have comments
520    pub fn dummy_with_cmt() -> Self {
521        #[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
522        {
523            let lo = BytePos(unsafe { __span_dummy_with_cmt_proxy() });
524
525            return Span { lo, hi: lo };
526        }
527
528        #[cfg(not(all(any(feature = "__plugin_mode"), target_arch = "wasm32")))]
529        return GLOBALS.with(|globals| {
530            let lo = BytePos(
531                globals
532                    .dummy_cnt
533                    .fetch_add(1, std::sync::atomic::Ordering::SeqCst),
534            );
535            Span { lo, hi: lo }
536        });
537    }
538}
539
540#[derive(Clone, Debug)]
541pub struct SpanLabel {
542    /// The span we are going to include in the final snippet.
543    pub span: Span,
544
545    /// Is this a primary span? This is the "locus" of the message,
546    /// and is indicated with a `^^^^` underline, versus `----`.
547    pub is_primary: bool,
548
549    /// What label should we attach to this span (if any)?
550    pub label: Option<String>,
551}
552
553impl Default for Span {
554    fn default() -> Self {
555        DUMMY_SP
556    }
557}
558
559impl MultiSpan {
560    #[inline]
561    pub fn new() -> MultiSpan {
562        Self::default()
563    }
564
565    pub fn from_span(primary_span: Span) -> MultiSpan {
566        MultiSpan {
567            primary_spans: vec![primary_span],
568            span_labels: Vec::new(),
569        }
570    }
571
572    pub fn from_spans(vec: Vec<Span>) -> MultiSpan {
573        MultiSpan {
574            primary_spans: vec,
575            span_labels: Vec::new(),
576        }
577    }
578
579    pub fn push_span_label(&mut self, span: Span, label: String) {
580        self.span_labels.push(PrimarySpanLabel(span, label));
581    }
582
583    /// Selects the first primary span (if any)
584    pub fn primary_span(&self) -> Option<Span> {
585        self.primary_spans.first().cloned()
586    }
587
588    /// Returns all primary spans.
589    pub fn primary_spans(&self) -> &[Span] {
590        &self.primary_spans
591    }
592
593    /// Returns `true` if this contains only a dummy primary span with any
594    /// hygienic context.
595    pub fn is_dummy(&self) -> bool {
596        let mut is_dummy = true;
597        for span in &self.primary_spans {
598            if !span.is_dummy() {
599                is_dummy = false;
600            }
601        }
602        is_dummy
603    }
604
605    /// Replaces all occurrences of one Span with another. Used to move Spans in
606    /// areas that don't display well (like std macros). Returns true if
607    /// replacements occurred.
608    pub fn replace(&mut self, before: Span, after: Span) -> bool {
609        let mut replacements_occurred = false;
610        for primary_span in &mut self.primary_spans {
611            if *primary_span == before {
612                *primary_span = after;
613                replacements_occurred = true;
614            }
615        }
616        for span_label in &mut self.span_labels {
617            if span_label.0 == before {
618                span_label.0 = after;
619                replacements_occurred = true;
620            }
621        }
622        replacements_occurred
623    }
624
625    /// Returns the strings to highlight. We always ensure that there
626    /// is an entry for each of the primary spans -- for each primary
627    /// span P, if there is at least one label with span P, we return
628    /// those labels (marked as primary). But otherwise we return
629    /// `SpanLabel` instances with empty labels.
630    pub fn span_labels(&self) -> Vec<SpanLabel> {
631        let is_primary = |span| self.primary_spans.contains(&span);
632
633        let mut span_labels = self
634            .span_labels
635            .iter()
636            .map(|&PrimarySpanLabel(span, ref label)| SpanLabel {
637                span,
638                is_primary: is_primary(span),
639                label: Some(label.clone()),
640            })
641            .collect::<Vec<_>>();
642
643        for &span in &self.primary_spans {
644            if !span_labels.iter().any(|sl| sl.span == span) {
645                span_labels.push(SpanLabel {
646                    span,
647                    is_primary: true,
648                    label: None,
649                });
650            }
651        }
652
653        span_labels
654    }
655}
656
657impl From<Span> for MultiSpan {
658    fn from(span: Span) -> MultiSpan {
659        MultiSpan::from_span(span)
660    }
661}
662
663impl From<Vec<Span>> for MultiSpan {
664    fn from(spans: Vec<Span>) -> MultiSpan {
665        MultiSpan::from_spans(spans)
666    }
667}
668
669pub const NO_EXPANSION: SyntaxContext = SyntaxContext::empty();
670
671/// Identifies an offset of a multi-byte character in a SourceFile
672#[cfg_attr(
673    any(feature = "rkyv-impl"),
674    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
675)]
676#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
677#[cfg_attr(feature = "rkyv-impl", repr(C))]
678#[derive(Copy, Clone, Eq, PartialEq, Debug)]
679pub struct MultiByteChar {
680    /// The absolute offset of the character in the SourceMap
681    pub pos: BytePos,
682    /// The number of bytes, >=2
683    pub bytes: u8,
684}
685
686impl MultiByteChar {
687    /// Computes the extra number of UTF-8 bytes necessary to encode a code
688    /// point, compared to UTF-16 encoding.
689    ///
690    /// 1, 2, and 3 UTF-8 bytes encode into 1 UTF-16 char, but 4 UTF-8 bytes
691    /// encode into 2.
692    pub fn byte_to_char_diff(&self) -> u8 {
693        if self.bytes == 4 {
694            2
695        } else {
696            self.bytes - 1
697        }
698    }
699}
700
701/// Identifies an offset of a non-narrow character in a SourceFile
702#[cfg_attr(
703    any(feature = "rkyv-impl"),
704    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
705)]
706#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
707#[cfg_attr(feature = "rkyv-impl", repr(u32))]
708#[derive(Copy, Clone, Eq, PartialEq, Debug)]
709pub enum NonNarrowChar {
710    /// Represents a zero-width character
711    ZeroWidth(BytePos),
712    /// Represents a wide (fullwidth) character
713    Wide(BytePos, usize),
714    /// Represents a tab character, represented visually with a width of 4
715    /// characters
716    Tab(BytePos),
717}
718
719impl NonNarrowChar {
720    fn new(pos: BytePos, width: usize) -> Self {
721        match width {
722            0 => NonNarrowChar::ZeroWidth(pos),
723            4 => NonNarrowChar::Tab(pos),
724            w => NonNarrowChar::Wide(pos, w),
725        }
726    }
727
728    /// Returns the absolute offset of the character in the SourceMap
729    pub fn pos(self) -> BytePos {
730        match self {
731            NonNarrowChar::ZeroWidth(p) | NonNarrowChar::Wide(p, _) | NonNarrowChar::Tab(p) => p,
732        }
733    }
734
735    /// Returns the width of the character, 0 (zero-width) or 2 (wide)
736    pub fn width(self) -> usize {
737        match self {
738            NonNarrowChar::ZeroWidth(_) => 0,
739            NonNarrowChar::Wide(_, width) => width,
740            NonNarrowChar::Tab(_) => 4,
741        }
742    }
743}
744
745impl Add<BytePos> for NonNarrowChar {
746    type Output = Self;
747
748    fn add(self, rhs: BytePos) -> Self {
749        match self {
750            NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos + rhs),
751            NonNarrowChar::Wide(pos, width) => NonNarrowChar::Wide(pos + rhs, width),
752            NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos + rhs),
753        }
754    }
755}
756
757impl Sub<BytePos> for NonNarrowChar {
758    type Output = Self;
759
760    fn sub(self, rhs: BytePos) -> Self {
761        match self {
762            NonNarrowChar::ZeroWidth(pos) => NonNarrowChar::ZeroWidth(pos - rhs),
763            NonNarrowChar::Wide(pos, width) => NonNarrowChar::Wide(pos - rhs, width),
764            NonNarrowChar::Tab(pos) => NonNarrowChar::Tab(pos - rhs),
765        }
766    }
767}
768
769/// This is not a public interface, workaround for https://github.com/swc-project/swc/issues/7238
770#[doc(hidden)]
771#[cfg(feature = "rkyv-impl")]
772#[derive(Debug, Clone, Copy)]
773#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
774#[cfg_attr(feature = "rkyv-impl", repr(C))]
775pub struct EncodeArcString;
776
777#[cfg(feature = "rkyv-impl")]
778impl rkyv::with::ArchiveWith<Lrc<String>> for EncodeArcString {
779    type Archived = rkyv::Archived<String>;
780    type Resolver = rkyv::Resolver<String>;
781
782    fn resolve_with(
783        field: &Lrc<String>,
784        resolver: Self::Resolver,
785        out: rkyv::Place<Self::Archived>,
786    ) {
787        let s = field.to_string();
788        rkyv::Archive::resolve(&s, resolver, out);
789    }
790}
791
792#[cfg(feature = "rkyv-impl")]
793impl<S> rkyv::with::SerializeWith<Lrc<String>, S> for EncodeArcString
794where
795    S: ?Sized + rancor::Fallible + rkyv::ser::Writer,
796    S::Error: rancor::Source,
797{
798    fn serialize_with(field: &Lrc<String>, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
799        rkyv::string::ArchivedString::serialize_from_str(field, serializer)
800    }
801}
802
803#[cfg(feature = "rkyv-impl")]
804impl<D> rkyv::with::DeserializeWith<rkyv::Archived<String>, Lrc<String>, D> for EncodeArcString
805where
806    D: ?Sized + rancor::Fallible,
807{
808    fn deserialize_with(
809        field: &rkyv::Archived<String>,
810        deserializer: &mut D,
811    ) -> Result<Lrc<String>, D::Error> {
812        use rkyv::Deserialize;
813
814        let s: String = field.deserialize(deserializer)?;
815
816        Ok(s.into())
817    }
818}
819
820/// A single source in the SourceMap.
821#[cfg_attr(
822    any(feature = "rkyv-impl"),
823    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
824)]
825#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
826#[cfg_attr(feature = "rkyv-impl", repr(C))]
827#[derive(Clone)]
828pub struct SourceFile {
829    /// The name of the file that the source came from. Source that doesn't
830    /// originate from files has names between angle brackets by convention,
831    /// e.g. `<anon>`
832    pub name: Lrc<FileName>,
833    /// True if the `name` field above has been modified by
834    /// `--remap-path-prefix`
835    pub name_was_remapped: bool,
836    /// The unmapped path of the file that the source came from.
837    /// Set to `None` if the `SourceFile` was imported from an external crate.
838    pub unmapped_path: Option<Lrc<FileName>>,
839    /// Indicates which crate this `SourceFile` was imported from.
840    pub crate_of_origin: u32,
841    /// The complete source code
842    #[cfg_attr(any(feature = "rkyv-impl"), rkyv(with = EncodeArcString))]
843    pub src: Lrc<String>,
844    /// The source code's hash
845    pub src_hash: u128,
846    /// The start position of this source in the `SourceMap`
847    pub start_pos: BytePos,
848    /// The end position of this source in the `SourceMap`
849    pub end_pos: BytePos,
850    /// A hash of the filename, used for speeding up the incr. comp. hashing.
851    pub name_hash: u128,
852
853    lazy: CacheCell<SourceFileAnalysis>,
854}
855
856#[cfg_attr(
857    any(feature = "rkyv-impl"),
858    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
859)]
860#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
861#[cfg_attr(feature = "rkyv-impl", repr(C))]
862#[derive(Clone)]
863pub struct SourceFileAnalysis {
864    /// Locations of lines beginnings in the source code
865    pub lines: Vec<BytePos>,
866    /// Locations of multi-byte characters in the source code
867    pub multibyte_chars: Vec<MultiByteChar>,
868    /// Width of characters that are not narrow in the source code
869    pub non_narrow_chars: Vec<NonNarrowChar>,
870}
871
872impl fmt::Debug for SourceFile {
873    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
874        write!(fmt, "SourceFile({})", self.name)
875    }
876}
877
878impl SourceFile {
879    pub fn new(
880        name: Lrc<FileName>,
881        name_was_remapped: bool,
882        unmapped_path: Lrc<FileName>,
883        mut src: String,
884        start_pos: BytePos,
885    ) -> SourceFile {
886        remove_bom(&mut src);
887
888        Self::new_from(
889            name,
890            name_was_remapped,
891            unmapped_path,
892            Lrc::new(src),
893            start_pos,
894        )
895    }
896
897    /// `src` should not have UTF8 BOM
898    pub fn new_from(
899        name: Lrc<FileName>,
900        name_was_remapped: bool,
901        unmapped_path: Lrc<FileName>,
902        src: Lrc<String>,
903        start_pos: BytePos,
904    ) -> SourceFile {
905        debug_assert_ne!(
906            start_pos,
907            BytePos::DUMMY,
908            "BytePos::DUMMY is reserved and `SourceFile` should not use it"
909        );
910
911        let src_hash = {
912            let mut hasher: StableHasher = StableHasher::new();
913            hasher.write(src.as_bytes());
914            hasher.finish()
915        };
916        let name_hash = {
917            let mut hasher: StableHasher = StableHasher::new();
918            name.hash(&mut hasher);
919            hasher.finish()
920        };
921        let end_pos = start_pos.to_usize() + src.len();
922
923        SourceFile {
924            name,
925            name_was_remapped,
926            unmapped_path: Some(unmapped_path),
927            crate_of_origin: 0,
928            src,
929            src_hash,
930            start_pos,
931            end_pos: SmallPos::from_usize(end_pos),
932            name_hash,
933            lazy: CacheCell::new(),
934        }
935    }
936
937    /// Return the BytePos of the beginning of the current line.
938    pub fn line_begin_pos(&self, pos: BytePos) -> BytePos {
939        let line_index = self.lookup_line(pos).unwrap();
940        let analysis = self.analyze();
941        analysis.lines[line_index]
942    }
943
944    /// Get a line from the list of pre-computed line-beginnings.
945    /// The line number here is 0-based.
946    pub fn get_line(&self, line_number: usize) -> Option<Cow<'_, str>> {
947        fn get_until_newline(src: &str, begin: usize) -> &str {
948            // We can't use `lines.get(line_number+1)` because we might
949            // be parsing when we call this function and thus the current
950            // line is the last one we have line info for.
951            let slice = &src[begin..];
952            match slice.find('\n') {
953                Some(e) => &slice[..e],
954                None => slice,
955            }
956        }
957
958        let begin = {
959            let analysis = self.analyze();
960            let line = analysis.lines.get(line_number)?;
961            let begin: BytePos = *line - self.start_pos;
962            begin.to_usize()
963        };
964
965        Some(Cow::from(get_until_newline(&self.src, begin)))
966    }
967
968    pub fn is_real_file(&self) -> bool {
969        self.name.is_real()
970    }
971
972    pub fn byte_length(&self) -> u32 {
973        self.end_pos.0 - self.start_pos.0
974    }
975
976    pub fn count_lines(&self) -> usize {
977        let analysis = self.analyze();
978        analysis.lines.len()
979    }
980
981    /// Find the line containing the given position. The return value is the
982    /// index into the `lines` array of this SourceFile, not the 1-based line
983    /// number. If the `source_file` is empty or the position is located before
984    /// the first line, `None` is returned.
985    pub fn lookup_line(&self, pos: BytePos) -> Option<usize> {
986        let analysis = self.analyze();
987        if analysis.lines.is_empty() {
988            return None;
989        }
990
991        let line_index = lookup_line(&analysis.lines, pos);
992        assert!(line_index < analysis.lines.len() as isize);
993        if line_index >= 0 {
994            Some(line_index as usize)
995        } else {
996            None
997        }
998    }
999
1000    pub fn line_bounds(&self, line_index: usize) -> (BytePos, BytePos) {
1001        if self.start_pos == self.end_pos {
1002            return (self.start_pos, self.end_pos);
1003        }
1004
1005        let analysis = self.analyze();
1006
1007        assert!(line_index < analysis.lines.len());
1008        if line_index == (analysis.lines.len() - 1) {
1009            (analysis.lines[line_index], self.end_pos)
1010        } else {
1011            (analysis.lines[line_index], analysis.lines[line_index + 1])
1012        }
1013    }
1014
1015    #[inline]
1016    pub fn contains(&self, byte_pos: BytePos) -> bool {
1017        byte_pos >= self.start_pos && byte_pos <= self.end_pos
1018    }
1019
1020    pub fn analyze(&self) -> &SourceFileAnalysis {
1021        self.lazy.get_or_init(|| {
1022            let (lines, multibyte_chars, non_narrow_chars) =
1023                analyze_source_file::analyze_source_file(&self.src[..], self.start_pos);
1024            SourceFileAnalysis {
1025                lines,
1026                multibyte_chars,
1027                non_narrow_chars,
1028            }
1029        })
1030    }
1031}
1032
1033/// Remove utf-8 BOM if any.
1034pub(super) fn remove_bom(src: &mut String) {
1035    if src.starts_with('\u{feff}') {
1036        src.drain(..3);
1037    }
1038}
1039
1040// _____________________________________________________________________________
1041// Pos, BytePos, CharPos
1042//
1043
1044pub trait SmallPos {
1045    fn from_usize(n: usize) -> Self;
1046    fn to_usize(&self) -> usize;
1047    fn from_u32(n: u32) -> Self;
1048    fn to_u32(&self) -> u32;
1049}
1050
1051/// A byte offset. Keep this small (currently 32-bits), as AST contains
1052/// a lot of them.
1053///
1054///
1055/// # Reserved
1056///
1057///  - 0 is reserved for dummy spans. It means `BytePos(0)` means the `BytePos`
1058///    is synthesized by the compiler.
1059///
1060///  - Values larger than `u32::MAX - 2^16` are reserved for the comments.
1061///
1062/// `u32::MAX` is special value used to generate source map entries.
1063#[derive(
1064    Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize, Default,
1065)]
1066#[serde(transparent)]
1067#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1068#[cfg_attr(
1069    any(feature = "rkyv-impl"),
1070    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1071)]
1072#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1073#[cfg_attr(feature = "rkyv-impl", repr(C))]
1074#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
1075pub struct BytePos(#[cfg_attr(feature = "__rkyv", rkyv(omit_bounds))] pub u32);
1076
1077impl BytePos {
1078    /// Dummy position. This is reserved for synthesized spans.
1079    pub const DUMMY: Self = BytePos(0);
1080    const MIN_RESERVED: Self = BytePos(DUMMY_RESERVE);
1081    /// Placeholders, commonly used where names are required, but the names are
1082    /// not referenced elsewhere.
1083    pub const PLACEHOLDER: Self = BytePos(u32::MAX - 2);
1084    /// Reserved for PURE comments. e.g. `/* #__PURE__ */`
1085    pub const PURE: Self = BytePos(u32::MAX - 1);
1086    /// Synthesized, but should be stored in a source map.
1087    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    /// Returns `true`` if this is synthesized and has no relevant input source
1094    /// code.
1095    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    /// Returns `true`` if this is explicitly synthesized or has relevant input
1108    /// source so can have a comment.
1109    pub const fn can_have_comment(self) -> bool {
1110        self.0 != 0
1111    }
1112}
1113
1114/// A character offset. Because of multibyte utf8 characters, a byte offset
1115/// is not equivalent to a character offset. The SourceMap will convert BytePos
1116/// values to CharPos values as necessary.
1117#[cfg_attr(
1118    any(feature = "rkyv-impl"),
1119    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1120)]
1121#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1122#[cfg_attr(feature = "rkyv-impl", repr(C))]
1123#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
1124pub struct CharPos(pub usize);
1125
1126// FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
1127// have been unsuccessful
1128
1129impl SmallPos for BytePos {
1130    #[inline(always)]
1131    fn from_usize(n: usize) -> BytePos {
1132        BytePos(n as u32)
1133    }
1134
1135    #[inline(always)]
1136    fn to_usize(&self) -> usize {
1137        self.0 as usize
1138    }
1139
1140    #[inline(always)]
1141    fn from_u32(n: u32) -> BytePos {
1142        BytePos(n)
1143    }
1144
1145    #[inline(always)]
1146    fn to_u32(&self) -> u32 {
1147        self.0
1148    }
1149}
1150
1151impl Add for BytePos {
1152    type Output = BytePos;
1153
1154    #[inline(always)]
1155    fn add(self, rhs: BytePos) -> BytePos {
1156        BytePos((self.to_usize() + rhs.to_usize()) as u32)
1157    }
1158}
1159
1160impl Sub for BytePos {
1161    type Output = BytePos;
1162
1163    #[inline(always)]
1164    fn sub(self, rhs: BytePos) -> BytePos {
1165        BytePos((self.to_usize() - rhs.to_usize()) as u32)
1166    }
1167}
1168
1169impl SmallPos for CharPos {
1170    #[inline(always)]
1171    fn from_usize(n: usize) -> CharPos {
1172        CharPos(n)
1173    }
1174
1175    #[inline(always)]
1176    fn to_usize(&self) -> usize {
1177        self.0
1178    }
1179
1180    #[inline(always)]
1181    fn from_u32(n: u32) -> CharPos {
1182        CharPos(n as usize)
1183    }
1184
1185    #[inline(always)]
1186    fn to_u32(&self) -> u32 {
1187        self.0 as u32
1188    }
1189}
1190
1191impl Add for CharPos {
1192    type Output = CharPos;
1193
1194    #[inline(always)]
1195    fn add(self, rhs: CharPos) -> CharPos {
1196        CharPos(self.to_usize() + rhs.to_usize())
1197    }
1198}
1199
1200impl Sub for CharPos {
1201    type Output = CharPos;
1202
1203    #[inline(always)]
1204    fn sub(self, rhs: CharPos) -> CharPos {
1205        CharPos(self.to_usize() - rhs.to_usize())
1206    }
1207}
1208
1209// _____________________________________________________________________________
1210// Loc, LocWithOpt, SourceFileAndLine, SourceFileAndBytePos
1211//
1212
1213/// A source code location used for error reporting.
1214///
1215/// Note: This struct intentionally does not implement rkyv's archieve
1216/// to avoid redundant data copy (https://github.com/swc-project/swc/issues/5471)
1217/// source_map_proxy constructs plugin-side Loc instead with shared SourceFile
1218/// instance.
1219#[derive(Debug, Clone)]
1220pub struct Loc {
1221    /// Information about the original source
1222    pub file: Lrc<SourceFile>,
1223    /// The (1-based) line number
1224    pub line: usize,
1225    /// The (0-based) column offset
1226    pub col: CharPos,
1227    /// The (0-based) column offset when displayed
1228    pub col_display: usize,
1229}
1230
1231/// A struct to exchange `Loc` with omitting SourceFile as needed.
1232/// This is internal struct between plugins to the host, not a public interface.
1233#[cfg_attr(
1234    any(feature = "rkyv-impl"),
1235    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, Clone)
1236)]
1237#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1238#[cfg_attr(feature = "rkyv-impl", repr(C))]
1239pub struct PartialLoc {
1240    pub source_file: Option<Lrc<SourceFile>>,
1241    pub line: usize,
1242    pub col: usize,
1243    pub col_display: usize,
1244}
1245
1246/// A source code location used as the result of `lookup_char_pos_adj`
1247// Actually, *none* of the clients use the filename *or* file field;
1248// perhaps they should just be removed.
1249#[derive(Debug)]
1250pub struct LocWithOpt {
1251    pub filename: Lrc<FileName>,
1252    pub line: usize,
1253    pub col: CharPos,
1254    pub file: Option<Lrc<SourceFile>>,
1255}
1256
1257// used to be structural records. Better names, anyone?
1258#[derive(Debug)]
1259pub struct SourceFileAndLine {
1260    pub sf: Lrc<SourceFile>,
1261    pub line: usize,
1262}
1263
1264#[cfg_attr(
1265    any(feature = "rkyv-impl"),
1266    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1267)]
1268#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1269#[cfg_attr(feature = "rkyv-impl", repr(C))]
1270#[derive(Debug)]
1271pub struct SourceFileAndBytePos {
1272    pub sf: Lrc<SourceFile>,
1273    pub pos: BytePos,
1274}
1275
1276#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1277#[cfg_attr(
1278    any(feature = "rkyv-impl"),
1279    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1280)]
1281#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1282#[cfg_attr(feature = "rkyv-impl", repr(C))]
1283pub struct LineInfo {
1284    /// Index of line, starting from 0.
1285    pub line_index: usize,
1286
1287    /// Column in line where span begins, starting from 0.
1288    pub start_col: CharPos,
1289
1290    /// Column in line where span ends, starting from 0, exclusive.
1291    pub end_col: CharPos,
1292}
1293
1294/// Used to create a `.map` file.
1295#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1296pub struct LineCol {
1297    /// Index of line, starting from 0.
1298    pub line: u32,
1299
1300    /// UTF-16 column in line, starting from 0.
1301    pub col: u32,
1302}
1303
1304/// A struct to represent lines of a source file.
1305///
1306/// Note: This struct intentionally does not implement rkyv's archieve
1307/// to avoid redundant data copy (https://github.com/swc-project/swc/issues/5471)
1308/// source_map_proxy constructs plugin-side Loc instead with shared SourceFile
1309/// instance.
1310pub struct FileLines {
1311    pub file: Lrc<SourceFile>,
1312    pub lines: Vec<LineInfo>,
1313}
1314
1315/// A struct to exchange `FileLines` with omitting SourceFile as needed.
1316/// This is internal struct between plugins to the host, not a public interface.
1317#[cfg_attr(
1318    any(feature = "rkyv-impl"),
1319    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, Clone)
1320)]
1321#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1322#[cfg_attr(feature = "rkyv-impl", repr(C))]
1323pub struct PartialFileLines {
1324    pub file: Option<Lrc<SourceFile>>,
1325    pub lines: Vec<LineInfo>,
1326}
1327
1328// _____________________________________________________________________________
1329// SpanLinesError, SpanSnippetError, DistinctSources,
1330// MalformedSourceMapPositions
1331//
1332
1333pub type FileLinesResult = Result<FileLines, Box<SpanLinesError>>;
1334#[cfg(feature = "__plugin")]
1335pub type PartialFileLinesResult = Result<PartialFileLines, Box<SpanLinesError>>;
1336
1337#[derive(Clone, PartialEq, Eq, Debug)]
1338#[cfg_attr(
1339    any(feature = "rkyv-impl"),
1340    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1341)]
1342#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1343#[cfg_attr(feature = "rkyv-impl", repr(u32))]
1344pub enum SpanLinesError {
1345    IllFormedSpan(Span),
1346    DistinctSources(DistinctSources),
1347}
1348
1349#[derive(Clone, PartialEq, Eq, Debug)]
1350#[cfg_attr(
1351    any(feature = "rkyv-impl"),
1352    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1353)]
1354#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1355#[cfg_attr(feature = "rkyv-impl", repr(u32))]
1356pub enum SpanSnippetError {
1357    DummyBytePos,
1358    IllFormedSpan(Span),
1359    DistinctSources(DistinctSources),
1360    MalformedForSourcemap(MalformedSourceMapPositions),
1361    SourceNotAvailable { filename: FileName },
1362    LookupFailed(SourceMapLookupError),
1363}
1364
1365/// An error type for looking up source maps.
1366///
1367///
1368/// This type is small.
1369#[derive(Clone, PartialEq, Eq, Debug)]
1370#[cfg_attr(
1371    any(feature = "rkyv-impl"),
1372    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1373)]
1374#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1375#[cfg_attr(feature = "rkyv-impl", repr(u32))]
1376pub enum SourceMapLookupError {
1377    NoFileFor(BytePos),
1378}
1379
1380#[derive(Clone, PartialEq, Eq, Debug)]
1381#[cfg_attr(
1382    any(feature = "rkyv-impl"),
1383    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1384)]
1385#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1386#[cfg_attr(feature = "rkyv-impl", repr(C))]
1387pub struct FilePos(pub Lrc<FileName>, pub BytePos);
1388
1389#[derive(Clone, PartialEq, Eq, Debug)]
1390#[cfg_attr(
1391    any(feature = "rkyv-impl"),
1392    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1393)]
1394#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1395#[cfg_attr(feature = "rkyv-impl", repr(C))]
1396pub struct DistinctSources {
1397    pub begin: FilePos,
1398    pub end: FilePos,
1399}
1400
1401#[derive(Clone, PartialEq, Eq, Debug)]
1402#[cfg_attr(
1403    any(feature = "rkyv-impl"),
1404    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
1405)]
1406#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
1407#[cfg_attr(feature = "rkyv-impl", repr(C))]
1408pub struct MalformedSourceMapPositions {
1409    pub name: Lrc<FileName>,
1410    pub source_len: usize,
1411    pub begin_pos: BytePos,
1412    pub end_pos: BytePos,
1413}
1414
1415// Given a slice of line start positions and a position, returns the index of
1416// the line the position is on. Returns -1 if the position is located before
1417// the first line.
1418fn lookup_line(lines: &[BytePos], pos: BytePos) -> isize {
1419    match lines.binary_search(&pos) {
1420        Ok(line) => line as isize,
1421        Err(line) => line as isize - 1,
1422    }
1423}
1424
1425impl From<SourceMapLookupError> for Box<SpanSnippetError> {
1426    #[cold]
1427    fn from(err: SourceMapLookupError) -> Self {
1428        Box::new(SpanSnippetError::LookupFailed(err))
1429    }
1430}
1431
1432#[cfg(test)]
1433mod tests {
1434    use super::{lookup_line, BytePos, Span};
1435
1436    #[test]
1437    fn test_lookup_line() {
1438        let lines = &[BytePos(3), BytePos(17), BytePos(28)];
1439
1440        assert_eq!(lookup_line(lines, BytePos(0)), -1);
1441        assert_eq!(lookup_line(lines, BytePos(3)), 0);
1442        assert_eq!(lookup_line(lines, BytePos(4)), 0);
1443
1444        assert_eq!(lookup_line(lines, BytePos(16)), 0);
1445        assert_eq!(lookup_line(lines, BytePos(17)), 1);
1446        assert_eq!(lookup_line(lines, BytePos(18)), 1);
1447
1448        assert_eq!(lookup_line(lines, BytePos(28)), 2);
1449        assert_eq!(lookup_line(lines, BytePos(29)), 2);
1450    }
1451
1452    #[test]
1453    fn size_of_span() {
1454        assert_eq!(std::mem::size_of::<Span>(), 8);
1455    }
1456}