Skip to content

Broaden static-regex lint guard to cover supported lazy wrappers (#410) - #415

Open
lodyai[bot] wants to merge 3 commits into
mainfrom
issue-410-broaden-static-regex-lint-guard-to-cover-supported-lazy-wrappers
Open

Broaden static-regex lint guard to cover supported lazy wrappers (#410)#415
lodyai[bot] wants to merge 3 commits into
mainfrom
issue-410-broaden-static-regex-lint-guard-to-cover-supported-lazy-wrappers

Conversation

@lodyai

@lodyai lodyai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Summary

The check-static-regexes guard in make lint only detected the
LazyLock::new(|| Regex::new(...)) form, so a hand-rolled static regular
expression wrapped in once_cell::sync::Lazy::new(|| Regex::new(...)) slipped
past the lint and bypassed the repository's lazy_regex! convention.

This change broadens the scan to reject both supported lazy-wrapper
constructors — std::sync::LazyLock::new and once_cell::sync::Lazy::new
whether spelled directly or fully qualified. lazy_regex! remains the sole
sanctioned idiom for static regex declarations.

Closes #410.

Changes

  • Extract the scan into scripts/check-static-regexes.sh as a single source of
    truth, and broaden its pattern from LazyLock to (?:LazyLock|Lazy) so both
    wrapper families are rejected. The Makefile target now delegates to the
    script; the existing diagnostic, clean-scan, and ripgrep-failure behaviours
    are preserved unchanged.
  • Add tests/static_regex_lint.rs, which drives the script against fixtures
    for every supported wrapper form (direct and fully qualified LazyLock
    and once_cell Lazy), asserts a clean lazy_regex! source passes, and
    asserts a ripgrep scan failure propagates its exit status.
  • Add fixtures under tests/data/static_regex/ with a .rs.txt extension so
    the guard (which scans *.rs) does not match them in place; the test copies
    each into a temp dir as a .rs file before scanning.
  • Document the broadened guard in docs/developers-guide.md.

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 the 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 (check-fmt, lint, test, markdownlint, nixie); CodeRabbit review returned 0 findings.

References

🤖 Generated with Claude Code

The `check-static-regexes` guard only rejected `LazyLock::new(...
Regex::new(...))`, so a hand-rolled static regex wrapped in
`once_cell::sync::Lazy::new(|| Regex::new(...))` slipped past `make lint`.

Extract the scan into `scripts/check-static-regexes.sh` as the single source
of truth and broaden its pattern to reject both supported lazy-wrapper
constructors — `std::sync::LazyLock::new` and `once_cell::sync::Lazy::new` —
whether spelled directly or fully qualified. `lazy_regex!` remains the sole
sanctioned idiom.

Add `tests/static_regex_lint.rs`, which drives the script against fixtures for
every supported wrapper form, asserts a clean source passes, and asserts a
ripgrep scan failure propagates its exit status. Fixtures live under
`tests/data/static_regex/` with a `.rs.txt` extension so the guard does not
match them in place.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@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 26, 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

  • Broadens the static-regex lint guard to reject direct, fully qualified, and move closure forms using LazyLock::new or once_cell::sync::Lazy::new, preserving lazy_regex! as the sanctioned pattern.
  • Extracts the scan into scripts/check-static-regexes.sh, with Makefile delegation, diagnostic preservation, ripgrep failure propagation, and RG override support.
  • Adds regression fixtures and tests covering prohibited wrappers, valid usage, and scan failures.
  • Documents the lint behaviour and test coverage in the developers’ guide.

Walkthrough

The static regex lint scan now runs through a dedicated shell guard, wired from Make and documented for developers. Regression fixtures and tests cover six prohibited lazy wrappers, valid usage, and ripgrep failure propagation.

Changes

Static regex guard

Layer / File(s) Summary
Guard implementation
scripts/check-static-regexes.sh
Scan Rust files for prohibited LazyLock and Lazy wrappers around Regex::new, report matches, and propagate scan failures.
Makefile wiring and documentation
Makefile, docs/developers-guide.md
Delegate the Make target to the shell guard and document supported patterns and coverage.
Fixture-based regression coverage
tests/static_regex_lint.rs, tests/data/static_regex/*
Test six rejected wrapper forms, an accepted lazy_regex! fixture, and ripgrep failure propagation.

Sequence Diagram(s)

sequenceDiagram
  participant Make
  participant Guard
  participant Ripgrep
  participant RustFiles
  Make->>Guard: run with configured RG command
  Guard->>Ripgrep: scan *.rs for prohibited static regex wrappers
  Ripgrep->>RustFiles: inspect source files
  Ripgrep-->>Guard: return matches or scan status
  Guard-->>Make: return lint status and diagnostic
Loading

Possibly related issues

  • leynos/mdtablefix issue 410 — Expand the static-regex guard to cover LazyLock and once_cell::sync::Lazy forms with regression coverage.

Possibly related PRs

Suggested labels: Issue

Poem

Regex guards patrol the night,
Lazy wrappers meet their test in flight.
Make calls the script; clean forms pass,
Failed scans raise a warning glass.
Six forbidden paths are shown.


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Testing (Overall) ❌ Error New tests cover wrapper rejection and clean/scan-failure paths, but never exercise the restored RG='rg --pcre2' argument-bearing override. Add a test that invokes scripts/check-static-regexes.sh with an RG value containing extra args and assert the guard still works and the stub receives them.
✅ Passed checks (19 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the lint-guard expansion and includes the linked issue reference (#410).
Description check ✅ Passed The description clearly describes the broadened static-regex lint guard, tests, fixtures, and documentation changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
User-Facing Documentation ✅ Passed The change is a developer lint gate, not end-user functionality; the user guide has no relevant user workflow to update.
Developer Documentation ✅ Passed Document the new script and broader static-regex guard in docs/developers-guide.md; no related roadmap or execplan item needs syncing.
Module-Level Documentation ✅ Passed PASS: tests/static_regex_lint.rs starts with a //! module doc that explains the guard, its script/Makefile relationship, and the test role.
Testing (Unit And Behavioural) ✅ Passed PASS: the tests exercise the shell-script boundary with fixtures for all wrapper forms, the clean path, and rg failure, not just internal helpers.
Testing (Property / Proof) ✅ Passed The change hard-codes six finite wrapper forms and covers them exhaustively with fixtures; no broad invariant or lemma demands proptest/Kani/Verus.
Testing (Compile-Time / Ui) ✅ Passed This PR adds shell-script regression tests, not compile-time Rust/TS behaviour; direct stdout/stderr assertions cover the fixed diagnostics without needing trybuild or snapshots.
Unit Architecture ✅ Passed Accept the refactor: the rg/filesystem/process work sits behind the script boundary, dependencies are explicit, and the tests inject temp fixtures and RG cleanly.
Domain Architecture ✅ Passed PASS: The patch only touches lint tooling, docs, fixtures, and tests; no domain model, business rules, or adapter boundaries were changed.
Observability ✅ Passed PASS: This patch only changes lint/test/docs plumbing; it adds shell diagnostics at the guard boundaries and does not alter production runtime, metrics, tracing, or alerts.
Security And Privacy ✅ Passed No secrets or auth gaps added; the new shell guard only scans local Rust files and emits fixed diagnostics without sensitive data exposure.
Performance And Resource Use ✅ Passed Keep the guard to one ripgrep pass over the scan dir; the new tests use tiny temp files and bounded fixture copies, with no nested scans or unbounded growth.
Concurrency And State ✅ Passed PASS: The PR adds a single-process shell guard and temp-dir tests only; no shared mutable state, async tasks, locks, or interleaving-sensitive code paths were introduced.
Architectural Complexity And Maintainability ✅ Passed Use the new shell script as a single, explicit seam; it replaces duplicated Makefile logic without adding layers, registries, or hidden lifecycle hooks.
Rust Compiler Lint Integrity ✅ Passed No dead_code or unused-item suppressions were added, the new test helpers are all used, and there are no suspicious clone anchors in the touched Rust file.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-410-broaden-static-regex-lint-guard-to-cover-supported-lazy-wrappers

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

@leynos
leynos marked this pull request as ready for review July 26, 2026 22:44

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 013311db03

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/check-static-regexes.sh Outdated

@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.

Actionable comments posted: 4

🤖 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 `@Makefile`:
- Line 45: Update the check-static-regexes invocation in the Makefile so RG is
passed as a single executable path, matching check-ripgrep’s firstword handling;
alternatively, revise check-static-regexes.sh to parse RG command arguments
consistently. Preserve support for RG values that include flags such as “rg
--pcre2” without treating the entire value as one executable.

In `@scripts/check-static-regexes.sh`:
- Line 29: Update the static regex pattern in the shell script to accept an
optional move keyword between the lazy initializer opening parenthesis and the
closure delimiter, covering both LazyLock and Lazy wrappers. Add regression
fixtures exercising move closures for each wrapper family while preserving
rejection of the existing non-move forms.

In `@tests/static_regex_lint.rs`:
- Around line 64-70: Update run_guard so the None branch explicitly clears the
child process’s inherited RG environment variable, ensuring default-path tests
use the standard ripgrep resolution; keep the existing override behavior for
Some(rg) unchanged.
- Around line 18-24: Update the integration test imports and filesystem
operations to use camino path types and cap_std::fs_utf8 APIs instead of
std::path and std::fs. Apply the replacements throughout the test while
preserving the existing behavior of Command execution and TempDir handling.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 021fb5e4-7bc6-4187-9bbc-0927c7eee7a4

📥 Commits

Reviewing files that changed from the base of the PR and between cbf7cb0 and 013311d.

📒 Files selected for processing (9)
  • Makefile
  • docs/developers-guide.md
  • scripts/check-static-regexes.sh
  • tests/data/static_regex/clean.rs.txt
  • tests/data/static_regex/lazylock_direct.rs.txt
  • tests/data/static_regex/lazylock_qualified.rs.txt
  • tests/data/static_regex/once_cell_lazy_direct.rs.txt
  • tests/data/static_regex/once_cell_lazy_qualified.rs.txt
  • tests/static_regex_lint.rs

Comment thread Makefile
Comment thread scripts/check-static-regexes.sh Outdated
Comment on lines +18 to +24
use std::{
path::{Path, PathBuf},
process::Command,
};

use rstest::rstest;
use tempfile::TempDir;

@coderabbitai coderabbitai Bot Jul 26, 2026

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.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Use the repository’s capability-oriented filesystem APIs.

Replace std::path and all std::fs calls in this integration test with camino and cap_std::fs_utf8. As per coding guidelines, Rust code must use cap_std/cap_std::fs_utf8 and camino instead of std::fs and std::path.

🤖 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 `@tests/static_regex_lint.rs` around lines 18 - 24, Update the integration test
imports and filesystem operations to use camino path types and cap_std::fs_utf8
APIs instead of std::path and std::fs. Apply the replacements throughout the
test while preserving the existing behavior of Command execution and TempDir
handling.

Source: Coding guidelines

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please mark it as such using the API. Otherwise, please provide an AI agent prompt for the remaining work to be done to address this 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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- Codegraph: module outline ---'
ast-grep outline tests/static_regex_lint.rs --items all --view expanded || true

echo '--- Codegraph: imports and path/filesystem call sites ---'
ast-grep run --lang rust --pattern 'use std::{ $$$ };' tests/static_regex_lint.rs || true
ast-grep run --lang rust --pattern 'std::fs::$FUNC($$$ARGS)' tests/static_regex_lint.rs || true
ast-grep run --lang rust --pattern 'std::path::$TYPE' tests/static_regex_lint.rs || true
rg -n -C 3 '\b(std::(?:fs|path)|(?:fs|Path|PathBuf)::|Utf8Path|Utf8PathBuf|cap_std)' tests/static_regex_lint.rs

echo '--- Dependencies relevant to the required APIs ---'
fd -a '^Cargo\.toml$' . -x sh -c '
  echo "### $1"
  rg -n -C 2 "^(camino|cap-std|tempfile)\s*=" "$1" || true
' sh {}

echo '--- Review thread for the supplied discussion URL ---'
gh api graphql -F owner='leynos' -F name='mdtablefix' -F number=415 -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          id
          isResolved
          comments(first: 20) {
            nodes { databaseId author { login } body path line originalLine }
          }
        }
      }
    }
  }
}' --jq '
  .data.repository.pullRequest.reviewThreads.nodes[]
  | select(any(.comments.nodes[]; .databaseId == 3653618104))
'

Length of output: 6314


@leynos, the comment remains unresolved. Codegraph analysis shows that tests/static_regex_lint.rs still imports std::path::{Path, PathBuf} and uses std::fs::{read_to_string, write, set_permissions, Permissions} at Lines 54, 61, 121, and 125. The current Cargo.toml output also shows no declared camino or cap-std dependency. I have left GitHub review thread PRRT_kwDOO7HqGM6T5p3b unresolved.

Use this AI agent prompt to complete the remaining work:

Verify the current implementation before editing. Address the unresolved CodeRabbit finding in `tests/static_regex_lint.rs` by replacing `std::path::{Path, PathBuf}` with `camino` path types and replacing all `std::fs` operations with capability-oriented `cap_std::fs_utf8` APIs.

Preserve the test behaviour:
- Keep `std::process::Command` for subprocess execution.
- Keep `tempfile::TempDir` for isolated temporary directories.
- Adapt `TempDir` paths explicitly and safely to the chosen UTF-8/capability-oriented path APIs.
- Preserve fixture loading, temporary `.rs` materialization, ripgrep-stub creation, and Unix executable-permission setup.
- Add the required `camino` and `cap-std` dependencies to the appropriate `Cargo.toml` if they are absent.
- Avoid `unwrap()` in production code; test-only `.expect(...)` calls are acceptable with useful messages.
- Keep the module documentation and public API documentation requirements intact.

Run `make check-fmt`, `make lint`, and `make test` after the changes. Report any unavailable validation command rather than claiming it passed.

You are interacting with an AI system.

Comment thread tests/static_regex_lint.rs Outdated
Address review findings on the broadened static-regex lint:

- Restore support for `RG` overrides that carry arguments (for example
  `RG='rg --pcre2'`). Extracting the scan into the shell script replaced the
  Makefile's word-splitting `$(RG)` expansion with a quoted `"$RG"`, which
  treated the whole value as one executable name. Split `RG` into an array so
  arguments are preserved, matching `check-ripgrep`'s `firstword` handling.

- Reject `move` closures. The pattern only matched `|| Regex::new(...)`, so a
  hand-rolled `LazyLock::new(move || Regex::new(...))` (or the `once_cell`
  `Lazy` equivalent) slipped through. Add `(?:move\s+)?` and fixtures for both
  wrapper families.

- Make the regression tests deterministic: `run_guard` now clears any ambient
  `RG` on default-path runs so the guard's own `rg` default is exercised.

Skipped the suggestion to migrate the test to camino/cap-std path types: it
would add two dependencies absent from the tree and diverge from the existing
test suite, which uses std::fs and tempfile.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 27, 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 27, 2026

@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.

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 `@tests/static_regex_lint.rs`:
- Line 58: Update the Rust documentation comment for the fixture materialization
helper from “Materialise” to “Materialize,” preserving the repository’s
en-GB-oxendict spelling convention.
- Around line 66-79: The run_guard helper currently accepts only a path, so
tests cannot verify RG commands with arguments. Update run_guard and its callers
to accept an argument-bearing RG command or argv, preserve clearing RG when
unset, and add substantive regression coverage with a stub that asserts the
restored “rg --pcre2” command arguments used by the guard and Makefile.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: bb427583-e1b3-420d-a0b0-b4c6c433d8fa

📥 Commits

Reviewing files that changed from the base of the PR and between cbf7cb0 and 39eb1ed.

📒 Files selected for processing (11)
  • Makefile
  • docs/developers-guide.md
  • scripts/check-static-regexes.sh
  • tests/data/static_regex/clean.rs.txt
  • tests/data/static_regex/lazylock_direct.rs.txt
  • tests/data/static_regex/lazylock_move.rs.txt
  • tests/data/static_regex/lazylock_qualified.rs.txt
  • tests/data/static_regex/once_cell_lazy_direct.rs.txt
  • tests/data/static_regex/once_cell_lazy_move.rs.txt
  • tests/data/static_regex/once_cell_lazy_qualified.rs.txt
  • tests/static_regex_lint.rs

Comment thread tests/static_regex_lint.rs Outdated
Comment thread tests/static_regex_lint.rs Outdated
Address review findings on the static-regex lint regression tests:

- Widen `run_guard`'s `rg` parameter from `Option<&Path>` to `Option<&str>`
  so tests can supply an `RG` value that carries arguments, and add
  `preserves_arguments_supplied_through_rg`. It drives the guard with
  `RG='<stub> --pcre2'` and asserts, via a stub that records its argv, that
  the override's arguments are forwarded ahead of the guard's own and that
  the scan directory remains last. Reverting the array split makes this test
  fail with exit 127, the exact symptom the fix removed.

- Extract the executable-stub setup into a `write_stub` helper shared by the
  scan-failure and argument-preservation tests.

- Spell the `scan_dir_with` doc comment "Materialize", matching the
  repository's en-GB-oxendict Oxford-spelling convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

Broaden static-regex lint guard to cover supported lazy wrappers

2 participants