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, Hash)]
19#[repr(Rust, packed(4))]
20pub struct Span {
21    // This is `SpanRepr` packed into a single value for:
22    // - improved codegen of derived traits
23    // - passed in a single register as an argument/return value
24    // - better layout computation for structs containing `Span`s
25    data: u64,
26}
27
28impl Default for Span {
29    #[inline(always)]
30    fn default() -> Self {
31        Self::DUMMY
32    }
33}
34
35impl Default for &Span {
36    #[inline(always)]
37    fn default() -> Self {
38        &Span::DUMMY
39    }
40}
41
42impl fmt::Debug for Span {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        // Use the global `SourceMap` to print the span. If that's not
45        // available, fall back to printing the raw values.
46
47        fn fallback(span: Span, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48            write!(f, "Span({lo}..{hi})", lo = span.lo().0, hi = span.hi().0)
49        }
50
51        if SessionGlobals::is_set() {
52            SessionGlobals::with(|g| {
53                let sm = &g.source_map;
54                if !sm.is_empty() {
55                    write!(f, "{}", sm.span_to_diagnostic_string(*self))
56                } else {
57                    fallback(*self, f)
58                }
59            })
60        } else {
61            fallback(*self, f)
62        }
63    }
64}
65
66impl PartialOrd for Span {
67    #[inline]
68    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
69        Some(self.cmp(other))
70    }
71}
72
73impl Ord for Span {
74    #[inline]
75    fn cmp(&self, other: &Self) -> cmp::Ordering {
76        self.lo().cmp(&other.lo()).then(self.hi().cmp(&other.hi()))
77    }
78}
79
80impl Span {
81    /// A dummy span.
82    pub const DUMMY: Self = Self::new_(BytePos(0), BytePos(0));
83
84    /// Creates a new span from two byte positions.
85    #[inline]
86    pub fn new(mut lo: BytePos, mut hi: BytePos) -> Self {
87        if lo > hi {
88            std::mem::swap(&mut lo, &mut hi);
89        }
90        Self::new_(lo, hi)
91    }
92
93    /// Creates a new span from two byte positions, without checking if `lo` is less than or equal
94    /// to `hi`.
95    ///
96    /// This is not inherently unsafe, however the behavior of various methods is undefined if `lo`
97    /// is greater than `hi`.
98    #[inline]
99    #[cfg_attr(debug_assertions, track_caller)]
100    pub fn new_unchecked(lo: BytePos, hi: BytePos) -> Self {
101        debug_assert!(lo <= hi, "creating span with lo {lo:?} > hi {hi:?}");
102        Self::new_(lo, hi)
103    }
104
105    #[inline(always)]
106    const fn new_(lo: BytePos, hi: BytePos) -> Self {
107        Self { data: (lo.0 as u64) | ((hi.0 as u64) << 32) }
108    }
109
110    /// Returns the span as a `Range<usize>`.
111    ///
112    /// Note that this may not be directly usable to index into the source string.
113    /// See the [type-level documentation][Span] for more information.
114    #[inline]
115    pub fn to_range(self) -> Range<usize> {
116        self.lo().to_usize()..self.hi().to_usize()
117    }
118
119    /// Returns the span as a `Range<u32>`.
120    ///
121    /// Note that this may not be directly usable to index into the source string.
122    /// See the [type-level documentation][Span] for more information.
123    #[inline]
124    pub fn to_u32_range(self) -> Range<u32> {
125        self.lo().to_u32()..self.hi().to_u32()
126    }
127
128    /// Returns the span's start position.
129    ///
130    /// Note that this may not be directly usable to index into the source string.
131    /// See the [type-level documentation][Span] for more information.
132    #[inline(always)]
133    pub fn lo(self) -> BytePos {
134        BytePos(self.data as u32)
135    }
136
137    /// Creates a new span with the same hi position as this span and the given lo position.
138    #[inline]
139    pub fn with_lo(self, lo: BytePos) -> Self {
140        Self::new(lo, self.hi())
141    }
142
143    /// Returns the span's end position.
144    ///
145    /// Note that this may not be directly usable to index into the source string.
146    /// See the [type-level documentation][Span] for more information.
147    #[inline(always)]
148    pub fn hi(self) -> BytePos {
149        BytePos((self.data >> 32) as u32)
150    }
151
152    /// Creates a new span with the same lo position as this span and the given hi position.
153    #[inline]
154    pub fn with_hi(self, hi: BytePos) -> Self {
155        Self::new(self.lo(), hi)
156    }
157
158    /// Creates a new span representing an empty span at the beginning of this span.
159    #[inline]
160    pub fn shrink_to_lo(self) -> Self {
161        Self::new(self.lo(), self.lo())
162    }
163
164    /// Creates a new span representing an empty span at the end of this span.
165    #[inline]
166    pub fn shrink_to_hi(self) -> Self {
167        Self::new(self.hi(), self.hi())
168    }
169
170    /// Returns `true` if this is a dummy span.
171    #[inline]
172    pub fn is_dummy(self) -> bool {
173        self == Self::DUMMY
174    }
175
176    /// Returns `true` if `self` fully encloses `other`.
177    #[inline]
178    pub fn contains(self, other: Self) -> bool {
179        self.lo() <= other.lo() && other.hi() <= self.hi()
180    }
181
182    /// Returns `true` if `self` touches `other`.
183    #[inline]
184    pub fn overlaps(self, other: Self) -> bool {
185        self.lo() < other.hi() && other.lo() < self.hi()
186    }
187
188    /// Returns `true` if `self` and `other` are equal.
189    #[inline]
190    pub fn is_empty(self, other: Self) -> bool {
191        self.lo() == other.lo() && self.hi() == other.hi()
192    }
193
194    /// Splits a span into two composite spans around a certain position.
195    #[inline]
196    pub fn split_at(self, pos: u32) -> (Self, Self) {
197        let len = self.hi().0 - self.lo().0;
198        debug_assert!(pos <= len);
199
200        let split_pos = BytePos(self.lo().0 + pos);
201        (Self::new(self.lo(), split_pos), Self::new(split_pos, self.hi()))
202    }
203
204    /// Returns a `Span` that would enclose both `self` and `end`.
205    ///
206    /// Note that this can also be used to extend the span "backwards":
207    /// `start.to(end)` and `end.to(start)` return the same `Span`.
208    ///
209    /// ```text
210    ///     ____             ___
211    ///     self lorem ipsum end
212    ///     ^^^^^^^^^^^^^^^^^^^^
213    /// ```
214    #[inline]
215    pub fn to(self, end: Self) -> Self {
216        Self::new(cmp::min(self.lo(), end.lo()), cmp::max(self.hi(), end.hi()))
217    }
218
219    /// Returns a `Span` between the end of `self` to the beginning of `end`.
220    ///
221    /// ```text
222    ///     ____             ___
223    ///     self lorem ipsum end
224    ///         ^^^^^^^^^^^^^
225    /// ```
226    #[inline]
227    pub fn between(self, end: Self) -> Self {
228        Self::new(self.hi(), end.lo())
229    }
230
231    /// Returns a `Span` from the beginning of `self` until the beginning of `end`.
232    ///
233    /// ```text
234    ///     ____             ___
235    ///     self lorem ipsum end
236    ///     ^^^^^^^^^^^^^^^^^
237    /// ```
238    #[inline]
239    pub fn until(self, end: Self) -> Self {
240        Self::new(self.lo(), end.lo())
241    }
242
243    /// Joins all the spans in the given iterator using [`to`](Self::to).
244    ///
245    /// Returns [`DUMMY`](Self::DUMMY) if the iterator is empty.
246    #[inline]
247    pub fn join_many(spans: impl IntoIterator<Item = Self>) -> Self {
248        spans.into_iter().reduce(Self::to).unwrap_or_default()
249    }
250
251    /// Joins the first and last span in the given iterator.
252    ///
253    /// Returns [`DUMMY`](Self::DUMMY) if the iterator is empty.
254    #[inline]
255    pub fn join_first_last(
256        spans: impl IntoIterator<Item = Self, IntoIter: DoubleEndedIterator>,
257    ) -> Self {
258        let mut spans = spans.into_iter();
259        let first = spans.next().unwrap_or_default();
260        if let Some(last) = spans.next_back() { first.to(last) } else { first }
261    }
262}
263
264/// A value paired with a source code location.
265///
266/// Wraps any value with a [`Span`] to track its location in the source code.
267/// Implements `Deref` and `DerefMut` for transparent access to the inner value.
268#[derive(Clone, Copy, Debug, Default)]
269pub struct Spanned<T> {
270    pub span: Span,
271    pub data: T,
272}
273
274impl<T> Deref for Spanned<T> {
275    type Target = T;
276
277    fn deref(&self) -> &Self::Target {
278        &self.data
279    }
280}
281
282impl<T> DerefMut for Spanned<T> {
283    fn deref_mut(&mut self) -> &mut Self::Target {
284        &mut self.data
285    }
286}
287
288impl<T> Spanned<T> {
289    pub fn map<U, F>(self, f: F) -> Spanned<U>
290    where
291        F: FnOnce(T) -> U,
292    {
293        Spanned { span: self.span, data: f(self.data) }
294    }
295
296    pub fn as_ref(&self) -> Spanned<&T> {
297        Spanned { span: self.span, data: &self.data }
298    }
299
300    pub fn as_mut(&mut self) -> Spanned<&mut T> {
301        Spanned { span: self.span, data: &mut self.data }
302    }
303
304    pub fn into_inner(self) -> T {
305        self.data
306    }
307}
308
309/// An `Option`-like enum that tracks the source location of an absent value.
310///
311/// This type is used to represent comma-separated items that can be omitted.
312/// - The [`Some`](Self::Some) variant holds the parsed item `T`. The item `T` itself is expected to
313///   be a spanned type.
314/// - The [`None`](Self::None) variant holds the [`Span`] of the empty slot, typically the location
315///   of the comma separator.
316#[derive(Clone, Copy, Debug)]
317pub enum SpannedOption<T> {
318    Some(T),
319    None(Span),
320}
321
322impl<T> SpannedOption<T> {
323    /// Returns `true` if the `SpannedOption` is `None`.
324    pub fn is_none(&self) -> bool {
325        matches!(&self, Self::None(_))
326    }
327
328    /// Returns `true` if the `SpannedOption` is `Some`.
329    pub fn is_some(&self) -> bool {
330        matches!(&self, Self::Some(_))
331    }
332
333    /// Converts the `SpannedOption` into an `Option`.
334    pub fn unspan(self) -> Option<T> {
335        match self {
336            Self::Some(value) => Some(value),
337            Self::None(_) => None,
338        }
339    }
340
341    /// Maps the `SpannedOption` to a new `SpannedOption`.
342    pub fn map<U, F>(self, f: F) -> SpannedOption<U>
343    where
344        F: FnOnce(T) -> U,
345    {
346        match self {
347            Self::Some(value) => SpannedOption::Some(f(value)),
348            Self::None(span) => SpannedOption::None(span),
349        }
350    }
351
352    /// Converts from `&SpannedOption<T>` to `SpannedOption<&T>`.
353    pub fn as_ref(&self) -> SpannedOption<&T> {
354        match &self {
355            Self::Some(value) => SpannedOption::Some(value),
356            Self::None(span) => SpannedOption::None(*span),
357        }
358    }
359}
360
361impl<T: Deref> SpannedOption<T> {
362    /// Converts from `SpannedOption<T>` (or `&SpannedOption<T>`) to `SpannedOption<&T::Target>`.
363    pub fn as_deref(&self) -> SpannedOption<&<T as Deref>::Target> {
364        self.as_ref().map(Deref::deref)
365    }
366}
367
368impl<T> From<SpannedOption<T>> for Option<T> {
369    fn from(spanned: SpannedOption<T>) -> Self {
370        spanned.unspan()
371    }
372}