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 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 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 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}