fix: heredocs_to_strings writes a value, not the heredoc's text (#337) - #351
livingstaccato wants to merge 6 commits into
Conversation
…ify-education#337) The option converts a heredoc into a quoted string, and was quoting the heredoc's own source -- markers and all -- across as many physical lines as the original occupied: a = "<<EOT hello EOT" A quoted template cannot span lines. OpenTofu rejects that with "Invalid multi-line string", and reading it back here gave the marker text rather than the value, so the option produced neither a valid file nor the right content. It is exposed on the shipped `jsontohcl2` script, so the CLI had the same hole. The flattening the reader already performs is reused rather than written a second time: serializing the rule with `preserve_heredocs=False` is exactly the quoted form that option produces on the way in, so the two cannot drift apart the way the escape alphabets did. The value that form carries is the reader's, which on this branch still drops the body's trailing newline -- that is amplify-education#326, and this change follows whatever the reader does rather than second-guessing it.
|
Please hold off on merging this one for now — I want to do another review pass over it before it goes in. Opened as a draft for that reason; I will mark it ready and say so here once I am done. |
|
Review pass done, so the hold above no longer applies — this is ready for review now. Rebased on current 🤖 Drafted with Claude Code. |
…ions carry the heredoc newline
|
Brought up to date with current Merge order. Eight of the twelve open PRs apply to (1552 → 1563 → 1591 → 1627 → 1671 → 1696 → 1703 → 1711 passing, from a 1534 #335 changes a value two of the others assert on. It makes a heredoc body #350 and #351 were written before that. Once #335 is in, their expectations need A resolved integration of all twelve is on the fork if it is useful: Drafted with the help of an LLM, working on behalf of the author. |
A heredoc whose `${...}` runs across lines has no quoted spelling: the
newlines inside the span are expression source, which OpenTofu rejects
escaped and rejects raw. Flattening one raised UnexpectedToken out of
dumps. It is now written back as the heredoc it was.
The multi-line check counted a brace inside a comment as closing the
interpolation, so `${1 /* } */\n+ 2}` -- which OpenTofu evaluates to 3 --
was judged single-line, flattened, and dumps raised UnexpectedToken. Block
comments are skipped, a line comment inside a span means the span crosses a
line, and a string literal that opens a nested template keeps the heredoc,
which is always safe.
A heredoc is lexed a line at a time, so a ~} there strips only to the end of its line; in a quoted string it strips across the newline and the next line's indent. OpenTofu gives the two spellings of the same for-loop different values, so such a heredoc has no quoted spelling and stays one.
Fixes #337.
What
The option converts a heredoc into a quoted string, and was quoting the heredoc's own source — markers and all — across as many physical lines as the original occupied:
A quoted template cannot span lines. OpenTofu rejects that with "Invalid multi-line string", and reading it back here gives
'<<EOT\nhello\nEOT'— the marker text rather than the value. So the option produced neither a valid file nor the right content, and it is exposed on the shippedjsontohcl2script, so the CLI had the same hole.After:
How
By reusing the flattening the reader already performs, rather than writing a second one: serializing the rule with
preserve_heredocs=Falseis exactly the quoted form that option produces on the way in. The two therefore cannot drift apart the way the escape alphabets did in #329 — there is only one implementation of "what is this heredoc as a quoted string".Two heredocs have no quoted spelling and are left as heredocs: one whose
${...}runs across lines (the newlines inside are expression source, which OpenTofu rejects escaped or raw), and one using a~strip marker, which strips across the line break in a quoted string but not in a heredoc.What that inherits
The value carried is the reader's. On
mainthat still drops the body's trailing newline, so<<EOT\nhello\nEOTbecomes"hello"where Terraform reads"hello\n". That is #326, fixed in #335, and this change deliberately follows whatever the reader does rather than second-guessing it — when #335 lands, this output gains the newline with no change here.Eight tests, including that every converted form parses again and that no marker survives into the output.
Merging
Up to date with
mainas of 2f6d718, by merging rather than rebasing (themerge=unionCHANGELOG duplicates entries under a rebase).It merges cleanly on its own, and all twelve open PRs are merged together, with the full suite passing on 3.8-3.14, at
livingstaccato/python-hcl2@int/pyvider-hcl-9.This pull request, and the investigation behind it, were produced by an AI assistant (Claude) working on behalf of the author. Please review with that provenance in mind.