From 6f706a2747c621c3a880137cbef3290aeeaa68a5 Mon Sep 17 00:00:00 2001 From: konojunya Date: Thu, 3 Sep 2026 20:46:56 +0900 Subject: [PATCH] Define actionable diagnostic expectations --- INTERCHANGE.md | 10 +++++++--- SPECIFICATION.md | 19 +++++++++++++++---- conformance/README.md | 2 +- .../expected.diagnostics.json | 3 ++- .../expected.diagnostics.json | 3 ++- .../expected.diagnostics.json | 13 +++++++------ .../unknown-edge-endpoint/source.stack | 3 ++- .../expected.diagnostics.json | 14 ++++++++++++++ .../unknown-layout-direction/source.stack | 5 +++++ ...ze-compiler-interchange-and-conformance.md | 2 +- schemas/diagnostic-expectations.schema.json | 8 ++++++++ schemas/diagnostic.schema.json | 10 +++++++++- 12 files changed, 73 insertions(+), 19 deletions(-) create mode 100644 conformance/invalid/unknown-layout-direction/expected.diagnostics.json create mode 100644 conformance/invalid/unknown-layout-direction/source.stack diff --git a/INTERCHANGE.md b/INTERCHANGE.md index e7de5d2..274954b 100644 --- a/INTERCHANGE.md +++ b/INTERCHANGE.md @@ -132,7 +132,11 @@ A range contains an inclusive `start` position and an exclusive `end` position. `code` and its normative meaning are assigned by the language specification. `severity` is `error` or `warning`. `message` is concise human-readable text but its exact wording is not a compatibility guarantee. -`help` is either corrective guidance or `null`. `related` is always an array and identifies other source ranges involved in the diagnostic. Related-information message wording is not a compatibility guarantee. +`expected` is always an array of unique, non-empty strings. It contains source values or grammatical constructs that are valid at the primary range and is empty when no useful candidate exists. It provides correction context, not a replacement edit or a completion contract. + +Closed sets contain every valid value in specification order. Syntax diagnostics use exact source spellings for literal tokens and angle-bracket names such as `` or `` for token classes. Identifier suggestions contain at most three visible identifiers. Suggestions are eligible when their Unicode-scalar Levenshtein distance is no greater than one third of the longer identifier length, rounded down, with a minimum threshold of one. They are ordered by ascending distance and then by bytewise identifier order. + +`help` is either corrective guidance or `null`. `related` is always an array and identifies other source ranges involved in the diagnostic. When an identifier suggestion names an existing declaration, related information SHOULD identify that declaration. Related-information message wording is not a compatibility guarantee. Implementations may emit non-`STK` diagnostics. Canonical fixtures only require portable `STK` diagnostics unless a case explicitly documents an implementation extension. @@ -165,9 +169,9 @@ The source MUST NOT produce normalized IR. Its portable diagnostics MUST match t ### 5.3 Diagnostic Expectations -Expectation documents conform to [`diagnostic-expectations.schema.json`](./schemas/diagnostic-expectations.schema.json). Each expected diagnostic requires code, severity, and range. +Expectation documents conform to [`diagnostic-expectations.schema.json`](./schemas/diagnostic-expectations.schema.json). Each expected diagnostic requires code, severity, and range. A fixture may also include `expected` when its exact values and ordering are part of the case. -Runners compare diagnostics in the deterministic order emitted by the compiler. They MUST compare the number of diagnostics and MUST NOT ignore additional portable diagnostics. They do not compare message, help, or related information. +Runners compare diagnostics in the deterministic order emitted by the compiler. They MUST compare the number of diagnostics and MUST NOT ignore additional portable diagnostics. When a fixture includes `expected`, runners compare that array exactly. They do not compare message, help, or related information. ### 5.4 Runner Behavior diff --git a/SPECIFICATION.md b/SPECIFICATION.md index 0b9d97e..a780518 100644 --- a/SPECIFICATION.md +++ b/SPECIFICATION.md @@ -479,7 +479,8 @@ Every diagnostic MUST include: - a stable code; - severity: `error` or `warning`; - a concise message; -- a one-based source range with start and end line and column. +- a one-based source range with start and end line and column; +- an ordered list of expected source values or constructs, which is empty when no useful candidate exists. A diagnostic SHOULD also include a corrective hint and related source ranges when another declaration caused the problem. @@ -535,10 +536,20 @@ Example diagnostic shape: "severity": "error", "message": "Unknown node 'paymnt'.", "range": { - "start": { "line": 12, "column": 8 }, - "end": { "line": 12, "column": 14 } + "start": { "byteOffset": 184, "line": 12, "column": 8 }, + "end": { "byteOffset": 190, "line": 12, "column": 14 } }, - "help": "Did you mean 'payment'?" + "expected": ["payment"], + "help": "Use the declared node 'payment'.", + "related": [ + { + "message": "The suggested node is declared here.", + "range": { + "start": { "byteOffset": 92, "line": 7, "column": 8 }, + "end": { "byteOffset": 99, "line": 7, "column": 15 } + } + } + ] } ``` diff --git a/conformance/README.md b/conformance/README.md index 401196b..09b9a72 100644 --- a/conformance/README.md +++ b/conformance/README.md @@ -36,7 +36,7 @@ Formatter inputs are compiler-valid Stack documents. A formatter runner compares - A valid case must produce the expected normalized IR. - An absent valid-case diagnostic file means no portable diagnostics are expected. - An invalid case must not produce normalized IR. -- Diagnostic expectations compare code, severity, and range in emitted order. +- Diagnostic expectations compare code, severity, and range in emitted order. A fixture may also require exact `expected` values and ordering. - Diagnostic message, help, and related-information wording are not compared. The complete normative behavior is defined in the [Stack Compiler Interchange Specification](../INTERCHANGE.md). diff --git a/conformance/invalid/duplicate-theme-dense-node/expected.diagnostics.json b/conformance/invalid/duplicate-theme-dense-node/expected.diagnostics.json index 1a3381f..22ec655 100644 --- a/conformance/invalid/duplicate-theme-dense-node/expected.diagnostics.json +++ b/conformance/invalid/duplicate-theme-dense-node/expected.diagnostics.json @@ -7,7 +7,8 @@ "range": { "start": { "byteOffset": 44, "line": 4, "column": 3 }, "end": { "byteOffset": 54, "line": 4, "column": 13 } - } + }, + "expected": [] }, { "code": "STK4002", diff --git a/conformance/invalid/unexpected-token/expected.diagnostics.json b/conformance/invalid/unexpected-token/expected.diagnostics.json index bcd0bec..4d7fbb3 100644 --- a/conformance/invalid/unexpected-token/expected.diagnostics.json +++ b/conformance/invalid/unexpected-token/expected.diagnostics.json @@ -7,7 +7,8 @@ "range": { "start": { "byteOffset": 35, "line": 3, "column": 3 }, "end": { "byteOffset": 41, "line": 3, "column": 9 } - } + }, + "expected": ["node", "group", "edge", "theme", "layout", "}"] } ] } diff --git a/conformance/invalid/unknown-edge-endpoint/expected.diagnostics.json b/conformance/invalid/unknown-edge-endpoint/expected.diagnostics.json index 28a0cb7..764732c 100644 --- a/conformance/invalid/unknown-edge-endpoint/expected.diagnostics.json +++ b/conformance/invalid/unknown-edge-endpoint/expected.diagnostics.json @@ -6,16 +6,17 @@ "severity": "error", "range": { "start": { - "byteOffset": 71, - "line": 5, + "byteOffset": 96, + "line": 6, "column": 15 }, "end": { - "byteOffset": 78, - "line": 5, - "column": 22 + "byteOffset": 102, + "line": 6, + "column": 21 } - } + }, + "expected": ["payment"] } ] } diff --git a/conformance/invalid/unknown-edge-endpoint/source.stack b/conformance/invalid/unknown-edge-endpoint/source.stack index f70d86f..12dec21 100644 --- a/conformance/invalid/unknown-edge-endpoint/source.stack +++ b/conformance/invalid/unknown-edge-endpoint/source.stack @@ -2,5 +2,6 @@ stack 1.0 diagram "Unknown endpoint" { node api "API" - edge api -> missing + node payment "Payment" + edge api -> paymnt } diff --git a/conformance/invalid/unknown-layout-direction/expected.diagnostics.json b/conformance/invalid/unknown-layout-direction/expected.diagnostics.json new file mode 100644 index 0000000..7dca6ba --- /dev/null +++ b/conformance/invalid/unknown-layout-direction/expected.diagnostics.json @@ -0,0 +1,14 @@ +{ + "schemaVersion": "1.0", + "diagnostics": [ + { + "code": "STK2002", + "severity": "error", + "range": { + "start": { "byteOffset": 68, "line": 4, "column": 22 }, + "end": { "byteOffset": 71, "line": 4, "column": 25 } + }, + "expected": ["right", "down"] + } + ] +} diff --git a/conformance/invalid/unknown-layout-direction/source.stack b/conformance/invalid/unknown-layout-direction/source.stack new file mode 100644 index 0000000..b7bcd4a --- /dev/null +++ b/conformance/invalid/unknown-layout-direction/source.stack @@ -0,0 +1,5 @@ +stack 1.0 +diagram "Example" { + node app "App" + layout { direction hoo } +} diff --git a/docs/decisions/0003-standardize-compiler-interchange-and-conformance.md b/docs/decisions/0003-standardize-compiler-interchange-and-conformance.md index 45ea1e4..c66135c 100644 --- a/docs/decisions/0003-standardize-compiler-interchange-and-conformance.md +++ b/docs/decisions/0003-standardize-compiler-interchange-and-conformance.md @@ -27,7 +27,7 @@ JSON Schema Draft 2020-12 files in `schemas/` define their portable shapes. The Normalized IR includes specification-defined defaults, explicit containment references, deterministic declaration order, and layout constraints or hints. It excludes source spans, comments, theme or icon resolution results, layout coordinates, renderer state, and filesystem or network handles. -Diagnostic interchange includes stable code, severity, message, an end-exclusive source range, optional help, and related information. Portable conformance expectations compare code, severity, and range. They do not compare message, help, or related-information wording unless a future fixture explicitly opts into an additional assertion. +Diagnostic interchange includes stable code, severity, message, an end-exclusive source range, an ordered expected-value list, optional help, and related information. Portable conformance expectations compare code, severity, and range. A fixture may additionally require exact expected values and ordering without making message, help, or related-information wording part of compatibility. The specification repository owns conformance sources and expected documents. Each implementation owns its runner and records which specification revision or release it supports. Valid cases require normalized IR and may also expect warnings. Invalid cases require diagnostics and must not produce normalized IR. diff --git a/schemas/diagnostic-expectations.schema.json b/schemas/diagnostic-expectations.schema.json index 71c936e..893419a 100644 --- a/schemas/diagnostic-expectations.schema.json +++ b/schemas/diagnostic-expectations.schema.json @@ -62,6 +62,14 @@ }, "range": { "$ref": "#/$defs/range" + }, + "expected": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1 + } } } } diff --git a/schemas/diagnostic.schema.json b/schemas/diagnostic.schema.json index ad5ad83..bcdcfc2 100644 --- a/schemas/diagnostic.schema.json +++ b/schemas/diagnostic.schema.json @@ -52,7 +52,7 @@ "diagnostic": { "type": "object", "additionalProperties": false, - "required": ["code", "severity", "message", "range", "help", "related"], + "required": ["code", "severity", "message", "range", "expected", "help", "related"], "properties": { "code": { "type": "string", @@ -68,6 +68,14 @@ "range": { "$ref": "#/$defs/range" }, + "expected": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1 + } + }, "help": { "type": ["string", "null"], "minLength": 1