macroonz_compiler/diagnostic/types.rs
1//! The diagnostic home's declarations: one diagnostic, the typed parts of its one summary line, the registries a refusal reads its vocabulary off, and the trait a refusing step implements.
2//!
3//! Declarations only.
4//! Every road that reaches a private field lives in `type_guard.rs`, this file's own child, so the seats a caller may not write have exactly one way in.
5
6use crate::bounded::{Bounded, Capping};
7use crate::identity::{
8 Contract, HumanProjection, Identity, OwnerFact, RelatedBody, RelatedIssue, ServiceEntry,
9};
10use crate::token::{SourceCoordinate, SpanHandle, SpanResolutionRefusal, SpanTable};
11
12#[path = "type_guard.rs"]
13mod guard;
14pub(crate) use guard::intrinsic_diagnostic;
15
16/// Owner-declared repairs one diagnostic may carry.
17pub const REPAIR_LIMIT: usize = 8;
18
19/// Identities one diagnostic's related set may carry.
20///
21/// A step may establish a wider body, so a body that outruns this is a case the road meets rather than one the bounds rule out.
22pub const RELATED_ISSUE_LIMIT: usize = 64;
23
24// ---------------------------------------------------------------------------
25// The families.
26//
27// A family here is one refusal's issue space, and its name is what keeps two
28// spaces' identical bytes from deriving one identity. The name is namespaced
29// like an identity stem — the declarer's own name ahead of the space's — so an
30// adopter declares its families in its own crate and two crates' spaces cannot
31// collide without one wearing the other's name. A name is preimage material:
32// renaming a family renames every related identity derived in it.
33// ---------------------------------------------------------------------------
34
35/// One refusal's issue space, named by its declarer.
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37pub struct Family(&'static str);
38
39/// One adopter-declared diagnostic vocabulary name.
40///
41/// The spelling is lowercase ASCII kebab-case by construction, so an adopter-defined refusal class or observation cannot publish a name outside the contract both rosters state.
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43pub struct DiagnosticName(&'static str);
44
45/// How declaring one diagnostic vocabulary name refuses.
46#[must_use = "a diagnostic-name refusal states why no kebab-case name was declared"]
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
48pub enum DiagnosticNameRefusal {
49 /// The declaration supplied no name.
50 Empty,
51 /// The spelling is not lowercase ASCII kebab-case.
52 NotKebabCase,
53}
54
55/// Planning a projection.
56pub const PLANNING_FAMILY: Family = Family::declared("macroonz/planning");
57
58/// Proving a rendering closes over its plan.
59pub const CLOSURE_FAMILY: Family = Family::declared("macroonz/closure");
60
61/// Covering a kind's questions with an explanation.
62pub const EXPLANATION_FAMILY: Family = Family::declared("macroonz/explanation");
63
64/// Materializing a planned member.
65pub const RENDERING_FAMILY: Family = Family::declared("macroonz/rendering");
66
67/// Sealing a plan, a closure, and an explanation as one expansion.
68pub const BINDING_FAMILY: Family = Family::declared("macroonz/binding");
69
70/// Composing closed outputs into one carrier.
71pub const ASSEMBLY_FAMILY: Family = Family::declared("macroonz/assembly");
72
73/// Rendering the carrier shell itself.
74pub const SHELL_FAMILY: Family = Family::declared("macroonz/shell");
75
76/// The carrier's own declaration vocabulary.
77pub const DECLARATION_FAMILY: Family = Family::declared("macroonz/descriptor-declaration");
78
79/// The support home's own declaration vocabulary, which is not the descriptor's however alike the two read.
80pub const SUPPORT_DECLARATION_FAMILY: Family = Family::declared("macroonz/support-declaration");
81
82/// The trial helper's captured grammar.
83pub const FIRST_HELPER_FAMILY: Family = Family::declared("macroonz/trial-helper");
84
85/// The mutation helper's captured grammar.
86pub const SECOND_HELPER_FAMILY: Family = Family::declared("macroonz/mutation-helper");
87
88/// The bench helper's captured grammar.
89pub const BENCH_HELPER_FAMILY: Family = Family::declared("macroonz/bench-helper");
90
91/// The shadow face's captured grammar.
92pub const SHADOW_HELPER_FAMILY: Family = Family::declared("macroonz/shadow-helper");
93
94/// The network declaration's captured grammar.
95pub const NETWORK_HELPER_FAMILY: Family = Family::declared("macroonz/network-helper");
96
97/// The concurrency declaration's captured grammar.
98pub const CONCURRENCY_HELPER_FAMILY: Family = Family::declared("macroonz/concurrency-helper");
99
100/// Reading a declared input into a captured surface.
101pub const CAPTURE_FAMILY: Family = Family::declared("macroonz/capture");
102
103/// A codec shape's own declaration vocabulary.
104pub const CODEC_DECLARATION_FAMILY: Family = Family::declared("macroonz/codec-declaration");
105
106/// Which step of the road was running when the disagreement was observed.
107///
108/// Declared in the order the steps run.
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
110pub enum Phase {
111 /// Reading the declared input into a captured surface.
112 Capture,
113 /// Naming the complete output set, before a token of Rust exists.
114 Planning,
115 /// Materializing a planned member.
116 Rendering,
117 /// Proving the rendering closes over the plan it claims to materialize.
118 Closure,
119 /// Answering the questions the kind owes.
120 Explanation,
121 /// Sealing the plan, the closure, and the explanation as one expansion.
122 Binding,
123 /// Composing closed outputs into one exported carrier.
124 Assembly,
125}
126
127/// Which class of refusal one composed line is about.
128///
129/// The class is the second clause of every line and is READ off this roster rather than written at the seam that refused, so two seams reporting one class do not read as two.
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
131pub enum RefusalClass {
132 /// The declared input was not read into a captured surface.
133 DeclarationNotRead,
134 /// Planning refused before a token of Rust existed.
135 PlanNotStated,
136 /// A renderer did not produce the unit the plan named.
137 RenderingNotProduced,
138 /// The rendering does not close over the plan it claims to materialize.
139 RenderingNotClosed,
140 /// The written explanation does not cover its kind's questions.
141 ExplanationNotCovered,
142 /// A rendering would have passed a declared magnitude.
143 MagnitudeNotHeld,
144 /// The three values a binding seals do not belong to one expansion.
145 ExpansionNotBound,
146 /// A set of closed outputs does not compose into one exported carrier.
147 CarrierNotAssembled,
148 /// The carrier's own vocabulary was not declared.
149 CarrierNotDeclared,
150 /// A class an adopter declared, for a refusal none of the rows above say.
151 ///
152 /// The two spellings are the adopter's own, and a line about it reads them exactly as a line about any row above reads that row's.
153 Declared {
154 /// The stable kebab-case name.
155 name: DiagnosticName,
156 /// The second clause of a composed line about it.
157 described: &'static str,
158 },
159}
160
161/// How what was observed differs from the contract that was expected.
162///
163/// A typed classification, never a sentence: the sentence is a projection of this.
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
165pub enum Observed {
166 /// A required seat was unfurnished.
167 SeatAbsent,
168 /// What was present disagrees with the expected contract.
169 ContractDisagreement,
170 /// An identity that had to match did not.
171 IdentityDisagreement,
172 /// The material was presented under a profile that does not offer it.
173 ProfileDisagreement,
174 /// A declared magnitude was exceeded.
175 BoundExceeded,
176 /// Generated material arrived with no origin.
177 OriginAbsent,
178 /// A difference an adopter declared, for an observation none of the rows above classify.
179 Declared {
180 /// The stable kebab-case name.
181 name: DiagnosticName,
182 /// How the difference reads in a composed line.
183 described: &'static str,
184 },
185}
186
187/// One declared magnitude a rendering can pass, and the thing it governs.
188///
189/// The prose belongs to the magnitude rather than to whichever refusal named it; the number belongs to the home that declares the bound, so a refusal carries it and this roster never restates it.
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
191pub enum RenderedMagnitude {
192 /// The bytes one rendered unit may carry.
193 RenderedBytes,
194 /// The units one rendering may carry.
195 RenderedUnits,
196 /// The tokens one generated tree may carry at one nesting level.
197 GeneratedTokens,
198}
199
200/// What one composed line is a summary OF.
201///
202/// A single-cause refusal establishes one cause and enumerates nothing: there is no remainder to count and no bound anything could have been capped at, so a line reporting "and 0 further issues, complete" would answer a question never asked of it.
203#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
204pub enum LineBody {
205 /// One established cause, with nothing enumerated beside it.
206 SingleCause,
207 /// A body of independent issues.
208 Body {
209 /// Established issues past the one the line states in full.
210 further: usize,
211 /// Whether the body kept every issue it established.
212 capping: Capping,
213 },
214}
215
216/// The typed parts one compiler line is composed from.
217///
218/// They travel as one value because they are one line: a class handed to [`composed`](crate::diagnostic::composed) beside another refusal's first established issue composes a sentence that is well formed, complete-looking, and about nothing in particular.
219#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
220pub struct Line<'issue> {
221 /// Which class of refusal the line is about.
222 pub class: RefusalClass,
223 /// The first established issue, stated in full.
224 pub first: &'issue str,
225 /// What the line is a summary of.
226 pub body: LineBody,
227}
228
229/// Whether a composed line says where the refusal sits.
230///
231/// Not an option: a whole-declaration refusal is a STATED posture and not a site somebody forgot to supply, and adding a position to its line would send a reader to an arbitrary spot inside a declaration the refusal is not about.
232#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
233pub enum LineSite {
234 /// The refusal is about the declaration as a whole, and the line adds nothing.
235 WholeDeclaration,
236 /// The refusal sits somewhere the producer can name, and the line says where — or says that the producer's table does not reach it.
237 At(SiteCoordinate),
238}
239
240/// Where one diagnostic's token sits, or why the producer's table could not say.
241///
242/// A seat that cannot be furnished states the posture rather than being filled with a stand-in: a coordinate written where a table did not reach would read exactly like a coordinate the table resolved, and the reader has no third value to compare it against.
243#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
244pub enum SiteCoordinate {
245 /// The position the producer's table resolved, in the role it speaks.
246 Resolved(SourceCoordinate),
247 /// The producer's table does not reach this handle, and the refusal states how far it does reach.
248 NotReached(SpanResolutionRefusal),
249}
250
251/// Where one diagnostic points.
252///
253/// A diagnostic about a CAPTURED declaration names the offending token in the producer's own span table, so the producer can put a compiler error on exactly that token rather than on the declaration's first one.
254/// A diagnostic established BEFORE any capture has no token to name — no table was built and no handle was issued — and carries the byte it was born at instead.
255/// A diagnostic about the declaration AS A WHOLE has nowhere narrower to point, and says exactly that: the whole-declaration arm carries no token and no coordinate, because any it carried would send a reader to a spot the refusal is not about.
256///
257/// # Nonclaims
258///
259/// **The pre-capture and whole-declaration arms mint no handle, and that is the substitution this sum removes.**
260/// A required handle seat forces handle zero onto an observation that issued none, and handle zero reads exactly like an honest answer pointing at the declaration's first token.
261#[must_use = "a site names the token it points at, the byte it was born at, or the whole declaration"]
262#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
263pub enum Site {
264 /// One token of a captured declaration, and where the producer's table put it.
265 AtToken {
266 /// The offending token, as a handle into the producer's own span table.
267 token: SpanHandle,
268 /// Where that token sits, or the typed statement that the table does not reach the handle.
269 coordinate: SiteCoordinate,
270 },
271 /// One byte of the text a read refused on, before any capture existed to issue a handle.
272 BeforeCapture {
273 /// The byte the observation was born at, in the role its own text counts in.
274 coordinate: SourceCoordinate,
275 },
276 /// The declaration as a whole: a stated posture with no token seat to fabricate one into.
277 WholeDeclaration,
278}
279
280/// One identity a related set carries, at the level it is about.
281///
282/// The two levels are two types rather than two positions, because position is not a fact a reader can check and because one subject over two levels collides by construction: the body's preimage is the framing of its issues, so an issue whose own material happened to be that framing would derive the byte-for-byte identity of the body it aliased.
283#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
284pub enum RelatedIdentity {
285 /// The whole refusal body, as one commitment to every issue it established.
286 Body(Identity<RelatedBody>),
287 /// One established issue, on its own.
288 Issue(Identity<RelatedIssue>),
289}
290
291/// One diagnostic's related set: the identities it carries, married to how it was capped.
292///
293/// A capping that can be carried away from its set is a claim that can be told about a different one, so the set-building road is the only road in and the seats are private.
294///
295/// # Ordering
296///
297/// The body rides first, and a reader does not depend on that: an identity states its own level.
298#[derive(Debug, Clone, PartialEq, Eq, Hash)]
299pub struct RelatedSet {
300 carried: Bounded<RelatedIdentity, RELATED_ISSUE_LIMIT>,
301 capping: Capping,
302}
303
304/// One repair the owner declared, projected for a person to read.
305///
306/// The citation is the load-bearing member.
307/// The text is a projection of it, and nothing here ever composes a repair the owner did not declare.
308#[derive(Debug, Clone, PartialEq, Eq, Hash)]
309pub struct Repair {
310 /// The owner fact that declares this repair.
311 pub declared_by: OwnerFact,
312 /// The repair rendered for a person.
313 pub description: HumanProjection,
314}
315
316/// How to reach the same observation again.
317///
318/// The road is the callable entry point the door names, which needs no proc macro anywhere in the path.
319#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
320pub struct Route {
321 entry: Identity<ServiceEntry>,
322}
323
324/// Where one projected refusal sits.
325///
326/// A refusal about the DECLARATION has nowhere narrower to point, and a line naming a position inside it would send a reader to an arbitrary spot; a refusal about one CLAUSE of an authored attribute has exactly one place, and the reader is sent there.
327#[derive(Debug, Clone, Copy)]
328pub enum Placement<'table> {
329 /// The refusal is about the declaration as a whole.
330 WholeDeclaration,
331 /// The refusal sits at one token, resolved through the producer's own table.
332 AtToken {
333 /// The token it sits at.
334 token: SpanHandle,
335 /// The table the producer resolves handles through.
336 spans: &'table SpanTable,
337 },
338}
339
340/// One diagnostic.
341///
342/// Every seat is required and every one of them is readable: a diagnostic that could omit its phase, its site, its expected contract, or its observed classification would be one that sometimes says less than it knows.
343/// The seats a caller reads after deciding what refused ride behind one pointer, so every `Result` that answers with a diagnostic stays small on its passing side.
344#[must_use = "a diagnostic carries the observation, its site, and the owner-declared repair"]
345#[derive(Debug, Clone, PartialEq, Eq, Hash)]
346pub struct Diagnostic {
347 phase: Phase,
348 site: Site,
349 observed: Observed,
350 carried: Box<DiagnosticSeats>,
351}
352
353/// The seats one diagnostic carries behind its pointer.
354#[derive(Debug, Clone, PartialEq, Eq, Hash)]
355struct DiagnosticSeats {
356 summary: String,
357 expected: Identity<Contract>,
358 related: RelatedSet,
359 repairs: Bounded<Repair, REPAIR_LIMIT>,
360 route: Route,
361}
362
363/// How one step of the road says no.
364///
365/// A step refuses in the vocabulary of the home that owns it and implements this to say how that vocabulary reads; [`Diagnostic::refused`] projects caller-placed refusals, while intrinsically placed refusal types expose their own typed diagnostic road.
366/// The two associated constants are facts about the error's TYPE and not about a call site, for the reason this home's README gives.
367pub trait Refused {
368 /// The step of the road this refusal is raised at.
369 const PHASE: Phase;
370
371 /// The family whose issue space this refusal's related identities derive in.
372 ///
373 /// An adopter declares its own, under its own namespace; the compiler's are the [`Family`] constants beside this trait.
374 const FAMILY: Family;
375
376 /// Which class of refusal the summary line opens with.
377 fn class(&self) -> RefusalClass;
378
379 /// How the first established issue reads for a person.
380 fn first(&self) -> String;
381
382 /// How what was observed differs from the expected contract, read off that same first issue.
383 fn observed(&self) -> Observed;
384
385 /// What the summary line is a summary of.
386 fn body(&self) -> LineBody;
387
388 /// One canonical byte string per issue established BEYOND the primary cause, in the order the body established them.
389 ///
390 /// The primary cause is the summary's own subject and never a member of its related set, so a single-cause refusal answers with nothing — which is what [`LineBody::SingleCause`]'s "enumerates nothing" means, at the machine seat as well as in the line.
391 /// Two bodies that differ in any typed member must answer with different bytes; that completeness is the implementing home's.
392 fn related(&self) -> Vec<Vec<u8>>;
393
394 /// The owner-declared repairs that apply.
395 fn repairs(&self) -> Bounded<Repair, REPAIR_LIMIT>;
396}
397
398/// How a crate-owned refusal whose site is intrinsic carries that site into its diagnostic.
399///
400/// The refusal home's private projection implements this trait, so no caller supplies a second placement that can disagree with the refusal's own coordinate.
401pub(crate) trait IntrinsicRefused: Refused {
402 /// The site established by the same act that established the refusal.
403 fn site(&self) -> Site;
404}