Skip to content

Standardize static regex declarations (#363) - #404

Merged
leynos merged 4 commits into
mainfrom
issue-363-refactor-standardise-static-regex-declarations-on-the-lazy-regex-macro
Jul 23, 2026
Merged

Standardize static regex declarations (#363)#404
leynos merged 4 commits into
mainfrom
issue-363-refactor-standardise-static-regex-declarations-on-the-lazy-regex-macro

Conversation

@lodyai

@lodyai lodyai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

This branch standardizes static regular-expression declarations on the
canonical lazy_regex! macro so initialization uses one idiom and failures
identify the pattern that could not compile.

Closes #363.

It also documents and enforces the convention, and adds generated coverage for
the non-trivial wrapping-fence capture contract.

Review walkthrough

Validation

  • make check-fmt: passed.
  • make test: passed, including 676 library tests and the integration,
    property, snapshot and trybuild suites.
  • make typecheck: passed.
  • make lint: passed, including check-static-regexes.
  • make markdownlint: passed with no errors.
  • make nixie: passed.
  • mbake validate Makefile: passed.
  • cargo test fence_captures_round_trip_generated_delimiters: passed; a
    deliberate temporary three-to-four-character fence-threshold mutation was
    detected before restoration.
  • coderabbit review --agent --type committed --base origin/main: passed
    with zero findings on commit 2575e80.

Notes

  • Model checking is not applicable because this is a mechanical declaration
    migration with no state transitions or bounded structural invariant.
  • No new snapshot was added because the existing output-level snapshot and
    behavioural suites cover the affected table, list, fence, break and wrapping
    paths and remained green.

References

Route every static regular expression through `lazy_regex!` so
initialization uses one idiom and reports a pattern-specific failure.

Document and enforce the convention, and lock the non-trivial fence
capture contract with generated round-trip coverage.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @LodyAI[bot], you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Summary

  • Standardised static regular-expression declarations on the lazy_regex! macro with descriptive compilation-failure messages across production and test sources.
  • Added developer documentation establishing lazy_regex! as the sanctioned static-regex pattern.
  • Added make lint safeguards that require ripgrep and reject hand-rolled static regex declarations; CI now installs ripgrep.
  • Added property-based coverage for wrapping-fence capture behaviour.
  • Added missing documentation for inline test modules.
  • Existing validation, tests, and snapshot suites pass.

Walkthrough

The PR standardises static regex declarations on lazy_regex!, documents the required pattern, adds fence-capture property tests, and makes the lint workflow fail when hand-rolled static regex construction remains.

Changes

Static regex standardisation

Layer / File(s) Summary
Migrate static regex declarations
src/breaks.rs, src/fences.rs, src/lists.rs, src/table.rs, src/wrap/fence.rs, tests/wrap_leading_spaces.rs
Replace hand-rolled LazyLock and Regex initialisers with lazy_regex!, preserving regex patterns and usages.
Validate fence capture behaviour
src/wrap/fence.rs
Generate fence lines with property tests and verify captures plus round-trip reconstruction.
Document and enforce the regex policy
docs/developers-guide.md, Makefile, .github/workflows/ci.yml
Document the mandated macro usage, install ripgrep in CI, and run a source scan before Clippy to reject hand-rolled static regexes.
Document test and internal modules
src/**/*.rs, tests/cli_matrix/*
Add purpose-specific documentation to test modules and update the internal parsing wrapper documentation.

Possibly related PRs

Suggested labels: Issue

Suggested reviewers: leynos

Poem

Static patterns gather, neat and bright,
A macro guides them into light.
Fence tests dance, then round-trip true,
While lint checks what remains to do.
Regex rules now stand in queue.

🚥 Pre-merge checks | ✅ 20
✅ Passed checks (20 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change and includes the linked issue reference #363.
Description check ✅ Passed The description clearly covers the regex migration, guard rails, docs, tests, and validation work.
Linked Issues check ✅ Passed The changes satisfy #363 by standardising static regexes, adding docs, a regression guard, and a property test.
Out of Scope Changes check ✅ Passed The extra docs, CI ripgrep install, and test comments all support the regex migration and lint guard objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Testing (Overall) ✅ Passed PASS: the new property test in src/wrap/fence.rs exercises fence-capture semantics across generated prefixes, markers and info, and would fail on plausible regressions.
User-Facing Documentation ✅ Passed No user-facing behaviour changed; the edits are internal regex migrations, tests, and maintainer docs only.
Developer Documentation ✅ Passed PASS: docs/developers-guide.md documents the new Makefile lint gate and the lazy_regex! convention; no relevant execplan or translated docs were touched.
Module-Level Documentation ✅ Passed Every touched Rust module now has a module doc comment, and a repo-wide scan found no remaining module declarations lacking docs.
Testing (Unit And Behavioural) ✅ Passed Keep the new fence property test: it checks generated capture invariants, and existing CLI/snapshot tests still cover the external wrap boundary.
Testing (Property / Proof) ✅ Passed Add proptest coverage for the new fence-capture invariant, with generated inputs and a round-trip check.
Testing (Compile-Time / Ui) ✅ Passed No new compile-time/UI behaviour was added; the fence contract is covered by a focused proptest, and existing trybuild/snapshot suites already serve the repo.
Unit Architecture ✅ Passed Keep this mechanical: the touched functions stay read-only and single-purpose, with only lazy_regex! migrations, docs, and tests added.
Domain Architecture ✅ Passed Keep core code pure: this PR only swaps regex initialisation, docs, tests, and lint gates; it adds no transport, persistence, or adapter bleed.
Observability ✅ Passed PASS: this commit only changes Makefile and developer docs, adding a lint gate and guidance; it adds no runtime path, failure boundary, metric, log, trace, or alert.
Security And Privacy ✅ Passed PASS: Diff only standardizes regex statics, adds docs/tests, and a CI lint step; no secrets, auth gaps, unsafe deserialization, or sensitive-data exposure were introduced.
Performance And Resource Use ✅ Passed Keep the migration mechanical: no runtime algorithms changed, and the new lint/property checks are bounded linear scans/tests.
Concurrency And State ✅ Passed No new shared mutable state, locks, async tasks, or ordering-sensitive logic was introduced; the PR only swaps regex initialisers, updates docs/Makefile, and adds a pure property test.
Architectural Complexity And Maintainability ✅ Passed No new architectural layer was added; the PR reuses the existing lazy_regex! macro and adds an explicit lint guard plus docs, which reduces maintenance risk.
Rust Compiler Lint Integrity ✅ Passed PASS: This commit only changes Makefile and docs; no Rust lint suppressions, artificial anchors, or ownership-clone churn were introduced.
📋 Issue Planner

Built with CodeRabbit's Coding Plans for faster development and fewer bugs.

View plan used: #363

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-363-refactor-standardise-static-regex-declarations-on-the-lazy-regex-macro

Comment @coderabbitai help to get the list of available commands.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot added the Issue label Jul 22, 2026
coderabbitai[bot]

This comment was marked as resolved.

@leynos
leynos marked this pull request as ready for review July 22, 2026 10:01
chatgpt-codex-connector[bot]

This comment was marked as resolved.

leynos added 2 commits July 22, 2026 12:13
Scan every Rust source for qualified hand-rolled static regex
declarations and fail closed when the scanner cannot complete.

Migrate the missed integration-test regex and document each inline
module so the repository-wide policy checks hold consistently.
Install a pinned ripgrep release through the cargo-binstall provisioned
by the shared Rust setup action so the static-regex lint guard can run.
@leynos

leynos commented Jul 22, 2026

Copy link
Copy Markdown
Owner

@coderabbitai Have the following failed checks now been resolved?

If further work is required, please provide an AI agent prompt for the remaining work to be done to address these failures.

Do not treat warnings as optional or aspirational. Where a change is out of scope for this PR, propose a GitHub issue unless one exists already. (Treat documentation and validation coverage as in scope).

❌ Failed checks (1 error, 1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Module-Level Documentation ❌ Error Fail: 19 nested modules, including src/breaks.rs:67 and src/table.rs:235, start with code rather than a doc comment; the rule is unmet. Add //! module docs to every mod { ... } block, or state that test/private modules are exempt, then rerun the scan.
Linked Issues check ⚠️ Warning Most #363 requirements are covered, but the linked hand-rolled regex in src/reflow.rs is not shown as migrated. Convert the remaining static regex in src/reflow.rs to lazy_regex! with a descriptive expect message, then re-run the full gate.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== PR diff summary =='
git diff --stat "$(git merge-base HEAD origin/main)" HEAD 2>/dev/null || git diff --stat

echo
echo '== Rust module declarations =='
rg -n -U --type rust '(^|\n)\s*(?:#\[[^\]]+\]\s*)*(?:pub(?:\([^)]*\))?\s+)?mod\s+[A-Za-z_][A-Za-z0-9_]*\s*\{' src

echo
echo '== Static LazyLock regex declarations and Regex::new call context =='
rg -n -U --type rust -C 4 'static\s+[A-Z0-9_]+\s*:\s*(?:std::sync::)?LazyLock\s*<\s*Regex\s*>\s*=|LazyLock::new\s*\(\s*\|\|\s*Regex::new\s*\(' src

echo
echo '== reflow module outline and relevant source =='
if [ -f src/reflow.rs ]; then
  ast-grep outline src/reflow.rs --items all
  sed -n '1,280p' src/reflow.rs
fi

echo
echo '== Documentation convention and static-regex gate =='
sed -n '1,260p' docs/developers-guide.md
sed -n '1,240p' Makefile

echo
echo '== Direct scan for hand-rolled static regex construction =='
rg -n -U --type rust 'LazyLock::new\s*\(\s*\|\|\s*Regex::new\s*\(' src || true

Length of output: 44368


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base="$(git merge-base HEAD origin/main 2>/dev/null || true)"
if [ -n "$base" ]; then
  echo '== Documentation-related PR diff =='
  git diff --unified=3 "$base" HEAD -- \
    src/breaks.rs src/code_emphasis.rs src/ellipsis.rs src/ellipsis/protected.rs \
    src/footnotes/mod.rs src/frontmatter.rs src/headings.rs src/io.rs src/lists.rs \
    src/process.rs src/table.rs src/textproc.rs src/wrap/block.rs src/wrap/fence.rs \
    src/wrap/inline/fragment.rs src/wrap/inline/normalize.rs \
    src/wrap/inline/predicates.rs src/wrap/link_reference.rs \
    src/wrap/paragraph/code_span_trim.rs src/wrap/tokenize/link_span.rs \
    tests/cli_matrix/invariants.rs tests/cli_matrix/support.rs
fi

echo
echo '== All LazyLock construction sites in Rust sources =='
rg -n -U --type rust -C 3 '\bLazyLock::new\s*\(' src tests || true

echo
echo '== Static Regex declarations not visibly initialized with lazy_regex! =='
rg -n -U --type rust -C 2 '\bstatic\b[^;=]*\bRegex\b[^;=]*=' src tests || true

echo
echo '== Nested module declarations lacking an adjacent preceding //! doc line =='
python3 - <<'PY'
from pathlib import Path
import re

module = re.compile(r'^\s*(?:pub(?:\([^)]*\))?\s+)?mod\s+[A-Za-z_][A-Za-z0-9_]*\s*(?:\{|;)\s*$')
for root in (Path("src"), Path("tests")):
    if not root.exists():
        continue
    for path in root.rglob("*.rs"):
        lines = path.read_text(encoding="utf-8").splitlines()
        for i, line in enumerate(lines):
            if not module.match(line):
                continue
            j = i - 1
            while j >= 0 and (not lines[j].strip() or lines[j].lstrip().startswith("#[")):
                j -= 1
            if j < 0 or not lines[j].lstrip().startswith("//!"):
                print(f"{path}:{i + 1}: {line.strip()}")
PY

Length of output: 31902


@leynos Treat both reported findings as resolved.

  • Module-Level Documentation — resolved. The 19 implicated inline modules now start with inner module documentation (//!), including src/breaks.rs Line 67 and src/table.rs Line 235. The earlier broad scan’s trailing list was not a failure result: it searched for //! before each mod declaration, while Rust module documentation correctly belongs as the first item inside mod { ... }.

  • Linked Issues / static-regex migration — resolved. src/reflow.rs declares no regex; it imports SEP_RE from src/table.rs, which now uses lazy_regex!. The source scan found no remaining hand-rolled LazyLock::new(|| Regex::new(...)) construction under src/. All discovered static Regex declarations use lazy_regex! with descriptive compilation-failure messages.

  • Regression prevention — resolved. Makefile adds check-static-regexes as a prerequisite of lint. The guard fails when it finds the prohibited hand-rolled static-regex form and separately fails if rg itself cannot complete the scan.

No further implementation work or follow-up GitHub issue is required for these findings. Re-run the repository’s full validation gate in CI to record the final executable verification.

@buzzybee-df12

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Add an explicit ripgrep availability check before scanning Rust sources.
Document the lint guard and its local and CI tool prerequisite so
contributors receive a clear failure when `rg` is unavailable.
@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Makefile (1)

44-52: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Broaden check-static-regexes beyond LazyLock::new.
Match the other sanctioned lazy-wrapper shapes too; once_cell::sync::Lazy::new can slip past this guard. Add a regression case for each accepted form.

🤖 Prompt for 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.

In `@Makefile` around lines 44 - 52, Broaden the regex in the Makefile target
check-static-regexes to detect sanctioned lazy-wrapper forms beyond
LazyLock::new, including once_cell::sync::Lazy::new and the other accepted
wrapper shapes. Add regression cases covering each supported form, while
preserving the existing rejection and ripgrep-error handling.
🤖 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.

Outside diff comments:
In `@Makefile`:
- Around line 44-52: Broaden the regex in the Makefile target
check-static-regexes to detect sanctioned lazy-wrapper forms beyond
LazyLock::new, including once_cell::sync::Lazy::new and the other accepted
wrapper shapes. Add regression cases covering each supported form, while
preserving the existing rejection and ripgrep-error handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6eea5718-2585-4b5c-b123-62cc7d62e657

📥 Commits

Reviewing files that changed from the base of the PR and between ba07ab1 and 2575e80.

📒 Files selected for processing (2)
  • Makefile
  • docs/developers-guide.md

@leynos

leynos commented Jul 23, 2026

Copy link
Copy Markdown
Owner

@coderabbitai please create a new GitHub issue for the following:

Please address the following concerns and ensure all commit gates succeed:

Makefile (1)

44-52: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Broaden check-static-regexes beyond LazyLock::new.

Match the other sanctioned lazy-wrapper shapes too; once_cell::sync::Lazy::new can slip past this guard. Add a regression case for each accepted form.

🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @Makefile around lines 44 - 52, Broaden the regex in the Makefile target
check-static-regexes to detect sanctioned lazy-wrapper forms beyond
LazyLock::new, including once_cell::sync::Lazy::new and the other accepted
wrapper shapes. Add regression cases covering each supported form, while
preserving the existing rejection and ripgrep-error handling.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== repository =='
git remote get-url origin

echo
echo '== Makefile static-regex guard =='
nl -ba Makefile | sed -n '35,65p'

echo
echo '== lazy-wrapper regex construction sites =='
rg -n -U --type rust -C 2 \
  '(?:LazyLock|Lazy|OnceLock)::new\s*\(\s*\|\|\s*(?:Regex|regex::Regex)::new\s*\(' \
  src tests || true

echo
echo '== static-regex guard references and regression coverage =='
rg -n -i -C 3 \
  'check-static-regexes|static.regex|lazylock|once_cell::sync::Lazy|Lazy::new' \
  Makefile tests src docs || true

echo
echo '== originating PR comment permalink =='
gh api repos/leynos/mdtablefix/issues/404/comments \
  --paginate \
  --jq '.[] | select(.user.login == "leynos" and (.body | contains("Broaden `check-static-regexes` beyond `LazyLock::new`."))) | .html_url'

Length of output: 1254


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Makefile static-regex guard (Lines 35–65) =='
sed -n '35,65p' Makefile

echo
echo '== lazy-wrapper regex construction sites =='
rg -n -U --type rust -C 2 \
  '(?:LazyLock|Lazy|OnceLock)::new\s*\(\s*\|\|\s*(?:Regex|regex::Regex)::new\s*\(' \
  src tests || true

echo
echo '== static-regex guard references and regression coverage =='
rg -n -i -C 3 \
  'check-static-regexes|static.regex|lazylock|once_cell::sync::Lazy|Lazy::new' \
  Makefile tests src docs || true

echo
echo '== originating PR comment permalink =='
gh api repos/leynos/mdtablefix/issues/404/comments \
  --paginate \
  --jq '.[] | select(.user.login == "leynos" and (.body | contains("Broaden `check-static-regexes` beyond `LazyLock::new`."))) | .html_url' || true

Length of output: 20544


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="leynos/mdtablefix"
issue_url="$(
  gh issue create \
    --repo "$repo" \
    --assignee "leynos" \
    --title "Broaden static-regex lint guard to cover supported lazy wrappers" \
    --body "$(cat <<'EOF'
## Summary

Broaden `Makefile`'s `check-static-regexes` scan so hand-rolled static regex declarations cannot bypass the repository's `lazy_regex!` convention by using lazy-wrapper constructors other than `std::sync::LazyLock::new`.

## Rationale

The current pattern at `Makefile` Line 46 only detects `LazyLock::new(... Regex::new(...))`. A declaration using `once_cell::sync::Lazy::new(|| Regex::new(...))` is not detected, allowing a prohibited static-regex construction to pass `make lint`.

## Affected area

- `Makefile`: `check-static-regexes`
- Add focused regression coverage or fixtures for the lint pattern.

## Required changes

1. Extend the scan to detect direct and fully qualified supported lazy-wrapper constructors, including `once_cell::sync::Lazy::new`, in addition to `LazyLock::new`.
2. Define the supported wrapper forms in the regression coverage so each form is rejected when it directly wraps `Regex::new`.
3. Preserve the existing behaviour:
   - a matching prohibited declaration fails with the existing static-regex diagnostic;
   - no matches succeed;
   - ripgrep scan failures propagate with the existing error handling.
4. Keep the change minimal and retain `lazy_regex!` as the sole sanctioned idiom for static regex declarations.
5. Run every applicable formatting, lint, and test gate before closing the issue.

## Acceptance criteria

- [ ] A hand-rolled `LazyLock::new(|| Regex::new(...))` static regex is rejected.
- [ ] A hand-rolled `once_cell::sync::Lazy::new(|| Regex::new(...))` static regex is rejected, including qualified constructor spelling.
- [ ] Regression coverage exercises every lazy-wrapper constructor form supported by the guard.
- [ ] `make lint` retains its existing result and diagnostic behaviour for clean scans, prohibited declarations, and ripgrep failures.
- [ ] All applicable commit gates pass.

## Backlinks

- Originating pull request: https://github.com/leynos/mdtablefix/pull/404
- Originating review comment: https://github.com/leynos/mdtablefix/pull/404#issuecomment-5062833885
- Requested by: `@leynos`
EOF
)"
)"

printf '%s\n' "$issue_url"

Length of output: 2543


@leynos Created and assigned #410 — Broaden static-regex lint guard to cover supported lazy wrappers.

The issue records the verified bypass, requires regression coverage for each guarded wrapper form, preserves existing scan-error handling, and requires all applicable commit gates to pass.

@leynos
leynos merged commit 3c32fe5 into main Jul 23, 2026
4 checks passed
@leynos
leynos deleted the issue-363-refactor-standardise-static-regex-declarations-on-the-lazy-regex-macro branch July 23, 2026 20:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: standardise static regex declarations on the lazy_regex! macro

3 participants