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    pub fn contains_start<R>(&self, other: &Loc<R>) -> bool {
218        other.file_id == self.file_id
219            && other.span.start() >= self.span.start()
220            && other.span.start() < self.span.end()
221    }
222
223    /// Shrinks a Loc on the left size by the width of the specified string.
224    /// For example (( abc ))
225    ///             ^^^^^^^^^ .shrink_left("((")
226    /// results in
227    /// For example (( abc ))
228    ///               ^^^^^^^
229    ///
230    /// Note that this is only valid if the loc actually points to the specified string,
231    /// otherwise the behaviour is undefined
232    ///
233    /// If the span is empty, does nothing
234    pub fn shrink_left(&self, s: &str) -> Loc<()> {
235        if self.span.start() == self.span.end() {
236            self.loc()
237        } else {
238            Loc {
239                inner: (),
240                span: Span::new(
241                    self.span.start() + ByteOffset::from_str_len(s),
242                    self.span.end(),
243                ),
244                file_id: self.file_id,
245            }
246        }
247    }
248
249    /// See [shrink_right]
250    pub fn shrink_right(&self, s: &str) -> Loc<()> {
251        if self.span.start() == self.span.end() {
252            self.loc()
253        } else {
254            Loc {
255                inner: (),
256                span: Span::new(
257                    self.span.start(),
258                    self.span.end() - ByteOffset::from_str_len(s),
259                ),
260                file_id: self.file_id,
261            }
262        }
263    }
264}
265
266impl<T, E> Loc<Result<T, E>> {
267    pub fn map_err<E2>(self, err_fn: impl Fn(E, Loc<()>) -> E2) -> Result<Loc<T>, E2> {
268        match self.inner {
269            Ok(inner) => Ok(Loc {
270                inner,
271                span: self.span,
272                file_id: self.file_id,
273            }),
274            Err(e) => Err(err_fn(e, ().at(self.file_id, &self.span))),
275        }
276    }
277}
278
279impl<T> PartialEq for Loc<T>
280where
281    T: PartialEq,
282{
283    fn eq(&self, other: &Self) -> bool {
284        self.inner == other.inner
285    }
286}
287
288impl<T> Eq for Loc<T> where T: Eq {}
289
290impl<T> PartialOrd for Loc<T>
291where
292    T: PartialOrd,
293{
294    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
295        self.inner.partial_cmp(&other.inner)
296    }
297}
298
299impl<T> Ord for Loc<T>
300where
301    T: Ord,
302{
303    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
304        self.inner.cmp(&other.inner)
305    }
306}
307
308impl<T> std::fmt::Display for Loc<T>
309where
310    T: std::fmt::Display,
311{
312    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
313        write!(f, "{}", self.inner)
314    }
315}
316
317impl<T> std::hash::Hash for Loc<T>
318where
319    T: std::hash::Hash,
320{
321    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
322        self.inner.hash(state)
323    }
324}
325
326impl<T> std::fmt::Debug for Loc<T>
327where
328    T: std::fmt::Debug,
329{
330    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
331        self.inner.fmt(f)
332    }
333}
334
335impl<T> std::ops::Deref for Loc<T> {
336    type Target = T;
337
338    fn deref(&self) -> &Self::Target {
339        &self.inner
340    }
341}
342impl<T> std::ops::DerefMut for Loc<T> {
343    fn deref_mut(&mut self) -> &mut Self::Target {
344        &mut self.inner
345    }
346}
347
348impl<T> AsLabel for Loc<T> {
349    fn file_id(&self) -> usize {
350        self.file_id
351    }
352
353    fn span(&self) -> std::ops::Range<usize> {
354        self.span.into()
355    }
356}