Preserve user-supplied grouping parentheses in boolean expressions#3
Preserve user-supplied grouping parentheses in boolean expressions#3
Conversation
📝 WalkthroughWalkthroughBuffer and emit user-supplied literal parentheses in expression formatting; change structural-error detection to require a top-level statement at the parse root; add targeted parenthesis tests and a fixture-driven test runner. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
2854dd4 to
048210b
Compare
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
048210b to
bcb3aa4
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/formatter/expr.rs (1)
376-399: Parenthesis depth tracking looks correct.The logic properly handles:
- Opening parens: increments depth, pushes nested
(to buffer- Closing parens: decrements depth, joins and emits group when returning to depth 0, or pushes nested
)to buffer- Other tokens: routes to appropriate buffer based on depth
One minor observation:
select.rsusessaturating_add(1)andsaturating_sub(1)for itsparen_depthcounter (see context snippet lines 831-931). Using plain+=/-=here is fine for practical purposes (overflow requires ~4 billion nested parens), but you may want to align the style for consistency across the codebase.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/formatter/expr.rs` around lines 376 - 399, The paren depth increments/decrements in expr.rs currently use plain += 1 / -= 1; change these to use saturating_add(1) and saturating_sub(1) on the paren_depth counter to match the style in select.rs and avoid potential overflow edge-cases—update the branches that handle "(" (where paren_depth is increased) and ")" (where paren_depth is decreased) while keeping the same logic that pushes to paren_parts and emits to parts.tests/all_fixtures.rs (1)
98-103: Consider failing when known-failing fixtures unexpectedly pass.Currently, when a
KNOWN_FAILINGfixture passes, a warning is printed but the test still succeeds. This could allow the list to become stale. Making it a failure would enforce thatKNOWN_FAILINGis kept up to date.♻️ Optional: fail on unexpected passes
Ok(()) => { - passed += 1; if is_known_failing { - eprintln!("UNEXPECTED PASS {fixture_key}: remove from KNOWN_FAILING"); + failures.push(format!( + "{fixture_key}: UNEXPECTED PASS - remove from KNOWN_FAILING" + )); + } else { + passed += 1; } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/all_fixtures.rs` around lines 98 - 103, The current Ok(()) branch treats known-failing fixtures as passes; change it so unexpected passes count as failures: move passed += 1 into the non-known-failing branch and, when is_known_failing is true, print the "UNEXPECTED PASS {fixture_key}: remove from KNOWN_FAILING" message and increment failed (or otherwise mark overall test as failing) instead of incrementing passed; update the Ok(()) arm around fixture_key, is_known_failing, passed, and failed to reflect this behavior so the run exits non-zero when a KNOWN_FAILING unexpectedly succeeds.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/all_fixtures.rs`:
- Around line 97-124: The truncation of panic message bytes using &msg[..200]
can panic on UTF-8 boundaries; update the logic that builds short (in the match
-> Err branch where msg is created and failures.push is called) to perform a
char-safe truncation (e.g., take at most 200 characters rather than bytes) and
append "..." when truncated, then push the formatted "{fixture_key}: {short}" as
before; ensure you operate on msg (the String) and produce short without slicing
by byte indices.
---
Nitpick comments:
In `@src/formatter/expr.rs`:
- Around line 376-399: The paren depth increments/decrements in expr.rs
currently use plain += 1 / -= 1; change these to use saturating_add(1) and
saturating_sub(1) on the paren_depth counter to match the style in select.rs and
avoid potential overflow edge-cases—update the branches that handle "(" (where
paren_depth is increased) and ")" (where paren_depth is decreased) while keeping
the same logic that pushes to paren_parts and emits to parts.
In `@tests/all_fixtures.rs`:
- Around line 98-103: The current Ok(()) branch treats known-failing fixtures as
passes; change it so unexpected passes count as failures: move passed += 1 into
the non-known-failing branch and, when is_known_failing is true, print the
"UNEXPECTED PASS {fixture_key}: remove from KNOWN_FAILING" message and increment
failed (or otherwise mark overall test as failing) instead of incrementing
passed; update the Ok(()) arm around fixture_key, is_known_failing, passed, and
failed to reflect this behavior so the run exits non-zero when a KNOWN_FAILING
unexpectedly succeeds.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: ed138e91-ca0c-4369-972d-dcd27c2ef2c4
📒 Files selected for processing (4)
src/formatter/expr.rssrc/lib.rstests/all_fixtures.rstests/test_parens.rs
✅ Files skipped from review due to trivial changes (1)
- tests/test_parens.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- src/lib.rs
The tree-sitter-postgres grammar produces ERROR nodes for parenthesized boolean sub-expressions like (a IS NULL OR b > 1) and for complex AND chains with IS NOT NULL. Previously these were rejected as parse errors. Changes: - Simplified has_structural_error: only reject input when no valid toplevel_stmt was parsed at all. Grammar-level conflicts are tolerated since the statement structure is intact and can be formatted. - format_c_expr tracks paren depth to correctly handle nested parentheses - Added all_fixtures test that discovers and runs every .sql/.expected pair across all 7 style directories (57 passing, 8 known-failing due to formatting quality issues tracked in KNOWN_FAILING list) Fixes #2 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bcb3aa4 to
9165f87
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/all_fixtures.rs (1)
53-61: Consider deriving style list fromStyle::ALLto reduce drift.The styles are hardcoded here while
Style::ALLexists insrc/style.rs. If a new style variant is added to the enum but not updated here, its fixtures would silently be skipped.One option is to derive the directory name from the enum variant (e.g., via
Debugformatting or a method onStyle), iteratingStyle::ALLto ensure completeness.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/all_fixtures.rs` around lines 53 - 61, Replace the hardcoded styles array with an iteration over Style::ALL so new variants cannot be missed; for each variant from Style::ALL produce the tuple previously in `styles` by deriving the directory/name string from the enum (use Debug formatting or add a small method like `Style::dir_name(&self)` on the `Style` enum) and collect into the same &[(&str, Style)] shape used in tests; update the code that defines `styles` to build it from `Style::ALL` (or map to a Vec and borrow) and keep the test logic unchanged so every enum variant is covered automatically.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/all_fixtures.rs`:
- Around line 53-61: Replace the hardcoded styles array with an iteration over
Style::ALL so new variants cannot be missed; for each variant from Style::ALL
produce the tuple previously in `styles` by deriving the directory/name string
from the enum (use Debug formatting or add a small method like
`Style::dir_name(&self)` on the `Style` enum) and collect into the same &[(&str,
Style)] shape used in tests; update the code that defines `styles` to build it
from `Style::ALL` (or map to a Vec and borrow) and keep the test logic unchanged
so every enum variant is covered automatically.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 97023f2a-dde5-4ff0-a55f-a6d53c6b980b
📒 Files selected for processing (4)
src/formatter/expr.rssrc/lib.rstests/all_fixtures.rstests/test_parens.rs
✅ Files skipped from review due to trivial changes (1)
- tests/test_parens.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- src/lib.rs
Summary
(a IS NULL OR b > 1) AND c = 'x'now preserve the parentheses in formatted output instead of dropping themc_exprparents as valid parenthesized expressionsformat_c_exprdetects(...)grouping and joins inner content without extra spacesFixes #2
Test plan
preserve_parens_around_or_in_andverifies parens are keptno_unnecessary_parensverifies no spurious parens added🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests