Skip to content

A prefixItems slot that declares both oneOf and anyOf is unsatisfiable and passes validation #3062

Description

@YoofiTT96

Bug Report

A choice block is a oneOf or an anyOf array. A prefixItems slot can hold one. An items catalog is one.

A block can declare both keywords. JSON Schema then requires an element to satisfy both. CALM alternatives pin distinct unique-id values, so the two sets are normally disjoint and no element can satisfy them. The pattern is unsatisfiable. Where the sets overlap, the wider keyword declares alternatives that nothing can select.

Nothing reports this. calm validate accepts the pattern, and calm generate returns an empty selection without a message.

#3060 adds a rule for an items catalog. This issue covers the same fault in a prefixItems slot, which #3060 leaves alone because a slot is an existing construct and the rule is a behaviour change.

Steps to Reproduce:

  1. Write a pattern with a slot that declares both keywords:
"nodes": {
  "type": "array",
  "prefixItems": [
    {
      "oneOf": [ { "properties": { "unique-id": { "const": "redis" } } } ],
      "anyOf": [ { "properties": { "unique-id": { "const": "memcached" } } } ]
    }
  ]
}
  1. Run calm validate.
  2. Run calm generate and choose memcached.

Expected Result:

calm validate reports an error. The block must declare one keyword.

Actual Result:

Step 2 reports nothing. Step 3 returns an empty selection:

askedFor: "memcached"   selected: []

The two shapes

JSON Schema combines the keywords with AND. An element must satisfy oneOf and anyOf. It is not a union of alternatives. Each CALM alternative pins a distinct unique-id, so an element matches at most one alternative in each keyword. That gives two shapes, both confirmed against Ajv.

Disjoint. The keywords name different alternatives.

oneOf anyOf Result
redis memcached No element validates. redis fails the anyOf, memcached fails the oneOf. The pattern is unsatisfiable.

Overlapping. The keywords share alternatives.

oneOf anyOf Result
A, B, C A, B, C, D, E Only A, B and C validate. The narrower keyword binds. D and E are declared and can never be selected.

Severity

Both shapes are errors.

The disjoint shape is plainly an error, because no architecture can ever match the pattern.

The overlapping shape does generate a valid architecture, so warn is arguable. It is still an error, because the author has declared alternatives that no answer can reach, and the pattern does not say what it appears to say. A rule that reports the shape needs no satisfiability test, which keeps it simple and keeps the message the same in both cases.

Note on the tooling

flattenOneOfAndAnyOf in options.ts resolves item.oneOf ?? item.anyOf, and patternTransformer.ts resolves hasOneOf ? item['oneOf'] : item['anyOf']. Making them read both keywords would not fix this. The two keywords are combined with AND, not OR, so their union is not a list of alternatives. Rejecting the shape is the correct response.

Environment:

shared (@finos/calm-shared). Reproduced on main at 0034591f. No pattern in this repository declares both keywords, so the rule regresses nothing here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions