feat(format): expand the runtime safety gate
Replace the bare SemanticallyEqual call in every frontend (CLI fmt, LSP
formatting + rangeFormatting) with format.VerifySafe, which runs four
checks before any formatted output is emitted:
- semantic equivalence - the code token stream is unchanged
- comment preservation - no -- or /* */ comment is dropped, merged,
split, reordered, or reworded (line endings / indentation normalised
away; recurses into dollar-quoted bodies)
- structural balance - the () [] and BEGIN/CASE/IF/LOOP...END nesting
profile matches, ignoring anything inside a comment or a string
- idempotence - a second format pass would not change it
On failure the CLI now prints the specific reason and keeps the original.
The comment check surfaced two real formatter bugs, both fixed in
formatBodyStatements:
- multi-line /* */ comments inside a PL/pgSQL body had their interior
lines re-split and reindented as if they were statements; they are
now tracked and carried verbatim with the opening line
- a column-0 -- line was glued onto the preceding line by the
split-line-join, which merged consecutive comment lines into one
Regenerate testdata/corpus/test_mm_proc.pgsql (was carrying the mangled
output). TestCorpusIdempotentAndSafe now runs the full VerifySafe bundle;
add safety_test.go with targeted cases.
This commit is contained in:
@@ -60,13 +60,22 @@ testdata/corpus/ — real-world .pgsql procedures used as the safety/idempoten
|
||||
4. **Graceful degradation**: any span the parser cannot handle is passed through verbatim
|
||||
rather than corrupted.
|
||||
5. **Runtime safety gate**: every frontend (CLI `fmt`, LSP `textDocument/formatting` and
|
||||
`rangeFormatting`) calls `format.SemanticallyEqual(src, out)` before writing or returning
|
||||
formatted output. It re-lexes both sides and compares non-trivia token streams
|
||||
(case-insensitive for identifiers/keywords, exact otherwise, recursing into dollar-quoted
|
||||
bodies). If it ever returns false, the formatter has a bug — the caller must refuse to
|
||||
write/emit the result and keep the original source, never guess or best-effort it. This is
|
||||
not just a test assertion (`pkg/format/format_test.go`); it is enforced at runtime so a
|
||||
formatter bug can never silently drop or alter code.
|
||||
`rangeFormatting`) calls `format.VerifySafe(src, out, style)` before writing or returning
|
||||
formatted output. If it returns a non-nil error the formatter has a bug — the caller must
|
||||
refuse to write/emit the result and keep the original source, never guess or best-effort
|
||||
it. This is enforced at runtime, not just in `pkg/format/*_test.go`, so a formatter bug can
|
||||
never silently drop or alter code. `VerifySafe` runs four checks:
|
||||
- **Semantic equivalence** (`SemanticallyEqual`): re-lex both sides, compare the non-trivia
|
||||
token streams — case-insensitive for identifiers/keywords, exact otherwise, recursing
|
||||
into dollar-quoted bodies. Comments and whitespace are trivia and are ignored here.
|
||||
- **Comment preservation** (`CommentsPreserved`): every `--` and `/* */` comment in `src`
|
||||
reappears in `out`, in order, with the same content (line endings and indentation are
|
||||
normalised away; dropping, merging, splitting, reordering, or rewording a comment is
|
||||
not). Descends into dollar-quoted bodies.
|
||||
- **Structural balance** (`StructurallyBalanced`): the `( ) [ ]` and BEGIN/CASE/IF/LOOP…END
|
||||
nesting profile of `out` matches `src`, counting only real code tokens (comment and
|
||||
string/dollar-quote contents are skipped).
|
||||
- **Idempotence**: re-formatting `out` yields `out` unchanged.
|
||||
|
||||
**Line endings**: the formatter re-emits all layout with `st.Newline` (default `\n`), so a
|
||||
CRLF input file is normalised to LF on write. This includes `\r\n` that sits *inside* a
|
||||
|
||||
Reference in New Issue
Block a user