Fix four expression-formatting correctness bugs#37
Conversation
- #22: collapse_whitespace_outside_quotes now honors backslash escaping inside E'...' escape strings, so \' is not misread as the terminator and interior whitespace is preserved. - #23: format_qualified_name collapses double dots only outside quoted identifiers, so "a..b" keeps both dots instead of becoming "a.b". - #24: the CAST-to-:: rewrite parenthesizes any compound operand (one with a top-level operator) even when it also contains a function call, so CAST(foo(x) + 1 AS integer) becomes (foo(x) + 1)::INTEGER rather than foo(x) + 1::INTEGER. Bare function calls stay unparenthesized. - #25: field selection on a parenthesized base keeps its parens and drops the stray space, so (foo).bar formats as (foo).bar, preserving the composite-field meaning. Adds river regression fixtures for each fix and two CAST parenthesization tests in test_parens.rs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis PR updates SQL expression formatting to preserve parentheses and quoting semantics for field selection, CAST-to-:: rewriting, qualified identifiers with consecutive dots, and PostgreSQL escape strings. It also adds regression fixtures and tests for each case. ChangesExpression formatter edge-case fixes
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@src/formatter/expr.rs`:
- Around line 1466-1495: has_top_level_space does not preserve backslash-escape
context for E'...' string literals, which can misclassify CAST expressions like
E'a\'s value'::text as compound. Update has_top_level_space in expr.rs to mirror
the escape-string handling used by collapse_whitespace_outside_quotes, so
escaped quotes inside E-prefixed literals are tracked correctly and top-level
whitespace detection stays accurate.
- Around line 416-429: The field-selection handling in formatter::expr::format
needs to distinguish function-call results from already-parenthesized
expressions, because checking last.contains('(') is too broad. Update the logic
around the formatted.starts_with('.') branch so it re-wraps any base expression
that is not explicitly enclosed in outer parentheses, ensuring cases like
foo(x).bar become (foo(x)).bar while preserving existing parentheses for other
expressions.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 6a07a4af-48fa-4531-92c5-4842fbffd11e
📒 Files selected for processing (10)
src/formatter/expr.rstests/fixtures/river/expr_cast_compound_func_parens.expectedtests/fixtures/river/expr_cast_compound_func_parens.sqltests/fixtures/river/expr_escape_string_whitespace.expectedtests/fixtures/river/expr_escape_string_whitespace.sqltests/fixtures/river/expr_paren_field_selection.expectedtests/fixtures/river/expr_paren_field_selection.sqltests/fixtures/river/expr_quoted_identifier_double_dots.expectedtests/fixtures/river/expr_quoted_identifier_double_dots.sqltests/test_parens.rs
- Field selection on a function-call result now stays parenthesized.
The old `contains('(')` guard skipped the re-wrap for `(foo(x)).bar`
because the base already held a paren, collapsing it to the invalid
`foo(x).bar`. Re-wrap now keys off outer enclosure instead.
- has_top_level_space now tracks E'...' backslash escapes and doubled
'' quotes, mirroring collapse_whitespace_outside_quotes. Previously a
CAST like `CAST(E'a\'s value' AS text)` was misread as a compound
operand and wrapped in needless parens.
Both paths gain regression tests in tests/test_parens.rs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
Fixes four correctness bugs in
src/formatter/expr.rs, three of which silently changed SQL semantics.Problem
collapse_whitespace_outside_quotesonly understood SQL-standard''doubled-quote escaping. In a PostgreSQL escape stringE'...', a backslash-escaped quote\'was misread as the terminator, so the rest of the literal had its whitespace collapsed.SELECT E'it\'s here'silently lost a space.format_qualified_nameranresult.replace("..", ".")across the whole string, including inside double-quoted identifiers.SELECT * FROM "a..b"became"a.b", referencing a different object.CAST(expr AS type)→expr::typerewrite dropped required parentheses for a compound operand that also contained a function call.CAST(foo(x) + 1 AS integer)emittedfoo(x) + 1::INTEGER, which parses asfoo(x) + (1::integer)— the sum is no longer cast.SELECT (foo).barbecamefoo .bar.Solution
E'...'escape string (anE/eimmediately preceding the opening quote and not part of a longer identifier) and honor backslash escaping inside it. Plain'...'strings still use only''doubling...→.collapse withcollapse_double_dots_outside_quotes, which skips dots inside double-quoted identifiers.needs_parensheuristic withhas_top_level_space, which parenthesizes an operand that carries a top-level operator (whitespace outside any parens/quotes) regardless of inner function calls, while leaving bare identifiers, literals, and single function calls likefoo(x)alone..bar) attaches to the preceding expression, restore the base's parens if they were stripped, so(foo).barkeeps its composite-field meaning and the stray space is removed.Adds river regression fixtures for each fix and two CAST parenthesization tests in
test_parens.rs. Full suite (71 fixtures + unit/doc tests) is green;just checkpasses.Fixes #22
Fixes #23
Fixes #24
Fixes #25
🤖 Generated with Claude Code
Summary by CodeRabbit
::typecasts.