1use crate::Severity;
14
15pub struct DiagnosticCodeInfo {
17 pub code: &'static str,
19 pub severity: Severity,
21 pub summary: &'static str,
23 pub description: &'static str,
25 pub spec_refs: &'static [&'static str],
27}
28
29#[must_use]
46pub fn diagnostic_catalog() -> Vec<DiagnosticCodeInfo> {
47 vec![
48 DiagnosticCodeInfo {
50 code: "E1001",
51 severity: Severity::Error,
52 summary: "Unexpected character in source.",
53 description: "The lexer encountered a character it does not recognize as part of any valid token. (Lexer-only: the name-resolution pass reports an undefined name as `E1009`, an unfound module as `E1010`, and an unfound symbol as `E1011`.)",
54 spec_refs: &["§1"],
55 },
56 DiagnosticCodeInfo {
57 code: "E1002",
58 severity: Severity::Error,
59 summary: "Unterminated string literal.",
60 description: "A string literal was opened but never closed before end of input.",
61 spec_refs: &["§1.3"],
62 },
63 DiagnosticCodeInfo {
64 code: "E1003",
65 severity: Severity::Error,
66 summary: "Invalid escape sequence in string.",
67 description: "An escape sequence in a string literal is not one of the recognized forms.",
68 spec_refs: &["§1.3"],
69 },
70 DiagnosticCodeInfo {
71 code: "E1004",
72 severity: Severity::Error,
73 summary: "Invalid character literal.",
74 description: "A character literal is empty, contains more than one character, or has an invalid escape.",
75 spec_refs: &["§1.3"],
76 },
77 DiagnosticCodeInfo {
78 code: "E1005",
79 severity: Severity::Error,
80 summary: "Invalid digit for numeric literal.",
81 description: "A digit was found that is outside the range of the declared numeric base (e.g. `8` in an octal literal or a non-hex digit after `0x`).",
82 spec_refs: &["§1.3"],
83 },
84 DiagnosticCodeInfo {
85 code: "E1006",
86 severity: Severity::Error,
87 summary: "Unterminated block comment.",
88 description: "A `/* ... */` block comment was opened but never closed before end of input.",
89 spec_refs: &["§1.2"],
90 },
91 DiagnosticCodeInfo {
98 code: "W1001",
99 severity: Severity::Warning,
100 summary: "Unused import.",
101 description: "An import was declared but never referenced. Uses of an imported effect name in `with`, `handling`, and `impl … for` positions count as references (Q-w1001-effect-import-false-positive).",
102 spec_refs: &["§10"],
103 },
104 DiagnosticCodeInfo {
105 code: "E1007",
106 severity: Severity::Error,
107 summary: "Symbol is not visible.",
108 description: "The referenced symbol exists but is private; declare it `public` to export it.",
109 spec_refs: &["§10"],
110 },
111 DiagnosticCodeInfo {
112 code: "E1008",
113 severity: Severity::Error,
114 summary: "Circular module dependency.",
115 description: "The `use` import graph contains a cycle, so the modules cannot be compiled in any dependency order. The message names every module in the cycle in order and points at one offending `use` edge. Fix by removing one of the `use` edges in the cycle, or by extracting the shared items into a third module that both can import.",
116 spec_refs: &["§10"],
117 },
118 DiagnosticCodeInfo {
119 code: "E1009",
120 severity: Severity::Error,
121 summary: "Undefined name.",
122 description: "An identifier reference could not be resolved to any binding in scope. Most often a name is not imported or is misspelled; check the `use` declarations. (A bare effect operation such as `log` called outside a `with` clause or `handling` block is reported separately as `E6005`, not this code.) Emitted by the name-resolution pass.",
123 spec_refs: &["§10"],
124 },
125 DiagnosticCodeInfo {
126 code: "E1010",
127 severity: Severity::Error,
128 summary: "Imported module not found.",
129 description: "A `use` path named a module the registry could not locate. Check the import path and that the target file declares `module <path>`. Emitted by the name-resolution pass.",
130 spec_refs: &["§10"],
131 },
132 DiagnosticCodeInfo {
133 code: "E1011",
134 severity: Severity::Error,
135 summary: "Imported symbol not found in module.",
136 description: "A `use` path names a module that exists but does not export the requested symbol. Emitted by the name-resolution pass.",
137 spec_refs: &["§10"],
138 },
139 DiagnosticCodeInfo {
141 code: "E2000",
142 severity: Severity::Error,
143 summary: "Parse error at top level.",
144 description: "The parser encountered unexpected input while reading a top-level item.",
145 spec_refs: &["§11"],
146 },
147 DiagnosticCodeInfo {
148 code: "E2001",
149 severity: Severity::Error,
150 summary: "Unexpected token.",
151 description: "The next token did not match any alternative at the current grammar position.",
152 spec_refs: &["§11"],
153 },
154 DiagnosticCodeInfo {
155 code: "E2002",
156 severity: Severity::Error,
157 summary: "Missing expected token.",
158 description: "The parser required a specific token here and found something else.",
159 spec_refs: &["§11"],
160 },
161 DiagnosticCodeInfo {
162 code: "E2010",
163 severity: Severity::Error,
164 summary: "Invalid declaration.",
165 description: "A top-level declaration has malformed structure.",
166 spec_refs: &["§4"],
167 },
168 DiagnosticCodeInfo {
169 code: "E2020",
170 severity: Severity::Error,
171 summary: "Invalid expression.",
172 description: "An expression could not be parsed due to malformed structure.",
173 spec_refs: &["§5"],
174 },
175 DiagnosticCodeInfo {
176 code: "E2021",
177 severity: Severity::Error,
178 summary: "Invalid pattern.",
179 description: "A pattern in a `match` or `let` binding could not be parsed.",
180 spec_refs: &["§7"],
181 },
182 DiagnosticCodeInfo {
183 code: "E2022",
184 severity: Severity::Error,
185 summary: "Invalid type expression.",
186 description: "A type annotation could not be parsed as a valid type expression.",
187 spec_refs: &["§2"],
188 },
189 DiagnosticCodeInfo {
190 code: "E2030",
191 severity: Severity::Error,
192 summary: "Parentheses required (lambda parameters / `if` condition).",
193 description: "A construct that requires parentheses was written without them. Lambda parameters must be parenthesized; single-identifier forms (`x => …`) are not accepted. An `if` condition must be parenthesized (`if (cond) { … }`); the diagnostic names the offending token and a note gives the wrapped form. (A missing function name after `fn` is reported separately as `E2073`.)",
194 spec_refs: &["§5"],
195 },
196 DiagnosticCodeInfo {
197 code: "E2031",
198 severity: Severity::Error,
199 summary: "Invalid lambda / function parameter.",
200 description: "A parameter position expected a name and found something else — e.g. a missing name after `mut`, or a non-identifier where a parameter name was required.",
201 spec_refs: &["§5"],
202 },
203 DiagnosticCodeInfo {
204 code: "E2040",
205 severity: Severity::Error,
206 summary: "Invalid generic parameter list.",
207 description: "A generic parameter list is malformed.",
208 spec_refs: &["§4.5"],
209 },
210 DiagnosticCodeInfo {
211 code: "E2050",
212 severity: Severity::Error,
213 summary: "Invalid use declaration.",
214 description: "A `use` import is malformed.",
215 spec_refs: &["§10"],
216 },
217 DiagnosticCodeInfo {
218 code: "E2060",
219 severity: Severity::Error,
220 summary: "Invalid attribute / annotation.",
221 description: "An `@annotation` or `#attribute` could not be parsed.",
222 spec_refs: &["§4.7"],
223 },
224 DiagnosticCodeInfo {
225 code: "E2061",
226 severity: Severity::Error,
227 summary: "Expected constant name.",
228 description: "A `const` declaration was missing its name: a constant name was expected and a different token was found.",
229 spec_refs: &["§4"],
230 },
231 DiagnosticCodeInfo {
232 code: "E2070",
233 severity: Severity::Error,
234 summary: "Invalid match arm.",
235 description: "A match arm is malformed; each arm is `pattern => expression`.",
236 spec_refs: &["§7"],
237 },
238 DiagnosticCodeInfo {
239 code: "E2071",
240 severity: Severity::Error,
241 summary: "Expected associated type name.",
242 description: "An associated-type position inside a `trait`/`impl` expected a type name (`type Name`) and found a different token.",
243 spec_refs: &["§4.4"],
244 },
245 DiagnosticCodeInfo {
246 code: "E2072",
247 severity: Severity::Error,
248 summary: "Expected method name.",
249 description: "A method declaration inside a `trait`/`impl`/`class` expected a method name after `fn` and found a different token.",
250 spec_refs: &["§4.4"],
251 },
252 DiagnosticCodeInfo {
253 code: "E2073",
254 severity: Severity::Error,
255 summary: "Expected function name.",
256 description: "A top-level (or nested) function declaration expected a function name after `fn` and found a different token.",
257 spec_refs: &["§4"],
258 },
259 DiagnosticCodeInfo {
260 code: "E2090",
261 severity: Severity::Error,
262 summary: "Invalid effect declaration.",
263 description: "An `effect` declaration or `with` clause is malformed.",
264 spec_refs: &["§8"],
265 },
266 DiagnosticCodeInfo {
267 code: "E2091",
268 severity: Severity::Error,
269 summary: "Expected effect operation name.",
270 description: "An effect operation declaration inside an `effect` block expected an operation name after `fn` and found a different token.",
271 spec_refs: &["§8"],
272 },
273 DiagnosticCodeInfo {
274 code: "E2092",
275 severity: Severity::Error,
276 summary: "Tuple positional indexing is not available in v1.",
277 description: "`t.0` / `t.1` positional tuple indexing is not a v1 form. Destructure with `let (a, b) = t` to bind tuple elements instead.",
278 spec_refs: &["§5"],
279 },
280 DiagnosticCodeInfo {
282 code: "E4001",
283 severity: Severity::Error,
284 summary: "Type mismatch.",
285 description: "Expected and actual types do not unify. The message reads `expected `T`, found `U``: `T` is the type the surrounding context requires and `U` is the type the expression actually has, both in surface Bock syntax. When a direct conversion to the expected type exists, a note suggests it (`.to_float()`, `.to_int()`, `.to_string()`, or `Int.try_from`/`Float.try_from` for parsing a String).",
286 spec_refs: &["§2"],
287 },
288 DiagnosticCodeInfo {
289 code: "E4002",
290 severity: Severity::Error,
291 summary: "Undefined variable.",
292 description: "A name referenced in an expression has no binding in scope.",
293 spec_refs: &["§2"],
294 },
295 DiagnosticCodeInfo {
296 code: "E4003",
297 severity: Severity::Error,
298 summary: "Arity mismatch in call.",
299 description: "The number of arguments does not match the callee's parameter count.",
300 spec_refs: &["§5"],
301 },
302 DiagnosticCodeInfo {
303 code: "E4004",
304 severity: Severity::Error,
305 summary: "Value is not callable.",
306 description: "An expression of non-function type was used in a call position.",
307 spec_refs: &["§5"],
308 },
309 DiagnosticCodeInfo {
310 code: "E4005",
311 severity: Severity::Error,
312 summary: "`where` clause predicate failed.",
313 description: "A refined-type predicate could not be satisfied.",
314 spec_refs: &["§2"],
315 },
316 DiagnosticCodeInfo {
317 code: "E4010",
318 severity: Severity::Error,
319 summary: "Overlapping trait implementations.",
320 description: "Two `impl` blocks apply to the same type and violate coherence.",
321 spec_refs: &["§4.4"],
322 },
323 DiagnosticCodeInfo {
324 code: "E4011",
325 severity: Severity::Error,
326 summary: "Cannot implement a core trait for a primitive type.",
327 description: "Core traits (`Equatable`, `Comparable`, `Displayable`, `Hashable`) have sealed, compiler-provided conformances for primitive types. User code may not add its own `impl` of a core trait for a primitive (an orphan-rule violation). Wrap the primitive in a newtype and implement the trait for that instead.",
328 spec_refs: &["§18.5"],
329 },
330 DiagnosticCodeInfo {
331 code: "E4012",
332 severity: Severity::Error,
333 summary: "Conversion could not be resolved.",
334 description: "A `.into()` call (or `from`/`try_from`) could not be resolved: no `From`/`Into`/`TryFrom` impl relates the source and target types. For `.into()` the target comes from the expected type, so the call site must have a reachable annotation (a `let y: U =`, an `fn -> U` return position, or a typed argument).",
335 spec_refs: &["§18.4"],
336 },
337 DiagnosticCodeInfo {
338 code: "E4013",
339 severity: Severity::Error,
340 summary: "No such method on a concrete type.",
341 description: "A method that does not exist on the receiver's concrete type was called. The receiver resolved to a fully-known type (a primitive, a built-in collection, an `Optional`/`Result`, or a user record/class/enum in scope) and the method is in none of that type's method sets (intrinsic, canonical-trait, inherent/trait impl, or inherited trait default). When a near-miss name exists a \"did you mean `…`?\" suggestion is offered. Not raised for unresolved inference variables or §4.9 sketch-mode receivers.",
342 spec_refs: &["§18.3"],
343 },
344 DiagnosticCodeInfo {
345 code: "E4014",
346 severity: Severity::Error,
347 summary: "Bare module-qualified import.",
348 description: "A `use` declaration named a module path with neither a brace-list nor a wildcard (a bare `use core.error`). Per §12.2 this is not a v1 import form; module-qualified access is deferred to v1.x. Import the names you need with the braced form (`use core.error.{ Error }`) or the discouraged wildcard (`use core.error.*`).",
349 spec_refs: &["§12.2"],
350 },
351 DiagnosticCodeInfo {
352 code: "E4015",
353 severity: Severity::Error,
354 summary: "Operand or bound instantiation is not `Equatable`.",
355 description: "An `==`/`!=` operand (or a type instantiating an `Equatable` bound) does not conform to `Equatable` (DQ29). Records and enums conform STRUCTURALLY iff every field / variant payload type conforms (recursively); `List[T]`/`Set[T]`/`Optional[T]` iff `T`, `Map[K, V]` iff `K` and `V`, `Result[T, E]` iff `T` and `E`, tuples iff all components; generic user types decide per instantiation. A non-Equatable leaf (e.g. an `Fn` field) poisons the type, and the message names the offending field path and type. Classes are excluded from the structural default and need an explicit `impl Equatable`. An explicit impl always wins over the structural rules. Fix: implement `Equatable` for the type, or remove the comparison.",
356 spec_refs: &["§18.5"],
357 },
358 DiagnosticCodeInfo {
360 code: "E5001",
361 severity: Severity::Error,
362 summary: "Use after move.",
363 description: "A value was used after it had been moved into another binding or call.",
364 spec_refs: &["§3"],
365 },
366 DiagnosticCodeInfo {
367 code: "E5002",
368 severity: Severity::Error,
369 summary: "Mutable borrow of non-mut binding.",
370 description: "The callee takes a `mut` borrow but the binding was not declared `mut`.",
371 spec_refs: &["§3"],
372 },
373 DiagnosticCodeInfo {
374 code: "E5003",
375 severity: Severity::Error,
376 summary: "Value moved inside loop.",
377 description: "The loop body moves a value captured from outside, which would move it more than once.",
378 spec_refs: &["§3"],
379 },
380 DiagnosticCodeInfo {
381 code: "E5004",
382 severity: Severity::Error,
383 summary: "In-place `List` mutator requires a `mut` receiver.",
384 description: "An in-place `List` mutator (`push`/`append`, DQ18; `pop`/`remove_at`/`insert`/`reverse` and indexed `set`, DQ30) was called on a receiver that is not a mutable lvalue. These methods mutate the list in place, so the receiver must be a `mut` binding. Fix: declare the list with `let mut`, take a `mut` parameter, or use a value-returning combinator (`+` / `concat`).",
385 spec_refs: &["§3"],
386 },
387 DiagnosticCodeInfo {
389 code: "E6001",
390 severity: Severity::Error,
391 summary: "Undeclared effect.",
392 description: "A function uses an algebraic effect that is not in its declared `with` clause.",
393 spec_refs: &["§8"],
394 },
395 DiagnosticCodeInfo {
396 code: "W6002",
397 severity: Severity::Warning,
398 summary: "Public function has undeclared effect (development mode).",
399 description: "A public function uses an effect that is not in its declared `with` clause. Promotes to error in production strictness.",
400 spec_refs: &["§8"],
401 },
402 DiagnosticCodeInfo {
403 code: "E6003",
404 severity: Severity::Error,
405 summary: "Propagated effect not declared.",
406 description: "A called function has an effect that the caller does not declare or handle.",
407 spec_refs: &["§8"],
408 },
409 DiagnosticCodeInfo {
410 code: "W6004",
411 severity: Severity::Warning,
412 summary: "Public function propagates undeclared effect (development mode).",
413 description: "A public function calls a function whose effects escape its declared clause.",
414 spec_refs: &["§8"],
415 },
416 DiagnosticCodeInfo {
417 code: "E6005",
418 severity: Severity::Error,
419 summary: "Effect operation called without its effect declared or handled.",
420 description: "An effect operation (e.g. `log(...)` of `effect Log`) was called, but the operation's effect is neither declared by the enclosing function (`with <Effect>`) nor handled in an enclosing scope (a `handling (<Effect> with <handler>) { ... }` block or a module-level `handle <Effect> with <handler>`). Fix by declaring the effect on the function or installing a handler. Emitted by the name-resolution pass: effect operations are only in scope in those contexts.",
421 spec_refs: &["§8", "§10.3", "§10.4"],
422 },
423 DiagnosticCodeInfo {
424 code: "E6006",
425 severity: Severity::Error,
426 summary: "Lambda-handler form is reserved until v1.x.",
427 description: "The lambda-based handler surface `Effect.handler(op: (args) => ...)` is reserved for v1.x and is not a v1 form. v1 supports exactly one handler form: declare a record, `impl <Effect> for <Record>`, and install it with `handle <Effect> with <record>` (module level) or `handling (<Effect> with <record>) { ... }` (block level).",
428 spec_refs: &["§10.4"],
429 },
430 DiagnosticCodeInfo {
432 code: "E7001",
433 severity: Severity::Error,
434 summary: "Missing capability.",
435 description: "A function requires a capability that is not granted by the caller.",
436 spec_refs: &["§9"],
437 },
438 DiagnosticCodeInfo {
439 code: "W7002",
440 severity: Severity::Warning,
441 summary: "Public function requires uninstalled capability (development mode).",
442 description: "A public function's required capability is not present in the caller's capability set.",
443 spec_refs: &["§9"],
444 },
445 DiagnosticCodeInfo {
446 code: "E7003",
447 severity: Severity::Error,
448 summary: "Propagated capability not declared.",
449 description: "A callee requires a capability the caller has not declared.",
450 spec_refs: &["§9"],
451 },
452 DiagnosticCodeInfo {
453 code: "W7004",
454 severity: Severity::Warning,
455 summary: "Public function propagates uninstalled capability.",
456 description: "A public function calls into code requiring capabilities it has not declared.",
457 spec_refs: &["§9"],
458 },
459 DiagnosticCodeInfo {
462 code: "E8001",
463 severity: Severity::Error,
464 summary: "Unknown capability in `@capability`.",
465 description: "A `@capability` annotation named a capability the compiler does not recognize.",
466 spec_refs: &["§9"],
467 },
468 DiagnosticCodeInfo {
469 code: "E8002",
470 severity: Severity::Error,
471 summary: "Expected capability name in `@capability`.",
472 description: "A `@capability` argument was not a capability name (e.g. `Capability.Network` or `Network`).",
473 spec_refs: &["§9"],
474 },
475 DiagnosticCodeInfo {
476 code: "E8003",
477 severity: Severity::Error,
478 summary: "Expected duration or byte size in `@performance`.",
479 description: "A `@performance` budget argument was not a duration (e.g. `100.ms`) or a byte size (e.g. `50.mb`).",
480 spec_refs: &["§17"],
481 },
482 DiagnosticCodeInfo {
483 code: "E8004",
484 severity: Severity::Error,
485 summary: "`@invariant` requires an expression argument.",
486 description: "An `@invariant` annotation was written without the boolean expression it constrains.",
487 spec_refs: &["§17"],
488 },
489 DiagnosticCodeInfo {
490 code: "E8005",
491 severity: Severity::Error,
492 summary: "Invalid `@security` argument.",
493 description: "A `@security` annotation expected a string `level` or a boolean `pii` flag, or was given neither.",
494 spec_refs: &["§17"],
495 },
496 DiagnosticCodeInfo {
497 code: "E8006",
498 severity: Severity::Error,
499 summary: "Expected string argument in `@domain`.",
500 description: "A `@domain` annotation argument was not a string literal.",
501 spec_refs: &["§17"],
502 },
503 DiagnosticCodeInfo {
504 code: "E8010",
505 severity: Severity::Error,
506 summary: "`@invariant` expression must be boolean-typed.",
507 description: "An `@invariant` expression must be a comparison, logical, or call expression that yields a boolean.",
508 spec_refs: &["§17"],
509 },
510 DiagnosticCodeInfo {
512 code: "E8011",
513 severity: Severity::Error,
514 summary: "Child security level less restrictive than parent.",
515 description: "An item's `@security` level is less restrictive than the level it inherits from an enclosing context, which would weaken the parent's guarantee.",
516 spec_refs: &["§17"],
517 },
518 DiagnosticCodeInfo {
519 code: "W8011",
520 severity: Severity::Warning,
521 summary: "Child declares `pii=false` but parent declares `pii=true`.",
522 description: "PII status is inherited: a child cannot drop a parent's `pii=true` to `pii=false`. The narrower flag is ignored and a warning is issued.",
523 spec_refs: &["§17"],
524 },
525 DiagnosticCodeInfo {
526 code: "E8013",
527 severity: Severity::Error,
528 summary: "Public item is missing context annotations (production mode).",
529 description: "In production (strict) mode every public item (`fn`/`class`/`trait`/`record`/`enum`) must carry a `@context` annotation. The standard-mode form of this rule is the warning `W8013`.",
530 spec_refs: &["§17"],
531 },
532 DiagnosticCodeInfo {
533 code: "W8013",
534 severity: Severity::Warning,
535 summary: "Public item is missing context annotations (standard mode).",
536 description: "In standard mode a public item (`fn`/`class`/`trait`/`record`/`enum`) without a `@context` annotation is flagged as a recommendation, once per item. Promotes to the error `E8013` in production (strict) mode; suppressed entirely in lax mode and for embedded-stdlib modules.",
537 spec_refs: &["§17"],
538 },
539 DiagnosticCodeInfo {
540 code: "W8015",
541 severity: Severity::Warning,
542 summary: "Unknown security level in `@security`.",
543 description: "A `@security(level: …)` string was not one of the known security levels; the annotation is kept but flagged.",
544 spec_refs: &["§17"],
545 },
546 DiagnosticCodeInfo {
547 code: "E8016",
548 severity: Severity::Error,
549 summary: "`@performance` budget must be positive.",
550 description: "A `@performance` budget (`max_latency` or `max_memory`) was zero or negative; budgets must be positive values.",
551 spec_refs: &["§17"],
552 },
553 DiagnosticCodeInfo {
556 code: "E8020",
557 severity: Severity::Error,
558 summary: "Effect operation has no handler in scope.",
559 description: "A called effect operation has no handler installed in any enclosing scope and the effect is not declared, so it can never be discharged.",
560 spec_refs: &["§8", "§10"],
561 },
562 DiagnosticCodeInfo {
563 code: "W8020",
564 severity: Severity::Warning,
565 summary: "Effect declared-but-unused.",
566 description: "An effect declared in a function's `with` clause is never used in its body (capability verification). Drop the unused effect from the `with` clause. (A PII-tainted signature without a security context is reported separately as `W8023`.)",
567 spec_refs: &["§8"],
568 },
569 DiagnosticCodeInfo {
570 code: "E8021",
571 severity: Severity::Error,
572 summary: "Callee requires an undeclared capability.",
573 description: "A called function requires a capability that is not declared in the current scope, so the requirement cannot be satisfied.",
574 spec_refs: &["§9"],
575 },
576 DiagnosticCodeInfo {
577 code: "W8021",
578 severity: Severity::Warning,
579 summary: "Importing a PII-returning function into a module without a PII security context.",
580 description: "A `use` imports a function that returns PII-tainted types into a module whose `@security` context does not acknowledge PII.",
581 spec_refs: &["§17"],
582 },
583 DiagnosticCodeInfo {
584 code: "W8022",
585 severity: Severity::Warning,
586 summary: "PII-tainted type passed to a logging/output function.",
587 description: "A PII-tainted type flows into a logging or output sink, which is a potential data leak. One warning is emitted per call site.",
588 spec_refs: &["§17"],
589 },
590 DiagnosticCodeInfo {
591 code: "W8023",
592 severity: Severity::Warning,
593 summary: "PII-tainted signature without a security context.",
594 description: "A function has PII-tainted types in its signature but its module lacks a `@security` context acknowledging PII (e.g. `@security(level: \"confidential\")` or `@security(pii: true)`). Emitted by context composition.",
595 spec_refs: &["§17"],
596 },
597 DiagnosticCodeInfo {
598 code: "E8023",
599 severity: Severity::Error,
600 summary: "Public declaration is missing `@context` (production mode).",
601 description: "In production mode a public function, class, trait, or type must carry a `@context` annotation; capability verification rejects the bare declaration.",
602 spec_refs: &["§17"],
603 },
604 ]
605}
606
607#[cfg(test)]
608mod tests {
609 use super::*;
610
611 #[test]
612 fn catalog_non_empty() {
613 assert!(!diagnostic_catalog().is_empty());
614 }
615
616 #[test]
617 fn codes_have_prefix() {
618 for info in diagnostic_catalog() {
619 let first = info.code.chars().next().unwrap();
620 assert!(
621 matches!(first, 'E' | 'W'),
622 "code {:?} must start with E or W",
623 info.code
624 );
625 }
626 }
627
628 fn parse_code(code: &str) -> Option<(char, u16)> {
636 let mut chars = code.chars();
637 let prefix = chars.next()?;
638 if !matches!(prefix, 'E' | 'W') {
639 return None;
640 }
641 let digits: String = chars.collect();
642 let number: u16 = digits.parse().ok()?;
643 Some((prefix, number))
644 }
645
646 #[test]
647 fn every_catalog_code_parses_and_round_trips() {
648 for info in diagnostic_catalog() {
649 let (prefix, number) = parse_code(info.code).unwrap_or_else(|| {
650 panic!(
651 "catalog code {:?} is not a parseable `E`/`W` + digits code \
652 (no suffixes like `(module)` — split or fold collisions instead)",
653 info.code
654 )
655 });
656 let expected_prefix = match info.severity {
658 Severity::Error => 'E',
659 Severity::Warning => 'W',
660 Severity::Info | Severity::Hint => prefix,
662 };
663 assert_eq!(
664 prefix, expected_prefix,
665 "code {} prefix disagrees with its severity {:?}",
666 info.code, info.severity
667 );
668 let rendered = crate::DiagnosticCode { prefix, number }.to_string();
670 assert_eq!(
671 rendered, info.code,
672 "catalog code {:?} does not round-trip through DiagnosticCode \
673 Display (got {rendered:?})",
674 info.code
675 );
676 }
677 }
678
679 #[test]
680 fn catalog_has_no_duplicate_codes() {
681 let mut seen = std::collections::BTreeSet::new();
682 for info in diagnostic_catalog() {
683 assert!(
684 seen.insert(info.code),
685 "duplicate catalog entry for code {:?}",
686 info.code
687 );
688 }
689 }
690
691 fn scan_emitted_codes(src: &str) -> std::collections::BTreeSet<String> {
699 let flat: String = src.split_whitespace().collect::<Vec<_>>().join(" ");
703 let mut out = std::collections::BTreeSet::new();
704 let needle = "DiagnosticCode { prefix: '";
705 let mut rest = flat.as_str();
706 while let Some(pos) = rest.find(needle) {
707 rest = &rest[pos + needle.len()..];
708 let mut it = rest.chars();
710 let Some(prefix) = it.next() else { break };
711 if !matches!(prefix, 'E' | 'W') {
712 continue;
713 }
714 let Some(num_pos) = rest.find("number:") else {
716 break;
717 };
718 let after = rest[num_pos + "number:".len()..].trim_start();
719 let digits: String = after.chars().take_while(|c| c.is_ascii_digit()).collect();
720 if let Ok(number) = digits.parse::<u16>() {
721 if number >= 1000 {
722 out.insert(format!("{prefix}{number:04}"));
723 }
724 }
725 }
726 out
727 }
728
729 #[test]
730 fn scan_emitted_codes_basic() {
731 let src = r#"
732 self.diag.error(
733 DiagnosticCode { prefix: 'E', number: 8003 },
734 "x", span,
735 );
736 let c = DiagnosticCode {
737 prefix: 'W',
738 number: 1001,
739 };
740 // a test fixture, must be ignored:
741 DiagnosticCode { prefix: 'E', number: 204 }
742 "#;
743 let codes = scan_emitted_codes(src);
744 assert!(codes.contains("E8003"), "got {codes:?}");
745 assert!(codes.contains("W1001"), "got {codes:?}");
746 assert!(
747 !codes.contains("E0204"),
748 "test fixtures (<1000) must be skipped: {codes:?}"
749 );
750 }
751
752 #[test]
762 fn every_emitted_code_is_registered() {
763 let crates_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
764 .parent()
765 .expect("bock-errors lives under compiler/crates/");
766
767 let registered: std::collections::BTreeSet<String> = diagnostic_catalog()
768 .iter()
769 .map(|i| i.code.to_string())
770 .collect();
771
772 let mut emitted = std::collections::BTreeSet::new();
773 let mut files_scanned = 0usize;
774 let mut stack = vec![crates_dir.to_path_buf()];
775 while let Some(dir) = stack.pop() {
776 let Ok(entries) = std::fs::read_dir(&dir) else {
777 continue;
778 };
779 for entry in entries.flatten() {
780 let path = entry.path();
781 if path.is_dir() {
782 let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
785 if name == "target" || name.starts_with('.') {
786 continue;
787 }
788 stack.push(path);
789 } else if path.extension().and_then(|e| e.to_str()) == Some("rs") {
790 if let Ok(src) = std::fs::read_to_string(&path) {
791 files_scanned += 1;
792 emitted.extend(scan_emitted_codes(&src));
793 }
794 }
795 }
796 }
797
798 assert!(
799 files_scanned > 50,
800 "expected to scan the whole crates tree, only saw {files_scanned} files \
801 (CARGO_MANIFEST_DIR layout changed?)"
802 );
803 for expected in ["E1001", "E2000", "E4001", "E6005", "E8003", "W1001"] {
805 assert!(
806 emitted.contains(expected),
807 "scanner failed to find {expected}; scan is broken"
808 );
809 }
810
811 let unregistered: Vec<&String> = emitted.difference(®istered).collect();
812 assert!(
813 unregistered.is_empty(),
814 "these emitted diagnostic codes are NOT in the catalog \
815 (register them in diagnostic_catalog(); do not renumber): {unregistered:?}"
816 );
817 }
818}