beck_diag/
index.rs

1//! The error index: every code the compiler can emit, in one place.
2//!
3//! [`crate::Diagnostic::code`] promised "a stable code (`B0101`) so that tests, docs and the
4//! eventual error index agree". This is that index, and the agreement is a test rather than an
5//! intention: `beck-cli/tests/docs.rs` scans every non-test source file in the workspace for a
6//! `"Bnnnn"` literal and fails if the set it finds differs from [`INDEX`] in either direction. A
7//! code cannot be added without an entry, and an entry cannot outlive its code.
8//!
9//! # What an entry says, and where it comes from
10//!
11//! Every diagnostic site in the compiler already carries a label, usually a note and often a
12//! fix-it — prose the project wrote next to the condition that raises it.
13//! [`CodeEntry::explain`] is that prose, condensed. Nothing here is a second, independent account
14//! of what the compiler does, because a second account is a thing that goes out of date.
15//!
16//! Ranges are not decorative: the first two digits say which stage refused the program, which is
17//! usually the first question.
18//!
19//! | Range | Stage |
20//! |---|---|
21//! | `B01xx` | Reading the source — lexer, S-expression reader, parser |
22//! | `B02xx` | Macro expansion, and the `ui` block it desugars |
23//! | `B03xx` | Names, types and effects — the checker |
24//! | `B04xx` | Placement (`B040x`) and security (`B041x`) |
25//! | `B05xx` | The signal graph and the slicer |
26//! | `B06xx` | Modules, interfaces and separate compilation |
27//! | `B07xx` | Tests written in Beck |
28
29/// The stage that raises a code.
30#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
31pub enum Stage {
32    Syntax,
33    Macros,
34    Types,
35    Placement,
36    Security,
37    Signals,
38    Modules,
39    Tests,
40}
41
42impl Stage {
43    pub fn title(&self) -> &'static str {
44        match self {
45            Stage::Syntax => "Reading the source",
46            Stage::Macros => "Macro expansion",
47            Stage::Types => "Names, types and effects",
48            Stage::Placement => "Placement",
49            Stage::Security => "Placement and security",
50            Stage::Signals => "The signal graph and the slicer",
51            Stage::Modules => "Modules and interfaces",
52            Stage::Tests => "Tests written in Beck",
53        }
54    }
55
56    /// Every stage, in the order the compiler runs them — which is the order the index is read in.
57    pub fn all() -> &'static [Stage] {
58        &[
59            Stage::Syntax,
60            Stage::Macros,
61            Stage::Types,
62            Stage::Placement,
63            Stage::Security,
64            Stage::Signals,
65            Stage::Modules,
66            Stage::Tests,
67        ]
68    }
69}
70
71/// One code, as the index describes it.
72#[derive(Copy, Clone, Debug, PartialEq, Eq)]
73pub struct CodeEntry {
74    pub code: &'static str,
75    pub stage: Stage,
76    /// The diagnostic's message, with the parts that vary at the call site generalised.
77    pub title: &'static str,
78    /// Why the compiler refuses this, in the terms the diagnostic itself uses.
79    pub explain: &'static str,
80    /// True for the codes raised as warnings rather than errors.
81    pub warning: bool,
82}
83
84/// Look a code up. The lookup `beck explain error B0301` and the rendered index both use.
85pub fn lookup(code: &str) -> Option<&'static CodeEntry> {
86    INDEX.iter().find(|e| e.code.eq_ignore_ascii_case(code))
87}
88
89/// Every code in one stage, in code order.
90pub fn in_stage(stage: Stage) -> impl Iterator<Item = &'static CodeEntry> {
91    INDEX.iter().filter(move |e| e.stage == stage)
92}
93
94const fn e(
95    code: &'static str,
96    stage: Stage,
97    title: &'static str,
98    explain: &'static str,
99) -> CodeEntry {
100    CodeEntry {
101        code,
102        stage,
103        title,
104        explain,
105        warning: false,
106    }
107}
108
109const fn w(
110    code: &'static str,
111    stage: Stage,
112    title: &'static str,
113    explain: &'static str,
114) -> CodeEntry {
115    CodeEntry {
116        code,
117        stage,
118        title,
119        explain,
120        warning: true,
121    }
122}
123
124/// Every code the compiler can emit, in code order.
125///
126/// Sorted, and the test asserts it: an index a reader has to search is an index nobody reads.
127pub const INDEX: &[CodeEntry] = &[
128    // ------------------------------------------------------------------ B01xx: reading the source
129    e(
130        "B0100",
131        Stage::Syntax,
132        "unrecognised character",
133        "The character is not a Beck token. Lexing continues past it so that one stray character \
134         does not hide every other problem in the file.",
135    ),
136    e(
137        "B0101",
138        Stage::Syntax,
139        "inconsistent indentation",
140        "This line's indentation matches no enclosing block. Indentation is significant, and \
141         §2.6 fixes it as spaces only, four per level — a tab is a lint rather than a width \
142         question. Blank and comment-only lines have no indentation at all, so a comment at \
143         column zero does not close a block.",
144    ),
145    e(
146        "B0102",
147        Stage::Syntax,
148        "an invisible control character in the source",
149        "A bidirectional formatting character (UTS #39 §4.1; CVE-2021-42574) or a zero-width \
150         no-break space away from the start of the file. These change how the text after them is \
151         *displayed* without changing what it means, so a reviewer and the compiler can read the \
152         same file differently. Write `\\u{...}` if a string genuinely needs one.",
153    ),
154    e(
155        "B0103",
156        Stage::Syntax,
157        "an identifier outside the ASCII profile",
158        "Beck's identifiers are `[A-Za-z_][A-Za-z0-9_]*`, which is UTS #39's ASCII-Only \
159         restriction level. Confusable and mixed-script identifiers are therefore not something \
160         the compiler checks for; they are something a program cannot contain.",
161    ),
162    e(
163        "B0110",
164        Stage::Syntax,
165        "expected a single form",
166        "`beck ast` and the macro-debugging paths read exactly one S-expression. A file with a \
167         second form at top level is a module, and is read with the module reader instead.",
168    ),
169    e(
170        "B0111",
171        Stage::Syntax,
172        "unbalanced closing delimiter",
173        "A `)`, `]` or `}` with no matching opening delimiter.",
174    ),
175    e(
176        "B0112",
177        Stage::Syntax,
178        "unclosed list",
179        "A list opened here and the file ended before it closed. The span points at the opening \
180         delimiter, which is the one worth looking at.",
181    ),
182    e(
183        "B0113",
184        Stage::Syntax,
185        "mismatched closing delimiter",
186        "The closing delimiter is not the one this form opened with — `(a]`. Both spans are \
187         reported: where it opened, and where it was closed wrongly.",
188    ),
189    e(
190        "B0114",
191        Stage::Syntax,
192        "empty application",
193        "`()` has no meaning: an application's head is its first element, and there is none. \
194         Write `unit` for the unit value.",
195    ),
196    e(
197        "B0115",
198        Stage::Syntax,
199        "unclosed string",
200        "A string literal opened and the line or file ended before the closing quote. Beck string \
201         literals do not span lines.",
202    ),
203    e(
204        "B0116",
205        Stage::Syntax,
206        "unreadable character",
207        "The S-expression reader cannot begin an atom with this character.",
208    ),
209    e(
210        "B0120",
211        Stage::Syntax,
212        "unexpected token",
213        "The Python-surface parser expected something else here; the message names what. One \
214         error per item: the parser recovers to the next top-level item, so a bad line does not \
215         make the rest of the file unparseable.",
216    ),
217    e(
218        "B0121",
219        Stage::Syntax,
220        "nesting is too deep to read",
221        "The source nests deeper than the front end follows — `beck_diag::depth::MAX_NESTING` \
222         levels of brackets, indentation or S-expression lists. The bound is a fixed count rather \
223         than a reading of the stack, so the same file is accepted or refused identically in every \
224         build; without it, deep enough input aborted the process with no span at all.",
225    ),
226    e(
227        "B0122",
228        Stage::Syntax,
229        "an expression chains more operators than the reader will follow",
230        "A left-associative chain — `1 + 1 + 1 + …` — is flat in source and builds a left-leaning \
231         tree of the same depth, one level per operator. The Pratt loop that reads it does not \
232         recurse, so none of the parser's recursion counters sees the depth, and a long enough \
233         chain reached the end of the host stack in whatever walked the tree afterwards. The bound \
234         is `beck_diag::depth::MAX_BLOCK` — the same ceiling a block of sequential bindings takes, \
235         because it is the same axis: a flat run of things that costs one tree level each.",
236    ),
237    // ------------------------------------------------------------------------ B02xx: macros
238    e(
239        "B0200",
240        Stage::Macros,
241        "macro is defined twice",
242        "Two macros in one module share a name, and a later definition would silently win.",
243    ),
244    e(
245        "B0201",
246        Stage::Macros,
247        "macro expansion did not terminate",
248        "Expansion ran past the depth limit — usually a macro that expands to a call to itself.",
249    ),
250    e(
251        "B0202",
252        Stage::Macros,
253        "macro expects an argument",
254        "A parameter of the macro received nothing at this call site. The macro's definition is \
255         reported as a secondary span.",
256    ),
257    e(
258        "B0203",
259        Stage::Macros,
260        "too many arguments for macro",
261        "More arguments than the macro declares parameters.",
262    ),
263    e(
264        "B0204",
265        Stage::Macros,
266        "macro returns nothing",
267        "A macro body ends without `return quote: …`, so there is no template to instantiate.",
268    ),
269    e(
270        "B0205",
271        Stage::Macros,
272        "a form is not available in a macro body",
273        "A macro body is pure compile-time computation — bindings, `if`, `for`, `while`, lambdas, \
274         calls and `quote:`. `match`, `try:`, `raise`, `parallel:` and declarations belong to the \
275         program the macro expands to, not to the expander.",
276    ),
277    e(
278        "B0206",
279        Stage::Macros,
280        "unquoting an unbound name",
281        "`$x` inside a template names something that is bound nowhere in this macro — usually a \
282         parameter that was renamed. `$e` evaluates `e` in the macro body's own environment, so \
283         anything the body bound is fair game.",
284    ),
285    e(
286        "B0207",
287        Stage::Macros,
288        "a primitive may not be called while expanding a macro",
289        "Macro expansion is capability-restricted (`docs/02` §2.4): it is pure computation over \
290         the module's own definitions, so that what a compile produces depends on the source and \
291         on nothing else. The primitive named performs an effect — reading the clock, the \
292         environment, the network — and is refused *by name* so that reaching for one is a \
293         diagnostic rather than a spelling mistake.",
294    ),
295    e(
296        "B0208",
297        Stage::Macros,
298        "cannot find a name at compile time",
299        "The macro interpreter's environment is a whitelist: locals, the `def`s of this module \
300         and of the ones it imports, and the pure builtins. There is no name in it for a file, a \
301         socket or a process, which is the sandbox rather than an omission.",
302    ),
303    e(
304        "B0209",
305        Stage::Macros,
306        "a macro body computed the wrong kind of value",
307        "A compile-time computation applied an operation to something it does not apply to — \
308         adding a Str to an Int, indexing past the end of a list, calling a value that is not a \
309         function. Macro bodies are evaluated before the checker runs, so these are caught by \
310         running rather than by typing.",
311    ),
312    e(
313        "B0210",
314        Stage::Macros,
315        "`ui` needs an indented block",
316        "Write `ui:` followed by an indented element.",
317    ),
318    e(
319        "B0211",
320        Stage::Macros,
321        "`ui` block is empty",
322        "A view must produce exactly one root element.",
323    ),
324    e(
325        "B0212",
326        Stage::Macros,
327        "`ui` block has more than one root",
328        "An `Html` value is a single tree. Wrap the elements in one — a `div:` or `main:` block.",
329    ),
330    e(
331        "B0213",
332        Stage::Macros,
333        "the form nests too deep to expand",
334        "The expander walks a form's arguments as deeply as they nest, and stops at the count the \
335         reader stops at. This is not `B0201`: nothing here says a macro failed to terminate — the \
336         two were one counter until they were separated, and a deep expression with no macros in \
337         it reported the wrong one.",
338    ),
339    e(
340        "B0214",
341        Stage::Macros,
342        "macro expansion produced too much",
343        "`B0201` and `B0213` bound how *deep* expansion goes; this bounds how much it makes. A \
344         macro that doubles its output at each of a few levels is shallow, terminates, and is \
345         enormous — eight nestings of a two-line macro is 256 copies of its argument — so the \
346         expander charges what it produces against a budget for the whole module. Reaching it means \
347         a macro is generating far more than any program in this repository does, and the fix is \
348         almost never a bigger budget.",
349    ),
350    e(
351        "B0215",
352        Stage::Macros,
353        "a macro body ran too long",
354        "`B0214` bounds what expansion *produces*; this bounds what it *does*. A macro body is a \
355         Beck program running at compile time, so `while true:` in one is a compiler that does \
356         not finish — the budget is a bound on how long a compile takes, shared by the whole \
357         module because that is what a compile is.",
358    ),
359    e(
360        "B0216",
361        Stage::Macros,
362        "a macro body recursed too deep",
363        "A compile-time call chain — usually a `def` that calls itself with no base case — went \
364         past the front end's nesting ceiling. A fixed count rather than a reading of the stack, \
365         for `adr/0012`'s reason: a diagnostic that depends on the build profile is not a \
366         diagnostic.",
367    ),
368    e(
369        "B0217",
370        Stage::Macros,
371        "that is not an event the client listens for",
372        "A handler is a declarative attribute carrying a command, and the client interprets a \
373         closed set of them — `on_click`, `on_enter`, `on_submit`, `on_input`, `on_change`. Any \
374         other name reaches the browser as an attribute wired to nothing, which is why this is a \
375         refusal rather than a lint: there is no such thing as a custom event here.",
376    ),
377    e(
378        "B0218",
379        Stage::Macros,
380        "that is not an HTML attribute",
381        "A `ui:` element carries its keyword arguments to the page as attributes, and a name HTML \
382         does not have is one the browser ignores — a page that quietly does less than it says. An \
383         attribute of your own is spelled `data_…`, which is HTML's own extension point; `aria_…` \
384         is admitted the same way.",
385    ),
386    e(
387        "B0219",
388        Stage::Macros,
389        "image has no alt text",
390        "A screen reader announces an `img` by its `alt` attribute; without one it announces the \
391         file name, or nothing. `alt=\"\"` is HTML's own spelling for an image that carries no \
392         meaning and is accepted — the check is for the attribute, not for a value, because a \
393         compile-time check knows the shape of the tree and not the text in it. \
394         `a11y_exempt=\"…\"` turns it off on one element, with the reason written down.",
395    ),
396    e(
397        "B0220",
398        Stage::Macros,
399        "button has no accessible name",
400        "A `button` is named by its own text. One with no children and no `aria_label`, \
401         `aria_labelledby` or `title` announces nothing at all, which is the icon-button mistake. \
402         Any child is accepted, because whether an expression renders to empty text is not a \
403         question this stage can answer.",
404    ),
405    e(
406        "B0221",
407        Stage::Macros,
408        "form control has no label",
409        "An `input`, `select` or `textarea` is named by `aria_label`, `aria_labelledby`, `title`, \
410         or a `label(for=…)` pointing at its `id`. A **placeholder is not a label** — it disappears \
411         as soon as somebody types, which is WCAG 3.3.2's commonest real failure. An `id` is \
412         accepted as evidence of a label elsewhere, because a `ui:` block composes out of functions \
413         and this check sees one element at a time.",
414    ),
415    e(
416        "B0222",
417        Stage::Macros,
418        "class is one slip from a utility",
419        "The class vocabulary is **open** — a name this compiler does not know is a name of your \
420         own, and is left alone — so this is a warning rather than a refusal. What it says is that \
421         the name is within one edit of a utility that would have had a rule, which is what \
422         `rounded-ful` is to `rounded-full`. `beck explain style` lists which of a page's classes \
423         are utilities and which are the program's own.",
424    ),
425    e(
426        "B0223",
427        Stage::Macros,
428        "typed macro over a declaration",
429        "A `typed macro` is expanded by the **checker**, and what its body is given is what the \
430         checker inferred an *expression* to be. A declaration has nothing inferred about it — a \
431         model's fields are in its syntax — so a macro that decorates one is an ordinary `macro`, \
432         which receives it before checking runs at all. `docs/02` §2.4 is the division, and \
433         `lib/json.beck`'s `derive_json` is the shape a declaration macro takes.",
434    ),
435    e(
436        "B0224",
437        Stage::Macros,
438        "refused by a macro",
439        "A macro that reads a type and writes the code a value of it goes through meets types it \
440         has no rule for. `refuse(\"…\")` is how it says so: the message is the macro author's, the \
441         position is the call, and the second label is the line in the macro body that decided. \
442         Without it such a macro emits code that fails to check for a reason nobody can trace back \
443         to the macro.",
444    ),
445    // ------------------------------------------------------- B03xx: names, types and effects
446    e(
447        "B0300",
448        Stage::Types,
449        "not a tier",
450        "`@on(…)` takes `client`, `server`, `data` or `any`.",
451    ),
452    w(
453        "B0301",
454        Stage::Types,
455        "unsupported decorator",
456        "The compiler understands `@on(client|server|data|any)` and `@signal`. A warning rather \
457         than an error, so an unknown decorator does not stop a build — but it does nothing.",
458    ),
459    e(
460        "B0302",
461        Stage::Types,
462        "type is declared twice",
463        "Two declarations in this module share a type name.",
464    ),
465    e(
466        "B0303",
467        Stage::Types,
468        "a top-level parameter needs a type annotation",
469        "Inference is intra-module and boundaries are declared (§3.6): a top-level definition's \
470         parameters are part of its published signature, so they are written rather than guessed.",
471    ),
472    e(
473        "B0304",
474        Stage::Types,
475        "needs a return type",
476        "Same reason as B0303: a top-level definition's result type is part of its contract.",
477    ),
478    e(
479        "B0305",
480        Stage::Types,
481        "neither an effect nor a row",
482        "A `uses` clause names effect atoms and row aliases, and this is neither. `beck doc \
483         reference` lists the atom set; a `row Name = …` declaration in the module is what makes a \
484         name for a bundle of them.",
485    ),
486    e(
487        "B0306",
488        Stage::Types,
489        "is not a rendering mode",
490        "`@render` takes `server` (Mode A: the server renders and the wire carries DOM patches) \
491         or `client` (Mode B: the browser renders and the wire carries the state).",
492    ),
493    e(
494        "B0307",
495        Stage::Types,
496        "unsupported top-level item",
497        "This form is not something a module may contain at top level.",
498    ),
499    e(
500        "B0308",
501        Stage::Types,
502        "expected a type",
503        "A type position holds something that is not a type expression.",
504    ),
505    e(
506        "B0310",
507        Stage::Types,
508        "cannot find type",
509        "No declaration, import or builtin of that name is in scope.",
510    ),
511    e(
512        "B0311",
513        Stage::Types,
514        "wrong number of type arguments",
515        "A mention of a type carries one argument per declared parameter — `union Tree[T]` is \
516         written `Tree[Int]`, never bare `Tree`.",
517    ),
518    e(
519        "B0312",
520        Stage::Types,
521        "a type alias defined in terms of itself, or a name the language reserves",
522        "An alias is transparent, so a self-referential one describes no type — a `union` may be \
523         recursive, an alias may not. The same code covers a definition named after a reserved \
524         form such as `record`, which would compile and never be reachable.",
525    ),
526    e(
527        "B0313",
528        Stage::Types,
529        "a type parameter takes no type arguments",
530        "A type parameter of the definition or declaration being read names an unknown type, so it \
531         has no structure to apply arguments to: `T[Int]` says nothing, whatever `T` turns out to \
532         be at the call site.",
533    ),
534    e(
535        "B0314",
536        Stage::Types,
537        "a type parameter shadows an existing type",
538        "A type parameter is a name the definition or declaration invents, and one that shadowed \
539         an existing type would make its fields or its signature read as though they mentioned \
540         that type.",
541    ),
542    e(
543        "B0315",
544        Stage::Types,
545        "a type parameter is repeated",
546        "The same name appears twice in one type-parameter list, where the second would silently \
547         shadow the first.",
548    ),
549    e(
550        "B0316",
551        Stage::Types,
552        "a declaration cannot bound its type parameter",
553        "A bound says what a body may call, and a `model`, a `union`, a `newtype` and a `type` have \
554         no body. The definitions that take the type apart are where the bound belongs.",
555    ),
556    e(
557        "B0317",
558        Stage::Types,
559        "a declaration shadows a builtin type",
560        "`Int`, `Str`, `Bool`, `Float`, `Unit`, `Html`, `Attr`, `list`, `Map`, `Stream`, `Signal`, \
561         `Envelope`, `secret` and `internal` are the language's own type names, and a `model`, \
562         `union`, `newtype` or `type` may not take one. B0314 has refused the same shadowing for a \
563         type *parameter* since docs/27; a declaration was the production it did not cover, so the \
564         name meant the builtin in one signature and the declaration in the next (docs/63 §63.10).",
565    ),
566    e(
567        "B0320",
568        Stage::Types,
569        "type mismatch",
570        "Unification failed; the message names what was being unified — an argument, a field, a \
571         result, or the two branches of an `if`. The branches of an `if` are reported as two \
572         alternatives rather than as actual-and-expected: typing one as the other's expectation \
573         is what refused SICP exercise 1.43 (docs/27 §27.2).",
574    ),
575    w(
576        "B0330",
577        Stage::Types,
578        "statements after `return` are unreachable",
579        "A `return` ends its block; anything after it never runs.",
580    ),
581    e(
582        "B0331",
583        Stage::Types,
584        "loops are not available in Phase 1",
585        "Everything is an expression and `var` is not yet mutable, so a loop has nothing to \
586         accumulate into. Use `map_list`, `filter_list` or `fold`.",
587    ),
588    e(
589        "B0332",
590        Stage::Types,
591        "a `quote` survived macro expansion",
592        "A template reached the checker, which means it was never instantiated. Quoted forms are \
593         data for a macro, not code.",
594    ),
595    e(
596        "B0333",
597        Stage::Types,
598        "a keyword argument outside a call",
599        "`name=value` is call syntax and has no meaning as an expression on its own.",
600    ),
601    e(
602        "B0334",
603        Stage::Types,
604        "unsupported expression",
605        "This form has no meaning in expression position.",
606    ),
607    e(
608        "B0335",
609        Stage::Types,
610        "has no body",
611        "A bodyless `def` is a declaration, which is what a `.becki` interface file is made of; \
612         an ordinary module has to define what it declares.",
613    ),
614    e(
615        "B0340",
616        Stage::Types,
617        "cannot find name in this scope",
618        "No local, parameter, top-level definition, import or prelude name matches — after \
619         hygiene, which means a name a macro introduced is not visible to code the macro did not \
620         write.",
621    ),
622    e(
623        "B0341",
624        Stage::Types,
625        "match is not exhaustive",
626        "The missing variants are listed. This is deliberate and load-bearing: adding a variant \
627         must break every fold that consumes it, which is what makes a missed migration a compile \
628         error rather than a 3 a.m. page.",
629    ),
630    e(
631        "B0342",
632        Stage::Types,
633        "unsupported pattern",
634        "A `case` arm's pattern is a variant applied to field bindings, or `_`. This form is \
635         neither.",
636    ),
637    e(
638        "B0343",
639        Stage::Types,
640        "not a constructor",
641        "The head of a pattern must be a variant of the union being matched.",
642    ),
643    e(
644        "B0344",
645        Stage::Types,
646        "cannot tell which field this binds or sets",
647        "A pattern or record update names a field the compiler cannot resolve to the type in hand.",
648    ),
649    e(
650        "B0345",
651        Stage::Types,
652        "the tail of a list pattern is a name, not a pattern",
653        "The tail of a list pattern binds the rest of the list, so it takes a name or `_`. \
654         `[a, *[b, c]]` is `[a, b, c]` written twice over.",
655    ),
656    e(
657        "B0346",
658        Stage::Types,
659        "cannot tell which model this record builds",
660        "A record literal's type comes from what is expected of it, and nothing here says which \
661         model it is.",
662    ),
663    e(
664        "B0347",
665        Stage::Types,
666        "expected a field or method name",
667        "The right-hand side of a `.` must be a name.",
668    ),
669    e(
670        "B0348",
671        Stage::Types,
672        "`with` takes named fields",
673        "Functional record update is written `t.with(done=…)`.",
674    ),
675    e(
676        "B0349",
677        Stage::Types,
678        "no such field on this type",
679        "The field named in a `with` is not declared on the model being updated.",
680    ),
681    e(
682        "B0350",
683        Stage::Types,
684        "no field or function for this type",
685        "A `.` reached neither a field of the receiver nor a function that could take it.",
686    ),
687    e(
688        "B0351",
689        Stage::Types,
690        "wrong number of arguments",
691        "A call passes a different number of arguments than the function or constructor takes.",
692    ),
693    e(
694        "B0352",
695        Stage::Types,
696        "not callable",
697        "The callee's type is not a function type.",
698    ),
699    e(
700        "B0353",
701        Stage::Types,
702        "no such variant",
703        "The union being constructed or matched has no variant of that name. The type is named in \
704         the message.",
705    ),
706    e(
707        "B0354",
708        Stage::Types,
709        "cannot construct this type",
710        "The name is a type, but not one with a constructor — an alias or a builtin.",
711    ),
712    e(
713        "B0355",
714        Stage::Types,
715        "this case can never match",
716        "Every value the arm matches is already matched by an arm above it, so it cannot run. A \
717         warning rather than an error: `case _` written after every variant of a union is a habit \
718         rather than a mistake.",
719    ),
720    e(
721        "B0356",
722        Stage::Types,
723        "the alternatives of an or-pattern bind different names",
724        "Every alternative of `a | b` has to bind the same names at the same types, because the \
725         body reads them without knowing which one matched.",
726    ),
727    e(
728        "B0357",
729        Stage::Types,
730        "`|` and `@` are only meaningful in a `case` pattern",
731        "Beck has no bitwise operators. `|` separates the alternatives of an or-pattern, `@` names \
732         the value a pattern takes apart, and neither means anything anywhere else.",
733    ),
734    e(
735        "B0358",
736        Stage::Types,
737        "the left of `@` is a name",
738        "`whole @ Circle(r)` binds `whole` to the value the pattern matched. What stands to the \
739         left of `@` is the name being bound.",
740    ),
741    e(
742        "B0359",
743        Stage::Types,
744        "an identity declaration this compiler cannot derive a deployment from",
745        "`identity = external(issuer=\"https://login.acme.com\")` and `identity = managed()` are \
746         the two forms, and a program declares at most one. An external issuer is an `https` URL — \
747         the key set's only integrity protection is the transport it arrives over — and its host \
748         has to be one an egress rule could name, because §6.5 derives the cluster's rule from it. \
749         `managed()` takes no arguments: it provisions a provider into the object graph, and the \
750         issuer is a Service the deployment names rather than the program.",
751    ),
752    e(
753        "B0360",
754        Stage::Types,
755        "cannot be called inside a fold",
756        "A fold must be replay-pure, and this would make replay non-deterministic. Time is data on \
757         the envelope (`env.at`) and entity ids are minted at the edge: mint the id in the \
758         client's command and read it from the event.",
759    ),
760    e(
761        "B0370",
762        Stage::Types,
763        "performs more than its signature declares",
764        "The undeclared atoms are listed. A `uses` clause is the published bound, and widening it \
765         is a breaking API change — so the compiler will not widen it for you.",
766    ),
767    // -------------------------------------------------------------------- B038x: traits
768    e(
769        "B0380",
770        Stage::Types,
771        "a trait cannot be declared here",
772        "The name already belongs to a type, the trait is declared twice, or the file is a \
773         `.becki` — a trait does not cross a module boundary, so an interface may not hold one.",
774    ),
775    e(
776        "B0381",
777        Stage::Types,
778        "a trait declaration is wrong",
779        "A trait holds `def` signatures with no bodies, each mentioning `Self` in a parameter so \
780         that a call has something to dispatch on. A method name belongs to one trait only.",
781    ),
782    e(
783        "B0382",
784        Stage::Types,
785        "an impl does not match its trait",
786        "An impl writes bodies and parameter *names*; the types, the return type and the effect \
787         row are the trait's. Every method must be implemented, exactly once, and no others.",
788    ),
789    e(
790        "B0383",
791        Stage::Types,
792        "cannot find the trait or the type this impl names",
793        "Both halves of `impl Trait for Type` have to resolve before the impl can be registered.",
794    ),
795    e(
796        "B0384",
797        Stage::Types,
798        "conflicting implementations",
799        "Coherence: one impl per trait per type constructor, and no blanket impl over a type \
800         parameter — so what a call means never depends on which impls happen to be in scope.",
801    ),
802    e(
803        "B0385",
804        Stage::Types,
805        "orphan impl",
806        "An impl belongs with the trait or with the type. Implementing somebody else's trait for \
807         somebody else's type is what lets two modules supply one and disagree.",
808    ),
809    e(
810        "B0386",
811        Stage::Types,
812        "no implementation can be chosen here",
813        "An implementation comes from a concrete type or from a bound on a type parameter — write \
814         `[T: Trait]` to say the parameter has one. A trait method and a bounded definition are \
815         both called rather than passed: the implementation is supplied at the call site, so a \
816         reference that is never called has nowhere to receive it.",
817    ),
818    e(
819        "B0387",
820        Stage::Types,
821        "the type does not implement the trait",
822        "There is no `impl Trait for Type` in scope for the receiver's type.",
823    ),
824    e(
825        "B0388",
826        Stage::Types,
827        "an imported module implements a trait this program does not import",
828        "The impl is dropped, so its methods cannot be called here — a trait is a name, and a name \
829         is visible where its module is imported directly rather than through somebody else's \
830         import. A warning rather than a refusal, because a module may legitimately publish an \
831         impl for a trait the importer never names; import the trait's module to use it.",
832    ),
833    e(
834        "B0389",
835        Stage::Types,
836        "a block has more statements than the checker will follow",
837        "A block is a chain of `let`s however flat it looks in source, so the checker recurses once \
838         per statement and a long enough body reaches the end of the host stack. The bound is \
839         `beck_diag::depth::MAX_BLOCK` — much larger than the nesting ceiling, because 256 levels \
840         of nesting is pathological and 256 sequential bindings is merely a long function. It is a \
841         fixed count rather than a reading of the stack, so the same file is accepted or refused \
842         identically in a debug and a release build; without it, a long enough body aborted the \
843         process with no span at all.",
844    ),
845    e(
846        "B0390",
847        Stage::Types,
848        "the expression nests too deep to check",
849        "The checker walks an expression and a type as deeply as they nest, and stops at \
850         `beck_diag::depth::MAX_NESTING` levels — the same count the reader stops at, because the \
851         checker can be handed a tree a macro produced rather than one anybody typed. Everything \
852         downstream walks the `Core` this pass built, so it is bounded by the same number.",
853    ),
854    e(
855        "B0391",
856        Stage::Types,
857        "a raised value must have a declared type",
858        "`raise` performs `raises(T)`, and the atom names `T` so that a handler can say what it \
859         catches. A builtin will not do: `raises(Int)` would make every integer failure in a \
860         program the same failure, and a handler could not tell them apart.",
861    ),
862    e(
863        "B0392",
864        Stage::Types,
865        "nothing here can fail, and nothing says what this would catch",
866        "A `try:` reifies one failure into a `Result`, and it takes the error type from the \
867         enclosing signature's `Result[T, E]` where there is one and from the block's own row \
868         where there is not. Neither said anything here. Either the call you meant to make is not \
869         there, or the `try:` is left over from a signature that has stopped failing — which is \
870         the good case, and the diagnostic is how you find out.",
871    ),
872    e(
873        "B0393",
874        Stage::Types,
875        "the block can fail in more than one way, and nothing says which to catch",
876        "A `Result[T, E]` has one error type, and a `try:` catches one — the rest keep travelling, \
877         which is what makes a handler composable. Here nothing named which: give the enclosing \
878         definition a `Result[T, E]` return type, and the handler catches that `E`.",
879    ),
880    e(
881        "B0394",
882        Stage::Types,
883        "the row is declared twice",
884        "Two `row Name = …` declarations with the same name. A row alias is a name for a bundle of \
885         effect atoms, and a second one would make every `uses` clause mentioning it ambiguous.",
886    ),
887    e(
888        "B0395",
889        Stage::Types,
890        "the host of an outbound call has to be written at the call site",
891        "`http_fetch` performs `net.out(host)`, and §6.5 derives the cluster's egress policy from \
892         that atom and nothing else. A host computed at run time is an outbound call the \
893         deployment cannot be told about, so the argument is read where it is written. Compute \
894         the path, the port, the headers and the body; or take a closure, so the caller names its \
895         own host and the row carries the atom out.",
896    ),
897    e(
898        "B0396",
899        Stage::Types,
900        "that is not a host an outbound call can name",
901        "The host becomes a NetworkPolicy peer and a `uses net.out(…)` clause, both of which are \
902         written as bare DNS labels — so a scheme, a port or a path in it is a name neither could \
903         carry. `origin` is refused for a different reason: it is the one outbound atom a client \
904         tier discharges, and a client reaches its own server over the command channel.",
905    ),
906    e(
907        "B0397",
908        Stage::Types,
909        "a parallel scope has fewer than two children",
910        "A `parallel:` scope runs its bindings as children. With one there is nothing to run it \
911         alongside, and with none there is nothing to run — either way the form is claiming a \
912         concurrency it does not have, and an ordinary block says the same thing without the \
913         claim. The tail is everything after the last binding, so a scope written with its work \
914         in the tail has no children either.",
915    ),
916    e(
917        "B0398",
918        Stage::Types,
919        "a child of a parallel scope names another child",
920        "The children of a `parallel:` scope run together, so none of them can see another's \
921         result — a child that could would have to run second, and then it is not a child but a \
922         next line. Move the reader into the scope's tail, which runs after the join with every \
923         child's result in scope, or out into a second scope below this one.",
924    ),
925    e(
926        "B0399",
927        Stage::Types,
928        "a child of a parallel scope performs an effect another child could observe",
929        "The claim a `parallel:` scope makes is that its answer does not depend on the order its \
930         children ran in, and an effect on state the program holds — the log, the document, the \
931         merge point, a file, an external store — breaks it: two children appending to the log in \
932         the other order is a different log. `net.out(host)` is not on that list and is the case \
933         the form exists for. Do the shared-state part in the tail, which runs once, after the \
934         join.",
935    ),
936    // -------------------------------------------------------------------- B04xx: placement
937    e(
938        "B0400",
939        Stage::Placement,
940        "performs effects no single tier can discharge",
941        "Each tier discharges a fixed set (§3.3). A row no tier covers has to be split across \
942         definitions that can each be placed.",
943    ),
944    e(
945        "B0401",
946        Stage::Placement,
947        "placed on a tier that cannot discharge an effect it performs",
948        "The written `@on(…)` and the inferred row disagree. The diagnostic names the atom, the \
949         tier, and the tiers that could discharge it. `ingress` is the merge point and only the \
950         server holds it; `durable` is the data tier's; `dom` is the browser's.",
951    ),
952    e(
953        "B0402",
954        Stage::Placement,
955        "a fold function must be replay-pure",
956        "The function reached by a `fold` performs effects that replay would not reproduce. Both \
957         the fold and the definition are reported.",
958    ),
959    e(
960        "B0403",
961        Stage::Placement,
962        "a program has exactly one merge point",
963        "A second `merge_clients()`. The merge point is where time and nondeterminism enter; two \
964         of them would mean two total orders, and replay would no longer be a function of the log.",
965    ),
966    e(
967        "B0404",
968        Stage::Placement,
969        "cannot be unplaced",
970        "`@on(any)` means every tier, and an atom in the row is not discharged on every tier. The \
971         fix-it names the tiers that can.",
972    ),
973    e(
974        "B0405",
975        Stage::Placement,
976        "only a component can say where it renders",
977        "`@render` was written on something that is not a `Signal[Html]`. A definition is unplaced \
978         code compiled to every tier that needs it (§3.3); rendering is decided per component.",
979    ),
980    e(
981        "B0410",
982        Stage::Security,
983        "runs on the client, so its value must be Sendable",
984        "This value crosses to the browser, and §3.5's claim is that the compiler proves a secret \
985         cannot. The offending field and the path that reaches it are both named — `beck explain \
986         flow <Type>` prints the same walk.",
987    ),
988    e(
989        "B0411",
990        Stage::Security,
991        "durable, so its state must be storable",
992        "The log is the only description of this program's history; a value it cannot read back \
993         is a state replay would not reproduce.",
994    ),
995    e(
996        "B0412",
997        Stage::Security,
998        "requires a capability nothing can discharge",
999        "A `Session` reaches exactly one place in a Beck program: the validator `decide` is given, \
1000         which is the only function handed a `Proposal`. Authority is one chokepoint (§3.5), so a \
1001         capability required outside it has no holder.",
1002    ),
1003    // ------------------------------------------------------ B05xx: the signal graph and slicer
1004    e(
1005        "B0500",
1006        Stage::Signals,
1007        "this program has no merge point",
1008        "A Beck application is a fold over an event stream, and the stream starts at \
1009         `merge_clients()`. Not an error for a library: this code, with B0501 and B0505, is what \
1010         says a module is a domain module rather than an application.",
1011    ),
1012    e(
1013        "B0501",
1014        Stage::Signals,
1015        "this program has no durable state",
1016        "`durable(fold(f, init, s))` is what makes the log a database.",
1017    ),
1018    e(
1019        "B0502",
1020        Stage::Signals,
1021        "`durable` must wrap a `fold`",
1022        "Only a fold has an accumulator to persist.",
1023    ),
1024    e(
1025        "B0504",
1026        Stage::Signals,
1027        "events must come from `decide`",
1028        "The fold has no chokepoint upstream of it. `decide` is the sole consumer of ingress and \
1029         the one place a command becomes an event — §3.5's \"authority is one chokepoint\".",
1030    ),
1031    e(
1032        "B0505",
1033        Stage::Signals,
1034        "no signal is placed on the client",
1035        "`page` is the tier crossing: a `Signal[Html]` the browser subscribes to.",
1036    ),
1037    e(
1038        "B0506",
1039        Stage::Signals,
1040        "not a signal",
1041        "A signal's inputs are other signals. A function is applied *through* a construct — \
1042         `signal_map(s, f)` — rather than named as an input.",
1043    ),
1044    e(
1045        "B0507",
1046        Stage::Signals,
1047        "not a signal construct",
1048        "§3.7's signal vocabulary is `merge_clients`, `filter_map`, `fold`, `durable`, \
1049         `signal_map`, `map2`, `per_session` and `decide`.",
1050    ),
1051    e(
1052        "B0508",
1053        Stage::Signals,
1054        "unsupported signal expression",
1055        "A signal is a node in the dataflow, not a computation. The computation goes in a `def` \
1056         and the signal names it: `summary: Signal[Summary] = signal_map(counts, summarise)`.",
1057    ),
1058    e(
1059        "B0509",
1060        Stage::Signals,
1061        "a signal defined in terms of itself",
1062        "The cycle is printed. A cycle through a `fold` is sound — an accumulator is a value, so \
1063         the recursion has a bottom, which is why `events → todos → events` is legal. One with no \
1064         fold in it has no first value to compute from.",
1065    ),
1066    e(
1067        "B0510",
1068        Stage::Signals,
1069        "two signals are the page, and there is no router yet",
1070        "The slicer will slice both; the runtime serves one document per connection, and choosing \
1071         between them is routing — a Phase 3 client bullet that is not built.",
1072    ),
1073    e(
1074        "B0511",
1075        Stage::Signals,
1076        "a program has one authority chokepoint",
1077        "A second `decide`. §3.5 rests on validation being one place: two of them are two answers \
1078         to \"may this actor do this\", and the log would record whichever ran.",
1079    ),
1080    e(
1081        "B0512",
1082        Stage::Signals,
1083        "the chokepoint does not read a durable fold",
1084        "`decide` threads the accumulator through validation, so what it reads has to be one — \
1085         that is what makes first-writer-wins and ownership decidable (§3.7).",
1086    ),
1087    e(
1088        "B0513",
1089        Stage::Signals,
1090        "a fold that is not durable",
1091        "Its accumulator has nowhere to live across a restart. The log is what survives, and \
1092         `durable` is what says an accumulator is folded from it.",
1093    ),
1094    e(
1095        "B0514",
1096        Stage::Signals,
1097        "renders differently for each actor, so it cannot render on the client",
1098        "The page reads who is asking — `session.actor` or `session.claims` — so it filters, \
1099         scopes or hides by identity. `@render(client)` sends the browser the state rather than \
1100         the page, which would hand every actor what the filter was removing (docs/94 §94.2). \
1101         Reading `session.path` is not this: the browser chose the route and already holds the \
1102         state, so a page that varies by route is eligible (docs/94 §94.3).",
1103    ),
1104    e(
1105        "B0515",
1106        Stage::Signals,
1107        "the chokepoint reads `presence`, which is not in the log",
1108        "Who was connected when an event was recorded is written down nowhere, so a `validate` \
1109         that decided from the roster would decide one thing now and another on replay. Record the \
1110         fact instead: propose a command when a client arrives, and decide from the state that \
1111         fold produces.",
1112    ),
1113    e(
1114        "B0516",
1115        Stage::Signals,
1116        "reads `presence`, so it cannot render on the client",
1117        "`@render(client)` sends the browser the accumulator, and who is connected is in neither \
1118         the accumulator nor the log — it is a fact the server holds about its own sockets \
1119         (docs/48 §48.9).",
1120    ),
1121    e(
1122        "B0517",
1123        Stage::Signals,
1124        "the chokepoint reads `freshness`, which is not in the log",
1125        "How many of a client's commands were in flight when an event was recorded is written \
1126         down nowhere, and on replay nothing is in flight at all — so a `validate` that decided \
1127         from it would accept a command today and refuse it on the way back. Decide from the \
1128         accumulator, which says the same thing now and on replay.",
1129    ),
1130    e(
1131        "B0518",
1132        Stage::Signals,
1133        "reads `freshness`, so it cannot render on the server",
1134        "§3.7's freshness dimension is a client's account of the commands it has proposed and not \
1135         yet had confirmed. A server renders what it has recorded, so its answer is `Confirmed` at \
1136         every position of every log and the page's other branch would be unreachable. This is \
1137         `B0516` from the other side: `@render(client)` is what makes a guess possible, and \
1138         therefore what makes saying so possible (docs/94 §94.5).",
1139    ),
1140    e(
1141        "B0519",
1142        Stage::Signals,
1143        "a fold over the log that is not `durable`",
1144        "The stream this folds is the log's, so its accumulator *is* a function of the log \
1145         whatever the program calls it: every event on it was validated and recorded, and replay \
1146         would reproduce this state whether or not anybody asked. Declining to write it down does \
1147         not make it ephemeral, it makes it a state the log can reconstruct and the process \
1148         cannot. `docs/10` D30 is the rule this enforces — **ephemerality comes from the stream, \
1149         never from the absence of a `durable` wrapper** — and it corrects D1, which named the \
1150         right problem and the wrong mechanism. State that should not survive a restart folds \
1151         gestures (`gestures(step, init)`), which are never recorded; state folded from events is \
1152         `durable(fold(…))`.",
1153    ),
1154    e(
1155        "B0520",
1156        Stage::Signals,
1157        "the chokepoint reads `awareness`, which is not in the log",
1158        "`B0515` for the roster that carries a payload. What each connection was contributing \
1159         when an event was recorded is written down nowhere, so a `validate` that decided from it \
1160         would decide one thing now and another on replay. Record the fact instead: propose a \
1161         command when the thing you are deciding from happens, and decide from the state that \
1162         fold produces.",
1163    ),
1164    e(
1165        "B0521",
1166        Stage::Signals,
1167        "reads `awareness`, so it cannot render on the client",
1168        "`B0516` for the roster that carries a payload. `@render(client)` sends the browser the \
1169         accumulator, and what every *other* connection is contributing is in neither the \
1170         accumulator nor the log — the runtime holds it, one row per socket.",
1171    ),
1172    e(
1173        "B0522",
1174        Stage::Signals,
1175        "reads a `gestures` fold, so it cannot render on the server",
1176        "`B0518` about the other fact only a client holds. A gesture is one client's movement of \
1177         its own interface — not proposed, not validated, not recorded — so it never reaches a \
1178         server, and a page rendered there would show the interface state's initial value at every \
1179         position of every log with every gesture-dependent branch unreachable. `docs/10` D30 \
1180         orders the homes for interface state and puts this one fourth: a page that reaches for a \
1181         client-local fold and cannot render in the browser usually wants markup the platform \
1182         already knows (`<dialog>`, `popover`, `<details name>`) or the route on the `Session`, \
1183         both of which survive a server render and cost nothing.",
1184    ),
1185    e(
1186        "B0523",
1187        Stage::Signals,
1188        "the chokepoint reads a `gestures` fold, which is not in the log",
1189        "`B0515` and `B0520` for D30's client-local interface state, and the clearest case of the \
1190         three. A gesture is never proposed, so no `validate` ever saw one, and never recorded, so \
1191         no replay can reach one — the log holds no trace that it happened. An event whose \
1192         existence depended on whether somebody had a panel open would be unreproducible by \
1193         construction. If interface state should decide an event then it is not interface state: \
1194         propose a command when it changes, and decide from the fold over the events that \
1195         produces, which is D30's fifth home.",
1196    ),
1197    e(
1198        "B0524",
1199        Stage::Signals,
1200        "a variant is both a command and a gesture",
1201        "A handler in the page carries the constructor it builds — `on_click=Open` is the value \
1202         `Open`, serialised — and the client routes on its variant name: a name in the gesture \
1203         union is folded where it was made, and a name in the command union is proposed to the \
1204         server. A name in both would make `on_click` mean whichever decoder ran first, which is \
1205         a page whose buttons do one of two very different things for a reason nobody can read. \
1206         The two unions are different types and the fix is to say so: rename one.",
1207    ),
1208    // --------------------------------------------------------- B06xx: modules and interfaces
1209    e(
1210        "B0600",
1211        Stage::Modules,
1212        "has a body, so it is not a signature",
1213        "A `.becki` publishes what a module offers, not how it does it. Regenerate it with \
1214         `beck iface`.",
1215    ),
1216    e(
1217        "B0601",
1218        Stage::Modules,
1219        "defined in more than one module",
1220        "Phase 2 links modules into one namespace and has no qualified reference to tell two \
1221         definitions apart, so a clash is an error rather than a shadowing rule.",
1222    ),
1223    e(
1224        "B0602",
1225        Stage::Modules,
1226        "a module imports itself, directly or through a cycle",
1227        "A module's interface is derived from its body, so a cycle would mean each module needed \
1228         the other's contract before either had one. The cycle is printed.",
1229    ),
1230    e(
1231        "B0603",
1232        Stage::Modules,
1233        "cannot find module",
1234        "The loader looked for `<name>.becki` and `<name>.beck` beside the root module, and for a \
1235         standard-library module of that name, and found neither.",
1236    ),
1237    e(
1238        "B0604",
1239        Stage::Modules,
1240        "has an interface but no implementation",
1241        "An interface is enough to compile against and never enough to run. `beck check` and \
1242         `beck iface` work against a `.becki` with no `.beck` beside it — that is what §3.6's \
1243         separate compilation is — so this is reported where a runnable program is produced, and \
1244         for the root module wherever it is read, because a project whose root is a contract is \
1245         not a program at all.",
1246    ),
1247    e(
1248        "B0605",
1249        Stage::Modules,
1250        "does not match its published interface",
1251        "The checked-in `.becki` and the module compile to different digests. Regenerate it with \
1252         `beck iface`, and review the diff — the difference is an API change.",
1253    ),
1254    // ------------------------------------------------------------ B07xx: tests written in Beck
1255    e(
1256        "B0700",
1257        Stage::Tests,
1258        "a test block performs effects",
1259        "A test block's own row must be empty: an expectation is a pure question about a state, a \
1260         log and a page. Effects belong to the *subject*, and §21.3 stubs those.",
1261    ),
1262    e(
1263        "B0701",
1264        Stage::Tests,
1265        "a property parameter needs a type",
1266        "The generator is type-directed, so it works from the parameter's declared type. A \
1267         `property` with no types has nothing to generate.",
1268    ),
1269    e(
1270        "B0702",
1271        Stage::Tests,
1272        "not a tier, or not an effect atom",
1273        "`expect place(name) == tier` takes a tier, and a `stub` names an effect atom.",
1274    ),
1275    e(
1276        "B0703",
1277        Stage::Tests,
1278        "not something a stub can stand in for",
1279        "Time, ids and persistence are not stubbed in Beck and there is nothing to write: the \
1280         clock is data on the envelope, ids are minted at the edge, and the durable fold is real \
1281         and in memory.",
1282    ),
1283    e(
1284        "B0704",
1285        Stage::Tests,
1286        "nothing in this program performs this atom",
1287        "The stub would never be reached. The complete list of what a program touches is its \
1288         effect rows, and this atom is not among them.",
1289    ),
1290    e(
1291        "B0705",
1292        Stage::Tests,
1293        "only `given`, `when`, `stub` and `expect` may appear in a test",
1294        "§21.2: a test names a log, an input and an expectation — there is no fixture to build and \
1295         no `setUp` to write.",
1296    ),
1297    e(
1298        "B0706",
1299        Stage::Tests,
1300        "a clause needs something this program does not have",
1301        "The state a test arranges is a fold over the program's own event stream, so a program \
1302         with no `merge_clients` → `decide` → `durable(fold(…))` has nothing for `given` and \
1303         `when` to mean.",
1304    ),
1305    e(
1306        "B0707",
1307        Stage::Tests,
1308        "an atom is performed by more than one definition, so a stub cannot answer from the call",
1309        "The performers are named. A stub is a value for an effect atom; where two definitions \
1310         perform the same atom with different result types, one value cannot serve both.",
1311    ),
1312    e(
1313        "B0708",
1314        Stage::Tests,
1315        "a stub raises what the definition it stands in for cannot",
1316        "A stub stands in for a definition, so it may answer the way that definition may answer — \
1317         failure included, because a `raises(E)` the signature declares is an answer rather than \
1318         an act. Callers were type-checked against the row the signature publishes, so a raise it \
1319         does not declare would unwind through code that provably cannot fail.",
1320    ),
1321];
1322
1323#[cfg(test)]
1324mod tests {
1325    use super::*;
1326
1327    #[test]
1328    fn the_index_is_sorted_and_has_no_duplicates() {
1329        let codes: Vec<&str> = INDEX.iter().map(|e| e.code).collect();
1330        let mut sorted = codes.clone();
1331        sorted.sort_unstable();
1332        sorted.dedup();
1333        assert_eq!(codes, sorted, "the index must be sorted and unique");
1334    }
1335
1336    #[test]
1337    fn every_entry_says_something() {
1338        for entry in INDEX {
1339            assert!(
1340                entry.code.len() == 5 && entry.code.starts_with('B'),
1341                "{entry:?}"
1342            );
1343            assert!(!entry.title.is_empty(), "{entry:?}");
1344            // Long enough that "see the source" cannot pass for an explanation.
1345            assert!(entry.explain.len() > 40, "{entry:?}");
1346        }
1347    }
1348
1349    #[test]
1350    fn a_code_can_be_looked_up_either_way() {
1351        assert_eq!(lookup("B0341").map(|e| e.stage), Some(Stage::Types));
1352        assert_eq!(lookup("b0341").map(|e| e.code), Some("B0341"));
1353        assert_eq!(lookup("B9999"), None);
1354    }
1355
1356    #[test]
1357    fn every_code_belongs_to_the_stage_its_number_names() {
1358        for entry in INDEX {
1359            let band = &entry.code[1..3];
1360            let expected: &[Stage] = match band {
1361                "01" => &[Stage::Syntax],
1362                "02" => &[Stage::Macros],
1363                "03" => &[Stage::Types],
1364                // B040x is placement, B041x is the security check that runs with it.
1365                "04" => &[Stage::Placement, Stage::Security],
1366                "05" => &[Stage::Signals],
1367                "06" => &[Stage::Modules],
1368                "07" => &[Stage::Tests],
1369                other => panic!("unknown band B{other}xx in {entry:?}"),
1370            };
1371            assert!(expected.contains(&entry.stage), "{entry:?}");
1372        }
1373    }
1374}