Skip to content

fix: quote names in fmt when they need backticks - #6200

Open
prql-bot wants to merge 2 commits into
mainfrom
fix/fmt-quote-declaration-names
Open

fix: quote names in fmt when they need backticks#6200
prql-bot wants to merge 2 commits into
mainfrom
fix/fmt-quote-declaration-names

Conversation

@prql-bot

@prql-bot prql-bot commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

prqlc fmt emits declaration and named-argument names verbatim, so any name that needs backticks — one containing a space, or one that collides with a keyword — loses its quoting and the formatted output no longer means what the source did. The fix routes those names through the existing write_ident_part helper, which is already used for aliases, function params, and idents.

Found by the nightly survey while reviewing prqlc/prqlc/src/codegen/ast.rs; the named-argument case was found by the review pass on this PR.

Before, fmt turned a working query into a broken one:

let `my var` = (from t | select {x})
from `my var`
# after `prqlc fmt`
let my var = (from t | select {x})

from `my var`
Error:
   ╭─[ :3:1 ]
   │
 3 │ from `my var`
   │ ──────┬──────
   │       ╰──────── expected a function, but found `default_db.`my var``
───╯

Named arguments fail more quietly, because the mangled output still parses — as a different call, with the first half of the name becoming a positional argument:

from t
derive x = (foo `my arg`:5)
# after `prqlc fmt`
from t
derive x = (foo my arg:5)

# and after a second `prqlc fmt`
from t
derive x = (foo arg:5 my)

Affected sites, all now quoted: let (both the typed/valueless branch and the plain branch), into, type, enum, module, type tuple field names (type t = {`my field` = int}), and function-call named arguments. Enum variant names were already correct — they parse as expression aliases, which went through write_ident_part already.

Verification

Regression tests are codegen::ast::test::test_quoted_declaration_names, covering all six declaration sites plus a keyword-named declaration, and codegen::ast::test::test_quoted_named_arg. Reverting just the source hunks while keeping the tests makes them fail:

---- codegen::ast::test::test_quoted_declaration_names stdout ----
assertion failed: `(left == right)`
  left: `"let `my var` = 5"`
 right: `"let my var = 5"`

assert_is_formatted round-trips through the parser, so each case also asserts the output re-parses to the same source.

Local runs: cargo test -p prqlc-parser -p prqlc -p prqlc-macros -p compile-files and -p mdbook-prql all pass; cargo fmt --check and cargo clippy --all-targets are clean. task prqlc:pull-request could not run here — cargo-insta and cargo-nextest aren't on the sandbox PATH (the subject of #6144) — so the equivalent package set was run with plain cargo test.

@prql-bot prql-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review. The six declaration sites are right and the round-trip test covers each of them, but the same class of bug survives one site further down the same file: named arguments in a function call are still emitted verbatim.

prqlc/prqlc/src/codegen/ast.rs, in the FuncCall arm — for (name, arg) in &func_call.named_args { … r += opt.consume(name)?; … } — writes name raw, while the Func arm just below it already routes param.name and the named_params through write_ident_part. ident_part() in the parser accepts any Ident token, so a backtick-quoted named arg parses fine, and this is worse than the declaration case because the mangled output still parses — as a different call:

$ printf 'from t\nderive x = (foo `my arg`:5)\n' | prqlc fmt -
from t
derive x = (foo my arg:5)

$ printf 'from t\nderive x = (foo my arg:5)\n' | prqlc fmt -
from t
derive x = (foo arg:5 my)

my silently becomes a positional argument. No error, no diagnostic — fmt just rewrites the query's meaning. Since this is the same concern the PR is fixing and it's four lines from the hunks, I'll fold it in rather than open a seventh open bot PR, and update the title/body to match.

The test-rust (x86_64-unknown-linux-gnu, …) failure is unrelated infrastructure: it failed in the Wait for database step with Error: Timed out waiting for: tcp:1433 — the dbs-mssql-1 container didn't accept connections inside the action's 60s timeout. Nothing in the diff touches DB tests. No open issue tracks this wait-on timeout, and it's a single occurrence so far; the push below re-runs the matrix.

Verification notes

Ran against the merged tree (refs/pull/6200/merge, head dee8270):

  • cargo test -p prqlc --lib — 81 passed, 1 ignored. cargo test -p prqlc --test integration — 489 passed, 5 ignored. So the TyTupleField change in codegen/types.rs doesn't churn any debug_lineage or error-message snapshot, which was the risk of touching write_ty's output path.

  • Confirmed the fix does what the PR body claims — this now round-trips instead of emitting let my var = …:

    let `my var` = (from t | select {x})
    
  • Checked the other raw-name emission sites in codegen/. Ty::name (in codegen/types.rs, if let Some(name) = &self.name { Some(name.clone()) }) is also unquoted, but it's only ever set during semantic resolution (semantic/resolver/stmt.rs, ty.name = Some(ident.name.clone())) — fmt runs on the parser AST where it's None, so it's not a fmt correctness gap. Internal(operator_name) and the QueryDef other keys aren't user idents. Everything else already goes through write_ident_part or Ident::write.

  • Spot-checked the PR body's claim about enum variants: TyKind::Enum holds a Box<Expr>, so variants really are expression aliases and were already quoted. A backtick-quoted variant name round-trips unchanged:

    enum e {`Paid Up` = 0}
    

A backtick-quoted named argument in a function call was emitted verbatim, so `derive x = (foo `my arg`:5)` formatted to `foo my arg:5` — which still parses, but as a different call, with `my` becoming a positional argument.
@prql-bot prql-bot changed the title fix: quote declaration names in fmt when they need backticks fix: quote names in fmt when they need backticks Aug 17, 2026
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