Skip to content

Escapes a backslash inside a quoted object key - #214

Merged
johnnyt merged 1 commit into
mainfrom
px-0tz-object-key-escaping
Sep 5, 2026
Merged

Escapes a backslash inside a quoted object key#214
johnnyt merged 1 commit into
mainfrom
px-0tz-object-key-escaping

Conversation

@johnnyt

@johnnyt johnnyt commented Sep 5, 2026

Copy link
Copy Markdown
Member

Closes px-0tz.

The defect

format_object_key/1 in StringVisitor escaped the quote character of the
style it was writing but never the escape character itself - the same defect
px-v3b fixed for string literals one screen above it, in the same visitor, and
deliberately left in place here because a quoted object key sits outside that
bead's acceptance criteria.

A quoted key is read by the same lexer string rule as a literal
(parse_object_key/1 in parser.ex consumes a STRING token), so it always
owed the same escaping. Before this branch, on main:

  • {'a\b': 1} rendered with the backslash unescaped, so parsing it back
    yielded the key ab - a silent data loss with no warning.
  • A key of a single backslash rendered {'\': 1}, an unterminated literal
    that does not parse at all.
  • A backslash immediately before the quote rendered source whose closing
    quote landed one character early.

The fix

Both quoted clauses now route through escape_within/2, the shared helper
px-v3b introduced, rather than growing a second implementation. Two escape
paths in one writer is how this defect outlived the first fix, so the bead
asked for the helper specifically.

The :identifier clause is untouched. The parser only ever produces it for a
bare identifier, which by construction carries neither a quote nor a
backslash, and quoting one on the writer's own initiative would switch a style
the writer is required to render as asked for (the px-v3b operator ruling).

Tests

test/predicator/visitors/string_visitor_object_key_escape_test.exs pins the
round trip for a corpus enumerated from the characters the lexer treats
specially, not transcribed from examples: 129 keys x 2 quote styles = 258
cases, plus the named cases the bead calls out and a guard that the identifier
style still renders bare.

Confirmed red before the fix (4 failures, including both the lone-backslash
and backslash-before-quote cases) and green after.

Provenance

Two files the bead did not name were forced by the change and are included:

  • test/test_helper.exs and
    test/predicator/visitors/string_visitor_escape_test.exs. The new suite
    needs the same awkward-value corpus as the px-v3b literal suite, and a
    second copy of the enumeration tripped Credo.Check.Design.DuplicatedCode
    on the full gate. The corpus moves to Predicator.EscapeCorpus in
    test_helper.exs - this repo's established test-support pattern, alongside
    Predicator.SpanSlicing and Predicator.ASTShape - and both suites read
    it. No behavior in either suite changed; the literal suite runs over the
    same values it did before.

No new public surface. No ISA movement, no corpus regeneration, no version
bump.

Changelog

changelog.d/px-0tz.md, a Fixed entry: user-visible, since a caller
decompiling an object with a quoted key was losing data.

Gate

mix quality green on this HEAD (rebase was a no-op; main is d952dd0):
format, compile with warnings-as-errors, deps, Credo clean, 2,895 tests at
95.5% coverage, Dialyzer clean.

format_object_key/1 escaped the quote character of the style it was
writing but never the escape character itself - the same defect px-v3b
had just fixed one screen above it, in the same visitor. An object key
carrying a backslash rendered it unescaped, so the source parsed back to
a different key with the backslash silently gone, and a key ending in
one rendered an unterminated literal that did not parse at all. A quoted
key is read by the same lexer string rule as a literal, so it always
owed the same escaping.

Both quoted clauses now route through escape_within/2, the helper px-v3b
introduced, rather than growing a second implementation: two escape
paths in one writer is how this defect outlived the first fix. The
identifier style is untouched - the parser only produces it for a bare
identifier, which carries neither a quote nor a backslash.

The corpus the escaping is tested over moves to Predicator.EscapeCorpus
in test_helper.exs, matching this repo's test-support pattern, so the
literal suite and the new object-key suite enumerate the same awkward
cases from one place instead of drifting apart.

Refs: px-0tz
@codecov

codecov Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
lib/predicator/visitors/string_visitor.ex 95.77% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@johnnyt
johnnyt merged commit 5139f0f into main Sep 5, 2026
2 checks passed
@johnnyt
johnnyt deleted the px-0tz-object-key-escaping branch September 5, 2026 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant