Skip to main content

bynk_syntax/
diagnostics.rs

1//! Central registry of diagnostic codes.
2//!
3//! This is the single source of truth for the `bynk.*` codes the compiler can
4//! emit. The reference page `docs/src/reference/diagnostics.md` is generated
5//! from [`render_markdown`], and the test `tests/diagnostics_registry.rs`
6//! asserts that this table matches exactly the set of codes used across the
7//! compiler source — so a new code cannot be introduced without documenting it
8//! here, and a removed code cannot linger in the docs.
9//!
10//! Each entry is a `(code, summary)` pair, optionally tagged with the grammar
11//! production(s) it constrains (`grammar_symbol`). The category shown in the
12//! generated reference is derived from the second dotted segment of the code;
13//! the grammar weave (`docs/grammar-semantics.json`, the
14//! `{{#grammar-semantics}}` directive, and the diagnostics page's Construct
15//! column) is generated from `grammar_symbol`.
16
17/// One documented diagnostic: its stable code and a one-line summary of the
18/// cause. Richer "cause and fix" material for the common diagnostics lives in
19/// the troubleshooting how-to guides.
20pub struct DiagnosticInfo {
21    pub code: &'static str,
22    pub summary: &'static str,
23    /// The grammar production(s) this diagnostic constrains, by `tree-sitter`
24    /// rule name (e.g. `http_handler`). This is the single source of the
25    /// "static semantics" weave: a grammar-reference entry embeds the
26    /// diagnostics for a rule via `{{#grammar-semantics <rule>}}`, generated
27    /// from here. Empty for diagnostics with no single governing construct
28    /// (e.g. `bynk.boundary.structural_mismatch`). Every non-empty name is
29    /// checked against the grammar by `tests/diagnostics_registry.rs`.
30    pub grammar_symbol: &'static [&'static str],
31}
32
33/// Every diagnostic code the compiler emits, sorted by code.
34pub const REGISTRY: &[DiagnosticInfo] = &[
35    d(
36        "bynk.actor.bearer_identity_not_string_constructible",
37        "A `Bearer` actor's identity is not a string-constructible type.",
38    ),
39    d(
40        "bynk.actor.bearer_missing_secret",
41        "A `Bearer` actor does not name its signing secret.",
42    ),
43    d(
44        "bynk.actor.binder_shadows_param",
45        "A `by` actor binder collides with a handler parameter of the same name.",
46    ),
47    d(
48        "bynk.actor.duplicate_sum_scheme",
49        "Two peers in a multi-actor sum share an authentication scheme.",
50    ),
51    d(
52        "bynk.actor.identity_not_sealed",
53        "An actor identity type is not a context-ownable (sealed) value type.",
54    ),
55    d(
56        "bynk.actor.missing_by_on_http",
57        "An HTTP handler lacks the required `by` actor clause.",
58    ),
59    d(
60        "bynk.actor.outside_context",
61        "An `actor` was declared outside a context (e.g. in a commons).",
62    ),
63    d(
64        "bynk.actor.refinement_base_unsupported",
65        "A refinement actor's base is not a `Bearer` actor (no claims to authorise against).",
66    ),
67    d(
68        "bynk.actor.refinement_in_sum",
69        "A refinement actor appears as a member of a multi-actor sum.",
70    ),
71    d(
72        "bynk.actor.refinement_predicate_unsupported",
73        "A refinement actor's `where` predicate is outside the closed claim-predicate set.",
74    ),
75    d(
76        "bynk.actor.scheme_not_admissible",
77        "An actor's scheme is not admissible on this handler's protocol.",
78    ),
79    d(
80        "bynk.actor.signature_identity_unsupported",
81        "A `Signature` actor declared an `identity`, which is not yet supported.",
82    ),
83    d(
84        "bynk.actor.signature_missing_header",
85        "A `Signature` actor does not name its signature header.",
86    ),
87    d(
88        "bynk.actor.signature_missing_secret",
89        "A `Signature` actor does not name its signing secret.",
90    ),
91    d(
92        "bynk.actor.signature_requires_body",
93        "A `Signature` handler does not take a `body` parameter.",
94    ),
95    d(
96        "bynk.actor.signature_tolerance_without_timestamp",
97        "A `Signature` actor set `tolerance` without a `timestamp` header.",
98    ),
99    d(
100        "bynk.actor.sum_requires_binder",
101        "A multi-actor sum `by` clause has no binder to match the resolved actor.",
102    ),
103    d(
104        "bynk.actor.unknown_actor",
105        "A handler's `by` clause names an actor that is not declared.",
106    ),
107    d(
108        "bynk.actor.unknown_scheme",
109        "An actor declares an authentication scheme that is not compiler-known.",
110    ),
111    d(
112        "bynk.actor.unreachable_sum_arm",
113        "A multi-actor sum has an arm unreachable after a catch-all (`None`) peer.",
114    ),
115    dg(
116        "bynk.adapter.consumes_context",
117        "An `adapter` consumed a context; adapter dependencies are adapter-to-adapter.",
118        &["consumes_decl"],
119    ),
120    dg(
121        "bynk.adapter.consumes_requires_selection",
122        "An `adapter` used a whole-unit or aliased `consumes`; adapters must select capabilities with `consumes U { Cap, … }`.",
123        &["consumes_decl"],
124    ),
125    dg(
126        "bynk.adapter.disallowed_item",
127        "An `adapter` declared a `service`, `agent`, or other item it may not contain.",
128        &["adapter_decl"],
129    ),
130    dg(
131        "bynk.adapter.duplicate_binding",
132        "An `adapter` declared more than one `binding` clause.",
133        &["binding_decl"],
134    ),
135    dg(
136        "bynk.adapter.no_binding",
137        "An `adapter` declares an external provider but no `binding` module to supply it.",
138        &["adapter_decl"],
139    ),
140    dg(
141        "bynk.adapter.provider_has_body",
142        "A provider inside an `adapter` has a Bynk body; adapter providers must be external.",
143        &["provider_decl"],
144    ),
145    dg(
146        "bynk.agent.construction_arity",
147        "An agent was constructed with the wrong number of key arguments.",
148        &["agent_decl"],
149    ),
150    dg(
151        "bynk.agent.handler_arity",
152        "An agent handler was called with the wrong number of arguments.",
153        &["agent_decl"],
154    ),
155    dg(
156        "bynk.agent.handler_not_found",
157        "Called a handler the agent does not declare.",
158        &["agent_decl"],
159    ),
160    dg(
161        "bynk.agent.key_mismatch",
162        "An agent key argument has the wrong type.",
163        &["agent_decl"],
164    ),
165    dg(
166        "bynk.agent.outside_context",
167        "An `agent` was declared outside a context.",
168        &["agent_decl"],
169    ),
170    dg(
171        "bynk.agent.return_not_effect",
172        "An agent handler's return type is not an `Effect`.",
173        &["agent_decl"],
174    ),
175    dg(
176        "bynk.agents.bad_state_initialiser",
177        "An agent state-field initialiser is not a static value of the field's type.",
178        &["state_decl"],
179    ),
180    dg(
181        "bynk.agents.non_zeroable_state_field",
182        "An agent state field has no initialiser and no implicit zero value.",
183        &["state_decl"],
184    ),
185    dg(
186        "bynk.assert.non_bool",
187        "`assert` was given a non-`Bool` expression.",
188        &["assert_expr"],
189    ),
190    dg(
191        "bynk.assert.outside_test",
192        "`assert` was used outside a test case body.",
193        &["assert_expr"],
194    ),
195    d(
196        "bynk.boundary.structural_mismatch",
197        "Data crossing a context boundary did not match the expected shape.",
198    ),
199    dg(
200        "bynk.capability.op_arity",
201        "A capability operation was called with the wrong number of arguments.",
202        &["capability_decl"],
203    ),
204    dg(
205        "bynk.capability.outside_context",
206        "A `capability` was declared outside a context.",
207        &["capability_decl"],
208    ),
209    dg(
210        "bynk.capability.unknown_operation",
211        "Referenced an operation the capability does not declare.",
212        &["capability_decl"],
213    ),
214    dg(
215        "bynk.commit.outside_agent",
216        "`commit` was used outside an agent handler.",
217        &["commit_stmt"],
218    ),
219    dg(
220        "bynk.commit.two_reachable_commits",
221        "Two `commit` statements are reachable on the same execution path.",
222        &["commit_stmt"],
223    ),
224    dg(
225        "bynk.commit.wrong_state_type",
226        "A `commit` value does not match the agent's state type.",
227        &["commit_stmt"],
228    ),
229    dg(
230        "bynk.consumes.alias_conflict",
231        "Two `consumes` aliases collide.",
232        &["consumes_decl"],
233    ),
234    dg(
235        "bynk.consumes.capability_name_clash",
236        "Two flattened `consumes U { Cap }` capabilities collide, or one clashes with a local capability.",
237        &["consumes_decl"],
238    ),
239    dg(
240        "bynk.consumes.in_commons",
241        "`consumes` appears in a `commons` (it is only valid in a context).",
242        &["consumes_decl"],
243    ),
244    dg(
245        "bynk.consumes.name_conflict",
246        "A `consumes` name collides with another name in scope.",
247        &["consumes_decl"],
248    ),
249    dg(
250        "bynk.consumes.self_reference",
251        "A context `consumes` itself.",
252        &["consumes_decl"],
253    ),
254    dg(
255        "bynk.consumes.service_arity",
256        "A consumed service was called with the wrong number of arguments.",
257        &["consumes_decl"],
258    ),
259    dg(
260        "bynk.consumes.target_is_commons",
261        "`consumes` targets a `commons` instead of a context.",
262        &["consumes_decl"],
263    ),
264    dg(
265        "bynk.consumes.unknown_context",
266        "`consumes` names a context that does not exist.",
267        &["consumes_decl"],
268    ),
269    dg(
270        "bynk.consumes.unknown_service",
271        "Called a service the consumed context does not declare.",
272        &["consumes_decl"],
273    ),
274    d(
275        "bynk.context.consumes_cycle",
276        "Contexts form a `consumes` dependency cycle.",
277    ),
278    d(
279        "bynk.context.external_construction",
280        "A context-owned type was constructed from outside that context.",
281    ),
282    dg(
283        "bynk.context.external_provider",
284        "A bodiless (external) provider was declared outside an `adapter`.",
285        &["provider_decl"],
286    ),
287    d(
288        "bynk.context.opaque_inspection",
289        "An opaquely-exported type was inspected from outside its context.",
290    ),
291    dg(
292        "bynk.cron.bad_params",
293        "A cron handler declares more than one parameter, or a non-`Int` one.",
294        &["cron_handler"],
295    ),
296    dg(
297        "bynk.cron.duplicate_schedule",
298        "Two cron handlers declare the same schedule.",
299        &["cron_handler"],
300    ),
301    dg(
302        "bynk.cron.invalid_schedule",
303        "A cron expression is not five whitespace-separated fields.",
304        &["cron_handler"],
305    ),
306    dg(
307        "bynk.cron.return_not_effect_result",
308        "A cron handler does not return `Effect[Result[(), E]]`.",
309        &["cron_handler"],
310    ),
311    dg(
312        "bynk.effect.bind_in_pure_context",
313        "An `<-` bind was used in a pure (non-effectful) context.",
314        &["effect_let_stmt"],
315    ),
316    dg(
317        "bynk.effect.bind_on_non_effect",
318        "An `<-` bind was applied to a non-`Effect` value.",
319        &["effect_let_stmt"],
320    ),
321    d(
322        "bynk.effect.capability_in_pure_context",
323        "A capability was used in a pure context.",
324    ),
325    d(
326        "bynk.effect.cross_context_in_pure_context",
327        "A cross-context call was made in a pure context.",
328    ),
329    dg(
330        "bynk.effect.fn_value_in_pure_context",
331        "An effectful function value was called in a pure context; like a capability call, it is legal only where the enclosing body is effectful.",
332        &["call"],
333    ),
334    dg(
335        "bynk.exports.capability_not_provided",
336        "An exported capability has no provider in its context.",
337        &["exports_decl"],
338    ),
339    dg(
340        "bynk.exports.conflicting_visibility",
341        "A type is exported with conflicting visibilities.",
342        &["exports_decl"],
343    ),
344    dg(
345        "bynk.exports.duplicate_export",
346        "The same name is exported more than once.",
347        &["exports_decl"],
348    ),
349    dg(
350        "bynk.exports.duplicate_in_clause",
351        "A name appears twice in one `exports` clause.",
352        &["exports_decl"],
353    ),
354    dg(
355        "bynk.exports.undeclared_capability",
356        "`exports capability` names a capability that is not declared.",
357        &["exports_decl"],
358    ),
359    dg(
360        "bynk.exports.undeclared_type",
361        "`exports` names a type that is not declared.",
362        &["exports_decl"],
363    ),
364    dg(
365        "bynk.generics.no_bounds",
366        "A type parameter carries a bound (`[A: …]`); bounded generics are not in v0.20a.",
367        &["fn_decl"],
368    ),
369    dg(
370        "bynk.generics.no_generic_types",
371        "A `type` declaration carries a type-parameter list; generic type declarations are not in v0.20a (type parameters belong to functions).",
372        &["type_decl"],
373    ),
374    dg(
375        "bynk.generics.type_arg_mismatch",
376        "Inferred or explicit type arguments conflict, have the wrong arity, target a non-generic function, or a type parameter shadows a declared type.",
377        &["call"],
378    ),
379    dg(
380        "bynk.generics.uninferable_type_arg",
381        "A generic function's type parameter could not be inferred from the arguments and was not given explicitly (`name[T](…)`); a bare generic function also cannot be passed as a value in v0.20a.",
382        &["call"],
383    ),
384    dg(
385        "bynk.given.cross_context_unknown_capability",
386        "`given B.Cap` names a capability the consumed context does not export.",
387        &["given_clause"],
388    ),
389    dg(
390        "bynk.given.undeclared_capability",
391        "A handler uses a capability it did not declare with `given`.",
392        &["given_clause"],
393    ),
394    dg(
395        "bynk.given.unknown_capability",
396        "`given` names a capability that does not exist.",
397        &["given_clause"],
398    ),
399    dg(
400        "bynk.given.unused_capability",
401        "A `given` capability is never used (warning).",
402        &["given_clause"],
403    ),
404    dg(
405        "bynk.http.body_on_get_or_delete",
406        "A GET or DELETE handler declares a `body` parameter.",
407        &["http_handler"],
408    ),
409    dg(
410        "bynk.http.duplicate_route",
411        "Two handlers share the same method and route.",
412        &["http_handler"],
413    ),
414    dg(
415        "bynk.http.extra_param",
416        "A handler parameter is neither a path parameter nor `body`.",
417        &["http_handler"],
418    ),
419    dg(
420        "bynk.http.invalid_path",
421        "An HTTP route path is malformed.",
422        &["http_handler"],
423    ),
424    dg(
425        "bynk.http.path_param_not_stringy",
426        "A path parameter's type is not constructible from a string.",
427        &["http_handler"],
428    ),
429    dg(
430        "bynk.http.reserved_prefix",
431        "A route uses the reserved `/_bynk/` prefix.",
432        &["http_handler"],
433    ),
434    dg(
435        "bynk.http.return_not_effect_http_result",
436        "An HTTP handler does not return `Effect[HttpResult[T]]`.",
437        &["http_handler"],
438    ),
439    dg(
440        "bynk.http.unbound_path_param",
441        "A `:name` route segment has no matching handler parameter.",
442        &["http_handler"],
443    ),
444    dg(
445        "bynk.integration.duplicate_participant",
446        "A context is listed more than once in a `wires` clause.",
447        &["wires_decl"],
448    ),
449    dg(
450        "bynk.integration.duplicate_suite",
451        "Two integration tests share the same suite name.",
452        &["integration_decl"],
453    ),
454    dg(
455        "bynk.integration.mock_in_integration",
456        "`mocks` is not allowed in an integration test.",
457        &["mocks_decl"],
458    ),
459    dg(
460        "bynk.integration.too_few_participants",
461        "An integration test wires fewer than two contexts.",
462        &["wires_decl"],
463    ),
464    dg(
465        "bynk.integration.unknown_participant",
466        "A `wires` clause names something that is not a declared context.",
467        &["wires_decl"],
468    ),
469    dg(
470        "bynk.integration.unwired_dependency",
471        "A participant consumes a context that is not wired into the integration test.",
472        &["integration_decl"],
473    ),
474    dg(
475        "bynk.lambda.unannotated_param",
476        "A lambda parameter has no type annotation in a position where no function type is expected to infer it from.",
477        &["lambda_expr"],
478    ),
479    dg(
480        "bynk.lex.bad_escape",
481        "An invalid escape sequence in a string literal.",
482        &["string_literal"],
483    ),
484    dg(
485        "bynk.lex.float_literal_overflow",
486        "A float literal does not fit a finite 64-bit float.",
487        &["float_literal"],
488    ),
489    dg(
490        "bynk.lex.integer_overflow",
491        "An integer literal is out of range.",
492        &["number_literal"],
493    ),
494    d(
495        "bynk.lex.unclosed_doc_block",
496        "A documentation block is not closed.",
497    ),
498    d(
499        "bynk.lex.unexpected_character",
500        "An unexpected character in the source.",
501    ),
502    dg(
503        "bynk.lex.unterminated_interpolation",
504        "An interpolation hole `\\(…)` is not closed on its line.",
505        &["string_literal"],
506    ),
507    dg(
508        "bynk.lex.unterminated_string",
509        "A string literal is not terminated.",
510        &["string_literal"],
511    ),
512    dg(
513        "bynk.mock.arity",
514        "`Mock[T]` was given the wrong number of pin arguments.",
515        &["mock_expr"],
516    ),
517    dg(
518        "bynk.mock.duplicate_target",
519        "A `mocks` target is declared more than once.",
520        &["mocks_decl"],
521    ),
522    dg(
523        "bynk.mock.in_commons_test",
524        "`mocks` used in a commons test, where there is no dependency to inject.",
525        &["mocks_decl"],
526    ),
527    dg(
528        "bynk.mock.literal_violates",
529        "A pinned `Mock[T]` value violates the type's refinement.",
530        &["mock_expr"],
531    ),
532    dg(
533        "bynk.mock.needs_pin",
534        "A bare `Mock[T]` cannot generate a value (e.g. a `Matches` string); pin one.",
535        &["mock_expr"],
536    ),
537    dg(
538        "bynk.mock.outside_test",
539        "`Mock[T]` was used outside a test case body.",
540        &["mock_expr"],
541    ),
542    dg(
543        "bynk.mock.pin_not_literal",
544        "A `Mock[T]` pin argument is not a compile-time literal.",
545        &["mock_expr"],
546    ),
547    dg(
548        "bynk.mock.pin_unsupported",
549        "A pin was given for a type kind that does not support pinning.",
550        &["mock_expr"],
551    ),
552    dg(
553        "bynk.mock.signature_mismatch",
554        "A `mocks` implementation's signature does not match the capability.",
555        &["mocks_decl"],
556    ),
557    dg(
558        "bynk.mock.unknown_target",
559        "`mocks` names a capability that is not in scope.",
560        &["mocks_decl"],
561    ),
562    dg(
563        "bynk.mock.unknown_type",
564        "`Mock[T]` names a type that does not resolve.",
565        &["mock_expr"],
566    ),
567    dg(
568        "bynk.mock.unsupported_kind",
569        "`Mock[T]` cannot fabricate a value for this kind of type.",
570        &["mock_expr"],
571    ),
572    d(
573        "bynk.namespace.reserved",
574        "A user unit is named `bynk` or `bynk.*`; the `bynk` root is reserved for the toolchain.",
575    ),
576    dg(
577        "bynk.parse.consumes_after_decls",
578        "`consumes` appears after other declarations.",
579        &["consumes_decl"],
580    ),
581    dg(
582        "bynk.parse.empty_agent",
583        "An `agent` body is empty.",
584        &["agent_decl"],
585    ),
586    dg(
587        "bynk.parse.empty_capability",
588        "A `capability` body is empty.",
589        &["capability_decl"],
590    ),
591    d(
592        "bynk.parse.empty_interpolation",
593        "An interpolation hole `\\(…)` contains no expression.",
594    ),
595    dg(
596        "bynk.parse.empty_match",
597        "A `match` has no arms.",
598        &["match_expr"],
599    ),
600    dg(
601        "bynk.parse.empty_mock_body",
602        "A `mocks` body is empty.",
603        &["mocks_decl"],
604    ),
605    dg(
606        "bynk.parse.empty_service",
607        "A `service` body is empty.",
608        &["service_decl"],
609    ),
610    dg(
611        "bynk.parse.expected_agent_key",
612        "Expected a `key` declaration in an agent.",
613        &["agent_decl"],
614    ),
615    dg(
616        "bynk.parse.expected_base_type",
617        "Expected a base type.",
618        &["base_type"],
619    ),
620    dg(
621        "bynk.parse.expected_capability_op",
622        "Expected a capability operation.",
623        &["capability_op"],
624    ),
625    d("bynk.parse.expected_expression", "Expected an expression."),
626    dg(
627        "bynk.parse.expected_handler",
628        "Expected a handler.",
629        &["handler"],
630    ),
631    d("bynk.parse.expected_item", "Expected a declaration."),
632    dg(
633        "bynk.parse.expected_predicate",
634        "Expected a refinement predicate.",
635        &["refinement"],
636    ),
637    dg(
638        "bynk.parse.expected_provider_op",
639        "Expected a provider operation.",
640        &["provider_op"],
641    ),
642    d("bynk.parse.expected_token", "Expected a specific token."),
643    d("bynk.parse.expected_type", "Expected a type."),
644    d(
645        "bynk.parse.expected_unit_header",
646        "Expected a `commons` or `context` header.",
647    ),
648    dg(
649        "bynk.parse.expected_visibility",
650        "Expected a visibility keyword.",
651        &["exports_decl"],
652    ),
653    dg(
654        "bynk.parse.exports_after_decls",
655        "`exports` appears after other declarations.",
656        &["exports_decl"],
657    ),
658    d(
659        "bynk.parse.extra_tokens",
660        "Unexpected tokens after an otherwise complete construct.",
661    ),
662    dg(
663        "bynk.parse.generic_arg_count",
664        "Wrong number of generic type arguments.",
665        &["generic_type_ref"],
666    ),
667    dg(
668        "bynk.parse.handler_in_agent",
669        "A protocol handler (`on GET`/`schedule`/`message`) was declared in an agent.",
670        &["handler"],
671    ),
672    dg(
673        "bynk.parse.malformed_float_literal",
674        "A float literal is missing a digit on one side of the `.` (`1.`, `.5`).",
675        &["float_literal"],
676    ),
677    dg(
678        "bynk.parse.non_associative",
679        "A non-associative operator was chained (e.g. `a == b == c`).",
680        &["binary_expr"],
681    ),
682    d(
683        "bynk.parse.orphan_doc_block",
684        "A documentation block is not attached to a declaration (warning).",
685    ),
686    dg(
687        "bynk.parse.reserved_keyword",
688        "A reserved keyword was used as an identifier.",
689        &["identifier"],
690    ),
691    dg(
692        "bynk.parse.self_outside_method",
693        "`self` used outside a method or handler.",
694        &["self_expr"],
695    ),
696    d(
697        "bynk.parse.unexpected_adapter",
698        "An `adapter` appeared where it is not allowed.",
699    ),
700    dg(
701        "bynk.parse.unexpected_context",
702        "A `context` appeared where it is not allowed.",
703        &["context_decl"],
704    ),
705    d("bynk.parse.unexpected_eof", "Unexpected end of input."),
706    dg(
707        "bynk.parse.unexpected_test",
708        "A `test` appeared where it is not allowed.",
709        &["test_decl"],
710    ),
711    d(
712        "bynk.parse.unknown_effect_method",
713        "An unknown method on `Effect`.",
714    ),
715    dg(
716        "bynk.parse.unknown_handler_kind",
717        "An unknown handler form (expected `call`, an HTTP method, `schedule`, or `message`).",
718        &["handler"],
719    ),
720    dg(
721        "bynk.parse.unknown_predicate",
722        "An unknown refinement predicate.",
723        &["predicate_name"],
724    ),
725    dg(
726        "bynk.parse.uses_after_decls",
727        "`uses` appears after other declarations.",
728        &["uses_decl"],
729    ),
730    d(
731        "bynk.project.file_and_directory",
732        "A unit exists as both a file and a directory.",
733    ),
734    d(
735        "bynk.project.inconsistent_commons_name",
736        "A source file's path does not match its declared name.",
737    ),
738    d(
739        "bynk.project.inconsistent_test_path",
740        "A test file's path does not match its target's name.",
741    ),
742    d(
743        "bynk.project.kind_conflict",
744        "A name is declared as both a commons and a context.",
745    ),
746    d(
747        "bynk.project.no_root",
748        "No project root could be determined.",
749    ),
750    d(
751        "bynk.project.no_sources",
752        "The project contains no source files.",
753    ),
754    d(
755        "bynk.project.read_failed",
756        "A source file could not be read.",
757    ),
758    dg(
759        "bynk.provider.dependency_cycle",
760        "Providers form a capability dependency cycle through `given`.",
761        &["provider_decl"],
762    ),
763    dg(
764        "bynk.provider.extra_operation",
765        "A `provides` block implements an operation not in the capability.",
766        &["provider_decl"],
767    ),
768    dg(
769        "bynk.provider.missing_operation",
770        "A `provides` block is missing a capability operation.",
771        &["provider_decl"],
772    ),
773    dg(
774        "bynk.provider.outside_context",
775        "`provides` was declared outside a context.",
776        &["provider_decl"],
777    ),
778    dg(
779        "bynk.provider.signature_mismatch",
780        "A `provides` operation's signature does not match the capability.",
781        &["provider_decl"],
782    ),
783    dg(
784        "bynk.provider.unknown_capability",
785        "`provides` names a capability that does not exist.",
786        &["provider_decl"],
787    ),
788    dg(
789        "bynk.queue.bad_params",
790        "An `on message` handler does not take exactly one `message` parameter.",
791        &["queue_handler"],
792    ),
793    dg(
794        "bynk.queue.duplicate_consumer",
795        "Two `on message` handlers consume the same queue.",
796        &["queue_handler"],
797    ),
798    dg(
799        "bynk.queue.invalid_name",
800        "A `from queue(\"…\")` binding has an empty queue name.",
801        &["queue_handler"],
802    ),
803    dg(
804        "bynk.queue.return_not_queue_result",
805        "An `on message` handler does not return `Effect[QueueResult]`.",
806        &["handler"],
807    ),
808    dg(
809        "bynk.record_spread.field_type_mismatch",
810        "A record-spread override has the wrong type for the field.",
811        &["record_spread"],
812    ),
813    dg(
814        "bynk.record_spread.non_record_base",
815        "The base of a record spread is not a record.",
816        &["record_spread"],
817    ),
818    dg(
819        "bynk.record_spread.type_mismatch",
820        "A record spread's base is a different record type.",
821        &["record_spread"],
822    ),
823    dg(
824        "bynk.record_spread.unknown_field",
825        "A record spread overrides a field the record does not have.",
826        &["record_spread"],
827    ),
828    dg(
829        "bynk.refine.literal_violates",
830        "A literal does not satisfy the refined type's predicate.",
831        &["refined_type"],
832    ),
833    dg(
834        "bynk.requires.unpinned_dependency",
835        "An adapter `binding … requires { … }` entry has an unpinned version range.",
836        &["binding_decl"],
837    ),
838    d(
839        "bynk.resolve.ambiguous_variant",
840        "A variant name is ambiguous across several sum types.",
841    ),
842    dg(
843        "bynk.resolve.arity_mismatch",
844        "A function was called with the wrong number of arguments.",
845        &["call"],
846    ),
847    d("bynk.resolve.duplicate_actor", "Two actors share a name."),
848    dg(
849        "bynk.resolve.duplicate_agent",
850        "Two agents share a name.",
851        &["agent_decl"],
852    ),
853    dg(
854        "bynk.resolve.duplicate_capability",
855        "Two capabilities share a name.",
856        &["capability_decl"],
857    ),
858    dg(
859        "bynk.resolve.duplicate_field",
860        "A record declares a field twice.",
861        &["record_type"],
862    ),
863    dg(
864        "bynk.resolve.duplicate_field_init",
865        "A record construction initialises a field twice.",
866        &["record_construction"],
867    ),
868    dg(
869        "bynk.resolve.duplicate_fn",
870        "Two functions share a name.",
871        &["fn_decl"],
872    ),
873    dg(
874        "bynk.resolve.duplicate_method",
875        "Two methods share a name.",
876        &["fn_decl"],
877    ),
878    dg(
879        "bynk.resolve.duplicate_param",
880        "A parameter name is repeated.",
881        &["param"],
882    ),
883    dg(
884        "bynk.resolve.duplicate_provider",
885        "A capability is provided more than once.",
886        &["provider_decl"],
887    ),
888    dg(
889        "bynk.resolve.duplicate_service",
890        "Two services share a name.",
891        &["service_decl"],
892    ),
893    dg(
894        "bynk.resolve.duplicate_type",
895        "Two types share a name.",
896        &["type_decl"],
897    ),
898    dg(
899        "bynk.resolve.duplicate_variant",
900        "A sum type declares a variant twice.",
901        &["sum_type"],
902    ),
903    d(
904        "bynk.resolve.fn_without_call",
905        "A function was referenced without being called.",
906    ),
907    dg(
908        "bynk.resolve.let_shadows_fn",
909        "A `let` binding shadows a function.",
910        &["let_stmt"],
911    ),
912    dg(
913        "bynk.resolve.let_shadows_type",
914        "A `let` binding shadows a type.",
915        &["let_stmt"],
916    ),
917    d(
918        "bynk.resolve.method_unknown_type",
919        "A method is defined on an unknown type.",
920    ),
921    dg(
922        "bynk.resolve.missing_field",
923        "A record construction omits a required field.",
924        &["record_construction"],
925    ),
926    d(
927        "bynk.resolve.name_conflict",
928        "Two declarations share a name.",
929    ),
930    dg(
931        "bynk.resolve.not_a_record_type",
932        "Record syntax was used on a non-record type.",
933        &["record_construction"],
934    ),
935    dg(
936        "bynk.resolve.opaque_record_construction",
937        "An opaque type was constructed with record syntax.",
938        &["record_construction"],
939    ),
940    dg(
941        "bynk.resolve.param_as_function",
942        "A value (such as a parameter) was called as a function.",
943        &["call"],
944    ),
945    dg(
946        "bynk.resolve.recursive_record_field",
947        "A record directly contains a field of its own type.",
948        &["record_type"],
949    ),
950    dg(
951        "bynk.resolve.self_outside_method",
952        "`self` referenced outside a method or handler.",
953        &["self_expr"],
954    ),
955    dg(
956        "bynk.resolve.type_as_function",
957        "A type name was called as if it were a function.",
958        &["call"],
959    ),
960    d(
961        "bynk.resolve.type_in_expr",
962        "A type name was used where a value is expected.",
963    ),
964    dg(
965        "bynk.resolve.unconsumed_context",
966        "A context's service was called without a `consumes` declaration.",
967        &["consumes_decl"],
968    ),
969    dg(
970        "bynk.resolve.unknown_field",
971        "Accessed a field the record does not have.",
972        &["field_access"],
973    ),
974    dg(
975        "bynk.resolve.unknown_function",
976        "Called a function that does not exist.",
977        &["call"],
978    ),
979    d(
980        "bynk.resolve.unknown_name",
981        "Referenced a name that is not in scope.",
982    ),
983    dg(
984        "bynk.resolve.unknown_static_member",
985        "Referenced an unknown static member (e.g. `T.x`).",
986        &["field_access"],
987    ),
988    d(
989        "bynk.resolve.unknown_type",
990        "Referenced a type that does not exist.",
991    ),
992    dg(
993        "bynk.send.in_pure_context",
994        "A `~>` send was used in a pure (non-effectful) context.",
995        &["effect_send_stmt"],
996    ),
997    dg(
998        "bynk.send.non_effect",
999        "A `~>` send was applied to a non-`Effect` value.",
1000        &["effect_send_stmt"],
1001    ),
1002    dg(
1003        "bynk.send.requires_unit",
1004        "A `~>` send targets an operation whose reply is not `Effect[()]`.",
1005        &["effect_send_stmt"],
1006    ),
1007    dg(
1008        "bynk.service.missing_from",
1009        "A `from`-less service has a handler other than `on call`.",
1010        &["service_decl"],
1011    ),
1012    dg(
1013        "bynk.service.mixed_protocols",
1014        "A service mixes handler forms that do not match its `from <protocol>`.",
1015        &["service_decl"],
1016    ),
1017    dg(
1018        "bynk.service.outside_context",
1019        "A `service` was declared outside a context.",
1020        &["service_decl"],
1021    ),
1022    dg(
1023        "bynk.service.return_not_effect",
1024        "A service handler's return type is not an `Effect`.",
1025        &["service_decl"],
1026    ),
1027    dg(
1028        "bynk.service.unknown_protocol",
1029        "A `from <protocol>` names an unknown protocol (e.g. a transport like Kafka).",
1030        &["service_decl"],
1031    ),
1032    dg(
1033        "bynk.target.vendor_conflict",
1034        "One deployment unit's in-process closure uses platform-native capabilities from two mutually-exclusive platforms.",
1035        &["consumes_decl"],
1036    ),
1037    dg(
1038        "bynk.target.vendor_required",
1039        "A deployment unit uses a platform-native capability but the build selects another `--platform`.",
1040        &["consumes_decl"],
1041    ),
1042    dg(
1043        "bynk.test.duplicate_case_name",
1044        "Two test cases share a description.",
1045        &["test_case"],
1046    ),
1047    dg(
1048        "bynk.test.unknown_target",
1049        "A `test` block targets a unit that does not exist.",
1050        &["test_decl"],
1051    ),
1052    d(
1053        "bynk.types.ambiguous_constructor",
1054        "`Ok`/`Err` is ambiguous between `Result` and `HttpResult`; qualify it.",
1055    ),
1056    dg(
1057        "bynk.types.argument_mismatch",
1058        "A function argument has the wrong type.",
1059        &["call"],
1060    ),
1061    dg(
1062        "bynk.types.call_arity",
1063        "A function value was applied with the wrong number of arguments.",
1064        &["call"],
1065    ),
1066    dg(
1067        "bynk.types.cannot_infer_option_type_param",
1068        "The value type of `None` could not be inferred.",
1069        &["none_expr"],
1070    ),
1071    d(
1072        "bynk.types.cannot_infer_result_type_params",
1073        "The type parameters of a `Result` could not be inferred.",
1074    ),
1075    d(
1076        "bynk.types.constructor_arity",
1077        "A variant constructor got the wrong number of arguments.",
1078    ),
1079    d(
1080        "bynk.types.constructor_base_mismatch",
1081        "A `.of` constructor was given an argument of the wrong base type.",
1082    ),
1083    dg(
1084        "bynk.types.duplicate_variant_arm",
1085        "A `match` has two arms for the same variant.",
1086        &["match_arm"],
1087    ),
1088    dg(
1089        "bynk.types.empty_refinement",
1090        "A refinement admits no values (contradictory predicates).",
1091        &["refinement"],
1092    ),
1093    dg(
1094        "bynk.types.err_value_mismatch",
1095        "An `Err` payload has the wrong type.",
1096        &["err_expr"],
1097    ),
1098    dg(
1099        "bynk.types.field_access_on_non_record",
1100        "Field access on a value that is not a record.",
1101        &["field_access"],
1102    ),
1103    dg(
1104        "bynk.types.field_refinement_not_base",
1105        "An inline field refinement requires a base or refined type.",
1106        &["record_field"],
1107    ),
1108    dg(
1109        "bynk.types.field_value_mismatch",
1110        "A record field was given a value of the wrong type.",
1111        &["record_construction"],
1112    ),
1113    dg(
1114        "bynk.types.function_at_boundary",
1115        "A function type appeared in a serialisable or boundary position (a record field, sum payload, service/agent handler signature, capability operation signature, agent state field, or agent key); functions cannot serialise or cross a boundary.",
1116        &["function_type_ref"],
1117    ),
1118    dg(
1119        "bynk.types.if_branch_mismatch",
1120        "The branches of an `if` have different types.",
1121        &["if_expr"],
1122    ),
1123    dg(
1124        "bynk.types.if_non_bool_cond",
1125        "An `if` condition is not a `Bool`.",
1126        &["if_expr"],
1127    ),
1128    d(
1129        "bynk.types.interpolation_non_scalar",
1130        "An interpolation hole holds a value with no string form.",
1131    ),
1132    dg(
1133        "bynk.types.invalid_regex",
1134        "A `Matches` predicate contains an invalid regular expression.",
1135        &["refinement"],
1136    ),
1137    dg(
1138        "bynk.types.inverted_range",
1139        "An `InRange` predicate has its bounds inverted.",
1140        &["refinement"],
1141    ),
1142    dg(
1143        "bynk.types.is_base_mismatch",
1144        "An `is` refinement check is applied to a value of the wrong base type.",
1145        &["is_expr"],
1146    ),
1147    dg(
1148        "bynk.types.is_non_sum",
1149        "`is` was applied to a value that is not a sum type.",
1150        &["is_expr"],
1151    ),
1152    dg(
1153        "bynk.types.is_unknown_variant",
1154        "`is` names a variant the type does not have.",
1155        &["is_expr"],
1156    ),
1157    dg(
1158        "bynk.types.json_uncodable",
1159        "A `Json.encode`/`Json.decode` target type cannot pass through the typed JSON codec (functions, effects, error builtins).",
1160        &["method_call"],
1161    ),
1162    dg(
1163        "bynk.types.lambda_mismatch",
1164        "A lambda's parameter count, parameter annotations, or body type do not match the expected function type.",
1165        &["lambda_expr"],
1166    ),
1167    dg(
1168        "bynk.types.let_annotation_mismatch",
1169        "A `let` value does not match its type annotation.",
1170        &["let_stmt"],
1171    ),
1172    dg(
1173        "bynk.types.list_element_mismatch",
1174        "A list-literal element has a different type from the list's element type.",
1175        &["list_literal"],
1176    ),
1177    dg(
1178        "bynk.types.match_arm_mismatch",
1179        "A `match` arm has a different type from the others.",
1180        &["match_arm"],
1181    ),
1182    dg(
1183        "bynk.types.match_non_sum_discriminant",
1184        "`match` was applied to a value that is not a sum type.",
1185        &["match_expr"],
1186    ),
1187    dg(
1188        "bynk.types.method_arity",
1189        "A method was called with the wrong number of arguments.",
1190        &["method_call"],
1191    ),
1192    dg(
1193        "bynk.types.method_not_found",
1194        "Called a method the type does not have.",
1195        &["method_call"],
1196    ),
1197    dg(
1198        "bynk.types.method_on_non_named_type",
1199        "A method was called on a built-in type that has no methods.",
1200        &["method_call"],
1201    ),
1202    dg(
1203        "bynk.types.mixed_pattern_bindings",
1204        "A pattern mixes named and positional bindings.",
1205        &["variant_pattern"],
1206    ),
1207    dg(
1208        "bynk.types.negative_length",
1209        "A length predicate was given a negative value.",
1210        &["refinement"],
1211    ),
1212    dg(
1213        "bynk.types.no_numeric_coercion",
1214        "`Int` and `Float` were mixed without an explicit conversion — in an operation or in refinement bounds.",
1215        &["binary_expr", "refinement"],
1216    ),
1217    dg(
1218        "bynk.types.non_exhaustive_match",
1219        "A `match` does not cover every variant.",
1220        &["match_expr"],
1221    ),
1222    dg(
1223        "bynk.types.ok_value_mismatch",
1224        "An `Ok` payload has the wrong type.",
1225        &["ok_expr"],
1226    ),
1227    dg(
1228        "bynk.types.opaque_raw_outside",
1229        "`.raw` on an opaque type was used outside its defining commons.",
1230        &["field_access"],
1231    ),
1232    dg(
1233        "bynk.types.opaque_record_construction",
1234        "An opaque type was constructed with record syntax.",
1235        &["record_construction"],
1236    ),
1237    dg(
1238        "bynk.types.opaque_unsafe_outside",
1239        "`.unsafe` on an opaque type was used outside its defining context.",
1240        &["field_access"],
1241    ),
1242    dg(
1243        "bynk.types.pattern_arity",
1244        "A pattern binds the wrong number of payload fields.",
1245        &["variant_pattern"],
1246    ),
1247    dg(
1248        "bynk.types.pattern_type_mismatch",
1249        "A pattern's type does not match the matched value.",
1250        &["variant_pattern"],
1251    ),
1252    dg(
1253        "bynk.types.predicate_base_mismatch",
1254        "A predicate does not apply to the type's base (e.g. a string predicate on an `Int`).",
1255        &["refinement"],
1256    ),
1257    dg(
1258        "bynk.types.question_error_mismatch",
1259        "`?` propagates an error type incompatible with the function's.",
1260        &["question_expr"],
1261    ),
1262    dg(
1263        "bynk.types.question_on_non_result",
1264        "`?` was applied to a non-`Result` value.",
1265        &["question_expr"],
1266    ),
1267    dg(
1268        "bynk.types.question_outside_result",
1269        "`?` used in a function that does not return a `Result`.",
1270        &["question_expr"],
1271    ),
1272    d(
1273        "bynk.types.return_mismatch",
1274        "A returned value does not match the declared return type.",
1275    ),
1276    dg(
1277        "bynk.types.some_value_mismatch",
1278        "A `Some` payload has the wrong type.",
1279        &["some_expr"],
1280    ),
1281    d(
1282        "bynk.types.type_mismatch",
1283        "Two types that were required to match did not.",
1284    ),
1285    dg(
1286        "bynk.types.uninferable_element_type",
1287        "An empty `[]` (or `List.empty()` / `Map.empty()`) has no expected type to infer its element type from.",
1288        &["list_literal"],
1289    ),
1290    dg(
1291        "bynk.types.unkeyable_map_key",
1292        "A `Map` key type is not value-keyable (`String`, `Int`, or a refined/opaque type over them).",
1293        &["generic_type_ref"],
1294    ),
1295    dg(
1296        "bynk.types.unknown_field",
1297        "Referenced a field the record type does not declare.",
1298        &["field_access"],
1299    ),
1300    dg(
1301        "bynk.types.unknown_pattern_field",
1302        "A pattern names a field the variant does not have.",
1303        &["variant_pattern"],
1304    ),
1305    dg(
1306        "bynk.types.unknown_static_member",
1307        "Referenced an unknown static member on a type.",
1308        &["field_access"],
1309    ),
1310    dg(
1311        "bynk.types.unknown_variant_in_pattern",
1312        "A pattern names a variant the sum type does not have.",
1313        &["variant_pattern"],
1314    ),
1315    dg(
1316        "bynk.types.unreachable_arm",
1317        "A `match` arm is unreachable.",
1318        &["match_arm"],
1319    ),
1320    d(
1321        "bynk.types.variant_arity",
1322        "A variant constructor got the wrong number of payload values.",
1323    ),
1324    d(
1325        "bynk.types.variant_missing_payload",
1326        "A variant requiring a payload was used without one.",
1327    ),
1328    d(
1329        "bynk.types.variant_payload_mismatch",
1330        "A variant payload has the wrong type.",
1331    ),
1332    dg(
1333        "bynk.uses.name_conflict",
1334        "A `uses` name collides with another name.",
1335        &["uses_decl"],
1336    ),
1337    dg(
1338        "bynk.uses.self_reference",
1339        "A commons `uses` itself.",
1340        &["uses_decl"],
1341    ),
1342    dg(
1343        "bynk.uses.target_is_context",
1344        "`uses` targets a context instead of a commons.",
1345        &["uses_decl"],
1346    ),
1347    dg(
1348        "bynk.uses.unknown_commons",
1349        "`uses` names a commons that does not exist.",
1350        &["uses_decl"],
1351    ),
1352];
1353
1354/// A diagnostic with no single governing grammar construct.
1355const fn d(code: &'static str, summary: &'static str) -> DiagnosticInfo {
1356    DiagnosticInfo {
1357        code,
1358        summary,
1359        grammar_symbol: &[],
1360    }
1361}
1362
1363/// A diagnostic that constrains one or more grammar productions.
1364const fn dg(
1365    code: &'static str,
1366    summary: &'static str,
1367    grammar_symbol: &'static [&'static str],
1368) -> DiagnosticInfo {
1369    DiagnosticInfo {
1370        code,
1371        summary,
1372        grammar_symbol,
1373    }
1374}
1375
1376/// The category segment of a code (the part between the first two dots), e.g.
1377/// `"types"` for `"bynk.types.type_mismatch"`.
1378pub fn category(code: &str) -> &str {
1379    code.split('.').nth(1).unwrap_or("")
1380}
1381
1382/// A human-readable heading for a category segment.
1383fn category_title(cat: &str) -> &'static str {
1384    match cat {
1385        "agent" | "agents" => "Agents",
1386        "assert" => "Assertions",
1387        "boundary" => "Boundaries",
1388        "capability" => "Capabilities",
1389        "commit" => "Commit",
1390        "consumes" => "Consumes",
1391        "context" => "Contexts",
1392        "cron" => "Cron",
1393        "effect" => "Effects",
1394        "exports" => "Exports",
1395        "given" => "Given capabilities",
1396        "http" => "HTTP",
1397        "lex" => "Lexer",
1398        "mock" => "Mock and mocks",
1399        "parse" => "Parser",
1400        "project" => "Project",
1401        "provider" => "Providers",
1402        "queue" => "Queue",
1403        "record_spread" => "Record spread",
1404        "refine" => "Refinement",
1405        "resolve" => "Resolution",
1406        "service" => "Services",
1407        "test" => "Tests",
1408        "types" => "Type checking",
1409        "uses" => "Uses",
1410        _ => "Other",
1411    }
1412}
1413
1414/// Render the diagnostic index as a Markdown reference page, grouped by
1415/// category. This is the generator behind `docs/src/reference/diagnostics.md`.
1416pub fn render_markdown() -> String {
1417    use std::collections::BTreeMap;
1418
1419    // Group codes by their category title, preserving sorted code order.
1420    let mut by_category: BTreeMap<&str, Vec<&DiagnosticInfo>> = BTreeMap::new();
1421    for info in REGISTRY {
1422        by_category
1423            .entry(category_title(category(info.code)))
1424            .or_default()
1425            .push(info);
1426    }
1427
1428    let mut out = String::new();
1429    out.push_str("# Diagnostic index\n\n");
1430    out.push_str(
1431        "<!-- GENERATED FILE — do not edit by hand.\n     \
1432         Source: bynkc/src/diagnostics.rs (`render_markdown`).\n     \
1433         Regenerate with: BYNK_BLESS=1 cargo test -p bynkc --test diagnostics_registry -->\n\n",
1434    );
1435    out.push_str(
1436        "Every diagnostic code the compiler can emit, with a one-line summary of \
1437         the cause, grouped by category. For step-by-step cause-and-fix guidance \
1438         on the most common ones, see the [troubleshooting guides](../troubleshooting/index.md).\n\n",
1439    );
1440    out.push_str(&format!(
1441        "There are **{}** codes in total.\n",
1442        REGISTRY.len()
1443    ));
1444
1445    for (title, infos) in &by_category {
1446        out.push_str(&format!("\n## {title}\n\n"));
1447        out.push_str("| Code | Summary | Construct |\n|---|---|---|\n");
1448        for info in infos {
1449            // The construct column deep-links each governing production to its
1450            // entry in the annotated grammar reference; generated from
1451            // `grammar_symbol` (each value is an embeddable rule, so the
1452            // `#rule-<raw>` anchor resolves — enforced in diagnostics_registry).
1453            let construct = info
1454                .grammar_symbol
1455                .iter()
1456                .map(|sym| format!("[`{sym}`](grammar.md#rule-{sym})"))
1457                .collect::<Vec<_>>()
1458                .join(", ");
1459            out.push_str(&format!(
1460                "| `{}` | {} | {} |\n",
1461                info.code, info.summary, construct
1462            ));
1463        }
1464    }
1465
1466    out
1467}
1468
1469/// Invert the registry into a `{ "<rule>": [ { code, summary }, … ], … }` map,
1470/// serialised as pretty JSON with sorted keys and sorted codes. Only rules with
1471/// at least one diagnostic appear. This is the generator behind
1472/// `docs/grammar-semantics.json`, which the `{{#grammar-semantics <rule>}}`
1473/// preprocessor directive consumes.
1474pub fn render_grammar_semantics_json() -> String {
1475    use std::collections::BTreeMap;
1476
1477    // REGISTRY is sorted by code, so each rule's vector comes out code-sorted;
1478    // the BTreeMap gives sorted rule names.
1479    let mut by_symbol: BTreeMap<&str, Vec<&DiagnosticInfo>> = BTreeMap::new();
1480    for info in REGISTRY {
1481        for sym in info.grammar_symbol {
1482            by_symbol.entry(sym).or_default().push(info);
1483        }
1484    }
1485
1486    let mut map = serde_json::Map::new();
1487    map.insert(
1488        "_generated".to_string(),
1489        serde_json::Value::String(
1490            "Generated from the grammar_symbol field of bynkc/src/diagnostics.rs. \
1491             Do not edit by hand. Regenerate with: BYNK_BLESS=1 cargo test -p \
1492             bynkc --test diagnostics_registry"
1493                .to_string(),
1494        ),
1495    );
1496    for (sym, infos) in by_symbol {
1497        let arr: Vec<serde_json::Value> = infos
1498            .iter()
1499            .map(|info| serde_json::json!({ "code": info.code, "summary": info.summary }))
1500            .collect();
1501        map.insert(sym.to_string(), serde_json::Value::Array(arr));
1502    }
1503
1504    let mut s =
1505        serde_json::to_string_pretty(&serde_json::Value::Object(map)).expect("serialise semantics");
1506    s.push('\n');
1507    s
1508}