Skip to main content

noirc_errors/
position.rs

1use codespan::Span as ByteSpan;
2use fm::FileId;
3use serde::{Deserialize, Serialize};
4use std::{
5    cmp::Ordering,
6    hash::{Hash, Hasher},
7    ops::Range,
8};
9
10pub type Position = u32;
11
12#[derive(Eq, Debug, Clone)]
13pub struct Located<T> {
14    pub contents: T,
15    location: Location,
16}
17
18/// This is important for tests. Two Located objects are equal if their content is equal
19/// They may not have the same location. Use `.location()` to test for Location being equal specifically
20impl<T: PartialEq> PartialEq<Located<T>> for Located<T> {
21    fn eq(&self, other: &Located<T>) -> bool {
22        self.contents == other.contents
23    }
24}
25
26impl<T: PartialOrd> PartialOrd<Located<T>> for Located<T> {
27    fn partial_cmp(&self, other: &Located<T>) -> Option<Ordering> {
28        self.contents.partial_cmp(&other.contents)
29    }
30}
31
32impl<T: Ord> Ord for Located<T> {
33    fn cmp(&self, other: &Located<T>) -> Ordering {
34        self.contents.cmp(&other.contents)
35    }
36}
37
38impl<T: Default> Default for Located<T> {
39    fn default() -> Self {
40        Self { contents: Default::default(), location: Location::dummy() }
41    }
42}
43
44/// Hash-based data structures (HashMap, HashSet) rely on the inverse of Hash
45/// being injective, i.e. x.eq(y) => hash(x, H) == hash(y, H), we hence align
46/// this with the above
47impl<T: Hash> Hash for Located<T> {
48    fn hash<H: Hasher>(&self, state: &mut H) {
49        self.contents.hash(state);
50    }
51}
52
53impl<T> Located<T> {
54    pub fn from(location: Location, contents: T) -> Located<T> {
55        Located { location, contents }
56    }
57
58    pub fn span(&self) -> Span {
59        self.location.span
60    }
61
62    pub fn location(&self) -> Location {
63        self.location
64    }
65}
66
67#[derive(PartialOrd, Eq, Ord, Debug, Clone, Default)]
68pub struct Spanned<T> {
69    pub contents: T,
70    span: Span,
71}
72
73/// This is important for tests. Two Spanned objects are equal if their content is equal
74/// They may not have the same span. Use into_span to test for Span being equal specifically
75impl<T: PartialEq> PartialEq<Spanned<T>> for Spanned<T> {
76    fn eq(&self, other: &Spanned<T>) -> bool {
77        self.contents == other.contents
78    }
79}
80
81/// Hash-based data structures (HashMap, HashSet) rely on the inverse of Hash
82/// being injective, i.e. x.eq(y) => hash(x, H) == hash(y, H), we hence align
83/// this with the above
84impl<T: Hash> Hash for Spanned<T> {
85    fn hash<H: Hasher>(&self, state: &mut H) {
86        self.contents.hash(state);
87    }
88}
89
90impl<T> Spanned<T> {
91    pub fn from_position(start: Position, end: Position, contents: T) -> Spanned<T> {
92        Spanned { span: Span::inclusive(start, end), contents }
93    }
94
95    pub fn from(t_span: Span, contents: T) -> Spanned<T> {
96        Spanned { span: t_span, contents }
97    }
98
99    pub fn span(&self) -> Span {
100        self.span
101    }
102}
103
104impl<T> std::borrow::Borrow<T> for Spanned<T> {
105    fn borrow(&self) -> &T {
106        &self.contents
107    }
108}
109
110#[derive(
111    PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone, Default, Deserialize, Serialize,
112)]
113pub struct Span(ByteSpan);
114
115impl Span {
116    pub fn inclusive(start: u32, end: u32) -> Span {
117        Span(ByteSpan::from(start..end + 1))
118    }
119
120    pub fn single_char(start: u32) -> Span {
121        Span::inclusive(start, start)
122    }
123
124    pub fn empty(position: u32) -> Span {
125        Span::from(position..position)
126    }
127
128    #[must_use]
129    pub fn merge(self, other: Span) -> Span {
130        Span(self.0.merge(other.0))
131    }
132
133    pub fn to_byte_span(self) -> ByteSpan {
134        self.0
135    }
136
137    pub fn start(&self) -> u32 {
138        self.0.start().into()
139    }
140
141    pub fn end(&self) -> u32 {
142        self.0.end().into()
143    }
144
145    pub fn contains(&self, other: &Span) -> bool {
146        self.start() <= other.start() && self.end() >= other.end()
147    }
148
149    /// Returns `true` if any point of `self` intersects a point of `other`.
150    /// Adjacent spans are considered to intersect (so, for example, `0..1` intersects `1..3`).
151    pub fn intersects(&self, other: &Span) -> bool {
152        self.end() >= other.start() && self.start() <= other.end()
153    }
154
155    pub fn is_smaller(&self, other: &Span) -> bool {
156        let self_distance = self.end() - self.start();
157        let other_distance = other.end() - other.start();
158        self_distance < other_distance
159    }
160}
161
162impl From<Span> for Range<usize> {
163    fn from(span: Span) -> Self {
164        span.0.into()
165    }
166}
167
168impl From<Range<u32>> for Span {
169    fn from(Range { start, end }: Range<u32>) -> Self {
170        Self(ByteSpan::new(start, end))
171    }
172}
173
174#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
175pub struct Location {
176    pub span: Span,
177    pub file: FileId,
178}
179
180impl Location {
181    pub fn new(span: Span, file: FileId) -> Self {
182        Self { span, file }
183    }
184
185    pub fn dummy() -> Self {
186        Self { span: Span::default(), file: FileId::dummy() }
187    }
188
189    pub fn contains(&self, other: &Location) -> bool {
190        self.file == other.file && self.span.contains(&other.span)
191    }
192
193    #[must_use]
194    pub fn merge(self, other: Location) -> Location {
195        if self.file == other.file {
196            Location::new(self.span.merge(other.span), self.file)
197        } else {
198            self
199        }
200    }
201}
202
203impl Ord for Location {
204    fn cmp(&self, other: &Self) -> Ordering {
205        (self.file, self.span).cmp(&(other.file, other.span))
206    }
207}
208
209impl PartialOrd for Location {
210    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
211        Some(self.cmp(other))
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use crate::Span;
218
219    #[test]
220    fn test_intersects() {
221        assert!(Span::from(5..10).intersects(&Span::from(5..10)));
222
223        assert!(Span::from(5..10).intersects(&Span::from(5..5)));
224        assert!(Span::from(5..5).intersects(&Span::from(5..10)));
225
226        assert!(Span::from(10..10).intersects(&Span::from(5..10)));
227        assert!(Span::from(5..10).intersects(&Span::from(10..10)));
228
229        assert!(Span::from(5..10).intersects(&Span::from(6..9)));
230        assert!(Span::from(6..9).intersects(&Span::from(5..10)));
231
232        assert!(Span::from(5..10).intersects(&Span::from(4..11)));
233        assert!(Span::from(4..11).intersects(&Span::from(5..10)));
234
235        assert!(Span::from(5..10).intersects(&Span::from(4..6)));
236        assert!(Span::from(4..6).intersects(&Span::from(5..10)));
237
238        assert!(Span::from(5..10).intersects(&Span::from(9..11)));
239        assert!(Span::from(9..11).intersects(&Span::from(5..10)));
240
241        assert!(!Span::from(5..10).intersects(&Span::from(3..4)));
242        assert!(!Span::from(3..4).intersects(&Span::from(5..10)));
243
244        assert!(!Span::from(5..10).intersects(&Span::from(11..12)));
245        assert!(!Span::from(11..12).intersects(&Span::from(5..10)));
246    }
247}