---
generated: true
model: claude-fable-5
generated_at: 2026-07-08T15:27:31Z
source_commit: 6b490bc8cb3cf11b356ddf70168c5609efa46341
watched_paths_digest: 60e51623e794f61d16d542039b22969034d84551bebe0572c397b5ad2817e830
source_doc: docs/generated/design/pipeline/01-lex-preprocess.md
source_doc_digest: 563f90496a27dd39b1a9b8d65a3047faa7586bf0967c03e7df031620deaef6bf
warning: "Auto-generated. May drift from source. Do not edit by hand."
---

# Tests for pipeline/01-lex-preprocess

## Intent

Tests verify the lex-and-preprocess pipeline behaviors described in
[`docs/generated/design/pipeline/01-lex-preprocess.md`](../../../../design/pipeline/01-lex-preprocess.md):
the lexer's special-case handling (backslash continuation), literal
scanning and value extraction (escape decoding, scan-time vs
decode-time diagnostics, float overflow/underflow), macro expansion
(object-like, function-like, parameter-op replay, `#`, `##`,
`__LINE__`, `__FILE__`), conditional compilation
(`#if`/`#ifdef`/`#elif`/`#else`/`#endif`/`#undef`), `#include`
resolution (quoted and angle forms), `#pragma warning` state
tracking, and the failure modes called out by the `## Failure
modes` section.

Coverage strategy: at least one functional test per substantive
positive claim, boundary probes along the numeric/escape axes the
doc names, and at least one diagnostic test per "is rejected"-style
claim with the diagnostic code pinned. Pure lex-surface claims that
`syntax-reference/tokens` already exercises (numeric suffixes,
comment shapes, raw strings) are intentionally NOT duplicated
here.

## Functional coverage

| Claim                                                                                                                                                                                      | Intent     | Anchor                                                                                                                        | Tests                                                                                                            |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| A `#if` directive with no matching `#endif` is reported as a preprocessor diagnostic.                                                                                                      | negative   | [#failure-modes](../../../../design/pipeline/01-lex-preprocess.md#failure-modes)                                               | [`unbalanced-if-diagnostic.slang`](unbalanced-if-diagnostic.slang)                                               |
| A `#include` whose target cannot be resolved emits a preprocessor diagnostic through the DiagnosticSink.                                                                                   | negative   | [#failure-modes](../../../../design/pipeline/01-lex-preprocess.md#failure-modes)                                               | [`missing-include-diagnostic.slang`](missing-include-diagnostic.slang)                                           |
| An unknown preprocessor directive emits a diagnostic through the DiagnosticSink.                                                                                                           | negative   | [#failure-modes](../../../../design/pipeline/01-lex-preprocess.md#failure-modes)                                               | [`unknown-directive-diagnostic.slang`](unknown-directive-diagnostic.slang)                                       |
| Negative probe: a `#include` of the file itself triggers the documented "cyclic include" preprocessor diagnostic.                                                                          | negative   | [#failure-modes](../../../../design/pipeline/01-lex-preprocess.md#failure-modes)                                               | [`include-self-cycle-diagnostic.slang`](include-self-cycle-diagnostic.slang)                                     |
| An angle-form `#include` builds its path by concatenating the raw tokens between `<` and `>` and resolves it in system-include mode.                                                       | functional | [#include-resolution](../../../../design/pipeline/01-lex-preprocess.md#include-resolution)                                     | [`include-angle-bracket-mode.slang`](include-angle-bracket-mode.slang)                                           |
| Stress probe of nested `#include`: chain depth 5 (this file -> d1 -> d2 -> d3 -> d4 -> d5) leaves the leaf declaration visible at the root.                                                | stress     | [#include-resolution](../../../../design/pipeline/01-lex-preprocess.md#include-resolution)                                     | [`include-nested-5deep.slang`](include-nested-5deep.slang)                                                       |
| `#include` resolves the named string against `IncludeSystem` and pushes a fresh input stream so the file's tokens become part of the enclosing translation unit.                           | functional | [#include-resolution](../../../../design/pipeline/01-lex-preprocess.md#include-resolution)                                     | [`include-pushes-fresh-stream.slang`](include-pushes-fresh-stream.slang)                                         |
| Boundary probe of backslash line continuation: a `\<newline>` inside a string literal is folded out, so the two physical lines become one logical literal.                                 | boundary   | [#lexer-flags-and-special-case-rules](../../../../design/pipeline/01-lex-preprocess.md#lexer-flags-and-special-case-rules)     | [`backslash-line-continuation-in-string.slang`](backslash-line-continuation-in-string.slang)                     |
| The lexer handles backslash line continuations; a `\` immediately before a newline is consumed so the macro definition spans multiple physical lines as if it were one logical line.       | functional | [#lexer-flags-and-special-case-rules](../../../../design/pipeline/01-lex-preprocess.md#lexer-flags-and-special-case-rules)     | [`backslash-line-continuation-in-macro.slang`](backslash-line-continuation-in-macro.slang)                       |
| A `\uNNNN` escape (exactly 4 hex digits) in a character literal decodes to the named code point; `'\u0041'` yields 65.                                                         | functional | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`char-literal-unicode-4digit-value.slang`](char-literal-unicode-4digit-value.slang)                             |
| A `\UNNNNNNNN` escape (exactly 8 hex digits) in a character literal decodes to the named code point; `'\U00000041'` yields 65.                                                             | functional | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`char-literal-unicode-8digit-value.slang`](char-literal-unicode-8digit-value.slang)                             |
| A `\xNN` escape in a character literal decodes to the code point it names; `'\x41'` yields 65.                                                                                             | functional | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`char-literal-hex-escape-value.slang`](char-literal-hex-escape-value.slang)                                     |
| An octal `\NNN` escape in a character literal decodes to the code point it names; `'\101'` yields 65.                                                                                      | functional | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`char-literal-octal-escape-value.slang`](char-literal-octal-escape-value.slang)                                 |
| Bare non-ASCII source bytes in a character literal are decoded as UTF-8 into a single code point; `'é'` yields 233.                                                                        | functional | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`char-literal-utf8-multibyte-value.slang`](char-literal-utf8-multibyte-value.slang)                             |
| Boundary probe of floating-point value extraction: a literal whose magnitude overflows the type parses as INFINITY.                                                                        | boundary   | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`float-literal-overflow-is-infinity.slang`](float-literal-overflow-is-infinity.slang)                           |
| Boundary probe of floating-point value extraction: a literal whose magnitude underflows the type parses as 0.                                                                              | boundary   | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`float-literal-underflow-is-zero.slang`](float-literal-underflow-is-zero.slang)                                 |
| Boundary probe of the braced `\u{...}` escape: a code point above the BMP (`'\u{1F600}'`) decodes to 128512 as a 32-bit value.                                                             | boundary   | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`char-literal-braced-unicode-value.slang`](char-literal-braced-unicode-value.slang)                             |
| Boundary probe: `\x` consumes any number of hex digits, and in a string literal the decoded code unit must fit one byte, else it is rejected at decode time (E10009).                      | boundary   | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`string-hex-escape-out-of-range-code-unit-diagnostic.slang`](string-hex-escape-out-of-range-code-unit-diagnostic.slang) |
| Boundary probe: a Unicode escape naming a code point that cannot be encoded as UTF-8 is rejected at decode time (E10013).                                                                  | boundary   | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`string-unicode-codepoint-not-utf8-encodable-diagnostic.slang`](string-unicode-codepoint-not-utf8-encodable-diagnostic.slang) |
| In a string literal a `\x` escape decodes to a single byte spliced into the string's bytes.                                                                                                | functional | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`string-hex-escape-single-byte.slang`](string-hex-escape-single-byte.slang)                                     |
| Negative probe: a `\u` escape with fewer than 4 hex digits is diagnosed as an invalid Unicode string escape (E10008).                                                                      | negative   | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`string-escape-unicode-too-few-digits-diagnostic.slang`](string-escape-unicode-too-few-digits-diagnostic.slang) |
| Negative probe: a `\x` escape with no hex digits is diagnosed as an invalid string escape (E10007).                                                                                        | negative   | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`string-escape-hex-no-digits-diagnostic.slang`](string-escape-hex-no-digits-diagnostic.slang)                   |
| Negative probe: a character literal holding more than one character is diagnosed at scan time as an illegal character literal (E10001).                                                   | negative   | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`char-literal-multichar-diagnostic.slang`](char-literal-multichar-diagnostic.slang)                             |
| Negative probe: a raw newline inside a string literal is diagnosed at scan time as a newline in literal (E10005).                                                                          | negative   | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`string-literal-newline-diagnostic.slang`](string-literal-newline-diagnostic.slang)                             |
| Negative probe: an empty character literal is diagnosed at scan time as an illegal character literal (E10001).                                                                             | negative   | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`char-literal-empty-diagnostic.slang`](char-literal-empty-diagnostic.slang)                                     |
| `#include` filenames are decoded without escape processing, so a backslash-x sequence in the path is looked up (and reported) verbatim.                                                    | functional | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction) | [`include-filename-escapes-not-processed.slang`](include-filename-escapes-not-processed.slang)                   |
| A parameter reference in a macro body is compiled to a parameter op, and invocation replays the matching argument's token range by index, so the argument tokens replace the parameter at expansion. | functional | [#macro-expansion](../../../../design/pipeline/01-lex-preprocess.md#macro-expansion)                                           | [`function-macro-argument-substitution.slang`](function-macro-argument-substitution.slang)                       |
| A stringized parameter takes the argument's raw tokens, not their macro expansion, so `#x` of a macro name yields the name's spelling.                                                     | functional | [#macro-expansion](../../../../design/pipeline/01-lex-preprocess.md#macro-expansion)                                           | [`stringize-argument-not-expanded.slang`](stringize-argument-not-expanded.slang)                                 |
| Argument tokens replayed for an expanded-parameter reference are themselves macro-expanded, so a macro name passed as an argument expands in the body.                                     | functional | [#macro-expansion](../../../../design/pipeline/01-lex-preprocess.md#macro-expansion)                                           | [`function-macro-argument-tokens-are-expanded.slang`](function-macro-argument-tokens-are-expanded.slang)         |
| Boundary probe of function-style macros: a zero-parameter macro `FOO()` is valid and expands without taking arguments.                                                                     | boundary   | [#macro-expansion](../../../../design/pipeline/01-lex-preprocess.md#macro-expansion)                                           | [`function-macro-zero-args.slang`](function-macro-zero-args.slang)                                               |
| Boundary probe of function-style macros: an empty argument (the comma is present, the token list before/after is empty) substitutes to nothing.                                            | boundary   | [#macro-expansion](../../../../design/pipeline/01-lex-preprocess.md#macro-expansion)                                           | [`function-macro-empty-argument.slang`](function-macro-empty-argument.slang)                                     |
| Inside an inactive `#if` block, only directives that may toggle the active/inactive state (`#if`, `#else`, `#elif`, `#endif`) are evaluated; other directives are skipped without effect.  | functional | [#macro-expansion](../../../../design/pipeline/01-lex-preprocess.md#macro-expansion)                                           | [`inactive-block-skips-non-conditional-directives.slang`](inactive-block-skips-non-conditional-directives.slang) |
| Negative probe of function-style macros: invoking a 2-parameter macro with only 1 argument is rejected with the documented arg-count diagnostic.                                           | negative   | [#macro-expansion](../../../../design/pipeline/01-lex-preprocess.md#macro-expansion)                                           | [`macro-wrong-argument-count-diagnostic.slang`](macro-wrong-argument-count-diagnostic.slang)                     |
| Object-like `#define` stores a lexed token sequence that is replayed verbatim at every use, so the expanded value is what the parser sees.                                                 | functional | [#macro-expansion](../../../../design/pipeline/01-lex-preprocess.md#macro-expansion)                                           | [`object-macro-expansion.slang`](object-macro-expansion.slang)                                                   |
| Stress probe of function-style macros: an 8-parameter macro binds all 8 names to their arguments and substitutes correctly.                                                                | stress     | [#macro-expansion](../../../../design/pipeline/01-lex-preprocess.md#macro-expansion)                                           | [`function-macro-eight-args.slang`](function-macro-eight-args.slang)                                             |
| Tokens inside an inactive `#if` branch flow through the lexer for accounting but their contents are not expanded or parsed.                                                                | functional | [#macro-expansion](../../../../design/pipeline/01-lex-preprocess.md#macro-expansion)                                           | [`inactive-if-branch-not-expanded.slang`](inactive-if-branch-not-expanded.slang)                                 |
| `#pragma warning(disable)` state is tracked on a source-location timeline: the same construct warns before the directive and is silent after it.                                           | functional | [#pragma-warning-state-across-files](../../../../design/pipeline/01-lex-preprocess.md#pragma-warning-state-across-files)       | [`pragma-warning-disable-timeline.slang`](pragma-warning-disable-timeline.slang)                                 |
| `#pragma warning(push)` saves warning state and `#pragma warning(pop)` restores it, re-enabling a warning disabled inside the push/pop region.                                             | functional | [#pragma-warning-state-across-files](../../../../design/pipeline/01-lex-preprocess.md#pragma-warning-state-across-files)       | [`pragma-warning-push-pop-restores.slang`](pragma-warning-push-pop-restores.slang)                               |
| Boundary probe of `#error`: a bare `#error` with no message text still fires the documented preprocessor error.                                                                            | boundary   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`error-directive-empty-message.slang`](error-directive-empty-message.slang)                                     |
| Boundary probe of `#error`: a very long message body (upper edge of the message axis) is preserved verbatim in the diagnostic.                                                             | boundary   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`error-directive-long-message.slang`](error-directive-long-message.slang)                                       |
| Boundary probe of `#error`: an identifier in the message body is preserved verbatim, observable in the emitted diagnostic.                                                                 | boundary   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`error-directive-macro-name-in-message.slang`](error-directive-macro-name-in-message.slang)                     |
| Boundary probe of `#if`: `0xFFFFFFFF` (uint32 MAX) evaluates as non-zero and selects the active branch.                                                                                    | boundary   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`if-expression-uint32-max-is-true.slang`](if-expression-uint32-max-is-true.slang)                               |
| Boundary probe of `#if`: `2147483647` (int32 MAX) is parsed and evaluated as non-zero.                                                                                                     | boundary   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`if-expression-int32-max-is-true.slang`](if-expression-int32-max-is-true.slang)                                 |
| Boundary probe of `#if`: a literal `0` selects the inactive branch and `#else` activates.                                                                                                  | boundary   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`if-expression-zero-is-false.slang`](if-expression-zero-is-false.slang)                                         |
| Boundary probe of `#if`: an undefined identifier in the expression is treated as `0` per the standard C/HLSL `#if` semantics referenced by the doc.                                        | boundary   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`if-undefined-macro-is-zero.slang`](if-undefined-macro-is-zero.slang)                                           |
| Boundary probe of `#pragma once`: a header with `#pragma once` is only processed once even when included twice, preventing duplicate declarations.                                         | boundary   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`pragma-once-prevents-redefinition.slang`](pragma-once-prevents-redefinition.slang)                             |
| Boundary probe of `#pragma`: an unknown sub-directive followed by argument tokens still produces the "unknown pragma ignored" warning, and the trailing tokens are skipped to end-of-line. | boundary   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`pragma-unknown-with-args.slang`](pragma-unknown-with-args.slang)                                               |
| Boundary probe of `#pragma`: an unknown sub-directive name is ignored with a warning diagnostic, rather than rejected as a hard error.                                                     | boundary   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`pragma-unknown-emits-warning.slang`](pragma-unknown-emits-warning.slang)                                       |
| Boundary probe of `#warning`: a bare `#warning` with no message text still fires the documented preprocessor-warning diagnostic.                                                           | boundary   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`warning-directive-empty-message.slang`](warning-directive-empty-message.slang)                                 |
| Boundary probe of arithmetic in `#if`: `(INT_MAX + 1)` wraps to a negative value (observed behavior).                                                                                      | boundary   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`if-expression-signed-overflow-wraps.slang`](if-expression-signed-overflow-wraps.slang)                         |
| Boundary probe: the canonical `#ifndef GUARD / #define GUARD / #endif` header-guard pattern protects a second inclusion from re-defining its contents.                                     | boundary   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`header-guard-pattern.slang`](header-guard-pattern.slang)                                                       |
| Stress probe of `#elif`: in a 5-arm `#if`/`#elif*4`/`#else` chain, only the matching arm runs and the others are inactive.                                                                 | stress     | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`elif-chain-five-arms.slang`](elif-chain-five-arms.slang)                                                       |
| Stress probe of conditional nesting: five levels of `#if`/`#endif` track correctly and the innermost active branch wins.                                                                   | stress     | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`if-nested-five-deep.slang`](if-nested-five-deep.slang)                                                         |
| The preprocessor supports `#elif` as part of the standard C/HLSL conditional set; in a chain it selects the first arm whose expression evaluates to non-zero.                              | functional | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`elif-chain-selects-first-true.slang`](elif-chain-selects-first-true.slang)                                     |
| The preprocessor supports the standard C/HLSL `#define`/`#undef` pair, and `#undef` removes a macro so that a subsequent `#ifdef` is false.                                                | functional | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`undef-makes-ifdef-false.slang`](undef-makes-ifdef-false.slang)                                                 |
| The preprocessor supports the standard C/HLSL conditional set, including `#ifdef` selecting the active branch when the named macro is defined.                                             | functional | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`ifdef-selects-branch.slang`](ifdef-selects-branch.slang)                                                       |
| The standard C/HLSL `#error` directive is supported and causes the preprocessor to emit a diagnostic with the directive's message text.                                                    | negative   | [#preprocessor-directives](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-directives)                           | [`error-directive-emits-diagnostic.slang`](error-directive-emits-diagnostic.slang)                               |
| Boundary probe of `##`: pasting an identifier with the empty token sequence yields just that identifier.                                                                                   | boundary   | [#source-location-preservation](../../../../design/pipeline/01-lex-preprocess.md#source-location-preservation)                 | [`token-paste-with-empty-rhs.slang`](token-paste-with-empty-rhs.slang)                                           |
| Boundary probe of `__LINE__`: after a `#line 1000` directive, `__LINE__` reports a large value (boundary far from physical line 1).                                                        | boundary   | [#source-location-preservation](../../../../design/pipeline/01-lex-preprocess.md#source-location-preservation)                 | [`line-macro-after-line-directive.slang`](line-macro-after-line-directive.slang)                                 |
| Boundary probe of stringize `#x`: an empty argument stringizes to an empty string literal `""`.                                                                                            | boundary   | [#source-location-preservation](../../../../design/pipeline/01-lex-preprocess.md#source-location-preservation)                 | [`stringize-empty-argument.slang`](stringize-empty-argument.slang)                                               |
| Pasted tokens are re-lexed from a fresh token-paste source view, so pasting two integer literals forms one new integer literal.                                                            | functional | [#source-location-preservation](../../../../design/pipeline/01-lex-preprocess.md#source-location-preservation)                 | [`token-paste-relexed-numeric.slang`](token-paste-relexed-numeric.slang)                                         |
| The stringize operator `#x` synthesizes a fresh string-literal token whose content is the spelling of the argument.                                                                        | functional | [#source-location-preservation](../../../../design/pipeline/01-lex-preprocess.md#source-location-preservation)                 | [`stringize-operator.slang`](stringize-operator.slang)                                                           |
| The token-paste operator `x##y` synthesizes a fresh single token formed by concatenating the spellings of its two operands.                                                                | functional | [#source-location-preservation](../../../../design/pipeline/01-lex-preprocess.md#source-location-preservation)                 | [`token-paste-operator.slang`](token-paste-operator.slang)                                                       |
| `__FILE__` is a constructed token synthesized fresh at the use site; it expands to a string literal naming the current source file.                                                        | functional | [#source-location-preservation](../../../../design/pipeline/01-lex-preprocess.md#source-location-preservation)                 | [`file-macro-is-constructed-token.slang`](file-macro-is-constructed-token.slang)                                 |
| `__LINE__` is a constructed token synthesized fresh at each use; it expands to the line number of the use site.                                                                            | functional | [#source-location-preservation](../../../../design/pipeline/01-lex-preprocess.md#source-location-preservation)                 | [`line-macro-is-constructed-token.slang`](line-macro-is-constructed-token.slang)                                 |
| `__LINE__` referenced through nested macro expansion reports the line of the initiating top-level invocation, not a macro-definition line.                                                 | functional | [#source-location-preservation](../../../../design/pipeline/01-lex-preprocess.md#source-location-preservation)                 | [`line-macro-through-nested-expansion.slang`](line-macro-through-nested-expansion.slang)                         |

## Untested claims

| Claim                                                                                                                                                                                                                                                                        | Reason                 | Anchor                                                                                                                           | Why untested                                                                                                                                                                                                                                     |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `#pragma warning` state stays correct across `__include`d files because each `preprocessSource` pass seeds its absolute source-location counter from a persisted value, keeping the tracker timeline globally monotonic.                                                     | needs-multi-file-test  | [#pragma-warning-state-across-files](../../../../design/pipeline/01-lex-preprocess.md#pragma-warning-state-across-files)          | Observable only with 2+ `.slang` files joined by `__include`, where a `#pragma warning` in one file must govern (or not govern) diagnostics in another. This bundle's helper-header mechanism covers `#include`, not `__include` module pieces.  |
| A `SLANG_RELEASE_ASSERT` guards monotonicity of the handed-back source-location counter (e.g. against `uint32_t` wrap on very large translation units).                                                                                                                      | internal-source-fact   | [#pragma-warning-state-across-files](../../../../design/pipeline/01-lex-preprocess.md#pragma-warning-state-across-files)          | Triggering the assert would need a translation unit large enough to wrap a 32-bit counter; the guard itself is an internal invariant with no practical test-sized input.                                                                        |
| Bare non-ASCII bytes in string literals are passed through without enforcing well-formed UTF-8, while malformed UTF-8 in character literals is rejected (`invalidUtf8ByteSequence`, E10006).                                                                                 | requires-external-tool | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction)  | Both directions need a test file containing deliberately malformed UTF-8 bytes; the text-based tooling used to author and lint this bundle assumes well-formed UTF-8 sources, so the file would need byte-level generation outside this flow.    |
| An EOF inside an unterminated literal is diagnosed at scan time (`endOfFileInLiteral`, E10004).                                                                                                                                                                              | needs-cli-test         | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction)  | The input must end mid-literal with no trailing newline, so no source line can follow to carry the `DIAGNOSTIC_TEST` annotation; a wrapper invoking `slangc` directly on such a file would verify it.                                          |
| `getIntegerLiteralValue` optionally returns the suffix, decimal-base flag, and an overflow flag; `getFloatingPointLiteralValue` optionally reports `outPrecisionLost`.                                                                                                       | internal-source-fact   | [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction)  | The out-parameters are C++ helper outputs; the doc names no diagnostic or value consequence tied specifically to them at this stage. The user-facing literal-typing surface is exercised by the lang-ref conformance bundles instead.           |

## Doc gaps observed

| Anchor                                                                                                                                                | Kind                  | Gap                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | Suggested addition |
| ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------- |   | ------------------ |
| [#source-location-preservation](../../../../design/pipeline/01-lex-preprocess.md#source-location-preservation)                                         | undocumented-behavior | The `## Source-location preservation` section distinguishes three categories of expansion-time tokens — raw body, argument, and constructed — but does not give a directly observable, no-GPU way to verify the **raw-body** vs **argument** distinction (e.g. that an argument token's spelling location remains the call-site rather than the macro body). The distinction is observed through diagnostics whose exact wording is not promised by the doc. Tests deferred until either the diagnostics doc commits to a specific message or the source doc adds an observable claim. |                    |
| [#if](../../../../design/pipeline/01-lex-preprocess.md#if)                                                                                             | undocumented-behavior | `## Lexer flags and special-case rules` mentions `kLexerFlag_SuppressDiagnostics` as state used inside inactive `#if` blocks, but there is no user-surface claim about it beyond "tokens are still emitted as `TokenType::Invalid`". That is internal lexer state with no observable consequence at the slangc/slangi level. Recorded as undocumented-by-design.                                                                                                                                                                                                                       |                    |
| [#inputs-and-outputs](../../../../design/pipeline/01-lex-preprocess.md#inputs-and-outputs)                                                             | undocumented-behavior | `## Inputs and outputs` states that "phase 1 runs to completion and produces a flat token list" — this is an architectural invariant rather than a behavioral claim. There is no slangc observable that distinguishes "streamed" from "batched" tokens, so no test is written. Could be moved to an architecture-only section in a future doc revision.                                                                                                                                                                                                                                |                    |
| [#preprocessor-stack-of-input-streams](../../../../design/pipeline/01-lex-preprocess.md#preprocessor-stack-of-input-streams)                           | undocumented-behavior | `## Preprocessor / Stack of input streams` is described as a data-structure choice with no observable surface claim. Same rationale as above.                                                                                                                                                                                                                                                                                                                                                                                                                                          |                    |
| [#if](../../../../design/pipeline/01-lex-preprocess.md#if)                                                                                             | undocumented-behavior | The `## Preprocessor directives` section says "the standard C / HLSL set" and points readers at the source file to enumerate the list, rather than enumerating it in the doc itself. Tests here cover a representative sample (`#if`/`#ifdef`/`#elif`/ `#else`/`#endif`/`#define`/`#undef`/`#error`/`#include`); an authoritative enumeration in the doc would let this bundle grow exhaustively. Suggestion: add an explicit directive table to the doc.                                                                                                                              |                    |
| [#02-parse-astmd](../../../../design/pipeline/01-lex-preprocess.md#02-parse-astmd)                                                                     | undocumented-behavior | The lexer claim "no distinction between identifiers and keywords; the parser resolves keyword status via lookup" is technically a parser-stage observable (parsing succeeds where the user might expect a lexer error). The doc itself hands off to `02-parse-ast.md` for the lookup story, so no test is anchored here.                                                                                                                                                                                                                                                               |                    |
| [#if](../../../../design/pipeline/01-lex-preprocess.md#if)                                                                                             | undocumented-behavior | The `## Failure modes` section enumerates only "unbalanced `#if`, unknown directive, missing include" but the compiler also emits a dedicated **cyclic-include** diagnostic (`cyclic-include`, code 15302) — observed by `include-self-cycle-diagnostic.slang`. Suggestion: add the cyclic-include failure mode to the failure-modes list.                                                                                                                                                                                                                                             |                    |
| [#if](../../../../design/pipeline/01-lex-preprocess.md#if)                                                                                             | undocumented-behavior | The doc says the `#if` arithmetic evaluator handles "standard C/HLSL" expressions but does not commit to **integer overflow semantics** inside `#if`. The boundary tests (`if-expression-signed-overflow-wraps.slang`, `if-expression-uint32-max-is-true.slang`) probe the observed behavior (signed wrap, `0xFFFFFFFF` treated as non-zero) but the doc could promise these explicitly.                                                                                                                                                                                               |                    |
| [#error](../../../../design/pipeline/01-lex-preprocess.md#error)                                                                                       | undocumented-behavior | The doc treats `#error`, `#warning`, `#pragma`, and `#line` as members of "the standard C/HLSL set" without further detail. In particular the behavior that `#error` **does not expand macros in its message** (verified by `error-directive-macro-name-in-message.slang`) is not documented; nor is the unknown-pragma policy ("warning, ignore, continue" — verified by `pragma-unknown-emits-warning.slang`). Suggestion: add a short sub-section enumerating each directive and its message-expansion / failure-mode contract.                                                     |                    |
| [#a-macro-that-references-its-own-name-in-its-body](../../../../design/pipeline/01-lex-preprocess.md#a-macro-that-references-its-own-name-in-its-body) | undocumented-behavior | Recursive macro expansion ("a macro that references its own name in its body") is not explicitly addressed in the doc. The doc describes the parameter-op replay machinery but does not commit to a recursion-detection rule. Boundary test deferred until the doc commits a claim.                                                                                                                                                                                                                                                                                                    |                    |
| [#x](../../../../design/pipeline/01-lex-preprocess.md#x)                                                                                               | undocumented-behavior | The doc's `## Source-location preservation` lists `__LINE__`, `__FILE__`, `#x`, `x##y` as "constructed tokens" but does not discuss the `#line` directive's interaction with `__LINE__` (verified by `line-macro-after-line-directive.slang`). Suggestion: add a sentence noting that `#line N` shifts the logical counter used by constructed `__LINE__` tokens.                                                                                                                                                                                                                      |                    |
| [#include-resolution](../../../../design/pipeline/01-lex-preprocess.md#include-resolution)                                                             | ambiguous-claim       | The doc says angle-form includes "concatenate the raw tokens between `OpLess` and `OpGreater` into the path string" but does not say whether whitespace between those raw tokens is preserved or dropped — e.g. whether `<foo / bar.h>` resolves as `foo/bar.h` or `foo / bar.h`. A test cannot anchor to either outcome without guessing.                                                                                                                                                                                                                                             | State explicitly whether inter-token whitespace participates in the concatenated path string, with an example such as `<foo / bar.h>`. |
| [#literal-scanning-and-value-extraction](../../../../design/pipeline/01-lex-preprocess.md#literal-scanning-and-value-extraction)                       | missing-surface       | The doc names `getIntegerLiteralValue`'s overflow flag and `getFloatingPointLiteralValue`'s `outPrecisionLost` output but does not name any user-visible diagnostic or behavior that consumes them at this stage, so a doc-only reader cannot tell what (if anything) a shader author observes when they trip.                                                                                                                                                                                                                                                                         | Name the diagnostic (or downstream behavior) each out-flag feeds, or note explicitly that consumption happens at later pipeline stages. |
| [#pragma-warning-state-across-files](../../../../design/pipeline/01-lex-preprocess.md#pragma-warning-state-across-files)                               | ambiguous-claim       | The section introduces `#pragma warning(push/pop/disable/...)` with a trailing ellipsis, leaving the full set of accepted specifiers unstated; tests can only anchor to the three named ones.                                                                                                                                                                                                                                                                                                                                                                                          | Enumerate the complete specifier list the directive accepts (e.g. whether `error`, `suppress`, `default`, `once` are included).         |

## Boundary / stress coverage added in expansion

The expansion pass added boundary, stress, and negative probes
against the axes called out in `_common.md` (Pressure the
compiler):

- **`#if` expression**: 0 (lower edge), `0xFFFFFFFF` (uint32
  MAX), `2147483647` (int32 MAX), signed arithmetic overflow
  (`INT_MAX + 1` wraps), undefined identifier treated as 0,
  five-deep nesting, five-arm `#elif` chain.
- **Macro expansion**: zero-parameter macro, eight-parameter
  macro (many-args stress), empty argument substitution,
  wrong-argument-count diagnostic (negative), argument-is-a-macro
  expansion, stringized-argument non-expansion.
- **Stringize / paste**: empty argument to `#x` yields `""`,
  `##` with empty right-hand operand collapses to the left
  operand, `##` of two numeric literals re-lexes to one literal.
- **`#include`**: five-deep chain, self-include cycle (negative
  — cyclic include diagnostic), header-guard pattern via
  `#ifndef` / `#define`, escape sequences in include paths kept
  verbatim.
- **Predefined macros**: `__LINE__` after `#line N` reports a
  shifted value (far-line boundary); `__LINE__` through nested
  expansion reports the top-level line. `__FILE__` boundary is
  covered by the existing functional test.
- **Line continuation**: backslash-newline inside a string
  literal (in addition to the existing in-macro test).
- **`#error` / `#warning`**: empty-message edge, long-message
  edge, macro-name-in-message (verbatim, not expanded).
- **`#pragma`**: unknown pragma is a warning (not an error);
  unknown pragma with arguments still warns; `#pragma once` in
  a doubly-included header prevents duplicate declarations;
  `#pragma warning` disable timeline and push/pop restore.
- **Literal escapes**: every documented escape family (octal,
  hex, `\u`, `\U`, braced) at a known code point; braced form
  above the BMP (32-bit edge); hex escape with zero digits
  (negative), more digits than one byte allows in a string
  (decode-time negative), and a code point UTF-8 cannot encode
  (decode-time negative); empty and multi-character character
  literals (scan-time negatives); newline in a literal
  (scan-time negative).
- **Float literal range**: overflow parses as INFINITY,
  underflow parses as 0.

Skipped boundary axes:

- **Recursive macro definition** (negative) — the doc does not
  commit to detection semantics; recorded as a doc gap above.
- **Line continuation in `#include` path** — Slang does not
  document line-continuation behavior inside an include path
  specifically; no observable claim to anchor to.
- **Malformed UTF-8 bytes in literals** — needs byte-level file
  generation; recorded under Untested claims.
