pub struct SourceMap { /* private fields */ }Expand description
Tracks AST transformations for error reporting
This is the core of the source mapping system. It maintains three separate mappings:
- surface_positions: NodeId → SourcePos (from parser)
- surface_to_core: NodeId → NodeId (from macro expansion)
- core_to_rust: NodeId → NodeId (from lowering)
These three maps enable backward traversal from a Rust compiler error position back to the original Oxur source code.
§Concurrency Model
The SourceMap follows a “build-once, read-many” pattern:
- Recording methods require
&mut self(exclusive access during compilation) - Lookup methods require
&self(shared access during error translation) - Once frozen, recording operations will panic (defensive programming)
This design ensures thread-safety without runtime overhead:
- Compilation is single-threaded (sequential: parse → expand → lower)
- Error translation can be multi-threaded (read-only lookups)
Implementations§
Source§impl SourceMap
impl SourceMap
Sourcepub fn record_surface_node(&mut self, node: NodeId, pos: SourcePos)
pub fn record_surface_node(&mut self, node: NodeId, pos: SourcePos)
Record a surface form node position
Called by the parser when creating surface form AST nodes.
§Panics
Panics if the map is frozen.
§Example
use oxur_smap::{SourceMap, NodeId, SourcePos};
let mut map = SourceMap::new();
let node = NodeId::from_raw(100);
let pos = SourcePos::repl(1, 1, 10);
map.record_surface_node(node, pos);
assert_eq!(map.get_surface_position(&node).unwrap().line, 1);Sourcepub fn record_expansion(&mut self, surface: NodeId, core: NodeId)
pub fn record_expansion(&mut self, surface: NodeId, core: NodeId)
Record an expansion transformation
Called by the macro expander when transforming a surface form node into a core form node.
§Panics
Panics if the map is frozen.
§Example
use oxur_smap::{SourceMap, NodeId};
let mut map = SourceMap::new();
let surface = NodeId::from_raw(100);
let core = NodeId::from_raw(200);
map.record_expansion(surface, core);
assert_eq!(map.get_core_from_surface(&surface), Some(&core));Sourcepub fn record_lowering(&mut self, core: NodeId, rust: NodeId)
pub fn record_lowering(&mut self, core: NodeId, rust: NodeId)
Record a lowering transformation
Called by the Rust AST generator when transforming a core form node into a Rust AST node.
§Panics
Panics if the map is frozen.
§Example
use oxur_smap::{SourceMap, NodeId};
let mut map = SourceMap::new();
let core = NodeId::from_raw(200);
let rust = NodeId::from_raw(300);
map.record_lowering(core, rust);
assert_eq!(map.get_rust_from_core(&core), Some(&rust));Sourcepub fn freeze(&mut self)
pub fn freeze(&mut self)
Freeze the source map, preventing further modifications
This should be called after the lowering phase completes. Any subsequent calls to recording methods will panic.
§Example
use oxur_smap::{SourceMap, NodeId, SourcePos};
let mut map = SourceMap::new();
let node = NodeId::from_raw(100);
map.record_surface_node(node, SourcePos::repl(1, 1, 10));
map.freeze();
assert!(map.is_frozen());Sourcepub fn is_frozen(&self) -> bool
pub fn is_frozen(&self) -> bool
Check if the source map is frozen
Returns true if freeze() has been called.
Sourcepub fn lookup(&self, rust_node: &NodeId) -> Option<SourcePos>
pub fn lookup(&self, rust_node: &NodeId) -> Option<SourcePos>
Look up the original source position for a Rust AST node
This performs backward traversal through the transformation chain: Rust → Core → Surface → SourcePos
Returns None if any link in the chain is missing.
§Example
use oxur_smap::{SourceMap, NodeId, SourcePos};
let mut map = SourceMap::new();
let surface = NodeId::from_raw(100);
let core = NodeId::from_raw(200);
let rust = NodeId::from_raw(300);
let pos = SourcePos::repl(1, 5, 10);
map.record_surface_node(surface, pos.clone());
map.record_expansion(surface, core);
map.record_lowering(core, rust);
let result = map.lookup(&rust).unwrap();
assert_eq!(result.line, 1);
assert_eq!(result.column, 5);Sourcepub fn get_surface_position(&self, node: &NodeId) -> Option<&SourcePos>
pub fn get_surface_position(&self, node: &NodeId) -> Option<&SourcePos>
Get surface position directly (for testing/debugging)
Sourcepub fn get_core_from_surface(&self, surface: &NodeId) -> Option<&NodeId>
pub fn get_core_from_surface(&self, surface: &NodeId) -> Option<&NodeId>
Get core node from surface node (for testing/debugging)
Sourcepub fn get_rust_from_core(&self, core: &NodeId) -> Option<&NodeId>
pub fn get_rust_from_core(&self, core: &NodeId) -> Option<&NodeId>
Get rust node from core node (for testing/debugging)
Sourcepub fn stats(&self) -> SourceMapStats
pub fn stats(&self) -> SourceMapStats
Get statistics about the source map (for debugging)
Sourcepub fn lookup_stats(&self) -> LookupStats
pub fn lookup_stats(&self) -> LookupStats
Get performance statistics for lookup operations
Analyzes the transformation chains to provide insights into:
- Average and maximum chain lengths
- Number of complete vs broken chains
Useful for profiling and optimization.
§Example
use oxur_smap::{SourceMap, NodeId, SourcePos};
let mut map = SourceMap::new();
let surface = NodeId::from_raw(100);
let core = NodeId::from_raw(200);
let rust = NodeId::from_raw(300);
map.record_surface_node(surface, SourcePos::repl(1, 1, 10));
map.record_expansion(surface, core);
map.record_lowering(core, rust);
let stats = map.lookup_stats();
assert_eq!(stats.complete_chains, 1);
assert_eq!(stats.max_chain_length, 3);Sourcepub fn content_hash(&self) -> u64
pub fn content_hash(&self) -> u64
Generate a content hash for cache key generation
This hash includes the structure of all transformations but NOT the actual source positions. This allows cached artifacts to be reused when the transformation structure is identical, even if line numbers have changed.
§Design Decision: Structure-Only Hashing
We hash the transformation graph (NodeId → NodeId mappings) but NOT the surface positions. This means:
- Same code structure → Same hash (even if moved to different line)
- Cache hits more frequent (position changes don’t invalidate)
- Trade-off: Very subtle semantic changes might be missed
For v1.0, this is acceptable. If needed, we can add a flag for “strict hashing” that includes positions in v1.1+.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for SourceMap
impl RefUnwindSafe for SourceMap
impl Send for SourceMap
impl Sync for SourceMap
impl Unpin for SourceMap
impl UnwindSafe for SourceMap
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);