Skip to main content

cyrs_diag/
codes.rs

1//! Diagnostic code registry (spec 0001 §10.2).
2//!
3//! Codes are **stable**. Once assigned, a code's meaning cannot change.
4//! New checks get new codes; removed checks leave their code retired,
5//! never reused. CI (§17.2 / §17.6) enforces uniqueness.
6//!
7//! Code ranges:
8//!
9//! | Range           | Meaning                                  |
10//! | --------------- | ---------------------------------------- |
11//! | `E0001..=E0999` | Syntax (lexer + parser)                  |
12//! | `E1000..=E1999` | Name resolution                          |
13//! | `E2000..=E2999` | Semantic — schema-free                   |
14//! | `E3000..=E3999` | Semantic — schema-aware                  |
15//! | `E4000..=E4999` | Dialect / compatibility                  |
16//! | `E5000..=E5999` | Type system                              |
17//! | `W6000..=W6999` | Style / lint warnings                    |
18//! | `W7000..=W7999` | Performance warnings                     |
19//! | `N8000..=N8999` | Informational notes                      |
20
21use core::fmt;
22
23/// Stable diagnostic code. Rendered as `E0001` / `W6001` / `N8001`.
24#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26#[allow(non_camel_case_types)]
27pub enum DiagCode {
28    // --- syntax (E0001..=E0999) --------------------------------------
29    /// Generic / unclassified syntax error.
30    ///
31    /// Docs: `docs/errors/E0001.md`
32    E0001 = 1,
33    /// Unexpected token encountered.
34    ///
35    /// Docs: `docs/errors/E0002.md`
36    E0002 = 2,
37    /// Expected `<token>`, found `<token>`.
38    ///
39    /// Docs: `docs/errors/E0003.md`
40    E0003 = 3,
41    /// Unclosed string literal (missing closing quote).
42    ///
43    /// Docs: `docs/errors/E0004.md`
44    E0004 = 4,
45    /// Unclosed block comment (missing `*/`).
46    ///
47    /// Docs: `docs/errors/E0005.md`
48    E0005 = 5,
49    /// Invalid numeric literal (bad digits or suffix).
50    ///
51    /// Docs: `docs/errors/E0006.md`
52    E0006 = 6,
53    /// Expected a statement (clause keyword) but found something else.
54    ///
55    /// Docs: `docs/errors/E0007.md`
56    E0007 = 7,
57    /// Expected `;` or end of input after statement.
58    ///
59    /// Docs: `docs/errors/E0008.md`
60    E0008 = 8,
61    /// Expected `(` to start a node pattern.
62    ///
63    /// Docs: `docs/errors/E0009.md`
64    E0009 = 9,
65    /// Expected a node pattern after a relationship pattern.
66    ///
67    /// Docs: `docs/errors/E0010.md`
68    E0010 = 10,
69    /// Expected `)` to close a node pattern.
70    ///
71    /// Docs: `docs/errors/E0011.md`
72    E0011 = 11,
73    /// Expected `-` at the start of a relationship pattern.
74    ///
75    /// Docs: `docs/errors/E0012.md`
76    E0012 = 12,
77    /// Expected `-` to close a left-arrow relationship pattern.
78    ///
79    /// Docs: `docs/errors/E0013.md`
80    E0013 = 13,
81    /// Expected `-` or `->` to close a relationship pattern.
82    ///
83    /// Docs: `docs/errors/E0014.md`
84    E0014 = 14,
85    /// Expected `]` to close a relationship detail block.
86    ///
87    /// Docs: `docs/errors/E0015.md`
88    E0015 = 15,
89    /// Expected a label name after `:`.
90    ///
91    /// Docs: `docs/errors/E0016.md`
92    E0016 = 16,
93    /// Expected a relationship type name after `:`.
94    ///
95    /// Docs: `docs/errors/E0017.md`
96    E0017 = 17,
97    /// Expected `}` to close a property map.
98    ///
99    /// Docs: `docs/errors/E0018.md`
100    E0018 = 18,
101    /// Expected a property key identifier.
102    ///
103    /// Docs: `docs/errors/E0019.md`
104    E0019 = 19,
105    /// Expected `:` separating a property key from its value.
106    ///
107    /// Docs: `docs/errors/E0020.md`
108    E0020 = 20,
109    /// Expected an expression for a property value.
110    ///
111    /// Docs: `docs/errors/E0021.md`
112    E0021 = 21,
113    /// Expected an identifier.
114    ///
115    /// Docs: `docs/errors/E0022.md`
116    E0022 = 22,
117    /// Expression nesting depth exceeds the parser limit.
118    ///
119    /// Docs: `docs/errors/E0023.md`
120    E0023 = 23,
121    /// Expected an operand after a unary operator.
122    ///
123    /// Docs: `docs/errors/E0024.md`
124    E0024 = 24,
125    /// Expected `NULL` after `IS` (or `IS NOT`).
126    ///
127    /// Docs: `docs/errors/E0025.md`
128    E0025 = 25,
129    /// Expected a right-hand side operand for a binary expression.
130    ///
131    /// Docs: `docs/errors/E0026.md`
132    E0026 = 26,
133    /// Expected an expression inside parentheses.
134    ///
135    /// Docs: `docs/errors/E0027.md`
136    E0027 = 27,
137    /// Expected `)` to close a parenthesised expression.
138    ///
139    /// Docs: `docs/errors/E0028.md`
140    E0028 = 28,
141    /// Expected `WITH` after `STARTS` (i.e. `STARTS WITH`).
142    ///
143    /// Docs: `docs/errors/E0029.md`
144    E0029 = 29,
145    /// Expected `WITH` after `ENDS` (i.e. `ENDS WITH`).
146    ///
147    /// Docs: `docs/errors/E0030.md`
148    E0030 = 30,
149    /// Expected a property key name after `.`.
150    ///
151    /// Docs: `docs/errors/E0031.md`
152    E0031 = 31,
153    /// Expected an index expression inside `[…]`.
154    ///
155    /// Docs: `docs/errors/E0032.md`
156    E0032 = 32,
157    /// Expected `]` to close a subscript / index expression.
158    ///
159    /// Docs: `docs/errors/E0033.md`
160    E0033 = 33,
161    /// Expected `)` to close a function call argument list.
162    ///
163    /// Docs: `docs/errors/E0034.md`
164    E0034 = 34,
165    /// Expected a function call argument expression.
166    ///
167    /// Docs: `docs/errors/E0035.md`
168    E0035 = 35,
169    /// Expected an expression in a `RETURN` item.
170    ///
171    /// Docs: `docs/errors/E0036.md`
172    E0036 = 36,
173    /// Expected an identifier after `AS` (alias).
174    ///
175    /// Docs: `docs/errors/E0037.md`
176    E0037 = 37,
177    /// Expected `BY` after `ORDER` (i.e. `ORDER BY`).
178    ///
179    /// Docs: `docs/errors/E0038.md`
180    E0038 = 38,
181    /// Expected an expression in an `ORDER BY` item.
182    ///
183    /// Docs: `docs/errors/E0039.md`
184    E0039 = 39,
185    /// Expected an expression after `SKIP`.
186    ///
187    /// Docs: `docs/errors/E0040.md`
188    E0040 = 40,
189    /// Expected an expression after `LIMIT`.
190    ///
191    /// Docs: `docs/errors/E0041.md`
192    E0041 = 41,
193    /// Expected `MATCH` after `OPTIONAL`.
194    ///
195    /// Docs: `docs/errors/E0042.md`
196    E0042 = 42,
197    /// Expected an expression after `WHERE`.
198    ///
199    /// Docs: `docs/errors/E0043.md`
200    E0043 = 43,
201    /// Clause keyword encountered that is not yet implemented (deferred construct).
202    ///
203    /// Docs: `docs/errors/E0044.md`
204    E0044 = 44,
205    /// Expected a clause keyword (`MATCH`, `WITH`, `RETURN`, …).
206    ///
207    /// Docs: `docs/errors/E0045.md`
208    E0045 = 45,
209    /// Invalid escape sequence in a string literal.
210    ///
211    /// Docs: `docs/errors/E0046.md`
212    E0046 = 46,
213    /// Expected an expression in a list literal.
214    ///
215    /// Docs: `docs/errors/E0047.md`
216    E0047 = 47,
217    /// Expected `]` to close a list literal.
218    ///
219    /// Docs: `docs/errors/E0048.md`
220    E0048 = 48,
221    /// Expected a key in a map literal.
222    ///
223    /// Docs: `docs/errors/E0049.md`
224    E0049 = 49,
225    /// Expected `:` in a map literal entry.
226    ///
227    /// Docs: `docs/errors/E0050.md`
228    E0050 = 50,
229    /// Expected an expression for a map literal value.
230    ///
231    /// Docs: `docs/errors/E0051.md`
232    E0051 = 51,
233    /// Expected `}` to close a map literal.
234    ///
235    /// Docs: `docs/errors/E0052.md`
236    E0052 = 52,
237    /// Expected an expression after `UNWIND`.
238    ///
239    /// Docs: `docs/errors/E0053.md`
240    E0053 = 53,
241    /// Expected `AS` after an `UNWIND` expression.
242    ///
243    /// Docs: `docs/errors/E0054.md`
244    E0054 = 54,
245    /// Expected a pattern after `CREATE`.
246    ///
247    /// Docs: `docs/errors/E0055.md`
248    E0055 = 55,
249    /// Expected a pattern after `MERGE`.
250    ///
251    /// Docs: `docs/errors/E0056.md`
252    E0056 = 56,
253    /// Expected a SET item (property assignment or label add).
254    ///
255    /// Docs: `docs/errors/E0057.md`
256    E0057 = 57,
257    /// Expected a REMOVE item (property access or label).
258    ///
259    /// Docs: `docs/errors/E0058.md`
260    E0058 = 58,
261    /// Expected an expression after `DELETE`.
262    ///
263    /// Docs: `docs/errors/E0059.md`
264    E0059 = 59,
265    /// Expected `DELETE` after `DETACH`.
266    ///
267    /// Docs: `docs/errors/E0060.md`
268    E0060 = 60,
269    /// Expected `CREATE` or `MATCH` after `ON` in a MERGE action.
270    ///
271    /// Docs: `docs/errors/E0061.md`
272    E0061 = 61,
273    /// Expected `]` to close a variable-length hop quantifier.
274    ///
275    /// Docs: `docs/errors/E0062.md`
276    E0062 = 62,
277    /// Expected `=` after a path binder identifier.
278    ///
279    /// Docs: `docs/errors/E0063.md`
280    E0063 = 63,
281    /// Expected `(` after a list-predicate keyword (cy-8x5).
282    ///
283    /// Emitted by the parser when `ANY / ALL / NONE / SINGLE` is not
284    /// immediately followed by an opening parenthesis at atom position.
285    ///
286    /// Docs: `docs/errors/E0065.md`
287    E0065 = 65,
288    /// Expected `IN` between the list-predicate binder and its source
289    /// expression (cy-8x5).
290    ///
291    /// Docs: `docs/errors/E0066.md`
292    E0066 = 66,
293    /// Expected `)` to close a list-predicate expression (cy-8x5).
294    ///
295    /// Docs: `docs/errors/E0067.md`
296    E0067 = 67,
297
298    // --- name resolution (E1000–E1999) --------------------------------
299    /// Unresolved variable reference (spec §6.2).
300    ///
301    /// Emitted by `cyrs-sema` resolve pass when a name that appears in
302    /// an expression position cannot be found in any enclosing scope.
303    ///
304    /// Docs: `docs/errors/E1001.md`
305    E1001 = 1001,
306    /// Variable shadows an outer binding (spec §6.2).
307    ///
308    /// Emitted as a **warning** when `SemaOptions::warn_shadowing` is set
309    /// and a new binding introduces a name already visible in an outer scope.
310    ///
311    /// Docs: `docs/errors/E1002.md`
312    E1002 = 1002,
313
314    // --- semantic schema-free (E2000–E2999) --------------------------
315    /// Variable used in arithmetic / numeric context but has non-Value kind
316    /// (Node, Relationship, or Path). Schema-free kind-consistency check
317    /// (spec §6.3).
318    ///
319    /// Emitted by `cyrs-sema` kinds pass when a Node/Relationship/Path
320    /// variable appears as an operand of an arithmetic expression.
321    ///
322    /// Docs: `docs/errors/E2007.md`
323    E2007 = 2007,
324    /// Variable of incorrect kind used in pattern position (spec §6.3).
325    ///
326    /// E.g., a `Value` variable where a node-pattern binder is expected, or
327    /// a `Node` variable where a path binder is expected.
328    ///
329    /// Emitted by `cyrs-sema` kinds pass.
330    ///
331    /// Docs: `docs/errors/E2008.md`
332    E2008 = 2008,
333    /// Arithmetic operand has a non-numeric type (spec §7.4).
334    ///
335    /// Emitted when an operand of `+`, `-`, `*`, `/`, `%`, or `^` infers to
336    /// a type that cannot unify with `Num` (e.g., a boolean or a string).
337    ///
338    /// Docs: `docs/errors/E2009.md`
339    E2009 = 2009,
340    /// String-operator operand has a non-string type (spec §7.4).
341    ///
342    /// Emitted when an operand of `CONTAINS`, `STARTS WITH`, or `ENDS WITH`
343    /// infers to a type other than `String` or `Any`.
344    ///
345    /// Docs: `docs/errors/E2010.md`
346    E2010 = 2010,
347    /// Boolean-operator operand has a non-boolean type (spec §7.4).
348    ///
349    /// Emitted when an operand of `AND`, `OR`, `XOR`, or unary `NOT`
350    /// infers to a type other than `Bool` or `Any`.
351    ///
352    /// Docs: `docs/errors/E2011.md`
353    E2011 = 2011,
354    /// `IN` list element type mismatch (spec §7.4).
355    ///
356    /// Emitted when the left operand of `IN` infers to a type that cannot
357    /// unify with the element type of the right-hand list.
358    ///
359    /// Docs: `docs/errors/E2012.md`
360    E2012 = 2012,
361    /// `IN` right-hand operand is not a list (spec §7.4).
362    ///
363    /// Emitted when the right-hand side of `IN` infers to a non-list type
364    /// (e.g., `x IN 42`).
365    ///
366    /// Docs: `docs/errors/E2013.md`
367    E2013 = 2013,
368
369    // --- semantic schema-aware (E3000–E3999) -------------------------
370    /// Unknown node label (spec §7.5).
371    ///
372    /// Emitted by `cyrs-sema` schema-aware pass when a label referenced in
373    /// a node pattern is not declared in the schema.
374    ///
375    /// Docs: `docs/errors/E3001.md`
376    E3001 = 3001,
377    /// Unknown relationship type (spec §7.5).
378    ///
379    /// Emitted by `cyrs-sema` schema-aware pass when a relationship type
380    /// referenced in a pattern is not declared in the schema.
381    ///
382    /// Docs: `docs/errors/E3002.md`
383    E3002 = 3002,
384    /// Unknown property on label or relationship type (spec §7.5).
385    ///
386    /// Emitted when a property key in an inline pattern map is not declared
387    /// on any of the node's labels or the relationship's type.
388    ///
389    /// Docs: `docs/errors/E3003.md`
390    E3003 = 3003,
391    /// Property type mismatch (spec §7.5).
392    ///
393    /// Emitted when a literal value in an inline property map cannot be
394    /// stored in the declared property type (e.g., `String` into an `Int`
395    /// slot).
396    ///
397    /// Docs: `docs/errors/E3004.md`
398    E3004 = 3004,
399    /// Unknown function name (spec §7.5).
400    ///
401    /// Emitted by `cyrs-sema` schema-aware pass when a function call
402    /// references a name not present in the `SchemaProvider`'s catalog.
403    ///
404    /// Docs: `docs/errors/E3006.md`
405    E3006 = 3006,
406    /// Function or procedure arity mismatch (spec §7.5).
407    ///
408    /// Emitted when a function or procedure call provides the wrong number
409    /// of arguments compared to the declared signature.
410    ///
411    /// Docs: `docs/errors/E3007.md`
412    E3007 = 3007,
413    /// Unknown procedure name (spec §7.5).
414    ///
415    /// Emitted by `cyrs-sema` schema-aware pass when a `CALL` clause
416    /// references a procedure not present in the `SchemaProvider`'s catalog.
417    ///
418    /// Docs: `docs/errors/E3008.md`
419    E3008 = 3008,
420    /// Schema-file property type is unresolved (spec 0002 §9).
421    ///
422    /// Emitted by `cypher schema check` when a property or parameter
423    /// declares an opaque type (e.g. `DURATION`, `POINT`) whose v0
424    /// structural meaning is deferred. Load succeeds — the type round-
425    /// trips — but the linter flags it so operators know the type
426    /// participates as an opaque symbolic value only.
427    ///
428    /// Docs: `docs/errors/E3010.md`
429    E3010 = 3010,
430    /// Relationship-type endpoint cycles through the same label on both
431    /// sides (spec 0002 §9).
432    ///
433    /// Emitted by `cypher schema check` when a rel type declares the
434    /// same label in both `start_labels` and `end_labels`. The spec
435    /// permits self-loops but the linter surfaces them so operators
436    /// can confirm they are intentional (e.g. `KNOWS: Person → Person`
437    /// is fine; `REPORTS_TO: Team → Team` may be a modelling slip).
438    ///
439    /// Docs: `docs/errors/E3011.md`
440    E3011 = 3011,
441
442    // --- dialect (E4000–E4999) ----------------------------------------
443    /// Dialect not supported (spec §9.3).
444    ///
445    /// Emitted by `cyrs-sema::dialect::reject_neo4j_current` when the
446    /// caller attempts to use the `Neo4jCurrent` dialect, which is not
447    /// part of v1 (spec §9.3, §19–§20).
448    ///
449    /// Docs: `docs/errors/E4001.md`
450    E4001 = 4001,
451
452    // --- dialect gates (E4010–E4019) assigned by cy-z49 --------------
453    /// `label_negation` — `!` in label expressions; `GqlAligned` only (spec §9.2).
454    ///
455    /// Docs: `docs/errors/E4010.md`
456    E4010 = 4010,
457    /// `label_union` — `A|B` label-union in patterns; allowed in both v1 dialects (spec §9.2).
458    ///
459    /// Docs: `docs/errors/E4011.md`
460    E4011 = 4011,
461    /// `integer_division` — `/` with integer operands; `OpenCypherV9` only (spec §9.2).
462    ///
463    /// Docs: `docs/errors/E4012.md`
464    E4012 = 4012,
465    /// `union_set` — plain `UNION` (set-semantics); allowed in both v1 dialects.
466    ///
467    /// Docs: `docs/errors/E4013.md`
468    E4013 = 4013,
469    /// `call_procedure` — `CALL proc YIELD …`; allowed in both v1 dialects.
470    ///
471    /// Docs: `docs/errors/E4014.md`
472    E4014 = 4014,
473    /// `load_csv` — `LOAD CSV` clause; deferred (spec §9.3).
474    ///
475    /// Docs: `docs/errors/E4015.md`
476    E4015 = 4015,
477    /// `apoc_functions` — APOC-prefixed functions; deferred (spec §9.3).
478    ///
479    /// Docs: `docs/errors/E4016.md`
480    E4016 = 4016,
481    /// `exists_subquery` — `EXISTS { }` subquery; deferred (spec §9.3).
482    ///
483    /// Docs: `docs/errors/E4017.md`
484    E4017 = 4017,
485    /// `cypher_prefix` — `CYPHER n MATCH …` header; deferred (spec §9.3).
486    ///
487    /// Docs: `docs/errors/E4018.md`
488    E4018 = 4018,
489    /// `call_in_transactions` — `CALL { } IN TRANSACTIONS`; deferred (spec §9.3).
490    ///
491    /// Docs: `docs/errors/E4019.md`
492    E4019 = 4019,
493
494    // --- type system (E5000–E5999) ------------------------------------
495    /// Type mismatch in unification — two incompatible concrete types cannot
496    /// be unified (spec §7.2, §7.3).
497    ///
498    /// Produced by `cyrs-sema::unify::TypeMismatch::into_diagnostic`.
499    /// Call sites supply the source range; code is always `E5003`.
500    ///
501    /// Docs: `docs/errors/E5003.md`
502    E5003 = 5003,
503    /// Indexing or slicing applied to a non-list expression (spec §7.4 /
504    /// §19 row "List indexing / slicing").
505    ///
506    /// Emitted by `cyrs-sema::kinds` when `xs[0]` / `xs[i..j]` is applied
507    /// to a target whose inferred type is not a list (and not `Any` / `Unknown`).
508    /// v1 scope is list-only; string indexing is deferred to a follow-up
509    /// bead and is rejected by this code until then.
510    ///
511    /// Docs: `docs/errors/E5010.md`
512    E5010 = 5010,
513    /// List-predicate iterable is not a list (spec §7.4 / §19 row
514    /// "List predicates").
515    ///
516    /// Emitted by `cyrs-sema::kinds` when the source expression of
517    /// `ANY / ALL / NONE / SINGLE (x IN xs [WHERE p])` is structurally
518    /// non-list: a scalar literal, a `Node` / `Relationship` / `Path`
519    /// variable, a map literal, or a pattern predicate. `Value` variables
520    /// and call results are accepted because they may hold a list at
521    /// runtime — schema-aware refinement is the follow-up's job.
522    ///
523    /// Docs: `docs/errors/E5011.md`
524    E5011 = 5011,
525    /// Built-in function argument kind mismatch (spec §7.4 / §10.2).
526    ///
527    /// Emitted by `cyrs-sema::infer` when a call to a registered
528    /// builtin passes an argument whose inferred type is incompatible
529    /// with the parameter's declared `ArgShape`. The canonical case
530    /// is `id(42)` — the `id` builtin's parameter is `GraphEntity`
531    /// (`Node | Relationship | Path`), so a scalar literal is rejected.
532    ///
533    /// Docs: `docs/errors/E5012.md`
534    E5012 = 5012,
535
536    // --- syntax (E0064) continued -------------------------------------
537    /// Unclosed `[` in a list-indexing / slicing expression (spec §4.3).
538    ///
539    /// Emitted by the parser's `index_or_slice_postfix` recovery path
540    /// when no matching `]` follows the inner expression / slice bounds.
541    /// Distinct from E0033 (`SUBSCRIPT_EXPR` legacy path) so tooling can
542    /// tell the two apart; the typed cy-7s6.1 grammar path uses this
543    /// code exclusively.
544    ///
545    /// Docs: `docs/errors/E0064.md`
546    E0064 = 64,
547    /// Expected `IN` in list comprehension (spec §19 row
548    /// "List comprehensions").
549    ///
550    /// Emitted by the parser's list-comprehension production (cy-5gh)
551    /// when the iteration-variable identifier is not followed by the
552    /// `IN` keyword.
553    ///
554    /// Docs: `docs/errors/E0068.md`
555    E0068 = 68,
556    /// Expected `|` or `]` in list comprehension (spec §19 row
557    /// "List comprehensions").
558    ///
559    /// Emitted by the parser's list-comprehension production (cy-5gh)
560    /// when neither a projection pipe `|` nor the closing bracket `]`
561    /// follows the optional `WHERE` predicate / source expression.
562    ///
563    /// Docs: `docs/errors/E0069.md`
564    E0069 = 69,
565    /// Expected `THEN` in a `CASE` arm (spec §19 row "CASE").
566    ///
567    /// Emitted by the parser's CASE production (cy-41u) when a
568    /// `WHEN <value>` clause is not followed by the mandatory `THEN`
569    /// keyword.
570    ///
571    /// Docs: `docs/errors/E0070.md`
572    E0070 = 70,
573    /// Expected `END` to close a `CASE` expression (spec §19 row "CASE").
574    ///
575    /// Emitted by the parser's CASE production (cy-41u) when the
576    /// terminating `END` keyword is missing after the final arm /
577    /// optional `ELSE` branch.
578    ///
579    /// Docs: `docs/errors/E0071.md`
580    E0071 = 71,
581    /// Expected `)` to close an `EXISTS(<pattern>)` pattern predicate
582    /// (spec §6.1 / §19 row "Pattern predicates in expressions").
583    ///
584    /// Emitted by the parser's `exists_pattern_predicate` production
585    /// (cy-lve) when the closing paren of the wrapping
586    /// `EXISTS ( <pattern> )` form is missing. The bare-pattern form
587    /// `WHERE (a)-->(b)` (cy-7lf) does not emit this code — the inner
588    /// `)` belongs to the pattern's node-pattern production.
589    ///
590    /// Docs: `docs/errors/E0072.md`
591    E0072 = 72,
592    /// Expected `}` to close a map projection (spec §6.1 / §19 row
593    /// "Map projection"; cy-01q).
594    ///
595    /// Emitted by the parser's `map_projection_postfix` production
596    /// when the closing `}` of `n { ... }` is missing. Distinct from
597    /// E0052 (map literal `}`) so tools can distinguish the two
598    /// recovery paths.
599    E0078 = 78,
600    /// Expected property name or `*` after `.` in a map projection item
601    /// (cy-01q). Inside `n { ... }`, a `.` opens either `.NAME`
602    /// (property selector) or `.*` (all-properties spread); anything
603    /// else is an error.
604    E0079 = 79,
605    /// Expected `:` in a map projection literal item (cy-01q).
606    /// Distinct from E0050 (map literal `:`).
607    E0080 = 80,
608    /// Expected expression for map projection literal value (cy-01q).
609    E0081 = 81,
610    /// Expected an item in map projection: `.name`, `key: expr`, `.*`,
611    /// or `*` (cy-01q).
612    E0082 = 82,
613
614    // --- warnings (6000..) -------------------------------------------
615    /// Dead WITH — projection with no downstream reader.
616    W6001 = 6001,
617    /// Identifier collides with a reserved keyword; needs backtick quoting.
618    W6002 = 6002,
619    /// Duplicate key in map literal — last write wins.
620    W6003 = 6003,
621    /// Variable bound but never read.
622    W6004 = 6004,
623    /// Redundant OPTIONAL MATCH — no bound variables escape the clause.
624    W6005 = 6005,
625    /// Pattern has no label or type restriction — will scan broadly.
626    W6006 = 6006,
627    /// Inconsistent keyword casing inside one query.
628    W6007 = 6007,
629    /// Unreachable label — schema-file lint (spec 0002 §9).
630    ///
631    /// Emitted by `cypher schema check` when a label is declared but
632    /// not referenced by any relationship type's `start_labels` or
633    /// `end_labels`. Not fatal: isolated labels are permitted — they
634    /// are legitimate for nodes created without relationships — but
635    /// the lint surfaces them so operators catch typos in rel-type
636    /// endpoint lists.
637    ///
638    /// Docs: `docs/errors/W6010.md`
639    W6010 = 6010,
640
641    // --- performance (7000..) ----------------------------------------
642    /// Cartesian product between disconnected MATCH components.
643    W7001 = 7001,
644    /// Expensive function call inside a row-wise filter.
645    W7002 = 7002,
646    /// Variable-length path without an upper bound.
647    W7003 = 7003,
648    /// Property access on an unindexed label in a selective filter.
649    W7004 = 7004,
650
651    // --- notes (8000..) ----------------------------------------------
652    /// Informational — pattern normalised to canonical direction.
653    N8001 = 8001,
654    /// Informational — inferred type of an expression.
655    N8002 = 8002,
656    /// Informational — variable dropped from scope by this projection.
657    N8003 = 8003,
658}
659
660impl DiagCode {
661    /// Render as the stable wire-format string: `E0001`, `W6001`, `N8001`.
662    #[must_use]
663    pub const fn as_str(self) -> &'static str {
664        match self {
665            Self::E0001 => "E0001",
666            Self::E0002 => "E0002",
667            Self::E0003 => "E0003",
668            Self::E0004 => "E0004",
669            Self::E0005 => "E0005",
670            Self::E0006 => "E0006",
671            Self::E0007 => "E0007",
672            Self::E0008 => "E0008",
673            Self::E0009 => "E0009",
674            Self::E0010 => "E0010",
675            Self::E0011 => "E0011",
676            Self::E0012 => "E0012",
677            Self::E0013 => "E0013",
678            Self::E0014 => "E0014",
679            Self::E0015 => "E0015",
680            Self::E0016 => "E0016",
681            Self::E0017 => "E0017",
682            Self::E0018 => "E0018",
683            Self::E0019 => "E0019",
684            Self::E0020 => "E0020",
685            Self::E0021 => "E0021",
686            Self::E0022 => "E0022",
687            Self::E0023 => "E0023",
688            Self::E0024 => "E0024",
689            Self::E0025 => "E0025",
690            Self::E0026 => "E0026",
691            Self::E0027 => "E0027",
692            Self::E0028 => "E0028",
693            Self::E0029 => "E0029",
694            Self::E0030 => "E0030",
695            Self::E0031 => "E0031",
696            Self::E0032 => "E0032",
697            Self::E0033 => "E0033",
698            Self::E0034 => "E0034",
699            Self::E0035 => "E0035",
700            Self::E0036 => "E0036",
701            Self::E0037 => "E0037",
702            Self::E0038 => "E0038",
703            Self::E0039 => "E0039",
704            Self::E0040 => "E0040",
705            Self::E0041 => "E0041",
706            Self::E0042 => "E0042",
707            Self::E0043 => "E0043",
708            Self::E0044 => "E0044",
709            Self::E0045 => "E0045",
710            Self::E0046 => "E0046",
711            Self::E0047 => "E0047",
712            Self::E0048 => "E0048",
713            Self::E0049 => "E0049",
714            Self::E0050 => "E0050",
715            Self::E0051 => "E0051",
716            Self::E0052 => "E0052",
717            Self::E0053 => "E0053",
718            Self::E0054 => "E0054",
719            Self::E0055 => "E0055",
720            Self::E0056 => "E0056",
721            Self::E0057 => "E0057",
722            Self::E0058 => "E0058",
723            Self::E0059 => "E0059",
724            Self::E0060 => "E0060",
725            Self::E0061 => "E0061",
726            Self::E0062 => "E0062",
727            Self::E0063 => "E0063",
728            Self::E0064 => "E0064",
729            Self::E0065 => "E0065",
730            Self::E0066 => "E0066",
731            Self::E0067 => "E0067",
732            Self::E0068 => "E0068",
733            Self::E0069 => "E0069",
734            Self::E0070 => "E0070",
735            Self::E0071 => "E0071",
736            Self::E0072 => "E0072",
737            Self::E0078 => "E0078",
738            Self::E0079 => "E0079",
739            Self::E0080 => "E0080",
740            Self::E0081 => "E0081",
741            Self::E0082 => "E0082",
742            Self::E1001 => "E1001",
743            Self::E1002 => "E1002",
744            Self::E2007 => "E2007",
745            Self::E2008 => "E2008",
746            Self::E2009 => "E2009",
747            Self::E2010 => "E2010",
748            Self::E2011 => "E2011",
749            Self::E2012 => "E2012",
750            Self::E2013 => "E2013",
751            Self::E3001 => "E3001",
752            Self::E3002 => "E3002",
753            Self::E3003 => "E3003",
754            Self::E3004 => "E3004",
755            Self::E3006 => "E3006",
756            Self::E3007 => "E3007",
757            Self::E3008 => "E3008",
758            Self::E3010 => "E3010",
759            Self::E3011 => "E3011",
760            Self::E4001 => "E4001",
761            Self::E4010 => "E4010",
762            Self::E4011 => "E4011",
763            Self::E4012 => "E4012",
764            Self::E4013 => "E4013",
765            Self::E4014 => "E4014",
766            Self::E4015 => "E4015",
767            Self::E4016 => "E4016",
768            Self::E4017 => "E4017",
769            Self::E4018 => "E4018",
770            Self::E4019 => "E4019",
771            Self::E5003 => "E5003",
772            Self::E5010 => "E5010",
773            Self::E5011 => "E5011",
774            Self::E5012 => "E5012",
775            Self::W6001 => "W6001",
776            Self::W6002 => "W6002",
777            Self::W6003 => "W6003",
778            Self::W6004 => "W6004",
779            Self::W6005 => "W6005",
780            Self::W6006 => "W6006",
781            Self::W6007 => "W6007",
782            Self::W6010 => "W6010",
783            Self::W7001 => "W7001",
784            Self::W7002 => "W7002",
785            Self::W7003 => "W7003",
786            Self::W7004 => "W7004",
787            Self::N8001 => "N8001",
788            Self::N8002 => "N8002",
789            Self::N8003 => "N8003",
790        }
791    }
792
793    /// Severity letter derived from the numeric range (spec §10.2).
794    ///
795    /// `0..=5999` → `'E'`, `6000..=7999` → `'W'`, `8000..=8999` → `'N'`.
796    /// Panics if the discriminant falls outside any registered range —
797    /// the [`ALL`](Self::ALL) invariants enforced by `tests/registry.rs`
798    /// make this unreachable at runtime.
799    #[must_use]
800    pub const fn severity_char(self) -> char {
801        match self as u32 {
802            0..=5999 => 'E',
803            6000..=7999 => 'W',
804            8000..=8999 => 'N',
805            _ => panic!("DiagCode discriminant outside any registered range"),
806        }
807    }
808
809    /// Canonical enumeration of every registered diagnostic code, in
810    /// numeric order. This is THE registry used by
811    /// `tests/registry.rs` to enforce spec §10.2 invariants — every
812    /// variant added to [`DiagCode`] must also be appended here.
813    pub const ALL: &'static [DiagCode] = &[
814        Self::E0001,
815        Self::E0002,
816        Self::E0003,
817        Self::E0004,
818        Self::E0005,
819        Self::E0006,
820        Self::E0007,
821        Self::E0008,
822        Self::E0009,
823        Self::E0010,
824        Self::E0011,
825        Self::E0012,
826        Self::E0013,
827        Self::E0014,
828        Self::E0015,
829        Self::E0016,
830        Self::E0017,
831        Self::E0018,
832        Self::E0019,
833        Self::E0020,
834        Self::E0021,
835        Self::E0022,
836        Self::E0023,
837        Self::E0024,
838        Self::E0025,
839        Self::E0026,
840        Self::E0027,
841        Self::E0028,
842        Self::E0029,
843        Self::E0030,
844        Self::E0031,
845        Self::E0032,
846        Self::E0033,
847        Self::E0034,
848        Self::E0035,
849        Self::E0036,
850        Self::E0037,
851        Self::E0038,
852        Self::E0039,
853        Self::E0040,
854        Self::E0041,
855        Self::E0042,
856        Self::E0043,
857        Self::E0044,
858        Self::E0045,
859        Self::E0046,
860        Self::E0047,
861        Self::E0048,
862        Self::E0049,
863        Self::E0050,
864        Self::E0051,
865        Self::E0052,
866        Self::E0053,
867        Self::E0054,
868        Self::E0055,
869        Self::E0056,
870        Self::E0057,
871        Self::E0058,
872        Self::E0059,
873        Self::E0060,
874        Self::E0061,
875        Self::E0062,
876        Self::E0063,
877        Self::E0064,
878        Self::E0065,
879        Self::E0066,
880        Self::E0067,
881        Self::E0068,
882        Self::E0069,
883        Self::E0070,
884        Self::E0071,
885        Self::E0072,
886        Self::E0078,
887        Self::E0079,
888        Self::E0080,
889        Self::E0081,
890        Self::E0082,
891        Self::E1001,
892        Self::E1002,
893        Self::E2007,
894        Self::E2008,
895        Self::E2009,
896        Self::E2010,
897        Self::E2011,
898        Self::E2012,
899        Self::E2013,
900        Self::E3001,
901        Self::E3002,
902        Self::E3003,
903        Self::E3004,
904        Self::E3006,
905        Self::E3007,
906        Self::E3008,
907        Self::E3010,
908        Self::E3011,
909        Self::E4001,
910        Self::E4010,
911        Self::E4011,
912        Self::E4012,
913        Self::E4013,
914        Self::E4014,
915        Self::E4015,
916        Self::E4016,
917        Self::E4017,
918        Self::E4018,
919        Self::E4019,
920        Self::E5003,
921        Self::E5010,
922        Self::E5011,
923        Self::E5012,
924        Self::W6001,
925        Self::W6002,
926        Self::W6003,
927        Self::W6004,
928        Self::W6005,
929        Self::W6006,
930        Self::W6007,
931        Self::W6010,
932        Self::W7001,
933        Self::W7002,
934        Self::W7003,
935        Self::W7004,
936        Self::N8001,
937        Self::N8002,
938        Self::N8003,
939    ];
940
941    /// Recover the typed [`DiagCode`] for a numeric discriminant.
942    ///
943    /// Returns `None` for any value not registered in [`DiagCode::ALL`]
944    /// (including retired codes). Embedders that map cyrs errors to
945    /// their own typed errors should prefer this over matching on raw
946    /// `u16` values from [`cyrs_syntax::SyntaxError::code`] — names
947    /// survive renumbering, magic numbers do not (cy-emb3).
948    ///
949    /// # Examples
950    ///
951    /// ```
952    /// use cyrs_diag::DiagCode;
953    /// assert_eq!(DiagCode::try_from_u16(3), Some(DiagCode::E0003));
954    /// assert_eq!(DiagCode::try_from_u16(6001), Some(DiagCode::W6001));
955    /// assert_eq!(DiagCode::try_from_u16(9999), None);
956    /// ```
957    #[must_use]
958    pub fn try_from_u16(numeric: u16) -> Option<DiagCode> {
959        Self::ALL.iter().copied().find(|&c| (c as u16) == numeric)
960    }
961
962    /// Recover the typed [`DiagCode`] for a numeric discriminant, or
963    /// fall back to [`DiagCode::E0001`] for unknown values.
964    ///
965    /// Convenience for diagnostic-rendering paths that must always
966    /// produce a code (e.g. [`from`](From::from) impl on
967    /// [`cyrs_syntax::SyntaxError`]). Prefer
968    /// [`try_from_u16`](Self::try_from_u16) when the embedder needs to
969    /// distinguish "unknown code" from "registered E0001" (cy-emb3).
970    #[must_use]
971    pub fn from_u16_or_e0001(numeric: u16) -> DiagCode {
972        Self::try_from_u16(numeric).unwrap_or(Self::E0001)
973    }
974}
975
976impl fmt::Display for DiagCode {
977    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
978        f.write_str(self.as_str())
979    }
980}
981
982/// Lift a [`cyrs_syntax::SyntaxError`] to its typed [`DiagCode`].
983///
984/// `cyrs-syntax` carries diagnostic codes as `u16` to avoid a
985/// reverse dependency on `cyrs-diag` (spec §3 crate graph).
986/// Embedders that already depend on `cyrs-diag` for rendering can
987/// recover the typed enum without re-implementing the lookup table:
988///
989/// ```no_run
990/// use cyrs_diag::DiagCode;
991/// use cyrs_syntax::parse;
992///
993/// let parse = parse("MATCH");
994/// // First syntax error from a deliberately broken query.
995/// let err = parse.errors().first().expect("at least one error");
996/// let code: DiagCode = DiagCode::from(err);
997/// // Match on a stable name — survives any future renumbering.
998/// match code {
999///     DiagCode::E0001 | DiagCode::E0007 | DiagCode::E0045 => { /* generic / clause-level */ }
1000///     _ => { /* other syntax codes */ }
1001/// }
1002/// ```
1003///
1004/// Unknown numeric codes fall back to [`DiagCode::E0001`] — see
1005/// [`DiagCode::try_from_u16`] for a fallible alternative (cy-emb3).
1006impl From<&cyrs_syntax::SyntaxError> for DiagCode {
1007    fn from(err: &cyrs_syntax::SyntaxError) -> Self {
1008        Self::from_u16_or_e0001(err.code)
1009    }
1010}
1011
1012#[cfg(test)]
1013mod tests {
1014    use super::DiagCode;
1015
1016    /// Every code rendered as a string has a unique textual form — the
1017    /// canonical registry uniqueness check (spec §10.2).
1018    #[test]
1019    fn codes_are_unique_strings() {
1020        let all = [
1021            DiagCode::E0001,
1022            DiagCode::E0002,
1023            DiagCode::E0003,
1024            DiagCode::E0004,
1025            DiagCode::E0005,
1026            DiagCode::E0006,
1027            DiagCode::E0007,
1028            DiagCode::E0008,
1029            DiagCode::E0009,
1030            DiagCode::E0010,
1031            DiagCode::E0011,
1032            DiagCode::E0012,
1033            DiagCode::E0013,
1034            DiagCode::E0014,
1035            DiagCode::E0015,
1036            DiagCode::E0016,
1037            DiagCode::E0017,
1038            DiagCode::E0018,
1039            DiagCode::E0019,
1040            DiagCode::E0020,
1041            DiagCode::E0021,
1042            DiagCode::E0022,
1043            DiagCode::E0023,
1044            DiagCode::E0024,
1045            DiagCode::E0025,
1046            DiagCode::E0026,
1047            DiagCode::E0027,
1048            DiagCode::E0028,
1049            DiagCode::E0029,
1050            DiagCode::E0030,
1051            DiagCode::E0031,
1052            DiagCode::E0032,
1053            DiagCode::E0033,
1054            DiagCode::E0034,
1055            DiagCode::E0035,
1056            DiagCode::E0036,
1057            DiagCode::E0037,
1058            DiagCode::E0038,
1059            DiagCode::E0039,
1060            DiagCode::E0040,
1061            DiagCode::E0041,
1062            DiagCode::E0042,
1063            DiagCode::E0043,
1064            DiagCode::E0044,
1065            DiagCode::E0045,
1066            DiagCode::E0046,
1067            DiagCode::E0047,
1068            DiagCode::E0048,
1069            DiagCode::E0049,
1070            DiagCode::E0050,
1071            DiagCode::E0051,
1072            DiagCode::E0052,
1073            DiagCode::E0053,
1074            DiagCode::E0054,
1075            DiagCode::E0055,
1076            DiagCode::E0056,
1077            DiagCode::E0057,
1078            DiagCode::E0058,
1079            DiagCode::E0059,
1080            DiagCode::E0060,
1081            DiagCode::E0061,
1082            DiagCode::E0062,
1083            DiagCode::E0063,
1084            DiagCode::E0064,
1085            DiagCode::E0065,
1086            DiagCode::E0066,
1087            DiagCode::E0067,
1088            DiagCode::E0068,
1089            DiagCode::E0069,
1090            DiagCode::E0070,
1091            DiagCode::E0071,
1092            DiagCode::E0072,
1093            DiagCode::E0078,
1094            DiagCode::E0079,
1095            DiagCode::E0080,
1096            DiagCode::E0081,
1097            DiagCode::E0082,
1098            DiagCode::E1001,
1099            DiagCode::E1002,
1100            DiagCode::E2007,
1101            DiagCode::E2008,
1102            DiagCode::E2009,
1103            DiagCode::E2010,
1104            DiagCode::E2011,
1105            DiagCode::E2012,
1106            DiagCode::E2013,
1107            DiagCode::E3001,
1108            DiagCode::E3002,
1109            DiagCode::E3003,
1110            DiagCode::E3004,
1111            DiagCode::E3006,
1112            DiagCode::E3007,
1113            DiagCode::E3008,
1114            DiagCode::E3010,
1115            DiagCode::E3011,
1116            DiagCode::E4001,
1117            DiagCode::E4010,
1118            DiagCode::E4011,
1119            DiagCode::E4012,
1120            DiagCode::E4013,
1121            DiagCode::E4014,
1122            DiagCode::E4015,
1123            DiagCode::E4016,
1124            DiagCode::E4017,
1125            DiagCode::E4018,
1126            DiagCode::E4019,
1127            DiagCode::E5003,
1128            DiagCode::E5010,
1129            DiagCode::E5011,
1130            DiagCode::E5012,
1131            DiagCode::W6001,
1132            DiagCode::W6002,
1133            DiagCode::W6003,
1134            DiagCode::W6004,
1135            DiagCode::W6005,
1136            DiagCode::W6006,
1137            DiagCode::W6007,
1138            DiagCode::W6010,
1139            DiagCode::W7001,
1140            DiagCode::W7002,
1141            DiagCode::W7003,
1142            DiagCode::W7004,
1143            DiagCode::N8001,
1144            DiagCode::N8002,
1145            DiagCode::N8003,
1146        ];
1147        let mut strs: Vec<_> = all.iter().map(|c| c.as_str()).collect();
1148        strs.sort_unstable();
1149        strs.dedup();
1150        assert_eq!(strs.len(), all.len(), "duplicate DiagCode string");
1151    }
1152
1153    /// `try_from_u16` recovers the typed variant for a sample of known
1154    /// codes spanning every range (cy-emb3).
1155    #[test]
1156    fn try_from_u16_known_codes() {
1157        // Syntax range.
1158        assert_eq!(DiagCode::try_from_u16(1), Some(DiagCode::E0001));
1159        assert_eq!(DiagCode::try_from_u16(3), Some(DiagCode::E0003));
1160        assert_eq!(DiagCode::try_from_u16(46), Some(DiagCode::E0046));
1161        assert_eq!(DiagCode::try_from_u16(78), Some(DiagCode::E0078));
1162        // Name resolution / sema / dialect / type system.
1163        assert_eq!(DiagCode::try_from_u16(1001), Some(DiagCode::E1001));
1164        assert_eq!(DiagCode::try_from_u16(2009), Some(DiagCode::E2009));
1165        assert_eq!(DiagCode::try_from_u16(3001), Some(DiagCode::E3001));
1166        assert_eq!(DiagCode::try_from_u16(4001), Some(DiagCode::E4001));
1167        assert_eq!(DiagCode::try_from_u16(5003), Some(DiagCode::E5003));
1168        // Warnings and notes.
1169        assert_eq!(DiagCode::try_from_u16(6001), Some(DiagCode::W6001));
1170        assert_eq!(DiagCode::try_from_u16(7001), Some(DiagCode::W7001));
1171        assert_eq!(DiagCode::try_from_u16(8001), Some(DiagCode::N8001));
1172    }
1173
1174    /// Unregistered, retired, and out-of-range numeric codes are
1175    /// rejected by the fallible lookup (cy-emb3).
1176    #[test]
1177    fn try_from_u16_unknown_codes() {
1178        // Gaps inside ranges (E0073..=E0077, E0083+).
1179        assert_eq!(DiagCode::try_from_u16(0), None);
1180        assert_eq!(DiagCode::try_from_u16(73), None);
1181        assert_eq!(DiagCode::try_from_u16(77), None);
1182        assert_eq!(DiagCode::try_from_u16(83), None);
1183        // Unassigned ranges.
1184        assert_eq!(DiagCode::try_from_u16(999), None);
1185        assert_eq!(DiagCode::try_from_u16(9999), None);
1186    }
1187
1188    /// `from_u16_or_e0001` falls back to the generic-syntax code for
1189    /// any unregistered numeric (cy-emb3).
1190    #[test]
1191    fn from_u16_or_e0001_unknown_falls_back() {
1192        assert_eq!(DiagCode::from_u16_or_e0001(3), DiagCode::E0003);
1193        assert_eq!(DiagCode::from_u16_or_e0001(9999), DiagCode::E0001);
1194        assert_eq!(DiagCode::from_u16_or_e0001(0), DiagCode::E0001);
1195    }
1196
1197    /// Round-trip every registered code through [`DiagCode::ALL`]: the
1198    /// fallible lookup must recover the same variant we started from
1199    /// (cy-emb3).
1200    #[test]
1201    fn try_from_u16_round_trips_every_registered_code() {
1202        for &c in DiagCode::ALL {
1203            assert_eq!(
1204                DiagCode::try_from_u16(c as u16),
1205                Some(c),
1206                "round-trip failed for {c}",
1207            );
1208        }
1209    }
1210
1211    /// `From<&SyntaxError>` is the embedder-facing lift: parse a
1212    /// deliberately-broken query and confirm we surface a registered,
1213    /// renderable code rather than a magic number (cy-emb3).
1214    ///
1215    /// Ignored under Miri because this is the only test in cyrs-diag that
1216    /// drives `cyrs_syntax::parse`, which constructs a rowan
1217    /// `SyntaxNode` tree — rowan 0.16's `ThinArc::drop` trips Stacked
1218    /// Borrows. The other rowan-touching crates handle this via
1219    /// `rowan_thinarc_ub: true` in the miri matrix, but cyrs-diag is
1220    /// otherwise rowan-free and remains strict (§17.12). See
1221    /// `.github/workflows/miri.yml` for context.
1222    #[test]
1223    #[cfg_attr(miri, ignore)]
1224    fn from_syntax_error_lifts_to_typed_code() {
1225        // Bare clause keyword with no body — the parser emits at least
1226        // one syntax error.  We don't assert the exact code (recovery
1227        // strategy is not part of this bead's contract); we assert the
1228        // lift produces a registered DiagCode whose string form starts
1229        // with 'E', i.e. the fallback never silently swallows the
1230        // numeric.
1231        let parse = cyrs_syntax::parse("MATCH");
1232        let err = parse
1233            .errors()
1234            .first()
1235            .expect("MATCH on its own should produce at least one syntax error");
1236        let code = DiagCode::from(err);
1237        assert!(
1238            DiagCode::ALL.contains(&code),
1239            "lifted code {code} not in registry",
1240        );
1241        // And the round-trip matches the raw u16 when registered.
1242        if let Some(direct) = DiagCode::try_from_u16(err.code) {
1243            assert_eq!(code, direct);
1244        } else {
1245            assert_eq!(code, DiagCode::E0001, "unknown numeric should fall back");
1246        }
1247    }
1248}