Skip to content

fix(yamlfmt): six pre-existing formatter defects found while fixing #634 #644

Description

@arcusbuilds

Problem

Six defects in the YAML formatter, all reproducible on feat/3.0 and all found while working on #634. None are caused by that work — I verified each against a pristine checkout — and none are fixed by the PR for it. Filing them together because they were found by one effort and cluster in the same area; happy to split into one issue per defect to match #632–#635 if you'd prefer.

Four surfaced through differential fuzzing (pristine feat/3.0 compiled alongside a patched copy, asserting no data is lost that the baseline preserved). Two came from prettier comparison.

Ordered by severity as I read it.


1. Output fails to parse — flow tag loses its separating space

in:   "A: [0000,\n00,!00 ]\n"
out:  "A: [0000,\n00,!00]\n"

The space after the !00 tag is stripped as trailing whitespace, and the result is no longer valid YAML:

yaml: line 2: did not find expected ',' or ']'

Worst of the six: the formatter emits a file the formatter itself will then reject.

2. Idempotency break — --- inside a plain scalar gets indented

in:    "-:\n- 0\n---\"0:\n"
pass1: "-:\n  - 0\n  ---\"0:\n"
pass2: error — yaml: line 1: did not find expected '-' indicator

A --- that is scalar content is treated as a document marker and reindented, so the second format pass fails. Found by the repo's own FuzzYAMLFormatter.

3. Nested sequence loses a level

in:   "- \n - 00\n"
out:  "-\n- 00\n"

[[0]] becomes [null, 0] — the nesting is flattened and a null appears.

4. Explicit complex keys collapse into a duplicate-key collision

in:   "? 0\n\n\n 1\n? 0\n\n 1\n? 0\n\n\n !"
out:  "? 0\n\n 1\n? 0\n\n 1\n? 0\n\n !\n"

Every blank run is collapsed, so two keys that differ only by a blank line become identical, and the output fails to parse:

yaml: unmarshal errors: mapping key "0\n1" already defined at line 1

The blank lines are content here — YAML folds them into newlines in the key. Partially improved by the #634 PR (the output becomes parseable), not fully fixed.

5. Block scalar gains a trailing newline

in:   "A: |\n 0"        (no final newline)
out:  "A: |\n  0\n"
value before: "0"
value after:  "0\n"

Note this only shows up when the input has no final newline, so comparing the input's semantics against the output's requires care not to normalize the difference away.

6. Unterminated flow read as a plain-scalar continuation loses a blank line

in:   "a: foo\n\n\n {bar\n"
out:  "a: foo\n\n { bar\n"
value before: "foo\n\n{bar"
value after:  "foo\n{ bar"

The tokenizer emits TokFlow for the unterminated {, but yaml.v3 parses it as a continuation of the plain scalar. collapseFlowBlankLineRuns guards this case with isClosedFlow; the token-level path has no equivalent.


Reproduction

Each case above is yamlfmt.Formatter{}.Format(input, DefaultOptions()) on feat/3.0, comparing yaml.Unmarshal of the input against yaml.Unmarshal of the output. #1, #2 and #4 need no comparison — the output simply doesn't parse.

Note on coverage

pkg/formatter/yamlfmt has no CRLF test fixtures. Separately from the above, that gap hid a CRLF-flattening bug through several rounds of review on my #634 branch — worth adding CRLF coverage to the fixture set regardless of what happens with these six.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingv3.1

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions