SIGN IN SIGN UP
oven-sh / bun UNCLAIMED

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one

0 0 150 Rust

yaml: reject tab in s-indent position before structural tokens (DK95/06, Y79Y/005, Y79Y/008 — 402/402) (#31527)

## What

Per [62]/[63] `s-indent(n)` is **spaces only**. A tab in indent position
is `s-separate-in-line` — valid before [197] flow-in-block content ([69]
`s-flow-line-prefix(n) ::= s-indent(n) s-separate-in-line?`), but
**never** before a [184]/[192]/[195] structural sibling
(`-`/`?`/`:`/implicit-key), which the grammar places *immediately* at
`s-indent(n)` with no separation production in between.

Bun was accepting tab-tainted structural tokens because `Token.indent`
records the count of leading spaces (correctly), but the parser had no
way to ask "was there *additional* whitespace between those spaces and
the token?"

## How

**Scanner:** `Parser.tab_after_indent: bool` records "a tab was seen
between this line's `s-indent` (or post-indicator
`additional_parent_indent` column) and the current token." Set in
`scan()`'s tab arm (and `fold_lines()`' tab arm, which is what consumes
whitespace for the `DK95/06` shape). Reset on `newline()` and at
`scan()` entry when re-entering indent position.

**Parser:** checked at every structural-sibling recognition site (the
complete set per [184]/[192]/[195]):

| Site | Production |
|---|---|
| `parse_block_sequence` loop | [184] each `-` at `s-indent(n)` |
| `parse_block_mapping` loop | [195] each entry at `s-indent(n)` |
| explicit `:` indent check (first + subsequent) | [191] `s-indent(n)
":"` |
| `parse_node` MappingKey / MappingValue arms | first entry of new block
mapping |
| `parse_node` Scalar arm | [192] implicit key |
| `parse_block_indented` same-line compact | [185] |

Content paths (flow context, `s-separate` after indicator, plain-scalar
fold continuation) correctly do not check it — tab is valid there.

This is the general spec rule, not three test cases: it covers every
grammar position where `s-indent(n)` precedes a structural token, not
just the three official-suite inputs.

## yaml-test-suite

`DK95/06`, `Y79Y/005`, `Y79Y/008` activated. **402/402, 0 todos.**

| PR | Suite todos |
|---|---|
| #31112 | 27 → 10 |
| #31203 | 10 → 7 |
| #31308 | 7 → 3 |
| this | 3 → **0** |

## Validation

- 1887 pass / 0 fail / 11 todo
- 1015/1015 4-parser consensus harness
- 11 new unit tests (7 fail on system Bun, 4 lock in valid-tab content
cases)
- Also flips the `#31203` `test.todo("tab before block construct")` (3/3
asserts)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
D
Dylan Conway committed
d4b3a3ed47fcd5510f33be210be8a93b4a70eca3
Parent: 7688c09
Committed by GitHub <noreply@github.com> on 5/29/2026, 12:09:36 AM