---
generated: true
model: claude-opus-4-7
generated_at: 2026-05-20T12:30:00+00:00
source_commit: ef4af96c0996402dfe65ab0fdd347e4ae7e1a742
watched_paths_digest: a79bc9266564f8d663b4a60ade42dd146b58decdeb8ab1715ae4cd41e01a9a09
source_doc: docs/generated/design/cross-cutting/diagnostics.md
source_doc_digest: 35cfb9612e0af198f089e0c87a82055a4f43737c2865d74d83133722d9f18bda
warning: "Auto-generated. May drift from source. Do not edit by hand."
---

# Tests for cross-cutting/diagnostics

## Intent
Tests verify the diagnostic-system surface described in
[`docs/generated/design/cross-cutting/diagnostics.md`](../../../design/cross-cutting/diagnostics.md):
the severity names rendered to users, the integer ids attached to
every diagnostic, the parameter-interpolation rules for legacy and
rich diagnostics, the source-location decoding that produces
caret-aligned messages, and the user-facing suppression surface
(`#pragma warning(disable: <id>)`). Each test exercises one
documented claim of the diagnostic system, not the per-feature
condition that any particular diagnostic flags.

Most tests use `//DIAGNOSTIC_TEST:SIMPLE(diag=CHECK):` because the
diagnostic itself is the observable being verified. Suppression
tests use `//TEST:SIMPLE(filecheck=CHECK):-target hlsl` with
`CHECK-NOT:` patterns to assert the diagnostic is **absent**.

Diagnostics fire pre-codegen and are target-independent for every
claim in this bundle, so a single directive per file is sufficient;
no multi-backend matrix is needed.


## Functional coverage
| Claim | Intent | Anchor | Tests |
| --- | --- | --- | --- |
| A legacy diagnostic with `span` and `note` helpers renders a primary span on the offending site and a companion note on the previously-defined site. | negative | [#anatomy-of-a-legacy-diagnostic](../../../design/cross-cutting/diagnostics.md#anatomy-of-a-legacy-diagnostic) | [`note-multi-span-rendering.slang`](note-multi-span-rendering.slang) |
| A legacy warning with a note renders both the warning's primary message and the companion note pointing at the previous definition. | negative | [#anatomy-of-a-legacy-diagnostic](../../../design/cross-cutting/diagnostics.md#anatomy-of-a-legacy-diagnostic) | [`macro-redefinition-warning.slang`](macro-redefinition-warning.slang) |
| Boundary: an error diagnostic with a companion `note` helper renders the primary error code followed by a `note:` line on a separate location — the diagnostic chain. | boundary | [#anatomy-of-a-legacy-diagnostic](../../../design/cross-cutting/diagnostics.md#anatomy-of-a-legacy-diagnostic) | [`error-chain-with-companion-note.slang`](error-chain-with-companion-note.slang) |
| Legacy diagnostics interpolate ~param fields into the rendered text; the undeclared name appears quoted in the span message. | negative | [#anatomy-of-a-legacy-diagnostic](../../../design/cross-cutting/diagnostics.md#anatomy-of-a-legacy-diagnostic) | [`name-interpolation-in-message.slang`](name-interpolation-in-message.slang) |
| Type parameters interpolated into the message format appear as quoted type names in rendered output. | negative | [#anatomy-of-a-rich-diagnostic](../../../design/cross-cutting/diagnostics.md#anatomy-of-a-rich-diagnostic) | [`type-name-interpolation-in-message.slang`](type-name-interpolation-in-message.slang) |
| Boundary: the sink's severity filter upgrades a warning (41024) to error severity when -warnings-as-errors selects it by id. | boundary | [#diagnosticsink](../../../design/cross-cutting/diagnostics.md#diagnosticsink) | [`warnings-as-errors-promotes-warning.slang`](warnings-as-errors-promotes-warning.slang) |
| Stress: the sink emits every diagnostic from a sequence of unrelated undefined-identifier conditions; no documented cap drops later messages. | stress | [#diagnosticsink](../../../design/cross-cutting/diagnostics.md#diagnosticsink) | [`many-undefined-identifiers-stress.slang`](many-undefined-identifiers-stress.slang) |
| The sink emits diagnostics by default; without an override the comma-operator warning fires as expected. | negative | [#diagnosticsink](../../../design/cross-cutting/diagnostics.md#diagnosticsink) | [`diagnostic-fires-without-suppression.slang`](diagnostic-fires-without-suppression.slang) |
| Boundary: `#pragma warning(disable: N M)` with a whitespace-separated id list suppresses each listed id; the rendered output names none of them. | boundary | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`pragma-warning-disable-multiple-codes.slang`](pragma-warning-disable-multiple-codes.slang) |
| Boundary: a `#pragma warning(push)` with no matching pop emits E15612 with the "unmatched push" message. | boundary | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`pragma-warning-push-not-popped-warns.slang`](pragma-warning-push-not-popped-warns.slang) |
| Boundary: an unknown `#pragma warning` specifier renders E15613 with the actual offending specifier name interpolated. | boundary | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`pragma-warning-unknown-specifier-warns.slang`](pragma-warning-unknown-specifier-warns.slang) |
| Boundary: an unmatched `#pragma warning(pop)` produces the diagnostic E15611 with the documented "unmatched pop" text. | boundary | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`pragma-warning-pop-empty-warns.slang`](pragma-warning-pop-empty-warns.slang) |
| Distinct diagnostics carry distinct integer ids; function-redefinition renders as E30201. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-function-redefinition.slang`](error-code-function-redefinition.slang) |
| Every diagnostic has a unique integer id; for undefined-identifier the rendered code is E30015. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-undefined-identifier.slang`](error-code-undefined-identifier.slang) |
| Negative: a `#error` preprocessor directive emits E15900 with the documented "preprocessor error" code and the user message interpolated. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-user-defined-error.slang`](error-code-user-defined-error.slang) |
| Negative: a `#warning` preprocessor directive emits E15901 with the documented "preprocessor warning" code and the user message interpolated. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-user-defined-warning.slang`](error-code-user-defined-warning.slang) |
| Negative: a `break` outside any loop / switch construct renders E30003 with the documented "must appear inside loop or switch" message. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-break-outside-loop.slang`](error-code-break-outside-loop.slang) |
| Negative: a `continue` outside any loop construct renders E30004 with the documented "must appear inside loop" message. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-continue-outside-loop.slang`](error-code-continue-outside-loop.slang) |
| Negative: a bare `#endif` (or `#else`) without a matching `#if` renders E15001 with the directive name interpolated. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-directive-without-if.slang`](error-code-directive-without-if.slang) |
| Negative: a bare `return;` in a non-void function renders E30006 with the documented "return should have an expression" message. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-return-needs-expression.slang`](error-code-return-needs-expression.slang) |
| Negative: a conflicting variable redeclaration renders E30200 with the documented "declaration conflicts with existing declaration" message and emits a companion "see previous declaration" note. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-redeclaration-conflicts.slang`](error-code-redeclaration-conflicts.slang) |
| Negative: a file that `#include`s itself renders E15302 with the documented "cyclic include" message and the offending path interpolated. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-cyclic-include.slang`](error-code-cyclic-include.slang) |
| Negative: accessing a non-existent struct member renders E30027 with the documented "member not found" message and the member-name interpolated. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-no-member-of-type.slang`](error-code-no-member-of-type.slang) |
| Negative: an `#include` of a missing file renders E15300 with the documented "include file not found" message. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-include-failed.slang`](error-code-include-failed.slang) |
| Negative: an unknown preprocessor directive renders E15101 with the directive name interpolated into the message. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-unknown-preprocessor-directive.slang`](error-code-unknown-preprocessor-directive.slang) |
| Negative: an unterminated `#if` produces E15000 plus a "see 'if' directive" note pointing back at the opener. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-end-of-file-in-conditional.slang`](error-code-end-of-file-in-conditional.slang) |
| Negative: an unterminated struct body produces an E20001 unexpected-token error pointing at end-of-file. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-unexpected-eof.slang`](error-code-unexpected-eof.slang) |
| Negative: assigning a vector to a bool variable renders E30019 with the documented "type mismatch in expression" message and interpolated expected/actual types. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-type-mismatch.slang`](error-code-type-mismatch.slang) |
| Negative: assigning to a non-l-value expression renders E30011 with the documented "left of '=' is not an l-value" message. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-assign-non-lvalue.slang`](error-code-assign-non-lvalue.slang) |
| Negative: calling a non-callable value renders E30016 with the documented "no call operation found for type" message. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-call-operator-not-found.slang`](error-code-call-operator-not-found.slang) |
| Negative: declaring a local variable of type `void` renders E30009 with the documented "invalid type 'void'" message. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-invalid-type-void.slang`](error-code-invalid-type-void.slang) |
| Negative: declaring an array with a negative size renders E30025 with the documented "array size must be non-negative" message. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-invalid-array-size.slang`](error-code-invalid-array-size.slang) |
| Negative: divide-by-zero in a preprocessor `#if` expression renders E15202, distinct from the semantic-stage E30002. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-divide-by-zero-in-preprocessor.slang`](error-code-divide-by-zero-in-preprocessor.slang) |
| Negative: dividing by literal zero in a semantic-checked expression renders E30002 with the documented "divide by zero" message. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-divide-by-zero.slang`](error-code-divide-by-zero.slang) |
| Negative: subscripting a scalar value renders E30013 with the documented "no subscript declarations found for type" message. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-subscript-non-array.slang`](error-code-subscript-non-array.slang) |
| Negative: using a value identifier where a type is required renders E30060 with the documented "expected a type" message. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-expected-a-type.slang`](error-code-expected-a-type.slang) |
| Parser-stage errors carry integer ids in their own range (2xxxx) per the diagnostic-id convention. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`parser-error-has-code.slang`](parser-error-has-code.slang) |
| Preprocessor warnings carry integer ids in the same space as semantic warnings; undefined identifier in `#if` is 15205. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`preprocessor-warning-code.slang`](preprocessor-warning-code.slang) |
| Tools can suppress a specific diagnostic by integer id via overrideDiagnostic; the user-facing surface is `#pragma warning(disable: <id>)`. | functional | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`pragma-warning-disable-suppresses.slang`](pragma-warning-disable-suppresses.slang) |
| Warnings carry integer ids exactly like errors; comma-operator-used-in-expression renders as 41024. | negative | [#error-codes-and-the-name-field](../../../design/cross-cutting/diagnostics.md#error-codes-and-the-name-field) | [`error-code-warning-comma-operator.slang`](error-code-warning-comma-operator.slang) |
| A diagnostic emitted at Severity::Error renders with the user-facing name "error". | negative | [#severity-levels](../../../design/cross-cutting/diagnostics.md#severity-levels) | [`severity-name-error.slang`](severity-name-error.slang) |
| A diagnostic emitted at Severity::Note renders with the user-facing name "note". | negative | [#severity-levels](../../../design/cross-cutting/diagnostics.md#severity-levels) | [`severity-name-note.slang`](severity-name-note.slang) |
| A diagnostic emitted at Severity::Warning renders with the user-facing name "warning". | negative | [#severity-levels](../../../design/cross-cutting/diagnostics.md#severity-levels) | [`severity-name-warning.slang`](severity-name-warning.slang) |
| A diagnostic triggered inside a macro expansion renders both the use site and the underlying error message. | negative | [#source-locations-and-message-rendering](../../../design/cross-cutting/diagnostics.md#source-locations-and-message-rendering) | [`macro-expansion-stack-in-diagnostic.slang`](macro-expansion-stack-in-diagnostic.slang) |
| Boundary: a diagnostic whose offending token sits on a `#line 1`-remapped line (numeric edge of the line-number axis) still renders with `:1:` in the file:line:column triple. | boundary | [#source-locations-and-message-rendering](../../../design/cross-cutting/diagnostics.md#source-locations-and-message-rendering) | [`diagnostic-on-first-line.slang`](diagnostic-on-first-line.slang) |
| Boundary: a diagnostic whose offending token sits on the final non-newline line of the file still renders with a correctly-aligned caret and named file. | boundary | [#source-locations-and-message-rendering](../../../design/cross-cutting/diagnostics.md#source-locations-and-message-rendering) | [`diagnostic-on-final-source-line.slang`](diagnostic-on-final-source-line.slang) |
| Stress: a diagnostic fired inside deeply nested control-flow (>= 6 levels) still resolves to the correct source line and renders with full caret alignment. | stress | [#source-locations-and-message-rendering](../../../design/cross-cutting/diagnostics.md#source-locations-and-message-rendering) | [`deeply-nested-error-still-rendered.slang`](deeply-nested-error-still-rendered.slang) |
| The sink decodes SourceLoc via SourceManager and renders the location with the test file's path so consumers can navigate to the offending site. | negative | [#source-locations-and-message-rendering](../../../design/cross-cutting/diagnostics.md#source-locations-and-message-rendering) | [`source-location-file-line-format.slang`](source-location-file-line-format.slang) |
| The sink uses SourceManager to decode SourceLoc into file:line:column and renders a caret aligned to the offending token's column. | negative | [#source-locations-and-message-rendering](../../../design/cross-cutting/diagnostics.md#source-locations-and-message-rendering) | [`caret-points-at-offending-token.slang`](caret-points-at-offending-token.slang) |
| The sink uses SourceManager to decode source locations and renders carets that span the full offending token, not just the start. | negative | [#source-locations-and-message-rendering](../../../design/cross-cutting/diagnostics.md#source-locations-and-message-rendering) | [`source-span-multi-character.slang`](source-span-multi-character.slang) |


## Untested claims
NA


## Doc gaps observed
| Anchor | Kind | Gap | Suggested addition |
| --- | --- | --- | --- |
| [#how-to-trigger](../../../design/cross-cutting/diagnostics.md#how-to-trigger) | undocumented-behavior | The doc enumerates `Severity::Fatal` and `Severity::Internal` and states their rendered names (`fatal error`, `internal error`) but does not describe any user-reachable program that reliably triggers either severity. Both are typically driven by environment conditions (missing include file for fatal, ICE for internal) that are non-trivial to script as a regression. Suggest the doc either link to a representative example or add a short "how to trigger" note next to each severity. |  |
| [#pragma](../../../design/cross-cutting/diagnostics.md#pragma) | undocumented-behavior | The doc mentions `overrideDiagnostic` and the `flag` group as suppression surfaces but does not say what the user-facing spelling of either is (e.g. `#pragma warning(disable: <name>)` vs `<flag>`). The integer-id form is implicitly common, but the string-name and flag-group forms are under-documented. Suggest the doc add one explicit example of each suppression form. |  |
| [#source-locations-and-message-rendering](../../../design/cross-cutting/diagnostics.md#source-locations-and-message-rendering) | undocumented-behavior | The JSON-emitting writer described under `#source-locations-and-message-rendering` is mentioned but has no user-facing claim that is observable via `slangc` defaults. To test it we would need a doc claim about the command-line knob that selects the JSON writer; that is not currently in the doc. |  |
| [#name](../../../design/cross-cutting/diagnostics.md#name) | undocumented-behavior | The `name` field of a diagnostic is referenced as a unique identifier but the doc does not state whether the name appears in any rendered output. Empirically, slangc renders `error[E30201]` but not the name; we cannot test the name's externally observable consequence until the doc states one. |  |
| [#the-pragma-applies-to-the-rest-of-the-translation-unit](../../../design/cross-cutting/diagnostics.md#the-pragma-applies-to-the-rest-of-the-translation-unit) | undocumented-behavior | The doc describes per-source warning-state tracking (`SourceWarningStateTrackerBase`) for pragma overrides but does not state a claim about scope (e.g. "the pragma applies to the rest of the translation unit" vs "the pragma applies to the current `__include` only"). This is a real user-visible behavior worth a documented claim, but absent one we do not test it. |  |
| [#can-suppress-downgrade-or-upgrade-diagnostics-by-id](../../../design/cross-cutting/diagnostics.md#can-suppress-downgrade-or-upgrade-diagnostics-by-id) | undocumented-behavior | The doc mentions that the Severity filter "can suppress, downgrade, or upgrade diagnostics by id" but does not name the user-facing surfaces for `upgrade` and `downgrade` (the `-warnings-as-errors <id>` and `-warnings-disable <id>` command line flags). The boundary test `warnings-as-errors-promotes-warning.slang` exercises the `-warnings-as-errors` surface; the doc could call out both command-line flags explicitly. |  |
| [#pragma](../../../design/cross-cutting/diagnostics.md#pragma) | undocumented-behavior | The doc describes the `#pragma warning` overrides but does not enumerate the legal specifier set (`push`, `pop`, `disable`, `error`, `default`, `suppress`). The `pragma-warning-unknown- specifier-warns.slang` boundary test pins one rejected specifier; the doc could list the accepted set explicitly to pair with the rejection diagnostic. |  |
| [#rendered-with-their-expansion-stack](../../../design/cross-cutting/diagnostics.md#rendered-with-their-expansion-stack) | undocumented-behavior | The doc claims macro-expanded locations are "rendered with their expansion stack" but does not describe what an end-of-file-during-conditional diagnostic chain looks like (primary + companion `see 'if' directive` note). The boundary test `error-code-end-of-file-in-conditional.slang` exercises this chain; the doc could add a small example of the multi- record rendering. |  |
