From 9f5eee1959c735ae474d1b9e8da27ef09806cb0d Mon Sep 17 00:00:00 2001 From: ATKasem Date: Fri, 17 Jul 2026 14:08:42 -0500 Subject: [PATCH] Select a matching deprecated alternative in anyOf/oneOf A deprecation notice is advisory, not a validation failure, but hasProblems() counted it like any other problem. So a deprecated branch of an anyOf/oneOf that actually matched the value was treated as a non-match, and a non-matching sibling was reported instead (e.g. a const branch's 'Value is not accepted' message). The const case happened to work only because enumValueMatch is a separate tiebreaker; a pattern branch had no such luck. Exclude deprecation problems from hasProblems() so match selection ignores them. The deprecation diagnostic is still reported. Fixes #304 --- src/parser/jsonParser.ts | 6 +++++- src/test/parser.test.ts | 31 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/parser/jsonParser.ts b/src/parser/jsonParser.ts index b038313e..9a155b6a 100644 --- a/src/parser/jsonParser.ts +++ b/src/parser/jsonParser.ts @@ -276,7 +276,11 @@ export class ValidationResult { } public hasProblems(): boolean { - return !!this.problems.length; + // A deprecation notice is advisory, not a validation failure. It must not make an + // otherwise-valid schema look like a non-match, otherwise a deprecated-but-matching + // `anyOf`/`oneOf` alternative loses to a non-matching one (see #304). The deprecation + // problem is still reported; it just doesn't count towards match selection here. + return this.problems.some(p => p.code !== ErrorCode.Deprecated); } public merge(validationResult: ValidationResult): void { diff --git a/src/test/parser.test.ts b/src/test/parser.test.ts index fb0ebfa2..25ae6d47 100644 --- a/src/test/parser.test.ts +++ b/src/test/parser.test.ts @@ -2633,6 +2633,37 @@ suite('JSON Parser', () => { }); + test('deprecated alternative in anyOf is selected when it matches (#304)', function () { + // The value matches the second (deprecated) branch. The deprecation warning must not make + // that branch look like a non-match, otherwise the non-matching `const` branch is reported + // instead of the expected "Value is deprecated". + const schema: JSONSchema = { + type: 'object', + properties: { + 'key': { + anyOf: [ + { type: 'string', const: 'unused literal' }, + { type: 'string', pattern: '^text value$', deprecated: true } + ] + } + } + }; + { + const { textDoc, jsonDoc } = toDocument('{ "key": "text value" }'); + const semanticErrors = validate2(jsonDoc, textDoc, schema)!; + assert.strictEqual(semanticErrors.length, 1); + assert.strictEqual(semanticErrors[0].code, ErrorCode.Deprecated); + } + + // A value that matches neither branch still reports the non-match, unaffected by the fix. + { + const { textDoc, jsonDoc } = toDocument('{ "key": "other value" }'); + const semanticErrors = validate2(jsonDoc, textDoc, schema)!; + assert.strictEqual(semanticErrors.length, 1); + assert.notStrictEqual(semanticErrors[0].code, ErrorCode.Deprecated); + } + }); + test('Strings with spaces', function () { const { textDoc, jsonDoc } = toDocument('{"key1":"first string", "key2":["second string"]}');