From 2b35fe30e93f23b4a80dde20b3a75261e20c398d Mon Sep 17 00:00:00 2001 From: Chris Mounce Date: Fri, 4 Jul 2025 16:32:59 -0700 Subject: [PATCH] Add errors for broken anonymous label references --- src/labels/mod.rs | 2 ++ src/labels/parse.rs | 2 ++ src/labels/process.rs | 29 ++++++++++++++++++++--------- src/main.rs | 2 +- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/labels/mod.rs b/src/labels/mod.rs index 146d27b..115550a 100644 --- a/src/labels/mod.rs +++ b/src/labels/mod.rs @@ -1,3 +1,5 @@ pub mod parse; pub mod process; pub mod sanitize; + +pub use process::process_labels; diff --git a/src/labels/parse.rs b/src/labels/parse.rs index 20fa37a..190151e 100644 --- a/src/labels/parse.rs +++ b/src/labels/parse.rs @@ -22,6 +22,7 @@ pub struct LabelName { pub namespace: Option, pub name: CompactString, pub local: Option, + pub span: Range, } pub fn parse_stat_labels(stat: &Stat, ctx: &Context) -> ParsedStat { @@ -59,6 +60,7 @@ pub fn parse_stat_labels(stat: &Stat, ctx: &Context) -> ParsedStat { // Convert #Labels into (span, chunk) pairs let span_chunks = label_captures.iter().map(|(tag, cap)| { let mut name = LabelName::default(); + name.span = cap.span(); let mut is_anon = false; for child in cap.children() { match child.kind() { diff --git a/src/labels/process.rs b/src/labels/process.rs index 9e8d115..63976c7 100644 --- a/src/labels/process.rs +++ b/src/labels/process.rs @@ -22,8 +22,8 @@ pub fn process_labels(board: &mut Board, ctx: &Context) { assign_named_labels(&mut stats, &mut registry); // Assign names to anonymous labels - anonymous_forward_pass(&mut stats, &mut registry); - anonymous_backward_pass(&mut stats); + anonymous_forward_pass(&mut stats, &mut registry, ctx); + anonymous_backward_pass(&mut stats, ctx); // Join chunks together and replace old stats' code for (old_stat, parsed_stat) in board.stats.iter_mut().zip(stats.into_iter()) { @@ -124,11 +124,11 @@ fn assign_named_labels(stats: &mut [ParsedStat], registry: &mut Registry) { /// Simultaneously: /// 1. Assign names to anonymous labels. /// 2. Resolve anonymous backward references to their label names. -fn anonymous_forward_pass(stats: &mut [ParsedStat], registry: &mut Registry) { +fn anonymous_forward_pass(stats: &mut [ParsedStat], registry: &mut Registry, ctx: &Context) { // Save generated label names so they can be reused across multiple objects let mut label_names = vec![]; - for stat in stats.iter_mut() { + for (stat_index, stat) in stats.iter_mut().enumerate() { // Helper: Get the next label name that hasn't been used in this object yet let mut i = 0; let mut get_next_name = || -> CompactString { @@ -144,6 +144,7 @@ fn anonymous_forward_pass(stats: &mut [ParsedStat], registry: &mut Registry) { let mut namespace_to_latest: FxHashMap, CompactString> = FxHashMap::default(); + let ctx = ctx.with_stat(stat_index); for chunk in stat.iter_mut() { match chunk { Chunk::Label { @@ -161,8 +162,12 @@ fn anonymous_forward_pass(stats: &mut [ParsedStat], registry: &mut Registry) { name, } => { if name.name == "@b" { - // TODO: a more explicit check that a "before" label exists - name.name = namespace_to_latest.get(&name.namespace).unwrap().clone(); + if let Some(backward) = namespace_to_latest.get(&name.namespace) { + name.name = backward.clone(); + } else { + ctx.with_span(name.span.clone()) + .error("backward reference @b without prior anonymous label :@"); + } } } _ => {} @@ -172,9 +177,10 @@ fn anonymous_forward_pass(stats: &mut [ParsedStat], registry: &mut Registry) { } /// Resolve anonymous forward references to their label names. -fn anonymous_backward_pass(stats: &mut [ParsedStat]) { - for stat in stats.iter_mut() { +fn anonymous_backward_pass(stats: &mut [ParsedStat], ctx: &Context) { + for (stat_index, stat) in stats.iter_mut().enumerate() { let mut namespace_to_latest = FxHashMap::default(); + let ctx = ctx.with_stat(stat_index); for chunk in stat.iter_mut().rev() { match chunk { Chunk::Label { @@ -190,7 +196,12 @@ fn anonymous_backward_pass(stats: &mut [ParsedStat]) { name, } => { if name.name == "@f" { - name.name = namespace_to_latest.get(&name.namespace).unwrap().clone(); + if let Some(forward) = namespace_to_latest.get(&name.namespace) { + name.name = forward.clone(); + } else { + ctx.with_span(name.span.clone()) + .error("forward reference @f without following anonymous label :@"); + } } } _ => {} diff --git a/src/main.rs b/src/main.rs index 5ffd2e5..212a18d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ mod world; use anyhow::anyhow; use error::Context as ErrContext; -use labels::process::process_labels; +use labels::process_labels; use preprocess::eval::Context; use std::{env, error::Error, fs, path::PathBuf, process::exit}; use world::World;