swc_common/
syntax_pos.rs

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