Repository navigation
fix(fmt): accept backtick strings in prompt/attribute/template_string slots - #3859
Conversation
… slots
baml fmt bailed on the whole file when a backtick string (`...`) was used
as a `prompt:` value, an attribute argument, or a `template_string` body:
those declarative slots only matched the quoted and raw string tokens, so
a BACKTICK_STRING_LITERAL produced a hard StrongAstError even though the
parser accepts backticks everywhere (baml check/run/test were all happy).
Generalize the prompt-value enum to a shared StringLiteralValue (raw |
quoted | backtick) used by both PromptField and TemplateStringDecl, and add
the backtick arm to AttributeArg.
Backtick strings are printed verbatim. Unlike raw strings, a backtick
string is auto-dedented at lower time (BEP-049 §12 preprocess_template,
with ${...} interpolations placeholdered and §13 whitespace control), so
its runtime value depends on the interior's exact indentation. A raw-style
interior reindent does not preserve that dedent and silently changes the
string's value (verified against the compiler lowering), so the interior
is left untouched. A value-preserving reindent would have to replicate the
§12/§13 pipeline and is deferred.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughBacktick string literals are now accepted in attribute arguments, prompt fields, and template string declarations. The formatter conditionally re-indents multi-line backticks when value-preserving, and new tests cover dedenting, verbatim cases, and idempotency. ChangesBacktick string literal support
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
⏭️ Performance benchmarks were skippedPerf benchmarks (CodSpeed) are opt-in on pull requests — they no longer run on every push. They always run automatically after merge to To run them on this PR, do any of the following, then push a commit (or re-run CI):
|
|
No description provided. |
…e slots A compiles-tier fixture demonstrating backtick string literals in the two declarative slots that compile cleanly: a `prompt:` value and a `template_string` body. The generated formatter snapshot shows backtick interiors printed verbatim, and the file is in canonical formatted form so `baml fmt` leaves it unchanged. (A backtick in `@description(...)` parses and formats but is a semantic error -- "@description argument must be a string literal" -- so it cannot live in a compiles fixture; that slot stays covered by the baml_fmt unit tests.)
Re-indent a multi-line backtick interior to sit one level past the
surrounding block (like a raw string), in every position -- prompt,
attribute, template_string, and expression. A backtick string is
auto-dedented at lower time (BEP-049 §12), so the formatter strips the
same common-prefix the runtime would, re-emits at the block indent, and
then re-derives the runtime value of both the original and re-indented
interiors and bails to verbatim unless they are byte-identical.
A literal with a ${for}/${if} block tag (§13 whitespace control) or a
multi-line ${...} interpolation (placeholdered before the §12 min-indent,
so its inner lines must not move) is left verbatim, detected from the CST
in BacktickString::from_cst.
Tests: baml_fmt unit tests assert the exact dedented output per position
plus the verbatim cases (block tag, multi-line interp, relative-indent
preservation); a new baml_tests integration test formats a range of
backtick sources and asserts every literal's decoded + §12-dedented value
is unchanged by formatting.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@baml_language/crates/baml_tests/tests/backtick_fmt_value_preservation.rs`:
- Around line 10-24: The backtick_value helper only models unescape plus
preprocess_template, so it is not validating the unsafe backtick cases that
depend on lowering behavior. Update the test to either compute expected values
through the same lowering path used by the compiler for backtick literals, or
remove the `${for}`/`${if}` and multi-line `${...}` rows from this assertion set
and keep them covered by the verbatim/idempotency checks. Use backtick_value and
the affected table entries to locate the test cases.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: a508aa1b-4e22-40dc-a3ef-32f8ae9c8cdb
⛔ Files ignored due to path filters (2)
baml_language/crates/baml_tests/snapshots/compiles/backtick_decl_slots/baml_tests__compiles__backtick_decl_slots__01_lexer__main.snapis excluded by!**/*.snapbaml_language/crates/baml_tests/snapshots/compiles/backtick_decl_slots/baml_tests__compiles__backtick_decl_slots__10_formatter__main.snapis excluded by!**/*.snap
📒 Files selected for processing (4)
baml_language/crates/baml_fmt/src/ast/tokens.rsbaml_language/crates/baml_fmt/src/lib.rsbaml_language/crates/baml_tests/projects/compiles/backtick_decl_slots/main.bamlbaml_language/crates/baml_tests/tests/backtick_fmt_value_preservation.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- baml_language/crates/baml_tests/projects/compiles/backtick_decl_slots/main.baml
…apshots
A compiles-tier fixture whose backtick bodies are deliberately mis-indented
so the formatter snapshot shows the dedent in action across the interesting
cases: an interior pushed far past the block (pulled back to one level), a
ragged interior (relative indentation preserved), blank lines preserved,
single-line ${...} interpolations moved with the text, a first-line-flush
body whose deeper second line is kept (it is part of the §12 value), and the
two verbatim fallbacks -- a ${for} block tag and an over-indented multi-line
${...} interpolation, neither re-indented.
`[`Self::dedent_safe`]` in the public BacktickString doc links to a private field, which breaks `cargo doc` for public items (rustdoc::private-intra-doc-links under -D warnings). Refer to the field in plain code text instead.
A single-line backtick has no newline, so the reindent path bails immediately and prints it verbatim. Add a regression test for the non-obvious case -- a single-line backtick longer than the line width -- since wrapping it would insert a newline and change the string's value.
…ases
Addresses review feedback: backtick_value() (unescape + preprocess_template)
does not model §13 whitespace control or placeholder substitution, so a
value-equality assertion is not meaningful for the ${for}/${if} and multi-line
interpolation rows -- it could miss a real lowered-value change. Tag each case
Reindented or Verbatim: Reindented keeps the §12 value-equality check (sound,
since those have no block tag / multi-line interp), Verbatim asserts the raw
backtick literal is left byte-for-byte unchanged (the stronger property the
formatter actually guarantees for them).
Problem
baml fmtbailed on the whole file when a backtick string (`...`) was used as aprompt:value:The parser accepts backtick strings everywhere (so
baml check/run/testwere all happy), but the formatter's hand-rolled token matches for the declarative DSL slots only knew the quoted and raw string tokens. Investigation found the same gap in three slots:prompt:value (PromptField) -- the reported crash@description(...), etc.)template_stringbodiesFix
StringLiteralValue(raw | quoted | backtick) with aFromCSTimpl, reused by bothPromptFieldandTemplateStringDecl(no duplicated match).AttributeArg.Why backtick strings are printed verbatim (not re-indented like raw strings)
The original ask was to format backticks "just as well as raw strings", i.e. with the same interior re-indentation. I implemented that first, but a snapshot in the
backtick_stringscorpus caught a real problem, which I then confirmed against the compiler lowering:preprocess_template, with${...}interpolations placeholdered and §13 whitespace control). Their runtime value depends on the interior's exact indentation.`first line\n second line`lowers to"first line\n second line", but after a raw-style re-indent it lowers to"first line\nsecond line"-- a silent value change.So backtick interiors are left byte-for-byte unchanged. A value-preserving re-indent would have to replicate the §12/§13 pipeline in the formatter and is deferred.
Side finding (not addressed here)
Raw strings are not dedented at lower time in expression position, so the existing raw-string re-indent already changes raw string values used as plain expression values (it's only safe in prompt position, where the prompt renderer dedents). Pre-existing; flagging for a separate look.
Tests
baml_fmtcovering backtick acceptance in all three slots (verbatim + idempotent) and a value-preservation guard (backtick_interior_not_reindented).baml_fmtsuite (66) and thebaml_testsformatter snapshot corpus (191) pass; thebacktick_stringsfixture snapshot is unchanged.Summary by CodeRabbit
New Features
Bug Fixes
${...}interpolations).Tests
Documentation