solar_interface/
span.rs

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