Skip to content

feat(rules): add LT14 rule for keyword clause newline enforcement#2324

Merged
benfdking merged 4 commits intoquarylabs:mainfrom
ota2000:ota2000/feat/add-lt14-rule
Feb 16, 2026
Merged

feat(rules): add LT14 rule for keyword clause newline enforcement#2324
benfdking merged 4 commits intoquarylabs:mainfrom
ota2000:ota2000/feat/add-lt14-rule

Conversation

@ota2000
Copy link
Contributor

@ota2000 ota2000 commented Feb 14, 2026

Summary

  • Add new LT14 rule (layout.keyword_newline) that enforces each SQL keyword clause starts on its own line
  • Implemented using the reflow rebreak system (ReflowSequence::from_root().rebreak()), consistent with LT01/LT02
  • Fix two latent bugs in the reflow rebreak system:
    • identify_rebreak_spans: end_idx calculation for clause-level spans stopped at the first End/Solo block instead of scanning to the actual last block within the scope
    • RebreakIndices::from_elements: missing bounds checking for neighbor access in newline/pre_code scanning loops

Details

Rule behavior

The rule targets SELECT, FROM, WHERE, JOIN, GROUP BY, HAVING, ORDER BY, and LIMIT clauses. It respects the reflow line_position config:

  • alone (default): flags inconsistent newlines where some clauses have newlines but others don't. All-on-one-line queries are allowed.
  • alone:strict: always enforces newlines between clauses, even for single-line queries.

Rebreak bug fixes

identify_rebreak_spans had a bug where clause-level line_position_configs spans had incorrect end_idx. Within a clause like WHERE a = 1, all blocks in the last code child (a, =, 1) share StackPositionType::End at the parent level. The old code broke on the first End block (a) instead of continuing to scan to the actual last one (1), producing truncated spans that caused incorrect rebreak behavior.

RebreakIndices::from_elements had missing bounds checks where newline_point_idx + dir and pre_code_point_idx + dir could exceed array bounds when spans reached the edge of the element buffer.

Test plan

  • cargo test -p sqruff-lib --test rules -- -- "LT14.yml" — 12 test cases pass
  • cargo test -p sqruff-lib --test rules — all rule tests pass (CV09 jinja templater failure is pre-existing)
  • cargo fmt --all -- --check passes
  • cargo clippy -p sqruff-lib passes with no warnings

Closes #2323

@ota2000 ota2000 marked this pull request as ready for review February 15, 2026 00:54
Copy link
Collaborator

@benfdking benfdking left a comment

Choose a reason for hiding this comment

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

Thanks for another nice contribution! This will need a bit of a rebase, and you'll also need to re-generate the docs.

Implement LT14 (layout.keyword_newline) using the reflow rebreak
system, consistent with how LT01/LT02 work. The rule enforces that
each SQL clause (SELECT, FROM, WHERE, JOIN, GROUP BY, HAVING, ORDER BY,
LIMIT) starts on its own line.

Also fix two latent bugs in the reflow rebreak system that prevented
clause-level rebreak spans from working correctly:

- identify_rebreak_spans: fix end_idx calculation for clause-level spans
  that stopped at the first End/Solo block instead of scanning to the
  actual last block within the scope
- RebreakIndices::from_elements: add bounds checking for neighbor access
  in newline/pre_code scanning loops to prevent index out-of-bounds

Closes quarylabs#2323
- Add clause types list to long_description for generated docs
- Fix import: ahash::AHashMap -> hashbrown::HashMap
- Regenerate docs
@ota2000 ota2000 force-pushed the ota2000/feat/add-lt14-rule branch from ddf46e2 to 60d69ea Compare February 16, 2026 07:34
@ota2000
Copy link
Contributor Author

ota2000 commented Feb 16, 2026

@benfdking
Thanks for the review!
I've rebased on main, added the clause types list to the long_description for generated docs, and regenerated the docs.

@ota2000 ota2000 requested a review from benfdking February 16, 2026 07:36
- Update config_unknown_rule.stderr to include LT14 and layout.keyword_newline in available rules list
- Update playwright config test to handle multi-line format output when rules=all includes LT14
The placeholder test uses rules=all, so LT14 now enforces a newline
before the WHERE clause.
Copy link
Collaborator

@benfdking benfdking left a comment

Choose a reason for hiding this comment

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

Nice! Thanks again!

@benfdking benfdking merged commit d8ea084 into quarylabs:main Feb 16, 2026
26 of 28 checks passed
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.

feat: add LT14 rule (keyword clause newline enforcement)

2 participants