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 /// Returns the span as a `Range<usize>`.
76 ///
77 /// Note that this may not be directly usable to index into the source string.
78 /// See the [type-level documentation][Span] for more information.
79 #[inline]
80 pub fn to_range(self) -> Range<usize> {
81 self.lo().to_usize()..self.hi().to_usize()
82 }
83
84 /// Returns the span as a `Range<u32>`.
85 ///
86 /// Note that this may not be directly usable to index into the source string.
87 /// See the [type-level documentation][Span] for more information.
88 #[inline]
89 pub fn to_u32_range(self) -> Range<u32> {
90 self.lo().to_u32()..self.hi().to_u32()
91 }
92
93 /// Returns the span's start position.
94 ///
95 /// Note that this may not be directly usable to index into the source string.
96 /// See the [type-level documentation][Span] for more information.
97 #[inline(always)]
98 pub fn lo(self) -> BytePos {
99 self.lo
100 }
101
102 /// Creates a new span with the same hi position as this span and the given lo position.
103 #[inline]
104 pub fn with_lo(self, lo: BytePos) -> Self {
105 Self::new(lo, self.hi())
106 }
107
108 /// Returns the span's end position.
109 ///
110 /// Note that this may not be directly usable to index into the source string.
111 /// See the [type-level documentation][Span] for more information.
112 #[inline(always)]
113 pub fn hi(self) -> BytePos {
114 self.hi
115 }
116
117 /// Creates a new span with the same lo position as this span and the given hi position.
118 #[inline]
119 pub fn with_hi(self, hi: BytePos) -> Self {
120 Self::new(self.lo(), hi)
121 }
122
123 /// Creates a new span representing an empty span at the beginning of this span.
124 #[inline]
125 pub fn shrink_to_lo(self) -> Self {
126 Self::new(self.lo(), self.lo())
127 }
128
129 /// Creates a new span representing an empty span at the end of this span.
130 #[inline]
131 pub fn shrink_to_hi(self) -> Self {
132 Self::new(self.hi(), self.hi())
133 }
134
135 /// Returns `true` if this is a dummy span.
136 #[inline]
137 pub fn is_dummy(self) -> bool {
138 self == Self::DUMMY
139 }
140
141 /// Returns `true` if `self` fully encloses `other`.
142 #[inline]
143 pub fn contains(self, other: Self) -> bool {
144 self.lo() <= other.lo() && other.hi() <= self.hi()
145 }
146
147 /// Returns `true` if `self` touches `other`.
148 #[inline]
149 pub fn overlaps(self, other: Self) -> bool {
150 self.lo() < other.hi() && other.lo() < self.hi()
151 }
152
153 /// Returns `true` if `self` and `other` are equal.
154 #[inline]
155 pub fn is_empty(self, other: Self) -> bool {
156 self.lo() == other.lo() && self.hi() == other.hi()
157 }
158
159 /// Splits a span into two composite spans around a certain position.
160 #[inline]
161 pub fn split_at(self, pos: u32) -> (Self, Self) {
162 let len = self.hi().0 - self.lo().0;
163 debug_assert!(pos <= len);
164
165 let split_pos = BytePos(self.lo().0 + pos);
166 (Self::new(self.lo(), split_pos), Self::new(split_pos, self.hi()))
167 }
168
169 /// Returns a `Span` that would enclose both `self` and `end`.
170 ///
171 /// Note that this can also be used to extend the span "backwards":
172 /// `start.to(end)` and `end.to(start)` return the same `Span`.
173 ///
174 /// ```text
175 /// ____ ___
176 /// self lorem ipsum end
177 /// ^^^^^^^^^^^^^^^^^^^^
178 /// ```
179 #[inline]
180 pub fn to(self, end: Self) -> Self {
181 Self::new(cmp::min(self.lo(), end.lo()), cmp::max(self.hi(), end.hi()))
182 }
183
184 /// Returns a `Span` between the end of `self` to the beginning of `end`.
185 ///
186 /// ```text
187 /// ____ ___
188 /// self lorem ipsum end
189 /// ^^^^^^^^^^^^^
190 /// ```
191 #[inline]
192 pub fn between(self, end: Self) -> Self {
193 Self::new(self.hi(), end.lo())
194 }
195
196 /// Returns a `Span` from the beginning of `self` until the beginning of `end`.
197 ///
198 /// ```text
199 /// ____ ___
200 /// self lorem ipsum end
201 /// ^^^^^^^^^^^^^^^^^
202 /// ```
203 #[inline]
204 pub fn until(self, end: Self) -> Self {
205 Self::new(self.lo(), end.lo())
206 }
207
208 /// Joins all the spans in the given iterator using [`to`](Self::to).
209 ///
210 /// Returns [`DUMMY`](Self::DUMMY) if the iterator is empty.
211 #[inline]
212 pub fn join_many(spans: impl IntoIterator<Item = Self>) -> Self {
213 spans.into_iter().reduce(Self::to).unwrap_or_default()
214 }
215
216 /// Joins the first and last span in the given iterator.
217 ///
218 /// Returns [`DUMMY`](Self::DUMMY) if the iterator is empty.
219 #[inline]
220 pub fn join_first_last(
221 spans: impl IntoIterator<Item = Self, IntoIter: DoubleEndedIterator>,
222 ) -> Self {
223 let mut spans = spans.into_iter();
224 let first = spans.next().unwrap_or_default();
225 if let Some(last) = spans.next_back() { first.to(last) } else { first }
226 }
227}
228
229/// A value paired with a source code location.
230///
231/// Wraps any value with a [`Span`] to track its location in the source code.
232/// Implements `Deref` and `DerefMut` for transparent access to the inner value.
233#[derive(Clone, Copy, Debug, Default)]
234pub struct Spanned<T> {
235 pub span: Span,
236 pub data: T,
237}
238
239impl<T> Deref for Spanned<T> {
240 type Target = T;
241
242 fn deref(&self) -> &Self::Target {
243 &self.data
244 }
245}
246
247impl<T> DerefMut for Spanned<T> {
248 fn deref_mut(&mut self) -> &mut Self::Target {
249 &mut self.data
250 }
251}
252
253impl<T> Spanned<T> {
254 pub fn map<U, F>(self, f: F) -> Spanned<U>
255 where
256 F: FnOnce(T) -> U,
257 {
258 Spanned { span: self.span, data: f(self.data) }
259 }
260
261 pub fn as_ref(&self) -> Spanned<&T> {
262 Spanned { span: self.span, data: &self.data }
263 }
264
265 pub fn as_mut(&mut self) -> Spanned<&mut T> {
266 Spanned { span: self.span, data: &mut self.data }
267 }
268
269 pub fn into_inner(self) -> T {
270 self.data
271 }
272}