Skip to main content

adam_lang/
sheet_diagnostics.rs

1//! Owned source and provenance metadata for errors that outlive a sheet.
2//!
3//! Capture diagnostics before a sheet mutation that can fail. The snapshot keeps
4//! source owners and native error-site provenance after the sheet is discarded.
5//!
6//! # Examples
7//!
8//! ```
9//! use adam_lang::{AdamParser, TypeRegistry};
10//! let mut parsed = AdamParser::new(TypeRegistry::new(), cel_parser::OpLookup::new())
11//!     .parse_str("sheet s { cell x: i32 = 0; cell y: i32; relationship { y := 10i32 / x; } }")?;
12//! let diagnostics = parsed.diagnostics();
13//! let error = parsed.propagate().unwrap_err();
14//! drop(parsed);
15//! assert!(!diagnostics.locate_error(&error).is_empty());
16//! # Ok::<(), cel_parser::ParseError>(())
17//! ```
18
19use std::collections::HashMap;
20
21use adam_rs::{CellId, RelationshipId, RequirementId};
22use indexmap::IndexMap;
23
24use crate::{
25    ParsedSheet, SourceId, SourceLabel, composition::CellOrigin, composition::InstanceOrigin,
26    type_registry::TypeShape,
27};
28
29/// Retains source and native provenance without owning or borrowing a `Sheet`.
30///
31/// The snapshot owns names, spans, source identities, requirement labels, and
32/// expansion provenance. Its immutable source owners and authored records may
33/// share backing storage with the parsed model.
34///
35/// # Examples
36///
37/// ```
38/// let diagnostics = adam_lang::ParsedSheet::new().diagnostics();
39/// assert!(diagnostics.sources().is_empty());
40/// ```
41#[derive(Clone, Debug)]
42pub struct SheetDiagnostics {
43    /// Public root cell names and their IDs and complete shapes, in declaration order.
44    pub cell_names: IndexMap<String, (CellId, TypeShape)>,
45    /// Public output names and their IDs, in declaration order.
46    pub output_names: IndexMap<String, CellId>,
47    /// Explicit requirement names keyed by runtime requirement ID.
48    pub requirement_names: HashMap<RequirementId, String>,
49    /// Requirement IDs mapped to authored source labels, including unnamed checks.
50    pub requirement_labels: HashMap<RequirementId, SourceLabel>,
51    /// Root-local method spans keyed by relationship ID and method index.
52    pub method_spans: HashMap<(RelationshipId, usize), cel_parser::SourceSpan>,
53    /// Root-local relationship spans keyed by relationship ID.
54    pub relationship_spans: HashMap<RelationshipId, cel_parser::SourceSpan>,
55    /// Root-local declaration spans keyed by cell ID.
56    pub cell_spans: HashMap<CellId, cel_parser::SourceSpan>,
57    /// Retained source owners in insertion order.
58    pub sources: Vec<SourceId>,
59    /// Anonymous convenience source used for caller-selected display names.
60    pub anonymous_source: Option<SourceId>,
61    /// Parent-linked native occurrence provenance.
62    pub instance_origins: Vec<InstanceOrigin>,
63    /// Authored method origins keyed by runtime relationship and method index.
64    pub method_origins: HashMap<(RelationshipId, usize), adam_rs::MethodOrigin>,
65    /// Authored cell origins keyed by runtime cell ID.
66    pub cell_origins: HashMap<CellId, Vec<CellOrigin>>,
67    /// Complete shapes for root and private cells.
68    pub cell_shapes: HashMap<CellId, TypeShape>,
69}
70
71impl SheetDiagnostics {
72    /// Creates an owned metadata snapshot from a parsed sheet.
73    ///
74    /// - Complexity: O(m) time and space for retained metadata size m; immutable
75    ///   source owners and authored item slices are shared.
76    pub(crate) fn from_parsed(parsed: &ParsedSheet) -> Self {
77        Self {
78            cell_names: parsed.cell_names.clone(),
79            output_names: parsed.output_names.clone(),
80            requirement_names: parsed.requirement_names.clone(),
81            requirement_labels: parsed.requirement_labels.clone(),
82            method_spans: parsed.method_spans.clone(),
83            relationship_spans: parsed.relationship_spans.clone(),
84            cell_spans: parsed.cell_spans.clone(),
85            sources: parsed.sources.clone(),
86            anonymous_source: parsed.anonymous_source.clone(),
87            instance_origins: parsed.instance_origins.clone(),
88            method_origins: parsed.method_origins.clone(),
89            cell_origins: parsed.cell_origins.clone(),
90            cell_shapes: parsed.cell_shapes.clone(),
91        }
92    }
93
94    /// Resolves an error to owning definition and invocation labels.
95    ///
96    /// - Complexity: O(o + m + c + k + p × a + q × a + l²) time and
97    ///   O(o + m + c + k + l) space for occurrences o, methods m, public cells c,
98    ///   conditionals k, candidate cell origins p, visited occurrence/ancestor records q,
99    ///   local authored items a, and returned labels l.
100    ///
101    /// # Examples
102    ///
103    /// ```
104    /// use adam_lang::{AdamParser, TypeRegistry};
105    /// let mut parsed = AdamParser::new(TypeRegistry::new(), cel_parser::OpLookup::new())
106    ///     .parse_str("sheet s { cell x: i32 = 0; cell y: i32; relationship { y := 10i32 / x; } }")?;
107    /// let diagnostics = parsed.diagnostics();
108    /// let error = parsed.propagate().unwrap_err();
109    /// assert!(!diagnostics.locate_error(&error).is_empty());
110    /// # Ok::<(), cel_parser::ParseError>(())
111    /// ```
112    pub fn locate_error(&self, error: &adam_rs::Error) -> Vec<SourceLabel> {
113        crate::error_labels::locate_error(self, error)
114    }
115
116    /// Borrows retained source owners in their original insertion order.
117    ///
118    /// # Examples
119    ///
120    /// ```
121    /// use adam_lang::{AdamParser, TypeRegistry};
122    /// let parsed = AdamParser::new(TypeRegistry::new(), cel_parser::OpLookup::new())
123    ///     .parse_str("sheet s { cell x: i32 = 1; }")?;
124    /// assert_eq!(parsed.diagnostics().sources().len(), 1);
125    /// # Ok::<(), cel_parser::ParseError>(())
126    /// ```
127    pub fn sources(&self) -> &[SourceId] {
128        &self.sources
129    }
130
131    /// Borrows the anonymous convenience source used for caller-selected display names.
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// use adam_lang::{AdamParser, TypeRegistry};
137    /// let parsed = AdamParser::new(TypeRegistry::new(), cel_parser::OpLookup::new())
138    ///     .parse_str("sheet s { cell x: i32 = 1; }")?;
139    /// assert_eq!(parsed.diagnostics().anonymous_source().unwrap().file_name(), "<input>");
140    /// # Ok::<(), cel_parser::ParseError>(())
141    /// ```
142    pub fn anonymous_source(&self) -> Option<&SourceId> {
143        self.anonymous_source.as_ref()
144    }
145}
146
147/// Supplies the metadata needed to resolve native runtime error sites.
148pub(crate) trait ErrorMetadata {
149    /// Returns public cell names in declaration order.
150    fn cell_names(&self) -> &IndexMap<String, (CellId, TypeShape)>;
151
152    /// Returns retained occurrence provenance.
153    fn instance_origins(&self) -> &[InstanceOrigin];
154
155    /// Returns retained method provenance.
156    fn method_origins(&self) -> &HashMap<(RelationshipId, usize), adam_rs::MethodOrigin>;
157
158    /// Returns retained cell provenance.
159    fn cell_origins(&self) -> &HashMap<CellId, Vec<CellOrigin>>;
160}
161
162impl ErrorMetadata for ParsedSheet {
163    /// Returns public cell names in declaration order.
164    fn cell_names(&self) -> &IndexMap<String, (CellId, TypeShape)> {
165        &self.cell_names
166    }
167
168    /// Returns retained occurrence provenance.
169    fn instance_origins(&self) -> &[InstanceOrigin] {
170        &self.instance_origins
171    }
172
173    /// Returns retained method provenance.
174    fn method_origins(&self) -> &HashMap<(RelationshipId, usize), adam_rs::MethodOrigin> {
175        &self.method_origins
176    }
177
178    /// Returns retained cell provenance.
179    fn cell_origins(&self) -> &HashMap<CellId, Vec<CellOrigin>> {
180        &self.cell_origins
181    }
182}
183
184impl ErrorMetadata for SheetDiagnostics {
185    /// Returns public cell names in declaration order.
186    fn cell_names(&self) -> &IndexMap<String, (CellId, TypeShape)> {
187        &self.cell_names
188    }
189
190    /// Returns retained occurrence provenance.
191    fn instance_origins(&self) -> &[InstanceOrigin] {
192        &self.instance_origins
193    }
194
195    /// Returns retained method provenance.
196    fn method_origins(&self) -> &HashMap<(RelationshipId, usize), adam_rs::MethodOrigin> {
197        &self.method_origins
198    }
199
200    /// Returns retained cell provenance.
201    fn cell_origins(&self) -> &HashMap<CellId, Vec<CellOrigin>> {
202        &self.cell_origins
203    }
204}