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}