Skip to main content

mib_rs/types/
span.rs

1//! Source location types for tracking positions in MIB source text.
2//!
3//! [`ByteOffset`] represents a single position, [`Span`] represents a half-open
4//! byte range, and [`SpanDiagnostic`] is a diagnostic that carries a span
5//! (later converted to line/column in [`Diagnostic`](super::Diagnostic)).
6
7/// A byte position in source text.
8///
9/// Wraps a `u32` offset. The sentinel value [`ByteOffset::SYNTHETIC`] marks
10/// positions that do not correspond to real source text.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
12pub struct ByteOffset(pub u32);
13
14impl ByteOffset {
15    /// Sentinel value for positions not backed by source text (e.g. base modules).
16    pub const SYNTHETIC: ByteOffset = ByteOffset(u32::MAX);
17}
18
19/// A half-open byte range `[start, end)` in source text.
20///
21/// Used throughout the lexer, parser, and lowering stages to track where
22/// constructs originate. See [`ByteOffset`] for the underlying offset type.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
24pub struct Span {
25    /// Start of the range (inclusive).
26    pub start: ByteOffset,
27    /// End of the range (exclusive).
28    pub end: ByteOffset,
29}
30
31impl Span {
32    /// Zero-length span at the start of input.
33    pub const ZERO: Span = Span {
34        start: ByteOffset(0),
35        end: ByteOffset(0),
36    };
37
38    /// Span for compiler-generated constructs (base modules, built-in types).
39    pub const SYNTHETIC: Span = Span {
40        start: ByteOffset::SYNTHETIC,
41        end: ByteOffset::SYNTHETIC,
42    };
43
44    /// Creates a span from two [`ByteOffset`] values.
45    pub fn new(start: ByteOffset, end: ByteOffset) -> Self {
46        Span { start, end }
47    }
48
49    /// Creates a span from raw `u32` offsets.
50    pub fn from_offsets(start: u32, end: u32) -> Self {
51        Span {
52            start: ByteOffset(start),
53            end: ByteOffset(end),
54        }
55    }
56
57    /// Creates a span from `usize` offsets.
58    ///
59    /// Returns [`Span::SYNTHETIC`] if either offset exceeds `u32::MAX`.
60    pub fn from_usize_offsets(start: usize, end: usize) -> Self {
61        match (u32::try_from(start), u32::try_from(end)) {
62            (Ok(start), Ok(end)) => Self::from_offsets(start, end),
63            _ => Self::SYNTHETIC,
64        }
65    }
66
67    /// Returns `true` if this span is the [`Span::SYNTHETIC`] sentinel.
68    pub fn is_synthetic(self) -> bool {
69        self == Self::SYNTHETIC
70    }
71}
72
73/// Diagnostic emitted by the lexer or parser, carrying a [`Span`] instead of line/column.
74///
75/// Converted to [`Diagnostic`](super::Diagnostic) during lowering when module name
76/// and a line table become available.
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct SpanDiagnostic {
79    /// Severity of the issue.
80    pub severity: super::Severity,
81    /// Diagnostic code identifying the issue category.
82    pub code: DiagCode,
83    /// Source location of the issue.
84    pub span: Span,
85    /// Human-readable description of the issue.
86    pub message: String,
87}
88
89use super::DiagCode;
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn synthetic_span() {
97        assert!(Span::SYNTHETIC.is_synthetic());
98        assert!(!Span::from_offsets(0, 10).is_synthetic());
99    }
100}