Skip to main content

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}