SourceMap

Struct SourceMap 

Source
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:

  1. surface_positions: NodeId → SourcePos (from parser)
  2. surface_to_core: NodeId → NodeId (from macro expansion)
  3. 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

Source

pub fn new() -> SourceMap

Create a new empty source map

Source

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);
Source

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));
Source

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));
Source

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());
Source

pub fn is_frozen(&self) -> bool

Check if the source map is frozen

Returns true if freeze() has been called.

Source

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);
Source

pub fn get_surface_position(&self, node: &NodeId) -> Option<&SourcePos>

Get surface position directly (for testing/debugging)

Source

pub fn get_core_from_surface(&self, surface: &NodeId) -> Option<&NodeId>

Get core node from surface node (for testing/debugging)

Source

pub fn get_rust_from_core(&self, core: &NodeId) -> Option<&NodeId>

Get rust node from core node (for testing/debugging)

Source

pub fn stats(&self) -> SourceMapStats

Get statistics about the source map (for debugging)

Source

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);
Source

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§

Source§

impl Clone for SourceMap

Source§

fn clone(&self) -> SourceMap

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SourceMap

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for SourceMap

Source§

fn default() -> SourceMap

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.