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    fn between(
78        self,
79        file_id: usize,
80        start: &impl HasCodespan,
81        end: &impl HasCodespan,
82    ) -> Loc<Self> {
83        Loc::new(self, start.codespan().merge(end.codespan()), file_id)
84    }
85
86    fn between_locs<T, Y>(self, start: &Loc<T>, end: &Loc<Y>) -> Loc<Self> {
87        assert!(start.file_id == end.file_id);
88        Loc::new(self, start.codespan().merge(end.codespan()), end.file_id())
89    }
90
91    fn nowhere(self) -> Loc<Self>
92    where
93        Self: Sized,
94    {
95        self.at(0, &Span::new(0, 0))
96    }
97}
98
99impl<T> WithLocation for T {}
100
101pub fn lspan(s: logos::Span) -> Span {
102    Span::new(s.start as u32, s.end as u32)
103}
104
105#[cfg(test)]
106pub fn dummy() -> Span {
107    Span::new(0, 0)
108}
109
110#[derive(Clone, Copy, Serialize, Deserialize)]
111pub struct Loc<T> {
112    pub inner: T,
113    pub span: Span,
114    pub file_id: usize,
115}
116
117impl<T> Loc<T> {
118    pub fn new(inner: T, span: Span, file_id: usize) -> Self {
119        Self {
120            inner,
121            span,
122            file_id,
123        }
124    }
125    pub fn nowhere(inner: T) -> Self {
126        Self::new(inner, Span::new(0, 0), 0)
127    }
128
129    pub fn strip(self) -> T {
130        self.inner
131    }
132
133    pub fn strip_ref(&self) -> &T {
134        &self.inner
135    }
136
137    pub fn separate(self) -> (Self, Span) {
138        let span = self.span;
139        (self, span)
140    }
141
142    pub fn separate_loc(self) -> (Self, Loc<()>) {
143        let loc = self.loc();
144        (self, loc)
145    }
146
147    pub fn split(self) -> (T, Span) {
148        (self.inner, self.span)
149    }
150    pub fn split_ref(&self) -> (&T, Span) {
151        (&self.inner, self.span)
152    }
153    pub fn split_loc(self) -> (T, Loc<()>) {
154        let loc = self.loc();
155        (self.inner, loc)
156    }
157    pub fn split_loc_ref(&self) -> (&T, Loc<()>) {
158        let loc = self.loc();
159        (&self.inner, loc)
160    }
161
162    pub fn is_same_loc<R>(&self, other: &Loc<R>) -> bool {
163        self.span == other.span && self.file_id == other.file_id
164    }
165
166    pub fn map<Y>(self, mut op: impl FnMut(T) -> Y) -> Loc<Y> {
167        Loc {
168            inner: op(self.inner),
169            span: self.span,
170            file_id: self.file_id,
171        }
172    }
173
174    pub fn try_map<Y, E>(self, mut op: impl FnMut(T) -> Result<Y, E>) -> Result<Loc<Y>, E> {
175        Ok(Loc {
176            inner: op(self.inner)?,
177            span: self.span,
178            file_id: self.file_id,
179        })
180    }
181
182    pub fn map_ref<Y>(&self, mut op: impl FnMut(&T) -> Y) -> Loc<Y> {
183        Loc {
184            inner: op(&self.inner),
185            span: self.span,
186            file_id: self.file_id,
187        }
188    }
189
190    pub fn try_map_ref<Y, E>(&self, mut op: impl FnMut(&T) -> Result<Y, E>) -> Result<Loc<Y>, E> {
191        Ok(Loc {
192            inner: op(&self.inner)?,
193            span: self.span,
194            file_id: self.file_id,
195        })
196    }
197
198    pub fn loc(&self) -> Loc<()> {
199        Loc {
200            inner: (),
201            span: self.span,
202            file_id: self.file_id,
203        }
204    }
205
206    pub fn contains_start<R>(&self, other: &Loc<R>) -> bool {
207        other.file_id == self.file_id
208            && other.span.start() >= self.span.start()
209            && other.span.start() < self.span.end()
210    }
211
212    pub fn start_span(&self) -> Loc<()> {
213        ().at(
214            self.file_id,
215            &Span::new(self.span.start(), self.span.start()),
216        )
217    }
218    pub fn end_span(&self) -> Loc<()> {
219        ().at(self.file_id, &Span::new(self.span.end(), self.span.end()))
220    }
221
222    /// Shrinks a Loc on the left size by the width of the specified string.
223    /// For example (( abc ))
224    ///             ^^^^^^^^^ .shrink_left("((")
225    /// results in
226    /// For example (( abc ))
227    ///               ^^^^^^^
228    ///
229    /// Note that this is only valid if the loc actually points to the specified string,
230    /// otherwise the behaviour is undefined
231    ///
232    /// If the span is empty, does nothing
233    pub fn shrink_left(&self, s: &str) -> Loc<()> {
234        if self.span.start() == self.span.end() {
235            self.loc()
236        } else {
237            Loc {
238                inner: (),
239                span: Span::new(
240                    self.span.start() + ByteOffset::from_str_len(s),
241                    self.span.end(),
242                ),
243                file_id: self.file_id,
244            }
245        }
246    }
247
248    /// See [shrink_right]
249    pub fn shrink_right(&self, s: &str) -> Loc<()> {
250        if self.span.start() == self.span.end() {
251            self.loc()
252        } else {
253            Loc {
254                inner: (),
255                span: Span::new(
256                    self.span.start(),
257                    self.span.end() - ByteOffset::from_str_len(s),
258                ),
259                file_id: self.file_id,
260            }
261        }
262    }
263}
264
265impl<T, E> Loc<Result<T, E>> {
266    pub fn map_err<E2>(self, err_fn: impl Fn(E, Loc<()>) -> E2) -> Result<Loc<T>, E2> {
267        match self.inner {
268            Ok(inner) => Ok(Loc {
269                inner,
270                span: self.span,
271                file_id: self.file_id,
272            }),
273            Err(e) => Err(err_fn(e, ().at(self.file_id, &self.span))),
274        }
275    }
276}
277
278impl<T> Loc<Option<T>>
279where
280    T: WithLocation,
281{
282    pub fn transpose(self) -> Option<Loc<T>> {
283        let loc = self.loc();
284        match self.inner {
285            Some(inner) => Some(inner.at_loc(&loc)),
286            None => None,
287        }
288    }
289}
290
291impl<T> PartialEq for Loc<T>
292where
293    T: PartialEq,
294{
295    fn eq(&self, other: &Self) -> bool {
296        self.inner == other.inner
297    }
298}
299
300impl<T> Eq for Loc<T> where T: Eq {}
301
302impl<T> PartialOrd for Loc<T>
303where
304    T: PartialOrd,
305{
306    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
307        self.inner.partial_cmp(&other.inner)
308    }
309}
310
311impl<T> Ord for Loc<T>
312where
313    T: Ord,
314{
315    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
316        self.inner.cmp(&other.inner)
317    }
318}
319
320impl<T> std::fmt::Display for Loc<T>
321where
322    T: std::fmt::Display,
323{
324    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
325        write!(f, "{}", self.inner)
326    }
327}
328
329impl<T> std::hash::Hash for Loc<T>
330where
331    T: std::hash::Hash,
332{
333    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
334        self.inner.hash(state)
335    }
336}
337
338impl<T> std::fmt::Debug for Loc<T>
339where
340    T: std::fmt::Debug,
341{
342    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
343        self.inner.fmt(f)
344    }
345}
346
347impl<T> std::ops::Deref for Loc<T> {
348    type Target = T;
349
350    fn deref(&self) -> &Self::Target {
351        &self.inner
352    }
353}
354impl<T> std::ops::DerefMut for Loc<T> {
355    fn deref_mut(&mut self) -> &mut Self::Target {
356        &mut self.inner
357    }
358}
359
360impl<T> AsLabel for Loc<T> {
361    fn file_id(&self) -> usize {
362        self.file_id
363    }
364
365    fn span(&self) -> std::ops::Range<usize> {
366        self.span.into()
367    }
368}