From 691aaa3db021ba5d029234b859a50ee7079af08e Mon Sep 17 00:00:00 2001 From: Kin Lane Date: Thu, 29 Oct 2026 08:26:54 -0400 Subject: [PATCH] Fix contradictions and move normative text into requirements - /req/rules/recommended applies to the ruleset being linted; inherited rules follow their extends mode, so it no longer contradicts mode-all, mode-off and shorthands. - /req/result/range says where a block mapping or sequence value starts (just after its key's colon), matching the worked example and OpenLint. - ECMA-402 links to ECMA-402 12.0, not the ECMA-262 site. - The minimatch glob dialect, the /regex/flags form of pattern, and ignoring extends elements after the mode are now in requirements (new /req/extends/extra-elements). - New tests: /conf/result/range-nested, /conf/message/placeholder-value. Co-Authored-By: Claude Opus 5.5 --- .../extends/mode-all-extra-element/test.json | 2 +- .../message/placeholder-value/document.yaml | 2 ++ .../message/placeholder-value/ruleset.yaml | 6 +++++ .../message/placeholder-value/test.json | 16 ++++++++++++ .../result/range-nested/document.yaml | 4 +++ .../result/range-nested/ruleset.yaml | 9 +++++++ v1/conformance/result/range-nested/test.json | 26 +++++++++++++++++++ v1/index.bs | 14 ++++++---- 8 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 v1/conformance/message/placeholder-value/document.yaml create mode 100644 v1/conformance/message/placeholder-value/ruleset.yaml create mode 100644 v1/conformance/message/placeholder-value/test.json create mode 100644 v1/conformance/result/range-nested/document.yaml create mode 100644 v1/conformance/result/range-nested/ruleset.yaml create mode 100644 v1/conformance/result/range-nested/test.json diff --git a/v1/conformance/extends/mode-all-extra-element/test.json b/v1/conformance/extends/mode-all-extra-element/test.json index 910888e..58f04d0 100644 --- a/v1/conformance/extends/mode-all-extra-element/test.json +++ b/v1/conformance/extends/mode-all-extra-element/test.json @@ -1,6 +1,6 @@ { "for": [ - "/req/extends/mode-all" + "/req/extends/extra-elements" ], "purpose": "With `[./base.yaml, all, extra]`, the mode `all` applies and the extra element is ignored.", "ruleset": "ruleset.yaml", diff --git a/v1/conformance/message/placeholder-value/document.yaml b/v1/conformance/message/placeholder-value/document.yaml new file mode 100644 index 0000000..8bd71e7 --- /dev/null +++ b/v1/conformance/message/placeholder-value/document.yaml @@ -0,0 +1,2 @@ +info: + title: Petstore diff --git a/v1/conformance/message/placeholder-value/ruleset.yaml b/v1/conformance/message/placeholder-value/ruleset.yaml new file mode 100644 index 0000000..3c88651 --- /dev/null +++ b/v1/conformance/message/placeholder-value/ruleset.yaml @@ -0,0 +1,6 @@ +rules: + r: + message: "{{value}}" + given: $.info.title + then: + function: falsy diff --git a/v1/conformance/message/placeholder-value/test.json b/v1/conformance/message/placeholder-value/test.json new file mode 100644 index 0000000..b89c2a5 --- /dev/null +++ b/v1/conformance/message/placeholder-value/test.json @@ -0,0 +1,16 @@ +{ + "for": [ + "/req/message/placeholders" + ], + "purpose": "`{{value}}` is the offending value; a string is substituted as it is.", + "ruleset": "ruleset.yaml", + "document": "document.yaml", + "expect": { + "results": [ + { + "code": "r", + "message": "Petstore" + } + ] + } +} diff --git a/v1/conformance/result/range-nested/document.yaml b/v1/conformance/result/range-nested/document.yaml new file mode 100644 index 0000000..ea8ce91 --- /dev/null +++ b/v1/conformance/result/range-nested/document.yaml @@ -0,0 +1,4 @@ +paths: + /pets: + get: + operationId: list_pets diff --git a/v1/conformance/result/range-nested/ruleset.yaml b/v1/conformance/result/range-nested/ruleset.yaml new file mode 100644 index 0000000..9c7df33 --- /dev/null +++ b/v1/conformance/result/range-nested/ruleset.yaml @@ -0,0 +1,9 @@ +rules: + nested: + given: $.paths./pets.get + then: + function: falsy + scalar: + given: $.paths./pets.get.operationId + then: + function: falsy diff --git a/v1/conformance/result/range-nested/test.json b/v1/conformance/result/range-nested/test.json new file mode 100644 index 0000000..deaae29 --- /dev/null +++ b/v1/conformance/result/range-nested/test.json @@ -0,0 +1,26 @@ +{ + "for": [ + "/req/result/range" + ], + "purpose": "A block mapping that is the value of a key starts just after the key's `:`; a scalar starts at its first character. Both end just after their last character.", + "ruleset": "ruleset.yaml", + "document": "document.yaml", + "expect": { + "results": [ + { + "code": "nested", + "range": { + "start": { "line": 2, "character": 8 }, + "end": { "line": 3, "character": 28 } + } + }, + { + "code": "scalar", + "range": { + "start": { "line": 3, "character": 19 }, + "end": { "line": 3, "character": 28 } + } + } + ] + } +} diff --git a/v1/index.bs b/v1/index.bs index 7e1a937..b78faca 100644 --- a/v1/index.bs +++ b/v1/index.bs @@ -85,7 +85,7 @@ Max ToC Depth: 3 }, "ecma-402": { "title": "ECMAScript 2025 Internationalization API Specification, 12th edition (ECMA-402)", - "href": "https://262.ecma-international.org/", + "href": "https://402.ecma-international.org/12.0/", "publisher": "Ecma International" }, "jsonpath-plus": { @@ -825,7 +825,7 @@ A [=linter=] MUST NOT let an [=extension property=] change which [=results=] it An [=override=] changes the ruleset for the documents its `files` globs match, so a linter applies overrides per document, after loading and before working out the rule state. A [=file-scoped override=] can add rulesets, rules, aliases, and formats; a [=pointer-scoped override=] only changes severities at the locations its pointers name.
-A [=linter=] MUST apply an [=override=] to a [=document=] only when the document's path, relative to the directory of the ruleset file and with `/` as the separator, matches one of the override's `files` globs. +A [=linter=] MUST apply an [=override=] to a [=document=] only when the document's path, relative to the directory of the ruleset file and with `/` as the separator, matches one of the override's `files` globs, as minimatch 3.1.5 [[!MINIMATCH]] matches them.
@@ -892,7 +892,7 @@ When neither a [=rule=] nor any definition it redefines has a `severity`, a [=li
-A [=linter=] MUST [=enable=] every [=rule=] a [=ruleset=] defines in its own `rules` whose `recommended` is `true` or absent, and MUST NOT enable one whose `recommended` is `false` unless a [=shorthand=] in an extending ruleset enables it. +A [=linter=] MUST [=enable=] every [=rule=] the [=ruleset=] being linted defines in its own `rules` whose `recommended` is `true` or absent, and MUST NOT enable one whose `recommended` is `false` unless a [=shorthand=] or an [=override=] enables it. Whether a rule inherited through `extends` is enabled depends on the [=mode=] it is inherited with (/req/extends/mode-recommended, /req/extends/mode-all, /req/extends/mode-off) and on the shorthands that adjust it.
+
+A [=linter=] MUST ignore the elements of an `extends` array element after its [=mode=]. +
+
A [=linter=] MUST apply definitions and adjustments of a [=rule=] in this order: the rulesets identified by `extends`, in array order, then the extending [=ruleset=]'s own `rules`.
@@ -1088,7 +1092,7 @@ The `severity` of a [=result=] MUST be the numeric form of the rule's [=effectiv
-The `range` of a [=result=] MUST locate the value the result is about in the source text of the [=document=]: `start` at its first character and `end` just after its last, each with a zero-based `line` and a zero-based `character` counted in UTF-16 code units. +The `range` of a [=result=] MUST locate the value the result is about in the source text of the [=document=]: `start` at its first character, or for a block mapping or block sequence that is the value of a key, just after the `:` that follows the key, and `end` just after its last character, each with a zero-based `line` and a zero-based `character` counted in UTF-16 code units.
@@ -1300,7 +1304,7 @@ It applies to strings. A regular expression is written bare, `^[a-z-]+$`, or between slashes with flags, `/^[a-z-]+$/i`, with the flags of ECMAScript.
-`pattern` MUST fail when the option `match` is given and the value does not match it, or when the option `notMatch` is given and the value matches it. Both options are regular expressions as defined by ECMAScript [[!ECMA-262-2025]]. +`pattern` MUST fail when the option `match` is given and the value does not match it, or when the option `notMatch` is given and the value matches it. Both options are regular expressions as defined by ECMAScript [[!ECMA-262-2025]], written bare or between slashes followed by ECMAScript flags.
Issue: `pattern` takes the regular expressions and flags of ECMAScript, so a linter written in another language has to reproduce ECMAScript's semantics. I-Regexp [[RFC9485]] is a dialect meant to behave the same in every language, but it has no lookaround or backreferences, so rulesets that use them would break. Which dialect a later version adopts is open.