Skip to main content

spade_common/
location_info.rs

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