miden_diagnostics/
span.rs

1use std::cmp;
2use std::convert::{AsMut, AsRef};
3use std::fmt;
4use std::hash::{Hash, Hasher};
5use std::ops::{Deref, DerefMut, Range};
6
7use codespan::{ByteIndex, ByteOffset};
8
9use super::{SourceId, SourceIndex};
10
11/// Represents a range of bytes in a specific source file
12///
13/// A [SourceSpan] is a combination of [SourceId] and a range
14/// of byte indices in the corresponding file. With one, you may
15/// obtain a variety of useful information about the source to which
16/// it maps using the `CodeMap` from which it was created:
17///
18/// * Can be used to get a `str` of the original file content containing
19/// just the specified range.
20/// * Can be used to get file/line/column at which the span starts
21/// * Can be used to get the [SourceFile] from which it is derived
22///
23/// A [SourceSpan] has a canonical "default" value, which is represented
24/// by `SourceSpan::UNKNOWN`. It can be treated like a regular span, however
25/// when a request is made for content or location information corresponding
26/// to it, those APIs will return `None` or `Err`. This is useful when
27/// constructing syntax trees and the like without sources, such as in
28/// testing scenarios.
29#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub struct SourceSpan {
31    pub(crate) source_id: SourceId,
32    pub(crate) start: ByteIndex,
33    pub(crate) end: ByteIndex,
34}
35impl fmt::Debug for SourceSpan {
36    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37        write!(
38            f,
39            "{}..{}@{}",
40            self.start.to_usize(),
41            self.end.to_usize(),
42            self.source_id.get()
43        )
44    }
45}
46impl Default for SourceSpan {
47    #[inline(always)]
48    fn default() -> Self {
49        Self::UNKNOWN
50    }
51}
52impl SourceSpan {
53    /// Represents an invalid/unknown source location
54    pub const UNKNOWN: Self = Self {
55        source_id: SourceId::UNKNOWN,
56        start: ByteIndex(0),
57        end: ByteIndex(0),
58    };
59
60    /// Creates a new span from `start` to `end`
61    ///
62    /// This function will panic if the indices are in different source files
63    #[inline]
64    pub fn new(start: SourceIndex, end: SourceIndex) -> Self {
65        let source_id = start.source_id();
66        assert_eq!(
67            source_id,
68            end.source_id(),
69            "source spans cannot start and end in different files!"
70        );
71        let start = start.index();
72        let end = end.index();
73
74        Self {
75            source_id,
76            start,
77            end,
78        }
79    }
80
81    /// Returns true if this span represents an "unknown" source span
82    #[inline(always)]
83    pub fn is_unknown(self) -> bool {
84        self == Self::UNKNOWN
85    }
86
87    /// Returns the [SourceId] associated with this span
88    #[inline(always)]
89    pub fn source_id(&self) -> SourceId {
90        self.source_id
91    }
92
93    /// Returns the starting [SourceIndex] of this span
94    #[inline(always)]
95    pub fn start(&self) -> SourceIndex {
96        SourceIndex::new(self.source_id, self.start)
97    }
98
99    /// Returns the starting [ByteIndex] of this span in its [SourceFile]
100    #[inline(always)]
101    pub fn start_index(&self) -> ByteIndex {
102        self.start
103    }
104
105    /// Shrinks this span by truncating `offset` bytes from the start of its range
106    pub fn shrink_front(mut self, offset: ByteOffset) -> Self {
107        self.start += offset;
108        self
109    }
110
111    /// Returns the ending source index of this span
112    #[inline(always)]
113    pub fn end(&self) -> SourceIndex {
114        SourceIndex::new(self.source_id, self.end)
115    }
116
117    /// Returns the ending byte index of this span in its SourceFile
118    #[inline(always)]
119    pub fn end_index(&self) -> ByteIndex {
120        self.end
121    }
122
123    /// Creates a new span that covers both this span and `other`, forming a new contiguous span
124    ///
125    /// Returns `None` if either span is invalid or from a different source file.
126    ///
127    /// The order of the spans is not important.
128    pub fn merge(self, other: SourceSpan) -> Option<SourceSpan> {
129        if self.is_unknown() || other.is_unknown() {
130            return None;
131        }
132        let source_id = self.source_id();
133        if source_id != other.source_id() {
134            return None;
135        }
136        let start = cmp::min(self.start_index(), other.start_index());
137        let end = cmp::max(self.end_index(), other.end_index());
138        Some(SourceSpan::new(
139            SourceIndex::new(source_id, start),
140            SourceIndex::new(source_id, end),
141        ))
142    }
143}
144impl From<SourceSpan> for codespan::Span {
145    #[inline]
146    fn from(span: SourceSpan) -> Self {
147        Self::new(span.start, span.end)
148    }
149}
150impl From<SourceSpan> for ByteIndex {
151    #[inline]
152    fn from(span: SourceSpan) -> Self {
153        span.start_index()
154    }
155}
156impl From<SourceSpan> for Range<usize> {
157    fn from(span: SourceSpan) -> Range<usize> {
158        span.start.into()..span.end.into()
159    }
160}
161impl From<SourceSpan> for Range<SourceIndex> {
162    fn from(span: SourceSpan) -> Range<SourceIndex> {
163        let start = SourceIndex::new(span.source_id, span.start);
164        let end = SourceIndex::new(span.source_id, span.end);
165        start..end
166    }
167}
168
169/// This trait is implemented by any type which has a canoncial [SourceSpan]
170pub trait Spanned {
171    fn span(&self) -> SourceSpan;
172}
173impl Spanned for SourceSpan {
174    #[inline(always)]
175    fn span(&self) -> SourceSpan {
176        *self
177    }
178}
179impl<T: Spanned> Spanned for Box<T> {
180    #[inline]
181    fn span(&self) -> SourceSpan {
182        self.as_ref().span()
183    }
184}
185
186/// [Span] is used to wrap types which do not implement [Spanned] in a type that does.
187///
188/// [Span] is a bit special in that it is intended to be as transparent as possible, that
189/// means that it implements a variety of traits in a passthrough fashion, so that the span
190/// added to the underlying type does not change its behavior with regards to equality, hashing,
191/// ordering, etc. It does however have a [Debug] implementation that shows the span.
192pub struct Span<T: ?Sized> {
193    span: SourceSpan,
194    /// The underlying item wrapped by this [Span]
195    pub item: T,
196}
197impl<T: ?Sized> Spanned for Span<T> {
198    #[inline]
199    fn span(&self) -> SourceSpan {
200        self.span
201    }
202}
203impl<T> Span<T> {
204    /// Construct a new [Span] from a [SourceSpan] and a [T]
205    pub const fn new(span: SourceSpan, item: T) -> Self {
206        Self { span, item }
207    }
208}
209impl<T: ?Sized> AsRef<T> for Span<T> {
210    #[inline(always)]
211    fn as_ref(&self) -> &T {
212        &self.item
213    }
214}
215impl<T: ?Sized> AsMut<T> for Span<T> {
216    #[inline(always)]
217    fn as_mut(&mut self) -> &mut T {
218        &mut self.item
219    }
220}
221impl<T: ?Sized> Deref for Span<T> {
222    type Target = T;
223
224    #[inline(always)]
225    fn deref(&self) -> &Self::Target {
226        &self.item
227    }
228}
229impl<T: ?Sized> DerefMut for Span<T> {
230    #[inline(always)]
231    fn deref_mut(&mut self) -> &mut Self::Target {
232        &mut self.item
233    }
234}
235impl<T: Clone> Clone for Span<T> {
236    fn clone(&self) -> Self {
237        Self {
238            span: self.span,
239            item: self.item.clone(),
240        }
241    }
242}
243impl<T: Copy> Copy for Span<T> {}
244unsafe impl<T: Send> Send for Span<T> {}
245unsafe impl<T: Sync> Sync for Span<T> {}
246impl<T, U> PartialEq<Span<U>> for Span<T>
247where
248    T: PartialEq<U>,
249    U: PartialEq<T>,
250{
251    fn eq(&self, other: &Span<U>) -> bool {
252        self.item.eq(&other.item)
253    }
254}
255impl<T: Eq> Eq for Span<T> {}
256impl<T, U> PartialOrd<Span<U>> for Span<T>
257where
258    T: PartialOrd<U>,
259    U: PartialOrd<T>,
260{
261    fn partial_cmp(&self, other: &Span<U>) -> Option<std::cmp::Ordering> {
262        self.item.partial_cmp(&other.item)
263    }
264}
265impl<T: Ord> Ord for Span<T> {
266    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
267        self.item.cmp(&other.item)
268    }
269}
270impl<T: Hash> Hash for Span<T> {
271    fn hash<H: Hasher>(&self, state: &mut H) {
272        self.item.hash(state)
273    }
274}
275impl<T: fmt::Debug> fmt::Debug for Span<T> {
276    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
277        write!(f, "Spanned({:?}, {:?})", &self.span, &self.item)
278    }
279}
280impl<T: fmt::Display> fmt::Display for Span<T> {
281    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
282        write!(f, "{}", &self.item)
283    }
284}