@callcium/sdk 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,70 +1,58 @@
1
- import type { Hex, Issue, IssueCode } from "./types";
1
+ import { bigintToHex } from "./bytes";
2
2
 
3
- ///////////////////////////////////////////////////////////////////////////
4
- // Constants
5
- ///////////////////////////////////////////////////////////////////////////
3
+ import type { Hex, Issue, IssueCode, RuleLocation } from "./types";
6
4
 
7
- const ZERO: Hex = `0x${"0".repeat(64)}`;
5
+ /** The zero word. */
6
+ const ZERO = bigintToHex(0n);
8
7
 
9
8
  ///////////////////////////////////////////////////////////////////////////
10
9
  // Type mismatch
11
10
  ///////////////////////////////////////////////////////////////////////////
12
11
 
13
- /** Create an issue from operator compatibility results. */
14
- export function fromOpRule(
15
- code: IssueCode,
16
- message: string,
17
- groupIndex: number,
18
- constraintIndex: number,
19
- opCode: Hex,
20
- ): Issue {
12
+ /** Issue for a descriptor whose composite node at `offset` disagrees with its children. */
13
+ export function invalidDescriptor(offset: number): Issue {
21
14
  return {
22
15
  severity: "error",
23
16
  category: "typeMismatch",
24
- groupIndex,
25
- constraintIndex,
26
- code,
27
- value1: opCode,
17
+ code: "INVALID_DESCRIPTOR",
18
+ value1: bigintToHex(BigInt(offset)),
28
19
  value2: ZERO,
29
- message,
20
+ message: "Descriptor node metadata disagrees with its children",
30
21
  };
31
22
  }
32
23
 
33
- /** Issue for a calldata path that does not navigate the descriptor. */
34
- export function unnavigablePath(groupIndex: number, constraintIndex: number): Issue {
24
+ /** Issue for a path whose step at `stepIndex` breaks `bound`: a parameter, field or element count, or a type code. */
25
+ export function unnavigablePath(location: RuleLocation, stepIndex: number, bound: number): Issue {
35
26
  return {
27
+ ...location,
36
28
  severity: "error",
37
29
  category: "typeMismatch",
38
- groupIndex,
39
- constraintIndex,
40
30
  code: "UNNAVIGABLE_PATH",
41
- value1: ZERO,
42
- value2: ZERO,
31
+ value1: bigintToHex(BigInt(stepIndex)),
32
+ value2: bigintToHex(BigInt(bound)),
43
33
  message: "Path does not navigate the descriptor",
44
34
  };
45
35
  }
46
36
 
47
- /** Calldata path carrying more than one quantifier step. */
48
- export function nestedQuantifier(groupIndex: number, constraintIndex: number): Issue {
37
+ /** Issue for a loop whose array path ends at a node of `typeCode`, which is not an array. */
38
+ export function quantifierOnNonArray(location: RuleLocation, typeCode: number): Issue {
49
39
  return {
40
+ ...location,
50
41
  severity: "error",
51
42
  category: "typeMismatch",
52
- groupIndex,
53
- constraintIndex,
54
- code: "NESTED_QUANTIFIER",
55
- value1: ZERO,
43
+ code: "QUANTIFIER_ON_NON_ARRAY",
44
+ value1: bigintToHex(BigInt(typeCode)),
56
45
  value2: ZERO,
57
- message: "Path applies more than one quantifier",
46
+ message: "Loop path does not end at an array",
58
47
  };
59
48
  }
60
49
 
61
- /** Issue for a hint block that does not match its path compiled against the descriptor. */
62
- export function hintMismatch(groupIndex: number, constraintIndex: number): Issue {
50
+ /** Issue for a hint other than the compilation of its path. */
51
+ export function hintMismatch(location: RuleLocation): Issue {
63
52
  return {
53
+ ...location,
64
54
  severity: "error",
65
55
  category: "typeMismatch",
66
- groupIndex,
67
- constraintIndex,
68
56
  code: "HINT_MISMATCH",
69
57
  value1: ZERO,
70
58
  value2: ZERO,
@@ -72,31 +60,29 @@ export function hintMismatch(groupIndex: number, constraintIndex: number): Issue
72
60
  };
73
61
  }
74
62
 
75
- /** Issue for a context reference whose property cannot match the target type. */
76
- export function contextTypeMismatch(groupIndex: number, constraintIndex: number, contextId: Hex, typeCode: Hex): Issue {
63
+ /** Issue for an operator the compatibility matrix does not admit on its target type. */
64
+ export function fromOpRule(code: IssueCode, message: string, location: RuleLocation, opCode: number): Issue {
77
65
  return {
66
+ ...location,
78
67
  severity: "error",
79
68
  category: "typeMismatch",
80
- groupIndex,
81
- constraintIndex,
82
- code: "CONTEXT_TYPE_MISMATCH",
83
- value1: contextId,
84
- value2: typeCode,
85
- message: "Context property type does not match the target type",
69
+ code,
70
+ value1: bigintToHex(BigInt(opCode)),
71
+ value2: ZERO,
72
+ message,
86
73
  };
87
74
  }
88
75
 
89
- /** Issue for an operand that is not canonically encoded for the declared type. */
90
- export function nonCanonicalOperand(groupIndex: number, constraintIndex: number, operand: Hex, canonical: Hex): Issue {
76
+ /** Issue for a context operand whose property type does not pair with the target type. */
77
+ export function contextTypeMismatch(location: RuleLocation, contextPropertyId: number, typeCode: number): Issue {
91
78
  return {
79
+ ...location,
92
80
  severity: "error",
93
81
  category: "typeMismatch",
94
- groupIndex,
95
- constraintIndex,
96
- code: "NON_CANONICAL_OPERAND",
97
- value1: operand,
98
- value2: canonical,
99
- message: "Operand is not the canonical encoding for the declared type",
82
+ code: "CONTEXT_TYPE_MISMATCH",
83
+ value1: bigintToHex(BigInt(contextPropertyId)),
84
+ value2: bigintToHex(BigInt(typeCode)),
85
+ message: "Context property type does not match the target type",
100
86
  };
101
87
  }
102
88
 
@@ -104,221 +90,29 @@ export function nonCanonicalOperand(groupIndex: number, constraintIndex: number,
104
90
  // Contradiction
105
91
  ///////////////////////////////////////////////////////////////////////////
106
92
 
107
- /** eq(v)/neq(v) or lengthEq(v)/lengthNeq(v) on the same path. */
108
- export function eqNeqContradiction(isLength: boolean, groupIndex: number, constraintIndex: number, value: Hex): Issue {
109
- return {
110
- severity: "error",
111
- category: "contradiction",
112
- groupIndex,
113
- constraintIndex,
114
- code: isLength ? "LENGTH_EQ_NEQ_CONTRADICTION" : "EQ_NEQ_CONTRADICTION",
115
- value1: value,
116
- value2: ZERO,
117
- message: isLength ? "lengthEq(v) and lengthNeq(v) on same path" : "eq(v) and neq(v) on same path",
118
- };
119
- }
120
-
121
- /** Multiple eq()/lengthEq() operators with different values. */
122
- export function conflictingEquality(
123
- isLength: boolean,
124
- groupIndex: number,
125
- constraintIndex: number,
126
- existing: Hex,
127
- newValue: Hex,
128
- ): Issue {
129
- return {
130
- severity: "error",
131
- category: "contradiction",
132
- groupIndex,
133
- constraintIndex,
134
- code: isLength ? "CONFLICTING_LENGTH" : "CONFLICTING_EQUALITY",
135
- value1: existing,
136
- value2: newValue,
137
- message: isLength
138
- ? "Multiple lengthEq() operators with different values"
139
- : "Multiple eq() operators with different values",
140
- };
141
- }
142
-
143
- /** Value/length is outside the physical range of its type. */
144
- export function outOfPhysicalBounds(isLength: boolean, groupIndex: number, constraintIndex: number, value: Hex): Issue {
145
- return {
146
- severity: "error",
147
- category: "contradiction",
148
- groupIndex,
149
- constraintIndex,
150
- code: isLength ? "OUT_OF_PHYSICAL_LENGTH_BOUNDS" : "OUT_OF_PHYSICAL_BOUNDS",
151
- value1: value,
152
- value2: ZERO,
153
- message: isLength
154
- ? "Length value is outside the physical range"
155
- : "Value is outside the physical range of the type",
156
- };
157
- }
158
-
159
- /** gt()/lengthGt() on the maximum is impossible. */
160
- export function impossibleGt(isLength: boolean, groupIndex: number, constraintIndex: number, value: Hex): Issue {
161
- return {
162
- severity: "error",
163
- category: "contradiction",
164
- groupIndex,
165
- constraintIndex,
166
- code: isLength ? "IMPOSSIBLE_LENGTH_GT" : "IMPOSSIBLE_GT",
167
- value1: value,
168
- value2: ZERO,
169
- message: isLength ? "lengthGt() on maximum length is impossible" : "gt() on type maximum is impossible",
170
- };
171
- }
172
-
173
- /** lt()/lengthLt() on the minimum is impossible. */
174
- export function impossibleLt(isLength: boolean, groupIndex: number, constraintIndex: number, value: Hex): Issue {
175
- return {
176
- severity: "error",
177
- category: "contradiction",
178
- groupIndex,
179
- constraintIndex,
180
- code: isLength ? "IMPOSSIBLE_LENGTH_LT" : "IMPOSSIBLE_LT",
181
- value1: value,
182
- value2: ZERO,
183
- message: isLength ? "lengthLt() on minimum length is impossible" : "lt() on type minimum is impossible",
184
- };
185
- }
186
-
187
- /** eq()/lengthEq() value is excluded by a bound. */
188
- export function boundsExcludeEquality(
189
- isLength: boolean,
190
- groupIndex: number,
191
- constraintIndex: number,
192
- eq: Hex,
193
- bound: Hex,
194
- ): Issue {
195
- return {
196
- severity: "error",
197
- category: "contradiction",
198
- groupIndex,
199
- constraintIndex,
200
- code: isLength ? "BOUNDS_EXCLUDE_LENGTH" : "BOUNDS_EXCLUDE_EQUALITY",
201
- value1: eq,
202
- value2: bound,
203
- message: isLength ? "lengthEq() value is excluded by bound" : "eq() value is excluded by bound",
204
- };
205
- }
206
-
207
- /** Lower bound/length exceeds the upper bound. */
208
- export function impossibleRange(
209
- isLength: boolean,
210
- groupIndex: number,
211
- constraintIndex: number,
212
- lower: Hex,
213
- upper: Hex,
214
- ): Issue {
215
- return {
216
- severity: "error",
217
- category: "contradiction",
218
- groupIndex,
219
- constraintIndex,
220
- code: isLength ? "IMPOSSIBLE_LENGTH_RANGE" : "IMPOSSIBLE_RANGE",
221
- value1: lower,
222
- value2: upper,
223
- message: isLength ? "Lower length bound exceeds upper bound" : "Lower bound exceeds upper bound",
224
- };
225
- }
226
-
227
- /** Set operation excludes existing eq() value. */
228
- export function setExcludesEquality(groupIndex: number, constraintIndex: number, value: Hex): Issue {
93
+ /** Issue for a constant outside its type's values. */
94
+ export function outOfPhysicalBounds(location: RuleLocation, value: Hex): Issue {
229
95
  return {
96
+ ...location,
230
97
  severity: "error",
231
98
  category: "contradiction",
232
- groupIndex,
233
- constraintIndex,
234
- code: "SET_EXCLUDES_EQUALITY",
99
+ code: "OUT_OF_PHYSICAL_BOUNDS",
235
100
  value1: value,
236
101
  value2: ZERO,
237
- message: "Set operation excludes existing eq() value",
238
- };
239
- }
240
-
241
- /** Multiple isIn() sets have no intersection. */
242
- export function emptySetIntersection(groupIndex: number, constraintIndex: number): Issue {
243
- return {
244
- severity: "error",
245
- category: "contradiction",
246
- groupIndex,
247
- constraintIndex,
248
- code: "EMPTY_SET_INTERSECTION",
249
- value1: ZERO,
250
- value2: ZERO,
251
- message: "Multiple isIn() sets have no intersection",
252
- };
253
- }
254
-
255
- /** All values in isIn() set are excluded by neq/notIn. */
256
- export function setFullyExcluded(groupIndex: number, constraintIndex: number): Issue {
257
- return {
258
- severity: "error",
259
- category: "contradiction",
260
- groupIndex,
261
- constraintIndex,
262
- code: "SET_FULLY_EXCLUDED",
263
- value1: ZERO,
264
- value2: ZERO,
265
- message: "All values in isIn() set are excluded by neq/notIn",
266
- };
267
- }
268
-
269
- /** Bitmask operators conflict. */
270
- export function bitmaskContradiction(groupIndex: number, constraintIndex: number, mask: Hex, conflicting: Hex): Issue {
271
- return {
272
- severity: "error",
273
- category: "contradiction",
274
- groupIndex,
275
- constraintIndex,
276
- code: "BITMASK_CONTRADICTION",
277
- value1: mask,
278
- value2: conflicting,
279
- message: "Bitmask operators conflict",
280
- };
281
- }
282
-
283
- /** bitmaskAny is impossible because all bits are forbidden. */
284
- export function bitmaskAnyImpossible(groupIndex: number, constraintIndex: number, mask: Hex, mustBeZero: Hex): Issue {
285
- return {
286
- severity: "error",
287
- category: "contradiction",
288
- groupIndex,
289
- constraintIndex,
290
- code: "BITMASK_ANY_IMPOSSIBLE",
291
- value1: mask,
292
- value2: mustBeZero,
293
- message: "bitmaskAny is impossible because all bits are forbidden",
102
+ message: "Value is outside the physical range of the type",
294
103
  };
295
104
  }
296
105
 
297
- /** isIn/notIn set is not strictly sorted and deduplicated. */
298
- export function unsortedInSet(groupIndex: number, constraintIndex: number): Issue {
106
+ /** Issue for the rule at which the rules on its subject, in order, stop being satisfiable together. */
107
+ export function contradictoryRule(location: RuleLocation): Issue {
299
108
  return {
109
+ ...location,
300
110
  severity: "error",
301
111
  category: "contradiction",
302
- groupIndex,
303
- constraintIndex,
304
- code: "UNSORTED_IN_SET",
112
+ code: "CONTRADICTORY_RULE",
305
113
  value1: ZERO,
306
114
  value2: ZERO,
307
- message: "isIn/notIn set is not strictly sorted and deduplicated",
308
- };
309
- }
310
-
311
- /** Group contains zero constraints. */
312
- export function emptyGroup(groupIndex: number): Issue {
313
- return {
314
- severity: "error",
315
- category: "vacuity",
316
- groupIndex,
317
- constraintIndex: 0,
318
- code: "EMPTY_GROUP",
319
- value1: ZERO,
320
- value2: ZERO,
321
- message: "Group contains zero constraints",
115
+ message: "Rules on the subject are not satisfiable together",
322
116
  };
323
117
  }
324
118
 
@@ -326,133 +120,31 @@ export function emptyGroup(groupIndex: number): Issue {
326
120
  // Redundancy
327
121
  ///////////////////////////////////////////////////////////////////////////
328
122
 
329
- /** Numeric/length bound dominated by a stricter bound. */
330
- export function dominatedBound(isLength: boolean, groupIndex: number, constraintIndex: number, value: Hex): Issue {
123
+ /** Issue for a rule the other rules on its subject imply. */
124
+ export function redundantRule(location: RuleLocation): Issue {
331
125
  return {
126
+ ...location,
332
127
  severity: "warning",
333
128
  category: "redundancy",
334
- groupIndex,
335
- constraintIndex,
336
- code: isLength ? "DOMINATED_LENGTH_BOUND" : "DOMINATED_BOUND",
337
- value1: value,
129
+ code: "REDUNDANT_RULE",
130
+ value1: ZERO,
338
131
  value2: ZERO,
339
- message: isLength
340
- ? "Length bound is dominated by a stricter bound"
341
- : "Numeric bound is dominated by a stricter bound",
132
+ message: "Rule is implied by the other rules on its subject",
342
133
  };
343
134
  }
344
135
 
345
- /** Bound redundant because eq()/lengthEq() is set. */
346
- export function redundantBound(
347
- isLength: boolean,
348
- groupIndex: number,
349
- constraintIndex: number,
350
- bound: Hex,
351
- eq: Hex,
352
- ): Issue {
136
+ /** Issue for a bound looser than `tightest`, the tightest bound on its side of the subject. */
137
+ export function dominatedBound(isLength: boolean, location: RuleLocation, bound: Hex, tightest: Hex): Issue {
353
138
  return {
139
+ ...location,
354
140
  severity: "warning",
355
141
  category: "redundancy",
356
- groupIndex,
357
- constraintIndex,
358
- code: isLength ? "REDUNDANT_LENGTH_BOUND" : "REDUNDANT_BOUND",
142
+ code: isLength ? "DOMINATED_LENGTH_BOUND" : "DOMINATED_BOUND",
359
143
  value1: bound,
360
- value2: eq,
144
+ value2: tightest,
361
145
  message: isLength
362
- ? "Length bound is redundant because lengthEq() is set"
363
- : "Bound is redundant because eq() is set",
364
- };
365
- }
366
-
367
- /** notIn() value was present in isIn() set. */
368
- export function setReduction(groupIndex: number, constraintIndex: number, value: Hex): Issue {
369
- return {
370
- severity: "warning",
371
- category: "redundancy",
372
- groupIndex,
373
- constraintIndex,
374
- code: "SET_REDUCTION",
375
- value1: value,
376
- value2: ZERO,
377
- message: "notIn() value was present in isIn() set",
378
- };
379
- }
380
-
381
- /** isIn() sets partially overlap. */
382
- export function setRedundancy(groupIndex: number, constraintIndex: number, intersectionCount: Hex): Issue {
383
- return {
384
- severity: "warning",
385
- category: "redundancy",
386
- groupIndex,
387
- constraintIndex,
388
- code: "SET_REDUNDANCY",
389
- value1: intersectionCount,
390
- value2: ZERO,
391
- message: "isIn() sets partially overlap",
392
- };
393
- }
394
-
395
- /** Some values in isIn() set are excluded by neq/notIn. */
396
- export function setPartiallyExcluded(groupIndex: number, constraintIndex: number, excludedCount: Hex): Issue {
397
- return {
398
- severity: "warning",
399
- category: "redundancy",
400
- groupIndex,
401
- constraintIndex,
402
- code: "SET_PARTIALLY_EXCLUDED",
403
- value1: excludedCount,
404
- value2: ZERO,
405
- message: "Some values in isIn() set are excluded by neq/notIn",
406
- };
407
- }
408
-
409
- /** Bitmask operation is redundant. */
410
- export function redundantBitmask(groupIndex: number, constraintIndex: number, mask: Hex, existing: Hex): Issue {
411
- return {
412
- severity: "warning",
413
- category: "redundancy",
414
- groupIndex,
415
- constraintIndex,
416
- code: "REDUNDANT_BITMASK",
417
- value1: mask,
418
- value2: existing,
419
- message: "Bitmask operation is redundant",
420
- };
421
- }
422
-
423
- /** gte/lte pair fusible into a single range operator. */
424
- export function fusibleRange(
425
- isLength: boolean,
426
- groupIndex: number,
427
- constraintIndex: number,
428
- low: Hex,
429
- high: Hex,
430
- ): Issue {
431
- return {
432
- severity: "warning",
433
- category: "redundancy",
434
- groupIndex,
435
- constraintIndex,
436
- code: isLength ? "FUSIBLE_LENGTH_RANGE" : "FUSIBLE_RANGE",
437
- value1: low,
438
- value2: high,
439
- message: isLength
440
- ? "lengthGte() and lengthLte() fuse into a single lengthBetween()"
441
- : "gte() and lte() fuse into a single between()",
442
- };
443
- }
444
-
445
- /** Duplicate operator in constraint. */
446
- export function duplicateConstraint(groupIndex: number, constraintIndex: number): Issue {
447
- return {
448
- severity: "warning",
449
- category: "redundancy",
450
- groupIndex,
451
- constraintIndex,
452
- code: "DUPLICATE_CONSTRAINT",
453
- value1: ZERO,
454
- value2: ZERO,
455
- message: "Duplicate operator in constraint",
146
+ ? "Length bound is dominated by a stricter bound"
147
+ : "Numeric bound is dominated by a stricter bound",
456
148
  };
457
149
  }
458
150
 
@@ -460,55 +152,16 @@ export function duplicateConstraint(groupIndex: number, constraintIndex: number)
460
152
  // Vacuity
461
153
  ///////////////////////////////////////////////////////////////////////////
462
154
 
463
- /** gte(min)/lengthGte(0) is always true. */
464
- export function vacuousGte(isLength: boolean, groupIndex: number, constraintIndex: number, value: Hex): Issue {
155
+ /** Issue for a rule that holds for every value and every context. */
156
+ export function vacuousRule(location: RuleLocation): Issue {
465
157
  return {
158
+ ...location,
466
159
  severity: "info",
467
160
  category: "vacuity",
468
- groupIndex,
469
- constraintIndex,
470
- code: isLength ? "VACUOUS_LENGTH_GTE" : "VACUOUS_GTE",
471
- value1: value,
472
- value2: ZERO,
473
- message: isLength ? "lengthGte(0) is always true" : "gte() bound equals type minimum (always true)",
474
- };
475
- }
476
-
477
- /** lte(max)/lengthLte(max) is always true. */
478
- export function vacuousLte(isLength: boolean, groupIndex: number, constraintIndex: number, value: Hex): Issue {
479
- return {
480
- severity: "info",
481
- category: "vacuity",
482
- groupIndex,
483
- constraintIndex,
484
- code: isLength ? "VACUOUS_LENGTH_LTE" : "VACUOUS_LTE",
485
- value1: value,
161
+ code: "VACUOUS_RULE",
162
+ value1: ZERO,
486
163
  value2: ZERO,
487
- message: isLength
488
- ? "lengthLte() bound equals maximum (always true)"
489
- : "lte() bound equals type maximum (always true)",
490
- };
491
- }
492
-
493
- /** Negated range with inverted bounds excludes nothing (always true). */
494
- export function vacuousNegatedRange(
495
- isLength: boolean,
496
- groupIndex: number,
497
- constraintIndex: number,
498
- low: Hex,
499
- high: Hex,
500
- ): Issue {
501
- return {
502
- severity: "info",
503
- category: "vacuity",
504
- groupIndex,
505
- constraintIndex,
506
- code: isLength ? "VACUOUS_NEGATED_LENGTH_RANGE" : "VACUOUS_NEGATED_RANGE",
507
- value1: low,
508
- value2: high,
509
- message: isLength
510
- ? "Negated lengthBetween() with low > high excludes nothing (always true)"
511
- : "Negated between() with low > high excludes nothing (always true)",
164
+ message: "Rule holds for every value",
512
165
  };
513
166
  }
514
167
 
@@ -516,68 +169,28 @@ export function vacuousNegatedRange(
516
169
  // Compatibility
517
170
  ///////////////////////////////////////////////////////////////////////////
518
171
 
519
- /** Path deeper than the cap PWF-17 fixes. */
520
- export function pathDepthExceeded(groupIndex: number, constraintIndex: number, depth: Hex, maxDepth: Hex): Issue {
521
- return {
522
- severity: "warning",
523
- category: "compatibility",
524
- groupIndex,
525
- constraintIndex,
526
- code: "PATH_DEPTH_EXCEEDED",
527
- value1: depth,
528
- value2: maxDepth,
529
- message: "Path depth exceeds the maximum a well-formed policy may declare",
530
- };
531
- }
532
-
533
- /** Quantifier over a static array beyond the iteration cap PV-7 fixes. */
534
- export function quantifierOverStaticLimit(
535
- groupIndex: number,
536
- constraintIndex: number,
537
- arrayLength: Hex,
538
- maxLength: Hex,
539
- ): Issue {
172
+ /** Issue for a loop over a static array declared longer than `maxLength`. */
173
+ export function quantifierOverStaticLimit(location: RuleLocation, arrayLength: number, maxLength: number): Issue {
540
174
  return {
175
+ ...location,
541
176
  severity: "error",
542
177
  category: "compatibility",
543
- groupIndex,
544
- constraintIndex,
545
178
  code: "QUANTIFIER_OVER_STATIC_LIMIT",
546
- value1: arrayLength,
547
- value2: maxLength,
179
+ value1: bigintToHex(BigInt(arrayLength)),
180
+ value2: bigintToHex(BigInt(maxLength)),
548
181
  message: "Quantifier over static array exceeds the maximum quantified length",
549
182
  };
550
183
  }
551
184
 
552
- /** Context property ID outside the assigned set. */
553
- export function unknownContextProperty(
554
- groupIndex: number,
555
- constraintIndex: number,
556
- contextId: Hex,
557
- maxContextId: Hex,
558
- ): Issue {
185
+ /** Issue for a loop body that contains no test of the element. */
186
+ export function contextOnlyBody(location: RuleLocation): Issue {
559
187
  return {
560
- severity: "warning",
561
- category: "compatibility",
562
- groupIndex,
563
- constraintIndex,
564
- code: "UNKNOWN_CONTEXT_PROPERTY",
565
- value1: contextId,
566
- value2: maxContextId,
567
- message: "Unknown context property ID",
568
- };
569
- }
570
-
571
- /** Negated operator under the existential quantifier. */
572
- export function negationUnderAny(groupIndex: number, constraintIndex: number, opCode: Hex): Issue {
573
- return {
574
- severity: "warning",
188
+ ...location,
189
+ severity: "error",
575
190
  category: "compatibility",
576
- groupIndex,
577
- constraintIndex,
578
- code: "NEGATION_UNDER_ANY",
579
- value1: opCode,
191
+ code: "CONTEXT_ONLY_BODY",
192
+ value1: ZERO,
580
193
  value2: ZERO,
581
- message: "Negated operator under any() passes when a decoy element differs",
194
+ message: "Loop body holds no test of the element",
582
195
  };
583
196
  }