Skip to content

fix(strings): don't halve backslashes in interpolated string literals#2764

Open
truffle-dev wants to merge 1 commit into
mikefarah:masterfrom
truffle-dev:fix-interpolation-backslash-halving-2561
Open

fix(strings): don't halve backslashes in interpolated string literals#2764
truffle-dev wants to merge 1 commit into
mikefarah:masterfrom
truffle-dev:fix-interpolation-backslash-halving-2561

Conversation

@truffle-dev

Copy link
Copy Markdown

Fixes #2561.

What

A string literal in an expression loses half of any run of backslashes, so the value differs from the same string read as input (and from jq):

$ yq -Pn -oj '["\\","\\\\","\\\\\\"]'
[ "\\", "\\", "\\\\" ]          # wrong: 1,1,2 backslashes

$ yq -P -oj <<< '["\\","\\\\","\\\\\\"]'
[ "\\", "\\\\", "\\\\\\" ]      # correct: 1,2,3 backslashes

Why

Escapes in an expression string literal are processed twice. The lexer runs processEscapeCharacters, which already decodes \\ -> \ (while deliberately preserving \\( so interpolation escaping survives). interpolate() then collapses every remaining backslash pair a second time, halving runs of backslashes.

That second collapse is only needed for the \\( -> literal \( escape. A standalone pair should pass through untouched. The fix guards the skip on a following (, mirroring the special case the lexer already uses, so \\( still yields a literal \( but plain backslash runs survive.

Testing

  • Two regression scenarios added in operator_strings_test.go (\\\\ and \\\\\\); both fail on master and pass with the fix.
  • Existing interpolation scenarios ("\\", "Hi \\(.value)", real \(...) interpolation) still pass unchanged.

Thanks to the reporter for the clear before/after repro.

String literals in expressions have their escapes decoded once by the lexer
(processEscapeCharacters), then interpolate() decoded every backslash pair a
second time, halving runs of backslashes (\\ -> \, \\\\ -> \\). Output
from an expression diverged from the same string read as input, and from jq.

interpolate() only needs to treat a backslash pair as an escape when it guards
an interpolation paren (\\( -> literal \(); a standalone pair must pass
through unchanged. Guard the skip on a following '(' so plain backslash runs
survive, mirroring the lexer's own \\( special case.

Fixes mikefarah#2561
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.

Backslashes in yq expression got halved

1 participant