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}