From fc58592f06a1495c6c48ad552aba807fcdc4ff43 Mon Sep 17 00:00:00 2001 From: Roger Peppe Date: Fri, 17 Oct 2025 09:02:52 +0100 Subject: [PATCH] encoding/jsonschema: fix patternProperties in Extract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although the current code goes to some lengths to apply the `patternProperties` constraint only to fields that are not explicitly defined, this does not conform to the spec, which says [1]: > Validation succeeds if, for each instance name that matches any > regular expressions that appear as a property name in this keyword's > value, the child instance for that name successfully validates against > each schema that corresponds to a matching regular expression. That is, there is no interaction between this keyword and the `properties` keyword. I have verified that this is the case manually, and it's borne out by the fact that various external tests start passing. It's a nice bonus that this behavior actually aligns more closely with CUE itself. [1]: https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01#section-10.3.2.2 Signed-off-by: Roger Peppe Change-Id: I52a6fbb5299a1c66f9eb8e2707d5f5fdc267e62e Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1224485 Unity-Result: CUE porcuepine TryBot-Result: CUEcueckoo Reviewed-by: Daniel Martí --- encoding/jsonschema/constraints_object.go | 8 ++------ encoding/jsonschema/external_teststats.txt | 4 ++-- .../testdata/external/tests/draft2019-09/properties.json | 1 - .../testdata/external/tests/draft2020-12/properties.json | 1 - .../testdata/external/tests/draft4/properties.json | 1 - .../testdata/external/tests/draft6/properties.json | 1 - .../testdata/external/tests/draft7/properties.json | 1 - encoding/jsonschema/testdata/txtar/object.txtar | 9 ++++----- .../testdata/txtar/pattern_with_regexp_fields.txtar | 4 ++-- 9 files changed, 10 insertions(+), 20 deletions(-) diff --git a/encoding/jsonschema/constraints_object.go b/encoding/jsonschema/constraints_object.go index fe602c36b..764aeda0a 100644 --- a/encoding/jsonschema/constraints_object.go +++ b/encoding/jsonschema/constraints_object.go @@ -308,7 +308,6 @@ func constraintPatternProperties(key string, n cue.Value, s *state) { s.errf(n, `value of "patternProperties" must be an object, found %v`, n.Kind()) } obj := s.object(n) - existing := excludeFields(s.obj.Elts) s.processMap(n, func(key string, n cue.Value) { if !s.checkRegexp(n, key) { return @@ -321,12 +320,9 @@ func constraintPatternProperties(key string, n cue.Value, s *state) { &ast.UnaryExpr{Op: token.NMAT, X: ast.NewString(key)}) // We'll make a pattern constraint of the form: - // [pattern & !~(properties)]: schema + // [pattern]: schema f := embedStruct(ast.NewStruct(&ast.Field{ - Label: ast.NewList(ast.NewBinExpr( - token.AND, - append([]ast.Expr{&ast.UnaryExpr{Op: token.MAT, X: ast.NewString(key)}}, existing...)..., - )), + Label: ast.NewList(&ast.UnaryExpr{Op: token.MAT, X: ast.NewString(key)}), Value: s.schema(n), })) ast.SetRelPos(f, token.NewSection) diff --git a/encoding/jsonschema/external_teststats.txt b/encoding/jsonschema/external_teststats.txt index 7031ac75c..a4ecd11d2 100644 --- a/encoding/jsonschema/external_teststats.txt +++ b/encoding/jsonschema/external_teststats.txt @@ -4,8 +4,8 @@ Core tests: v3: schema extract (pass / total): 1072 / 1363 = 78.7% - tests (pass / total): 3908 / 4803 = 81.4% - tests on extracted schemas (pass / total): 3908 / 4041 = 96.7% + tests (pass / total): 3913 / 4803 = 81.5% + tests on extracted schemas (pass / total): 3913 / 4041 = 96.8% v3-roundtrip: schema extract (pass / total): 233 / 1363 = 17.1% diff --git a/encoding/jsonschema/testdata/external/tests/draft2019-09/properties.json b/encoding/jsonschema/testdata/external/tests/draft2019-09/properties.json index 96b17d8ca..569dbb3ca 100644 --- a/encoding/jsonschema/testdata/external/tests/draft2019-09/properties.json +++ b/encoding/jsonschema/testdata/external/tests/draft2019-09/properties.json @@ -138,7 +138,6 @@ }, "valid": false, "skip": { - "v3": "unexpected success", "v3-roundtrip": "could not extract schema" } }, diff --git a/encoding/jsonschema/testdata/external/tests/draft2020-12/properties.json b/encoding/jsonschema/testdata/external/tests/draft2020-12/properties.json index 74bbf2bba..11961262e 100644 --- a/encoding/jsonschema/testdata/external/tests/draft2020-12/properties.json +++ b/encoding/jsonschema/testdata/external/tests/draft2020-12/properties.json @@ -108,7 +108,6 @@ }, "valid": false, "skip": { - "v3": "unexpected success", "v3-roundtrip": "unexpected success" } }, diff --git a/encoding/jsonschema/testdata/external/tests/draft4/properties.json b/encoding/jsonschema/testdata/external/tests/draft4/properties.json index e2e0b301e..3d2c9d18f 100644 --- a/encoding/jsonschema/testdata/external/tests/draft4/properties.json +++ b/encoding/jsonschema/testdata/external/tests/draft4/properties.json @@ -136,7 +136,6 @@ }, "valid": false, "skip": { - "v3": "unexpected success", "v3-roundtrip": "could not extract schema" } }, diff --git a/encoding/jsonschema/testdata/external/tests/draft6/properties.json b/encoding/jsonschema/testdata/external/tests/draft6/properties.json index 42cd80a45..773db83fd 100644 --- a/encoding/jsonschema/testdata/external/tests/draft6/properties.json +++ b/encoding/jsonschema/testdata/external/tests/draft6/properties.json @@ -136,7 +136,6 @@ }, "valid": false, "skip": { - "v3": "unexpected success", "v3-roundtrip": "could not extract schema" } }, diff --git a/encoding/jsonschema/testdata/external/tests/draft7/properties.json b/encoding/jsonschema/testdata/external/tests/draft7/properties.json index 42cd80a45..773db83fd 100644 --- a/encoding/jsonschema/testdata/external/tests/draft7/properties.json +++ b/encoding/jsonschema/testdata/external/tests/draft7/properties.json @@ -136,7 +136,6 @@ }, "valid": false, "skip": { - "v3": "unexpected success", "v3-roundtrip": "could not extract schema" } }, diff --git a/encoding/jsonschema/testdata/txtar/object.txtar b/encoding/jsonschema/testdata/txtar/object.txtar index 6be91bbd2..cabac17b3 100644 --- a/encoding/jsonschema/testdata/txtar/object.txtar +++ b/encoding/jsonschema/testdata/txtar/object.txtar @@ -66,7 +66,6 @@ }, "additionalProperties": false } - -- out/decode/extract -- // Main schema @@ -86,9 +85,9 @@ close({ foo?: number bar?: number - {[=~"^\\P{Lu}" & !~"^(foo|bar)$"]: string} + {[=~"^\\P{Lu}"]: string} - {[=~"^\\P{Lo}" & !~"^(foo|bar)$"]: int} + {[=~"^\\P{Lo}"]: int} ... } patternsNoProps?: { @@ -101,9 +100,9 @@ close({ foo?: number bar?: number - {[=~"^\\P{Lu}" & !~"^(foo|bar)$"]: string} + {[=~"^\\P{Lu}"]: string} - {[=~"^\\P{Lo}" & !~"^(foo|bar)$"]: int} + {[=~"^\\P{Lo}"]: int} {[!~"^\\P{Lu}" & !~"^\\P{Lo}" & !~"^(foo|bar)$"]: string} } multi?: >=7 | struct.MaxFields(5) & { diff --git a/encoding/jsonschema/testdata/txtar/pattern_with_regexp_fields.txtar b/encoding/jsonschema/testdata/txtar/pattern_with_regexp_fields.txtar index 5a143c249..5f76b5f40 100644 --- a/encoding/jsonschema/testdata/txtar/pattern_with_regexp_fields.txtar +++ b/encoding/jsonschema/testdata/txtar/pattern_with_regexp_fields.txtar @@ -22,7 +22,7 @@ @jsonschema(schema="http://json-schema.org/draft-07/schema#") "c.e"?: bool -{[=~".*" & !~"^(c\\.e)$"]: string} +{[=~".*"]: string} ... -- test/err-field.json -- { @@ -31,5 +31,5 @@ -- out/decode/testerr/err-field -- cue: conflicting values 123 and string (mismatched types int and string): generated.cue:4:1 - generated.cue:4:28 + generated.cue:4:12 test/err-field.json:2:12 -- 2.51.2