Skip to main content

bock_errors/
catalog.rs

1//! Catalog of diagnostic codes emitted by the compiler.
2//!
3//! Source crates emit diagnostics with inline numeric codes (e.g. `E1001`).
4//! This catalog is the single queryable registry of those codes together
5//! with their human-facing metadata — used by editor extensions, the
6//! vocabulary emitter, and online documentation.
7//!
8//! The catalog is deliberately separate from emission sites: keeping it
9//! centralized means tools can render it without depending on the internal
10//! structure of every compiler pass. When a new code is introduced at an
11//! emission site, add an entry here too.
12
13use crate::Severity;
14
15/// Metadata for a single diagnostic code.
16pub struct DiagnosticCodeInfo {
17    /// Zero-padded code string (e.g. `"E1001"`).
18    pub code: &'static str,
19    /// Severity classification.
20    pub severity: Severity,
21    /// Short single-sentence summary shown by editors.
22    pub summary: &'static str,
23    /// Longer description (optional; markdown permitted).
24    pub description: &'static str,
25    /// Spec section references (e.g. `&["§6.1", "§17.4"]`).
26    pub spec_refs: &'static [&'static str],
27}
28
29/// The full catalog of diagnostic codes.
30///
31/// Codes are grouped by crate of origin:
32/// - `1xxx` — lexer (`bock-lexer`) and name resolution (`bock-air`)
33/// - `2xxx` — parser (`bock-parser`)
34/// - `4xxx` — type checker (`bock-types/checker`)
35/// - `5xxx` — ownership (`bock-types/ownership`)
36/// - `6xxx` — effects (`bock-types/effects`)
37/// - `7xxx` — capabilities (`bock-types/capabilities`)
38#[must_use]
39pub fn diagnostic_catalog() -> Vec<DiagnosticCodeInfo> {
40    vec![
41        // ── Lexer (1xxx) ────────────────────────────────────────────────
42        DiagnosticCodeInfo {
43            code: "E1001",
44            severity: Severity::Error,
45            summary: "Unexpected character in source.",
46            description: "The lexer encountered a character it does not recognize as part of any valid token.",
47            spec_refs: &["§1"],
48        },
49        DiagnosticCodeInfo {
50            code: "E1002",
51            severity: Severity::Error,
52            summary: "Unterminated string literal.",
53            description: "A string literal was opened but never closed before end of input.",
54            spec_refs: &["§1.3"],
55        },
56        DiagnosticCodeInfo {
57            code: "E1003",
58            severity: Severity::Error,
59            summary: "Invalid escape sequence in string.",
60            description: "An escape sequence in a string literal is not one of the recognized forms.",
61            spec_refs: &["§1.3"],
62        },
63        DiagnosticCodeInfo {
64            code: "E1004",
65            severity: Severity::Error,
66            summary: "Invalid character literal.",
67            description: "A character literal is empty, contains more than one character, or has an invalid escape.",
68            spec_refs: &["§1.3"],
69        },
70        DiagnosticCodeInfo {
71            code: "E1005",
72            severity: Severity::Error,
73            summary: "Invalid digit for numeric literal.",
74            description: "A digit was found that is outside the range of the declared numeric base.",
75            spec_refs: &["§1.3"],
76        },
77        DiagnosticCodeInfo {
78            code: "E1006",
79            severity: Severity::Error,
80            summary: "Unterminated block comment.",
81            description: "A `/* ... */` block comment was opened but never closed before end of input.",
82            spec_refs: &["§1.2"],
83        },
84        // ── Name resolution (also 1xxx) ─────────────────────────────────
85        DiagnosticCodeInfo {
86            code: "W1001",
87            severity: Severity::Warning,
88            summary: "Unused import.",
89            description: "An import was declared but never referenced.",
90            spec_refs: &["§10"],
91        },
92        // Note: E1001 overlaps with the lexer code above — historically
93        // the resolver reuses the slot for "undefined name" at a different
94        // phase. Tooling should disambiguate by pass context.
95        DiagnosticCodeInfo {
96            code: "E1005 (module)",
97            severity: Severity::Error,
98            summary: "Module not found.",
99            description: "An imported module could not be located by the module registry.",
100            spec_refs: &["§10"],
101        },
102        DiagnosticCodeInfo {
103            code: "E1006 (module)",
104            severity: Severity::Error,
105            summary: "Symbol not found in module.",
106            description: "A `use` path names a module that exists but does not export the requested symbol.",
107            spec_refs: &["§10"],
108        },
109        DiagnosticCodeInfo {
110            code: "E1007",
111            severity: Severity::Error,
112            summary: "Symbol is not visible.",
113            description: "The referenced symbol exists but is private; declare it `public` to export it.",
114            spec_refs: &["§10"],
115        },
116        // ── Parser (2xxx) ───────────────────────────────────────────────
117        DiagnosticCodeInfo {
118            code: "E2000",
119            severity: Severity::Error,
120            summary: "Parse error at top level.",
121            description: "The parser encountered unexpected input while reading a top-level item.",
122            spec_refs: &["§11"],
123        },
124        DiagnosticCodeInfo {
125            code: "E2001",
126            severity: Severity::Error,
127            summary: "Unexpected token.",
128            description: "The next token did not match any alternative at the current grammar position.",
129            spec_refs: &["§11"],
130        },
131        DiagnosticCodeInfo {
132            code: "E2002",
133            severity: Severity::Error,
134            summary: "Missing expected token.",
135            description: "The parser required a specific token here and found something else.",
136            spec_refs: &["§11"],
137        },
138        DiagnosticCodeInfo {
139            code: "E2010",
140            severity: Severity::Error,
141            summary: "Invalid declaration.",
142            description: "A top-level declaration has malformed structure.",
143            spec_refs: &["§4"],
144        },
145        DiagnosticCodeInfo {
146            code: "E2020",
147            severity: Severity::Error,
148            summary: "Invalid expression.",
149            description: "An expression could not be parsed due to malformed structure.",
150            spec_refs: &["§5"],
151        },
152        DiagnosticCodeInfo {
153            code: "E2021",
154            severity: Severity::Error,
155            summary: "Invalid pattern.",
156            description: "A pattern in a `match` or `let` binding could not be parsed.",
157            spec_refs: &["§7"],
158        },
159        DiagnosticCodeInfo {
160            code: "E2022",
161            severity: Severity::Error,
162            summary: "Invalid type expression.",
163            description: "A type annotation could not be parsed as a valid type expression.",
164            spec_refs: &["§2"],
165        },
166        DiagnosticCodeInfo {
167            code: "E2030",
168            severity: Severity::Error,
169            summary: "Invalid lambda parameter list.",
170            description: "Lambda parameters must be parenthesized; single-identifier forms are not accepted.",
171            spec_refs: &["§5"],
172        },
173        DiagnosticCodeInfo {
174            code: "E2040",
175            severity: Severity::Error,
176            summary: "Invalid generic parameter list.",
177            description: "A generic parameter list is malformed.",
178            spec_refs: &["§4.5"],
179        },
180        DiagnosticCodeInfo {
181            code: "E2050",
182            severity: Severity::Error,
183            summary: "Invalid use declaration.",
184            description: "A `use` import is malformed.",
185            spec_refs: &["§10"],
186        },
187        DiagnosticCodeInfo {
188            code: "E2060",
189            severity: Severity::Error,
190            summary: "Invalid attribute / annotation.",
191            description: "An `@annotation` or `#attribute` could not be parsed.",
192            spec_refs: &["§4.7"],
193        },
194        DiagnosticCodeInfo {
195            code: "E2070",
196            severity: Severity::Error,
197            summary: "Invalid match arm.",
198            description: "A match arm is malformed; each arm is `pattern => expression`.",
199            spec_refs: &["§7"],
200        },
201        DiagnosticCodeInfo {
202            code: "E2090",
203            severity: Severity::Error,
204            summary: "Invalid effect declaration.",
205            description: "An `effect` declaration or `with` clause is malformed.",
206            spec_refs: &["§8"],
207        },
208        // ── Type checker (4xxx) ────────────────────────────────────────
209        DiagnosticCodeInfo {
210            code: "E4001",
211            severity: Severity::Error,
212            summary: "Type mismatch.",
213            description: "Expected and actual types do not unify.",
214            spec_refs: &["§2"],
215        },
216        DiagnosticCodeInfo {
217            code: "E4002",
218            severity: Severity::Error,
219            summary: "Undefined variable.",
220            description: "A name referenced in an expression has no binding in scope.",
221            spec_refs: &["§2"],
222        },
223        DiagnosticCodeInfo {
224            code: "E4003",
225            severity: Severity::Error,
226            summary: "Arity mismatch in call.",
227            description: "The number of arguments does not match the callee's parameter count.",
228            spec_refs: &["§5"],
229        },
230        DiagnosticCodeInfo {
231            code: "E4004",
232            severity: Severity::Error,
233            summary: "Value is not callable.",
234            description: "An expression of non-function type was used in a call position.",
235            spec_refs: &["§5"],
236        },
237        DiagnosticCodeInfo {
238            code: "E4005",
239            severity: Severity::Error,
240            summary: "`where` clause predicate failed.",
241            description: "A refined-type predicate could not be satisfied.",
242            spec_refs: &["§2"],
243        },
244        DiagnosticCodeInfo {
245            code: "E4010",
246            severity: Severity::Error,
247            summary: "Overlapping trait implementations.",
248            description: "Two `impl` blocks apply to the same type and violate coherence.",
249            spec_refs: &["§4.4"],
250        },
251        // ── Ownership (5xxx) ───────────────────────────────────────────
252        DiagnosticCodeInfo {
253            code: "E5001",
254            severity: Severity::Error,
255            summary: "Use after move.",
256            description: "A value was used after it had been moved into another binding or call.",
257            spec_refs: &["§3"],
258        },
259        DiagnosticCodeInfo {
260            code: "E5002",
261            severity: Severity::Error,
262            summary: "Mutable borrow of non-mut binding.",
263            description: "The callee takes a `mut` borrow but the binding was not declared `mut`.",
264            spec_refs: &["§3"],
265        },
266        DiagnosticCodeInfo {
267            code: "E5003",
268            severity: Severity::Error,
269            summary: "Value moved inside loop.",
270            description: "The loop body moves a value captured from outside, which would move it more than once.",
271            spec_refs: &["§3"],
272        },
273        // ── Effects (6xxx) ─────────────────────────────────────────────
274        DiagnosticCodeInfo {
275            code: "E6001",
276            severity: Severity::Error,
277            summary: "Undeclared effect.",
278            description: "A function uses an algebraic effect that is not in its declared `with` clause.",
279            spec_refs: &["§8"],
280        },
281        DiagnosticCodeInfo {
282            code: "W6002",
283            severity: Severity::Warning,
284            summary: "Public function has undeclared effect (development mode).",
285            description: "A public function uses an effect that is not in its declared `with` clause. Promotes to error in production strictness.",
286            spec_refs: &["§8"],
287        },
288        DiagnosticCodeInfo {
289            code: "E6003",
290            severity: Severity::Error,
291            summary: "Propagated effect not declared.",
292            description: "A called function has an effect that the caller does not declare or handle.",
293            spec_refs: &["§8"],
294        },
295        DiagnosticCodeInfo {
296            code: "W6004",
297            severity: Severity::Warning,
298            summary: "Public function propagates undeclared effect (development mode).",
299            description: "A public function calls a function whose effects escape its declared clause.",
300            spec_refs: &["§8"],
301        },
302        // ── Capabilities (7xxx) ────────────────────────────────────────
303        DiagnosticCodeInfo {
304            code: "E7001",
305            severity: Severity::Error,
306            summary: "Missing capability.",
307            description: "A function requires a capability that is not granted by the caller.",
308            spec_refs: &["§9"],
309        },
310        DiagnosticCodeInfo {
311            code: "W7002",
312            severity: Severity::Warning,
313            summary: "Public function requires uninstalled capability (development mode).",
314            description: "A public function's required capability is not present in the caller's capability set.",
315            spec_refs: &["§9"],
316        },
317        DiagnosticCodeInfo {
318            code: "E7003",
319            severity: Severity::Error,
320            summary: "Propagated capability not declared.",
321            description: "A callee requires a capability the caller has not declared.",
322            spec_refs: &["§9"],
323        },
324        DiagnosticCodeInfo {
325            code: "W7004",
326            severity: Severity::Warning,
327            summary: "Public function propagates uninstalled capability.",
328            description: "A public function calls into code requiring capabilities it has not declared.",
329            spec_refs: &["§9"],
330        },
331    ]
332}
333
334#[cfg(test)]
335mod tests {
336    use super::*;
337
338    #[test]
339    fn catalog_non_empty() {
340        assert!(!diagnostic_catalog().is_empty());
341    }
342
343    #[test]
344    fn codes_have_prefix() {
345        for info in diagnostic_catalog() {
346            let first = info.code.chars().next().unwrap();
347            assert!(
348                matches!(first, 'E' | 'W'),
349                "code {:?} must start with E or W",
350                info.code
351            );
352        }
353    }
354}