solar_interface/
span.rs

1use crate::{BytePos, SessionGlobals};
2use std::{
3    cmp, fmt,
4    ops::{Deref, DerefMut, Range},
5};
6
7/// A source code location.
8///
9/// Essentially a `lo..hi` range into a `SourceMap` file's source code.
10///
11/// Note that `lo` and `hi` are both offset from the file's starting position in the source map,
12/// meaning that they are not always directly usable to index into the source string.
13///
14/// This is the case when there are multiple source files in the source map.
15/// Use [`SourceMap::span_to_snippet`](crate::SourceMap::span_to_snippet) to get the actual source
16/// code snippet of the span, or [`SourceMap::span_to_source`](crate::SourceMap::span_to_source) to
17/// get the source file and source code range.
18#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
19pub struct Span {
20    lo: BytePos,
21    hi: BytePos,
22}
23
24impl Default for Span {
25    #[inline(always)]
26    fn default() -> Self {
27        Self::DUMMY
28    }
29}
30
31impl Default for &Span {
32    #[inline(always)]
33    fn default() -> Self {
34        &Span::DUMMY
35    }
36}
37
38impl fmt::Debug for Span {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        // Use the global `SourceMap` to print the span. If that's not
41        // available, fall back to printing the raw values.
42
43        fn fallback(span: Span, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44            write!(f, "Span({lo}..{hi})", lo = span.lo().0, hi = span.hi().0)
45        }
46
47        if SessionGlobals::is_set() {
48            SessionGlobals::with(|g| {
49                let sm = &g.source_map;
50                if !sm.is_empty() {
51                    write!(f, "{}", sm.span_to_diagnostic_string(*self))
52                } else {
53                    fallback(*self, f)
54                }
55            })
56        } else {
57            fallback(*self, f)
58        }
59    }
60}
61
62impl Span {
63    /// A dummy span.
64    pub const DUMMY: Self = Self { lo: BytePos(0), hi: BytePos(0) };
65
66    /// Creates a new span from two byte positions.
67    #[inline]
68    pub fn new(mut lo: BytePos, mut hi: BytePos) -> Self {
69        if lo > hi {
70            std::mem::swap(&mut lo, &mut hi);
71        }
72        Self { lo, hi }
73    }
74
75    /// Creates a new span from two byte positions, without checking if `lo` is less than or equal
76    /// to `hi`.
77    ///
78    /// This is not inherently unsafe, however the behavior of various methods is undefined if `lo`
79    /// is greater than `hi`.
80    #[inline]
81    #[cfg_attr(debug_assertions, track_caller)]
82    pub fn new_unchecked(lo: BytePos, hi: BytePos) -> Self {
83        debug_assert!(lo <= hi, "creating span with lo {lo:?} > hi {hi:?}");
84        Self { lo, hi }
85    }
86
87    /// Returns the span as a `Range<usize>`.
88    ///
89    /// Note that this may not be directly usable to index into the source string.
90    /// See the [type-level documentation][Span] for more information.
91    #[inline]
92    pub fn to_range(self) -> Range<usize> {
93        self.lo().to_usize()..self.hi().to_usize()
94    }
95
96    /// Returns the span as a `Range<u32>`.
97    ///
98    /// Note that this may not be directly usable to index into the source string.
99    /// See the [type-level documentation][Span] for more information.
100    #[inline]
101    pub fn to_u32_range(self) -> Range<u32> {
102        self.lo().to_u32()..self.hi().to_u32()
103    }
104
105    /// Returns the span's start position.
106    ///
107    /// Note that this may not be directly usable to index into the source string.
108    /// See the [type-level documentation][Span] for more information.
109    #[inline(always)]
110    pub fn lo(self) -> BytePos {
111        self.lo
112    }
113
114    /// Creates a new span with the same hi position as this span and the given lo position.
115    #[inline]
116    pub fn with_lo(self, lo: BytePos) -> Self {
117        Self::new(lo, self.hi())
118    }
119
120    /// Returns the span's end position.
121    ///
122    /// Note that this may not be directly usable to index into the source string.
123    /// See the [type-level documentation][Span] for more information.
124    #[inline(always)]
125    pub fn hi(self) -> BytePos {
126        self.hi
127    }
128
129    /// Creates a new span with the same lo position as this span and the given hi position.
130    #[inline]
131    pub fn with_hi(self, hi: BytePos) -> Self {
132        Self::new(self.lo(), hi)
133    }
134
135    /// Creates a new span representing an empty span at the beginning of this span.
136    #[inline]
137    pub fn shrink_to_lo(self) -> Self {
138        Self::new(self.lo(), self.lo())
139    }
140
141    /// Creates a new span representing an empty span at the end of this span.
142    #[inline]
143    pub fn shrink_to_hi(self) -> Self {
144        Self::new(self.hi(), self.hi())
145    }
146
147    /// Returns `true` if this is a dummy span.
148    #[inline]
149    pub fn is_dummy(self) -> bool {
150        self == Self::DUMMY
151    }
152
153    /// Returns `true` if `self` fully encloses `other`.
154    #[inline]
155    pub fn contains(self, other: Self) -> bool {
156        self.lo() <= other.lo() && other.hi() <= self.hi()
157    }
158
159    /// Returns `true` if `self` touches `other`.
160    #[inline]
161    pub fn overlaps(self, other: Self) -> bool {
162        self.lo() < other.hi() && other.lo() < self.hi()
163    }
164
165    /// Returns `true` if `self` and `other` are equal.
166    #[inline]
167    pub fn is_empty(self, other: Self) -> bool {
168        self.lo() == other.lo() && self.hi() == other.hi()
169    }
170
171    /// Splits a span into two composite spans around a certain position.
172    #[inline]
173    pub fn split_at(self, pos: u32) -> (Self, Self) {
174        let len = self.hi().0 - self.lo().0;
175        debug_assert!(pos <= len);
176
177        let split_pos = BytePos(self.lo().0 + pos);
178        (Self::new(self.lo(), split_pos), Self::new(split_pos, self.hi()))
179    }
180
181    /// Returns a `Span` that would enclose both `self` and `end`.
182    ///
183    /// Note that this can also be used to extend the span "backwards":
184    /// `start.to(end)` and `end.to(start)` return the same `Span`.
185    ///
186    /// ```text
187    ///     ____             ___
188    ///     self lorem ipsum end
189    ///     ^^^^^^^^^^^^^^^^^^^^
190    /// ```
191    #[inline]
192    pub fn to(self, end: Self) -> Self {
193        Self::new(cmp::min(self.lo(), end.lo()), cmp::max(self.hi(), end.hi()))
194    }
195
196    /// Returns a `Span` between the end of `self` to the beginning of `end`.
197    ///
198    /// ```text
199    ///     ____             ___
200    ///     self lorem ipsum end
201    ///         ^^^^^^^^^^^^^
202    /// ```
203    #[inline]
204    pub fn between(self, end: Self) -> Self {
205        Self::new(self.hi(), end.lo())
206    }
207
208    /// Returns a `Span` from the beginning of `self` until the beginning of `end`.
209    ///
210    /// ```text
211    ///     ____             ___
212    ///     self lorem ipsum end
213    ///     ^^^^^^^^^^^^^^^^^
214    /// ```
215    #[inline]
216    pub fn until(self, end: Self) -> Self {
217        Self::new(self.lo(), end.lo())
218    }
219
220    /// Joins all the spans in the given iterator using [`to`](Self::to).
221    ///
222    /// Returns [`DUMMY`](Self::DUMMY) if the iterator is empty.
223    #[inline]
224    pub fn join_many(spans: impl IntoIterator<Item = Self>) -> Self {
225        spans.into_iter().reduce(Self::to).unwrap_or_default()
226    }
227
228    /// Joins the first and last span in the given iterator.
229    ///
230    /// Returns [`DUMMY`](Self::DUMMY) if the iterator is empty.
231    #[inline]
232    pub fn join_first_last(
233        spans: impl IntoIterator<Item = Self, IntoIter: DoubleEndedIterator>,
234    ) -> Self {
235        let mut spans = spans.into_iter();
236        let first = spans.next().unwrap_or_default();
237        if let Some(last) = spans.next_back() { first.to(last) } else { first }
238    }
239}
240
241/// A value paired with a source code location.
242///
243/// Wraps any value with a [`Span`] to track its location in the source code.
244/// Implements `Deref` and `DerefMut` for transparent access to the inner value.
245#[derive(Clone, Copy, Debug, Default)]
246pub struct Spanned<T> {
247    pub span: Span,
248    pub data: T,
249}
250
251impl<T> Deref for Spanned<T> {
252    type Target = T;
253
254    fn deref(&self) -> &Self::Target {
255        &self.data
256    }
257}
258
259impl<T> DerefMut for Spanned<T> {
260    fn deref_mut(&mut self) -> &mut Self::Target {
261        &mut self.data
262    }
263}
264
265impl<T> Spanned<T> {
266    pub fn map<U, F>(self, f: F) -> Spanned<U>
267    where
268        F: FnOnce(T) -> U,
269    {
270        Spanned { span: self.span, data: f(self.data) }
271    }
272
273    pub fn as_ref(&self) -> Spanned<&T> {
274        Spanned { span: self.span, data: &self.data }
275    }
276
277    pub fn as_mut(&mut self) -> Spanned<&mut T> {
278        Spanned { span: self.span, data: &mut self.data }
279    }
280
281    pub fn into_inner(self) -> T {
282        self.data
283    }
284}