spade_common/
location_info.rs

1use num::{BigInt, BigUint};
2use serde::{Deserialize, Serialize};
3use spade_codespan::{ByteOffset, Span};
4use spade_codespan_reporting::diagnostic::Label;
5
6pub trait AsLabel {
7    fn file_id(&self) -> usize;
8    fn span(&self) -> std::ops::Range<usize>;
9
10    fn primary_label(&self) -> Label<usize> {
11        Label::primary(self.file_id(), self.span())
12    }
13
14    fn secondary_label(&self) -> Label<usize> {
15        Label::secondary(self.file_id(), self.span())
16    }
17}
18
19pub type FullSpan = (Span, usize);
20
21impl<T> From<&Loc<T>> for FullSpan {
22    fn from(loc: &Loc<T>) -> Self {
23        (loc.span, loc.file_id)
24    }
25}
26
27impl<T> From<Loc<T>> for FullSpan {
28    fn from(loc: Loc<T>) -> Self {
29        FullSpan::from(&loc)
30    }
31}
32
33impl AsLabel for FullSpan {
34    fn span(&self) -> std::ops::Range<usize> {
35        self.0.into()
36    }
37
38    fn file_id(&self) -> usize {
39        self.1
40    }
41}
42
43pub trait HasCodespan {
44    fn codespan(&self) -> Span;
45}
46
47impl<T> HasCodespan for Loc<T> {
48    fn codespan(&self) -> Span {
49        self.span
50    }
51}
52
53impl HasCodespan for Span {
54    fn codespan(&self) -> Span {
55        *self
56    }
57}
58
59impl HasCodespan for std::ops::Range<usize> {
60    fn codespan(&self) -> Span {
61        lspan(self.clone())
62    }
63}
64
65pub trait WithLocation: Sized {
66    fn at(self, file_id: usize, span: &impl HasCodespan) -> Loc<Self>
67    where
68        Self: Sized,
69    {
70        Loc::new(self, span.codespan(), file_id)
71    }
72
73    /// Creates a new Loc from another Loc
74    fn at_loc<T: Sized>(self, loc: &Loc<T>) -> Loc<Self> {
75        Loc::new(self, loc.span, loc.file_id)
76    }
77
78    fn between(
79        self,
80        file_id: usize,
81        start: &impl HasCodespan,
82        end: &impl HasCodespan,
83    ) -> Loc<Self> {
84        Loc::new(self, start.codespan().merge(end.codespan()), file_id)
85    }
86
87    fn between_locs<T, Y>(self, start: &Loc<T>, end: &Loc<Y>) -> Loc<Self> {
88        assert!(start.file_id == end.file_id);
89        Loc::new(self, start.codespan().merge(end.codespan()), end.file_id())
90    }
91
92    fn nowhere(self) -> Loc<Self>
93    where
94        Self: Sized,
95    {
96        self.at(0, &Span::new(0, 0))
97    }
98}
99
100impl WithLocation for () {}
101impl WithLocation for BigInt {}
102impl WithLocation for BigUint {}
103impl WithLocation for u128 {}
104impl WithLocation for u64 {}
105impl WithLocation for i64 {}
106impl WithLocation for usize {}
107impl WithLocation for bool {}
108impl WithLocation for String {}
109impl<'a> WithLocation for &'a str {}
110impl<T> WithLocation for Vec<T> {}
111
112pub fn lspan(s: logos::Span) -> Span {
113    Span::new(s.start as u32, s.end as u32)
114}
115
116#[cfg(test)]
117pub fn dummy() -> Span {
118    Span::new(0, 0)
119}
120
121#[derive(Clone, Copy, Serialize, Deserialize)]
122pub struct Loc<T> {
123    pub inner: T,
124    pub span: Span,
125    pub file_id: usize,
126}
127
128impl<T> Loc<T> {
129    pub fn new(inner: T, span: Span, file_id: usize) -> Self {
130        Self {
131            inner,
132            span,
133            file_id,
134        }
135    }
136    pub fn nowhere(inner: T) -> Self {
137        Self::new(inner, Span::new(0, 0), 0)
138    }
139
140    pub fn strip(self) -> T {
141        self.inner
142    }
143
144    pub fn strip_ref(&self) -> &T {
145        &self.inner
146    }
147
148    pub fn separate(self) -> (Self, Span) {
149        let span = self.span;
150        (self, span)
151    }
152
153    pub fn separate_loc(self) -> (Self, Loc<()>) {
154        let loc = self.loc();
155        (self, loc)
156    }
157
158    pub fn split(self) -> (T, Span) {
159        (self.inner, self.span)
160    }
161    pub fn split_ref(&self) -> (&T, Span) {
162        (&self.inner, self.span)
163    }
164    pub fn split_loc(self) -> (T, Loc<()>) {
165        let loc = self.loc();
166        (self.inner, loc)
167    }
168    pub fn split_loc_ref(&self) -> (&T, Loc<()>) {
169        let loc = self.loc();
170        (&self.inner, loc)
171    }
172
173    pub fn is_same_loc<R>(&self, other: &Loc<R>) -> bool {
174        self.span == other.span && self.file_id == other.file_id
175    }
176
177    pub fn map<Y>(self, mut op: impl FnMut(T) -> Y) -> Loc<Y> {
178        Loc {
179            inner: op(self.inner),
180            span: self.span,
181            file_id: self.file_id,
182        }
183    }
184
185    pub fn try_map<Y, E>(self, mut op: impl FnMut(T) -> Result<Y, E>) -> Result<Loc<Y>, E> {
186        Ok(Loc {
187            inner: op(self.inner)?,
188            span: self.span,
189            file_id: self.file_id,
190        })
191    }
192
193    pub fn map_ref<Y>(&self, mut op: impl FnMut(&T) -> Y) -> Loc<Y> {
194        Loc {
195            inner: op(&self.inner),
196            span: self.span,
197            file_id: self.file_id,
198        }
199    }
200
201    pub fn try_map_ref<Y, E>(&self, mut op: impl FnMut(&T) -> Result<Y, E>) -> Result<Loc<Y>, E> {
202        Ok(Loc {
203            inner: op(&self.inner)?,
204            span: self.span,
205            file_id: self.file_id,
206        })
207    }
208
209    pub fn loc(&self) -> Loc<()> {
210        Loc {
211            inner: (),
212            span: self.span,
213            file_id: self.file_id,
214        }
215    }
216
217    /// Shrinks a Loc on the left size by the width of the specified string.
218    /// For example (( abc ))
219    ///             ^^^^^^^^^ .shrink_left("((")
220    /// results in
221    /// For example (( abc ))
222    ///               ^^^^^^^
223    ///
224    /// Note that this is only valid if the loc actually points to the specified string,
225    /// otherwise the behaviour is undefined
226    ///
227    /// If the span is empty, does nothing
228    pub fn shrink_left(&self, s: &str) -> Loc<()> {
229        if self.span.start() == self.span.end() {
230            self.loc()
231        } else {
232            Loc {
233                inner: (),
234                span: Span::new(
235                    self.span.start() + ByteOffset::from_str_len(s),
236                    self.span.end(),
237                ),
238                file_id: self.file_id,
239            }
240        }
241    }
242
243    /// See [shrink_right]
244    pub fn shrink_right(&self, s: &str) -> Loc<()> {
245        if self.span.start() == self.span.end() {
246            self.loc()
247        } else {
248            Loc {
249                inner: (),
250                span: Span::new(
251                    self.span.start(),
252                    self.span.end() - ByteOffset::from_str_len(s),
253                ),
254                file_id: self.file_id,
255            }
256        }
257    }
258}
259
260impl<T, E> Loc<Result<T, E>> {
261    pub fn map_err<E2>(self, err_fn: impl Fn(E, Loc<()>) -> E2) -> Result<Loc<T>, E2> {
262        match self.inner {
263            Ok(inner) => Ok(Loc {
264                inner,
265                span: self.span,
266                file_id: self.file_id,
267            }),
268            Err(e) => Err(err_fn(e, ().at(self.file_id, &self.span))),
269        }
270    }
271}
272
273impl<T> PartialEq for Loc<T>
274where
275    T: PartialEq,
276{
277    fn eq(&self, other: &Self) -> bool {
278        self.inner == other.inner
279    }
280}
281
282impl<T> Eq for Loc<T> where T: Eq {}
283
284impl<T> PartialOrd for Loc<T>
285where
286    T: PartialOrd,
287{
288    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
289        self.inner.partial_cmp(&other.inner)
290    }
291}
292
293impl<T> Ord for Loc<T>
294where
295    T: Ord,
296{
297    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
298        self.inner.cmp(&other.inner)
299    }
300}
301
302impl<T> std::fmt::Display for Loc<T>
303where
304    T: std::fmt::Display,
305{
306    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307        write!(f, "{}", self.inner)
308    }
309}
310
311impl<T> std::hash::Hash for Loc<T>
312where
313    T: std::hash::Hash,
314{
315    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
316        self.inner.hash(state)
317    }
318}
319
320impl<T> std::fmt::Debug for Loc<T>
321where
322    T: std::fmt::Debug,
323{
324    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
325        self.inner.fmt(f)
326    }
327}
328
329impl<T> std::ops::Deref for Loc<T> {
330    type Target = T;
331
332    fn deref(&self) -> &Self::Target {
333        &self.inner
334    }
335}
336impl<T> std::ops::DerefMut for Loc<T> {
337    fn deref_mut(&mut self) -> &mut Self::Target {
338        &mut self.inner
339    }
340}
341
342impl<T> AsLabel for Loc<T> {
343    fn file_id(&self) -> usize {
344        self.file_id
345    }
346
347    fn span(&self) -> std::ops::Range<usize> {
348        self.span.into()
349    }
350}