Skip to content

Complete: Shared user-editable span recovery helpers (8.1.2)#197

Open
leynos wants to merge 6 commits intomainfrom
add-shared-span-recovery-helpers-xh0sb5
Open

Complete: Shared user-editable span recovery helpers (8.1.2)#197
leynos wants to merge 6 commits intomainfrom
add-shared-span-recovery-helpers-xh0sb5

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Apr 10, 2026

Summary

  • Status: COMPLETE
  • Implements the plan for 8.1.2: shared user-editable span recovery helpers for macro-heavy rstest code paths. This turns the prior planning material into an implemented foundation with a pure policy in whitaker-common, a thin rustc_span::Span adapter in whitaker, and an adopter in function_attrs_follow_docs demonstrating the approach. Includes tests, docs, and execution-plan updates.

Rationale

  • Move from planning material toward a concrete implementation that can be exercised by a real adopter and validated with unit and behavioural tests.
  • Enforce a clean policy boundary: pure policy in whitaker-common, adapter in whitaker, and a single adopter to demonstrate correctness and reduce crate-local logic in macro-heavy paths.
  • Provide gating, tests, and documentation to prevent scope creep and ensure future updates stay aligned with 8.1.2 goals.

Changes

  • Add new span-recovery module and tests:
    • common/src/rstest/span.rs (new)
    • common/src/rstest/mod.rs (exports updated)
    • common/src/lib.rs (exported SpanRecoveryFrame, UserEditableSpan, recover_user_editable_span)
    • common/src/rstest/tests.rs (unit tests for recovery policy)
    • common/tests/features/rstest_span_recovery.feature (Behaviour-driven tests)
    • common/tests/rstest_span_recovery_behaviour.rs (BDD-based tests)
  • Implemented rustc-span adapter and helpers:
    • src/hir.rs (span_recovery_frames and recover_user_editable_hir_span added)
    • src/lib.rs (re-exports for adapter behind dylint-driver feature)
  • Adopter integration:
    • crates/function_attrs_follow_docs/src/driver.rs updated to use the shared recovery adapter.
    • crates/function_attrs_follow_docs/src/tests/order_detection.rs updated for new surface usage.
  • Documentation and exec plan:
    • docs/execplans/8-1-2-shared-user-editable-span-recovery-helpers.md added (ExecPlan with status COMPLETE)
    • docs/lints-for-rstest-fixtures-and-test-hygiene.md updated with 8.1.2 implementation decisions
    • docs/roadmap.md updated to mark 8.1.2 as done
  • Misc:
    • Updated internal exports and surface in src/lib.rs to expose recovery helpers via the existing whitaker facade when the dylint-driver feature is enabled.

Goals and implementation outline (updated)

  • Pure policy surface in whitaker-common for determining if a frame in a span chain is user-editable and whether recovery is possible.
  • Thin rustc_span::Span adapter in whitaker (via src/hir.rs) to walk the source-callsite chain and produce an ordered frame list consumable by the policy.
  • At least one real consumer (demonstrated by function_attrs_follow_docs) adopting the shared helper to reduce crate-local logic and properly handle macro-generated glue.
  • Documentation updates reflecting decisions and final design when implemented.

Implementation stages (summary)

  • Stage B: Define the contract with tests first (failing tests define the contract).
  • Stage C: Implement the pure shared policy (unit tests and docs).
  • Stage D: Implement the rustc_span::Span adapter (surface and re-exports behind dylint-driver).
  • Stage E: Adopt the helper in one consumer (e.g., update function_attrs_follow_docs and regression tests).
  • Stage F: Update design/docs and roadmap with implementation decisions.
  • Stage G: Mark roadmap item 8.1.2 as done after gates pass.
  • Stage H/I: Run full gates (make fmt, make markdownlint, make nixie, make check-fmt, make lint, make test).

Validation and evidence (planning gates)

  • The plan outlines unit tests and BDD-style tests demonstrating the contract (direct, recovered, macro-only, and first-match behavior).
  • End-of-turn gates include format, lint, and test runs consistent with 8.1.2 gates. ExecPlan notes reflect that gates pass at turn end.

Documentation impact

  • ExecPlan added at docs/execplans/8-1-2-shared-user-editable-span-recovery-helpers.md and marked COMPLETE.
  • Design decisions captured in docs/lints-for-rstest-fixtures-and-test-hygiene.md.
  • Roadmap updated to reflect item 8.1.2 as DONE once implementation and gates are satisfied.

Notes for reviewers

  • This PR transitions from planning to implementation for 8.1.2 and introduces new public surface on whitaker-common and whitaker behind a feature gate. One adopter demonstrates viability and helps validate policy correctness and integration points.
  • No user-facing runtime API changes beyond the new shared surface and its adoption in a consumer example.

Task link

◳ Generated by DevBoxer

Summary by Sourcery

Introduce shared user-editable span recovery helpers and adopt them in an existing rstest-related lint path to avoid diagnostics on macro-only macro expansion glue.

New Features:

  • Add a pure rstest span recovery policy in whitaker-common that classifies ordered span frames as direct, recovered, or macro-only user-editable spans.
  • Expose rustc_span::Span-based span recovery and frame-construction helpers in whitaker for use by compiler-integrated lints.
  • Adopt the shared user-editable span recovery helper in the function_attrs_follow_docs lint to drive attribute ordering and item-bound checks from recovered user spans.

Enhancements:

  • Export the new span recovery types and helpers through existing whitaker-common and whitaker facades behind the dylint-driver feature gate.

Documentation:

  • Document the 8.1.2 implementation decisions and span recovery behavior, add an execplan for the shared helpers, and update the roadmap to mark 8.1.2 complete.

Tests:

  • Add unit tests and rstest-bdd behavioral scenarios for the shared span recovery policy, and extend function_attrs_follow_docs tests to cover recovered span ordering and macro-only attribute filtering.

This adds a detailed living document ExecPlan for roadmap item 8.1.2, which defines the scope, goals, constraints, and steps to implement shared helpers for recovering user-editable spans in macro-heavy `rstest` code paths. The plan outlines a pure policy in `whitaker-common`, a `rustc` adapter in `whitaker`, and adoption in an existing lint consumer, along with documentation updates and validation steps. This groundwork facilitates improved lint diagnostics in macro-generated test code.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 10, 2026

This PR implements roadmap item 8.1.2 by adding shared, macro-aware user-editable span recovery helpers: a pure policy layer in whitaker-common, a rustc_span adapter in whitaker (feature-gated), and a first-adopter integration in function_attrs_follow_docs. The ExecPlan for this work is included at docs/execplans/8-1-2-shared-user-editable-span-recovery-helpers.md and the roadmap entry 8.1.2 is marked COMPLETE.

Key changes

Core policy layer (whitaker-common)

  • Added common/src/rstest/span.rs:
    • SpanRecoveryFrame — frame wrapper with from_expansion flag and accessors
    • UserEditableSpan — enum { Direct(T), Recovered(T), MacroOnly } with into_option()
    • recover_user_editable_span<T: Clone>(&[SpanRecoveryFrame]) -> UserEditableSpan
  • Re-exports added in common/src/rstest/mod.rs and common/src/lib.rs
  • Unit tests in common/src/rstest/tests.rs exercising Direct, Recovered, nested recovery, empty frames and all-macro scenarios; added private assert_span_recovery test helper to reduce duplication.
  • Behaviour tests (rstest-bdd) added: common/tests/rstest_span_recovery_behaviour.rs with feature file common/tests/features/rstest_span_recovery.feature covering Direct, Recovered, first-match, and MacroOnly scenarios.

Rustc adapter layer (whitaker) — feature-gated

  • Added src/hir.rs:
    • span_recovery_frames(span: Span) -> Vec<whitaker_common::SpanRecoveryFrame> — iteratively walks source_callsite()/from_expansion chain
    • recover_user_editable_hir_span(span: Span) -> Option — returns first non-expansion span or None
    • Implemented iterative walk without heap allocation where possible and added unit tests (dummy and non-expanded cases).
  • Re-exported under #[cfg(feature = "dylint-driver")] in src/lib.rs

Consumer integration (function_attrs_follow_docs)

  • Updated crates/function_attrs_follow_docs/src/driver.rs:
    • AttrInfo now stores user_editable_span: Option (recovered) alongside the raw span
    • AttrInfo::source_order_key() uses user_editable_span when present, otherwise falls back to raw span
    • attribute_within_item signature changed to (attribute_span: Option, item_span: Option, raw_item_span: Span) and logic updated to:
      • reject attributes whose recovery is MacroOnly (None)
      • treat DUMMY_SP raw item spans as in-bounds
      • reject recovered-None item spans except when raw_item_span is DUMMY_SP
    • Filtering updated to drop macro-only attributes and use recovered spans for ordering/containment
  • Tests in crates/function_attrs_follow_docs/src/tests/order_detection.rs expanded to cover recovered vs raw-span behaviour (including None item-span cases) with a test_span(lo, hi) helper.

Tests and QA-level changes

  • Unit and BDD tests added/expanded across the policy, adapter and consumer:
    • Policy unit tests include an assert_span_recovery helper to remove duplication.
    • BDD feature + scenarios added for shared rstest span recovery.
    • Adapter unit tests for span_recovery_frames and recover_user_editable_hir_span were added per review suggestions.
    • Consumer tests added/updated to cover attribute_within_item behaviour with None/DUMMY_SP and out-of-bounds recovered spans.
  • Minor panic-message improvement and test hygiene adjustments applied in behaviour tests.

Documentation & process

  • ExecPlan: docs/execplans/8-1-2-shared-user-editable-span-recovery-helpers.md (Execution Plan — Status: COMPLETE) included and referenced.
  • docs/lints-for-rstest-fixtures-and-test-hygiene.md: added “Implementation decisions (8.1.2)” documenting the two-layer design, API semantic change (bool → enum), frame-walk stop conditions, and consumer behaviour (drop macro-only attributes).
  • docs/users-guide.md: expanded function_attrs_follow_docs user-facing docs with a “Macro-aware span recovery” section explaining what is checked/ignored and examples.
  • docs/developers-guide.md: added “Shared Span Recovery Helpers” section describing the policy-vs-adapter pattern, public symbols, usage, and test locations.
  • docs/roadmap.md updated to mark 8.1.2 complete.

Review comments & fixes addressed

  • Removed test duplication by adding a private assert_span_recovery helper in common tests and rewrote multiple tests to use it.
  • Addressed three reviewer warnings with concrete fixes:
    1. Added adapter unit tests and improved behaviour-test panic messages; added missing rstest for None item span in the consumer tests.
    2. Expanded user-facing docs to explain macro-aware recovery and macro-only attribute handling.
    3. Inserted developer documentation describing the new policy/adapter pattern and APIs.
  • Implementation is feature-gated (dylint-driver) for rustc-specific adapters; no other user-facing runtime API changes are claimed.

Notes for reviewers

  • The policy intentionally returns UserEditableSpan::{Direct, Recovered, MacroOnly} to make recovery outcomes explicit and avoid boolean ambiguity.
  • The rustc adapter is kept thin and gated behind the existing dylint-driver feature.
  • See docs/execplans/8-1-2-shared-user-editable-span-recovery-helpers.md for the execution plan, validation commands and success criteria.

Walkthrough

Introduce shared, macro-aware span-recovery helpers: a pure policy module in whitaker_common::rstest::span, a rustc_span adapter in whitaker::hir, and consumer updates to use recovered user-editable spans for attribute ordering and containment checks.

Changes

Cohort / File(s) Summary
Span Recovery Core
common/src/rstest/span.rs, common/src/rstest/mod.rs, common/src/lib.rs
Add SpanRecoveryFrame<T>, UserEditableSpan<T> (Direct, Recovered, MacroOnly) and recover_user_editable_span<T>; re-export through rstest module and top-level common crate.
HIR Span Adapter
src/hir.rs, src/lib.rs
Add span_recovery_frames(span: Span) -> Vec<SpanRecoveryFrame<Span>> and recover_user_editable_hir_span(span: Span) -> Option<Span>; re-export under dylint-driver feature.
Consumer Adoption
crates/function_attrs_follow_docs/src/driver.rs, crates/function_attrs_follow_docs/src/tests/order_detection.rs
Compute and store recovered user-editable spans on AttrInfo; change ordering key to prefer recovered span; change containment checks to accept Option<Span> and fall back to raw span; add tests for recovered-span behaviour.
Test Coverage (behaviour & unit)
common/src/rstest/tests.rs, common/tests/rstest_span_recovery_behaviour.rs, common/tests/features/rstest_span_recovery.feature
Add unit tests and BDD scenarios for direct, recovered, first-match and macro-only outcomes; add Gherkin feature describing expected recovery behaviour.
Docs & Roadmap
docs/execplans/8-1-2-shared-user-editable-span-recovery-helpers.md, docs/lints-for-rstest-fixtures-and-test-hygiene.md, docs/roadmap.md, docs/developers-guide.md, docs/users-guide.md
Add ExecPlan, design notes and user/developer docs describing two-layer split, API semantics change, rollout plan; mark roadmap item 8.1.2 complete.
Minor docs/style fixes
docs/documentation-style-guide.md
Change two fenced code block language identifiers from markdown to text.

Sequence Diagram(s)

sequenceDiagram
    participant CA as Consumer (function_attrs_follow_docs)
    participant HA as HIR Adapter (whitaker::hir)
    participant RS as Rustc Span (source_callsite)
    participant RP as Recovery Policy (whitaker_common::rstest)

    CA->>HA: recover_user_editable_hir_span(span)
    HA->>HA: span_recovery_frames(span)

    loop while from_expansion and progressing
        HA->>RS: source_callsite()
        RS-->>HA: next span
        HA->>HA: push SpanRecoveryFrame(next_span, from_expansion)
    end

    HA->>RP: recover_user_editable_span(frames)
    RP->>RP: scan frames for first non-expansion

    alt first frame non-expansion
        RP-->>HA: UserEditableSpan::Direct
    else later non-expansion found
        RP-->>HA: UserEditableSpan::Recovered
    else no user frame
        RP-->>HA: UserEditableSpan::MacroOnly
    end

    HA-->>CA: Option<Span> (Recovered or None)
    CA->>CA: use for attribute ordering & containment checks
Loading

Suggested labels

Roadmap

Poem

Seek the span where humans wrote,
Through macro fog and generated smote,
Recover first that shows the hand,
Order attributes where users stand,
Ship the fix and stride by rote ✨

🚥 Pre-merge checks | ✅ 5 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Testing ⚠️ Warning Adapter layer functions in src/hir.rs lack comprehensive test coverage for loop logic, critical guards, and traversal semantics. Enhance unit tests to cover multi-frame expansion scenarios or rely on integration tests; document indirect validation through BDD scenarios.
Module-Level Documentation ⚠️ Warning The file crates/function_attrs_follow_docs/src/lib.rs lacks a module-level docstring, violating documentation requirements. Add a module-level docstring to crates/function_attrs_follow_docs/src/lib.rs before the feature configuration attribute explaining the crate's purpose.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title directly aligns with the main changeset—completing roadmap item 8.1.2 by implementing shared user-editable span recovery helpers.
Description check ✅ Passed The pull request description comprehensively documents the implementation of 8.1.2, including rationale, changes across multiple layers (policy, adapter, adopter), documentation updates, and validation evidence.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
User-Facing Documentation ✅ Passed The user's guide comprehensively documents the new macro-aware span recovery behaviour in function_attrs_follow_docs lint, including dedicated sections on macro handling, scope specification, and concrete examples.
Developer Documentation ✅ Passed The developers-guide.md contains comprehensive documentation of the Shared Span Recovery Helpers, including architectural patterns, API specifications, feature gating requirements, usage examples, and complete test coverage details.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 add-shared-span-recovery-helpers-xh0sb5
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch add-shared-span-recovery-helpers-xh0sb5

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

codescene-delta-analysis[bot]

This comment was marked as outdated.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Apr 10, 2026

Reviewer's Guide

Implements shared, pure span-recovery policy for user-editable spans in whitaker-common, adds a rustc_span::Span adapter in whitaker, and wires a first real adopter (function_attrs_follow_docs) to use these helpers, along with unit/BDD tests and documentation/roadmap updates marking roadmap item 8.1.2 complete.

Sequence diagram for function attribute checking using shared span recovery

sequenceDiagram
    participant Driver as LintDriver
    participant Check as FunctionAttributeCheck
    participant HirAdapter as HirSpanAdapter
    participant AttrInfo as AttrInfo
    participant Policy as rstest_span_module
    participant function_attrs_driver_module as function_attrs_driver_module

    Driver->>Check: construct FunctionAttributeCheck(item_span, attrs)
    Driver->>HirAdapter: recover_user_editable_hir_span(Check.item_span)
    HirAdapter->>HirAdapter: span_recovery_frames(item_span)
    HirAdapter->>Policy: recover_user_editable_span(frames)
    Policy-->>HirAdapter: UserEditableSpan~Span~
    HirAdapter-->>Driver: item_user_editable_span: Option~Span~

    loop for each attr in Check.attrs
        Driver->>AttrInfo: try_from_hir(attr)
        AttrInfo->>HirAdapter: recover_user_editable_hir_span(attr.span)
        HirAdapter->>HirAdapter: span_recovery_frames(attr.span)
        HirAdapter->>Policy: recover_user_editable_span(frames)
        Policy-->>HirAdapter: UserEditableSpan~Span~
        HirAdapter-->>AttrInfo: user_editable_span: Option~Span~
        AttrInfo-->>Driver: AttrInfo
    end

    Driver->>Driver: retain infos where
    Driver->>function_attrs_driver_module: attribute_within_item(info.user_editable_span(), item_user_editable_span, Check.item_span)
    function_attrs_driver_module-->>Driver: bool (discard if false or MacroOnly)

    Driver->>AttrInfo: source_order_key()
    AttrInfo-->>Driver: (lo, hi) from user_editable_span or span
    Driver->>Driver: sort infos by source_order_key
Loading

Class diagram for span recovery policy, adapter, and adopter

classDiagram
    class SpanRecoveryFrame~T~ {
        - value: T
        - from_expansion: bool
        + new(value: T, from_expansion: bool) SpanRecoveryFrame~T~
        + value() &T
        + into_value() T
        + from_expansion() bool
    }

    class UserEditableSpan~T~ {
        <<enum>>
        + Direct(T)
        + Recovered(T)
        + MacroOnly
        + into_option() Option~T~
    }

    class rstest_span_module {
        + recover_user_editable_span~T~(frames: &[SpanRecoveryFrame~T~]) UserEditableSpan~T~
    }

    rstest_span_module --> SpanRecoveryFrame
    rstest_span_module --> UserEditableSpan

    class HirSpanAdapter {
        + span_recovery_frames(span: Span) Vec~SpanRecoveryFrame~Span~~
        + recover_user_editable_hir_span(span: Span) Option~Span~
    }

    HirSpanAdapter --> SpanRecoveryFrame
    HirSpanAdapter --> UserEditableSpan

    class AttrInfo {
        - span: Span
        - user_editable_span: Option~Span~
        - is_doc: bool
        - is_outer: bool
        + try_from_hir(attr: Attribute) Option~AttrInfo~
        + span() Span
        + source_order_key() (BytePos, BytePos)
        + user_editable_span() Option~Span~
    }

    class FunctionAttributeCheck {
        - item_span: Span
        - attrs: Vec~Attribute~
        + new(item_span: Span, attrs: Vec~Attribute~) FunctionAttributeCheck
    }

    class function_attrs_driver_module {
        + check_function_attributes(check: FunctionAttributeCheck) void
        + attribute_within_item(attribute_span: Option~Span~, item_span: Option~Span~, raw_item_span: Span) bool
    }

    function_attrs_driver_module --> HirSpanAdapter
    function_attrs_driver_module --> AttrInfo
    function_attrs_driver_module --> FunctionAttributeCheck

    AttrInfo --> HirSpanAdapter
Loading

File-Level Changes

Change Details Files
Introduce pure shared span-recovery policy types and helpers in whitaker-common and export them via the rstest module.
  • Add SpanRecoveryFrame and UserEditableSpan enums with accessors and conversion helpers.
  • Implement recover_user_editable_span to select the first non-expansion frame as Direct/Recovered or MacroOnly when none exist.
  • Expose the new policy API through common::rstest and the crate root so other crates can consume it.
common/src/rstest/span.rs
common/src/rstest/mod.rs
common/src/lib.rs
Add unit and behaviour-driven tests that define and verify the span-recovery contract.
  • Add rstest-based unit tests that cover direct, recovered, nested-macro, empty, and macro-only frame chains.
  • Add an rstest-bdd-based world, steps, and feature file to exercise the behaviour in terms of macro vs user frames.
  • Use SourceSpan/SourceLocation helpers in tests to model spans in a rustc-independent way.
common/src/rstest/tests.rs
common/tests/rstest_span_recovery_behaviour.rs
common/tests/features/rstest_span_recovery.feature
Provide a rustc_span::Span adapter in whitaker to build recovery frames and recover user-editable HIR spans, and re-export it behind the dylint-driver feature.
  • Implement span_recovery_frames to walk a Span's source_callsite chain, tagging each frame with from_expansion and stopping on dummies or non-progress.
  • Implement recover_user_editable_hir_span to call the shared policy and convert its result into Option.
  • Expose span_recovery_frames and recover_user_editable_hir_span from the whitaker facade when the dylint-driver feature is enabled.
src/hir.rs
src/lib.rs
Adopt the shared span-recovery helper in function_attrs_follow_docs to drive attribute ordering and membership checks using user-editable spans.
  • Extend AttrInfo to cache an optional user_editable_span recovered via recover_user_editable_hir_span and use it for sorting keys.
  • Change attribute_within_item to operate on optional recovered spans, treating dummy item spans as in-bounds and skipping attributes or items with no recoverable span.
  • Add regression tests to verify source order is driven by recovered spans and macro-only attributes are excluded from item comparisons.
crates/function_attrs_follow_docs/src/driver.rs
crates/function_attrs_follow_docs/src/tests/order_detection.rs
Update documentation and roadmap to record 8.1.2 implementation decisions and mark the roadmap item complete.
  • Document the shared recovery split, explicit UserEditableSpan result, adapter stop conditions, and first adopter in the rstest lints design doc.
  • Add a detailed execplan for 8.1.2 capturing constraints, stages, validation, and outcomes, marked COMPLETE.
  • Mark roadmap item 8.1.2 as done in docs/roadmap.md.
docs/lints-for-rstest-fixtures-and-test-hygiene.md
docs/roadmap.md
docs/execplans/8-1-2-shared-user-editable-span-recovery-helpers.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

- Introduce SpanRecoveryFrame, UserEditableSpan, and recover_user_editable_span in common::rstest.
- Implement pure policy to locate first user-editable span from macro expansion chains.
- Expose rustc_span::Span adapter and re-exports in whitaker crate.
- Adopt shared recovery in function_attrs_follow_docs to improve attribute ordering and filtering.
- Add comprehensive unit tests and rstest-bdd scenarios for recovery behavior.
- Update documentation and roadmap to reflect implementation progress and rationale.

This enables consistent handling of macro-expanded spans and suppresses diagnostics on macro-only glue,
improving lint accuracy and source diagnostics across crates.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos leynos changed the title Plan shared user-editable span recovery helpers (8.1.2) Implement shared user-editable span recovery helpers (8.1.2) Apr 10, 2026
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Apr 10, 2026

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response.

common/src/rstest/tests.rs

Comment on file

fn treats_all_expansion_frames_as_macro_only() {
    let first = source_span(4, 1, 4);
    let second = source_span(5, 1, 6);

    assert_eq!(
        recover_user_editable_span(&[
            SpanRecoveryFrame::new(first, true),
            SpanRecoveryFrame::new(second, true),
        ]),
        UserEditableSpan::MacroOnly
    );
}

❌ New issue: Code Duplication
The module contains 2 functions with similar structure: recovers_macro_frame_to_first_user_span,treats_all_expansion_frames_as_macro_only

@coderabbitai

This comment was marked as resolved.

Introduced `assert_span_recovery` helper to simplify and unify span recovery assertions in tests. Replaced repetitive assertion code with concise calls to the helper, enhancing readability and maintainability of span recovery test cases.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
codescene-delta-analysis[bot]

This comment was marked as outdated.

…rtions

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos leynos changed the title Implement shared user-editable span recovery helpers (8.1.2) Complete: Shared user-editable span recovery helpers (8.1.2) Apr 11, 2026
@leynos leynos marked this pull request as ready for review April 13, 2026 08:13
sourcery-ai[bot]

This comment was marked as resolved.

@coderabbitai coderabbitai bot added the Roadmap label Apr 13, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@common/tests/rstest_span_recovery_behaviour.rs`:
- Around line 55-60: The panic message in function source_span does not include
which line failed; update the panic in the Err arm of SourceSpan::new (in
function source_span) to include the line parameter and the error (e.g.,
"behaviour test span invalid for line {line}: {error:?}") so test failures
report the offending line number and the underlying error.

In `@crates/function_attrs_follow_docs/src/tests/order_detection.rs`:
- Around line 113-130: Add a new test in
crates/function_attrs_follow_docs/src/tests/order_detection.rs that asserts
attribute_within_item returns false when the item span is None: create an
attribute_span (e.g., test_span(12,20)), pass Some(attribute_span) for the
attribute, None for the item_span, and the item_bound span (e.g.,
test_span(10,40)) as the third argument, then
assert!(!attribute_within_item(...)); reference the existing tests and the
attribute_within_item function to place the new test alongside
macro_only_attributes_are_dropped_from_item_comparison and
recovered_attribute_spans_stay_in_item_bounds.
🪄 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

Run ID: 1200fa2d-a1f2-45c4-ace6-65c26327b08e

📥 Commits

Reviewing files that changed from the base of the PR and between ae46b4f and 3e949a4.

📒 Files selected for processing (13)
  • common/src/lib.rs
  • common/src/rstest/mod.rs
  • common/src/rstest/span.rs
  • common/src/rstest/tests.rs
  • common/tests/features/rstest_span_recovery.feature
  • common/tests/rstest_span_recovery_behaviour.rs
  • crates/function_attrs_follow_docs/src/driver.rs
  • crates/function_attrs_follow_docs/src/tests/order_detection.rs
  • docs/execplans/8-1-2-shared-user-editable-span-recovery-helpers.md
  • docs/lints-for-rstest-fixtures-and-test-hygiene.md
  • docs/roadmap.md
  • src/hir.rs
  • src/lib.rs

Comment thread common/tests/rstest_span_recovery_behaviour.rs
Comment thread crates/function_attrs_follow_docs/src/tests/order_detection.rs
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Apr 16, 2026

@coderabbitai Please suggest a fix for the following concern and provide an AI coding agent prompt for the fix:

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Testing ⚠️ Warning Public functions span_recovery_frames and recover_user_editable_hir_span lack unit test coverage, and two review suggestions remain unaddressed in panic messaging and test case implementation. Add unit tests for the exported functions covering span scenarios, update panic message with line context, and implement the missing test case for None item span condition.
User-Facing Documentation ⚠️ Warning The PR implements user-facing behaviour changes to function_attrs_follow_docs (macro-aware span recovery and macro-only attribute dropping) that are documented in design docs but absent from the users guide. Update docs/users-guide.md function_attrs_follow_docs section with macro-aware span recovery behaviour, macro-only attribute handling, and rstest-specific examples.
Developer Documentation ⚠️ Warning The PR introduces new internal APIs and architectural patterns but fails to update the repository's primary developer documentation (docs/developers-guide.md) with guidance on span recovery helpers and the policy-vs-adapter pattern. Add a new 'Shared Span Recovery Helpers' section to docs/developers-guide.md documenting SpanRecoveryFrame, UserEditableSpan, and recover_user_editable_hir_span() with usage examples and architectural pattern explanation.

@coderabbitai

This comment was marked as resolved.

Replaced the usage of recover_user_editable_span with an explicit loop to recover the first user-editable HIR span from a macro expansion chain. This improves clarity by avoiding unnecessary dependencies and provides a straightforward iterative approach.

Additional improvements:
- Changed function attribute_within_item to unwrap item span or fallback to raw item span for recovery.
- Added tests for attribute_within_item to verify behavior with recovered attributes and raw spans.
- Minor fixes to error messages and documentation clarity in ExecPlan.

These changes enhance maintainability and correctness of span recovery mechanisms and related tests.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
codescene-delta-analysis[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai bot added Issue and removed Roadmap labels Apr 16, 2026
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Apr 17, 2026

@coderabbitai Have the following now been resolved?

Annul any requirements that violate the en-GB-oxendict spelling (-ize / -yse / -our) conventions (for example a request to replace "normalize" with "normalise" or "artefact" with "artifact"), or where the requirement unnecessarily increases cyclomatic complexity.

Please address the comments from this code review:

## Overall Comments
- In `recover_user_editable_hir_span`, you build an intermediate `Vec<SpanRecoveryFrame<Span>>` only to pass `as_slice` into `recover_user_editable_span`; consider iterating the callsite chain directly and feeding the policy without allocation to avoid per-call heap traffic on hot paths.
- The new `attribute_within_item` logic returns `false` whenever `item_user_editable_span` is `None`, even if the raw item span is non-dummy; consider falling back to `raw_item_span` in that case so you don’t accidentally drop all attributes for items whose spans cannot be recovered.

## Individual Comments

### Comment 1
<location path="crates/function_attrs_follow_docs/src/tests/order_detection.rs" line_range="99-108" />
<code_context>
+    assert_eq!(recover_user_editable_span(&frames), expected);
+}
+
+#[rstest]
+fn keeps_direct_user_editable_span() {
+    let span = source_span(1, 1, 8);
</code_context>
<issue_to_address>
**suggestion (testing):** Add tests for `attribute_within_item` covering dummy item spans, `None` item spans, and out-of-bounds recovered attribute spans.

To fully exercise the updated `attribute_within_item` logic, please add tests for: (1) a dummy `raw_item_span` that always returns `true`; (2) `item_span: None` returning `false` even when the attribute is within the raw item span; and (3) an attribute span extending beyond the item span to confirm it’s rejected. These will cover the remaining branches without going through the full lint path.
</issue_to_address>

### Comment 2
<location path="docs/execplans/8-1-2-shared-user-editable-span-recovery-helpers.md" line_range="3" />
<code_context>
+# Add shared user-editable span recovery helpers for macro-heavy `rstest` code paths
+
+This ExecPlan is a living document. The sections `Constraints`, `Tolerances`,
+`Risks`, `Progress`, `Surprises & Discoveries`, `Decision Log`, and
+`Outcomes & Retrospective` must be kept up to date as work proceeds.
</code_context>
<issue_to_address>
**suggestion (review_instructions):** ExecPlan is used as an acronym without being defined on first use.

Consider expanding this on first use, for example: `This Execution Plan (ExecPlan) is a living document.` Subsequent occurrences can then safely use the shortened form.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.md`

**Instructions:**
Define uncommon acronyms on first use.

</details>
</issue_to_address>

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 17, 2026

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== src/hir.rs (recover_user_editable_hir_span and span_recovery_frames) ===" 
grep -n "recover_user_editable_hir_span\|span_recovery_frames\|fn " src/hir.rs | head -60
echo ""
echo "=== src/hir.rs full ===" && cat src/hir.rs
echo ""
echo "=== attribute_within_item in driver.rs ===" 
grep -n "attribute_within_item\|item_user_editable\|raw_item" crates/function_attrs_follow_docs/src/driver.rs
echo ""
echo "=== full driver.rs ===" && cat crates/function_attrs_follow_docs/src/driver.rs
echo ""
echo "=== order_detection.rs ===" && cat crates/function_attrs_follow_docs/src/tests/order_detection.rs
echo ""
echo "=== execplan first 20 lines ===" && head -20 docs/execplans/8-1-2-shared-user-editable-span-recovery-helpers.md

Length of output: 22822


@leynos: All four concerns are resolved in the current branch. Here is the breakdown:

Overall Comment 1 — allocation in recover_user_editable_hir_span
Resolved. The function no longer calls span_recovery_frames or builds a Vec. It walks source_callsite() directly in a loop, returning Some(current) on the first non-expansion span and None on a dummy or stalled walk. Zero heap allocation.

Overall Comment 2 — attribute_within_item drops attributes when item_span is None
Resolved. The function now contains let item_span = item_span.unwrap_or(raw_item_span);, so when item_user_editable_span is None the raw item span is used as the containment boundary rather than unconditionally returning false.

Individual Comment 1 — tests for attribute_within_item
Resolved. crates/function_attrs_follow_docs/src/tests/order_detection.rs now includes:

Test Branch covered
dummy_item_spans_accept_recovered_attributes DUMMY_SP raw_item_spantrue
raw_item_span_is_used_when_recovery_fails item_span: None, attribute within raw_item_spantrue
recovered_attribute_spans_outside_item_are_rejected attribute outside item_spanfalse

Individual Comment 2 — "ExecPlan" acronym undefined on first use
Resolved. The execplan opens with "This Execution Plan (ExecPlan) is a living document.", establishing the short form on first use.

Annulment check: None of the requirements violated en-GB-oxendict spelling conventions or increased cyclomatic complexity, so no annulments are needed.

…ntation

- Introduce a policy layer with reusable span recovery utilities in whitaker-common.
- Add adapter layer in whitaker crate for rustc_span integration.
- Provide functions to recover user-editable spans, excluding macro-only glue spans.
- Add detailed documentation on usage, extension, and test coverage.
- Include integration tests in function_attrs_follow_docs for attribute span handling.
- Improve lint handling by ignoring macro-generated attribute spans, avoiding spurious diagnostics.
- Update docs (developers-guide, users-guide, documentation-style-guide) to reflect the new span recovery approach and usage guidelines.
- Enhance tests in hir.rs and order_detection.rs to validate span recovery behavior.

This feature enables lints and tools to correctly handle and recover source locations when procedural macros reorder or inject attributes, improving the accuracy of diagnostics and user experience.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@coderabbitai coderabbitai bot added Roadmap and removed Issue labels Apr 17, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/documentation-style-guide.md`:
- Line 187: Replace the incorrect fenced-block language identifier `text` with
`plaintext` in the documentation (change the opening fence that currently reads
```text to ```plaintext) so the non-code text blocks comply with the project's
documentation-style-guide; update any other occurrences of the `text` fenced
code marker in the file to `plaintext` to ensure consistency.
- Line 269: Replace any fenced-code block language identifier of ```text with
```plaintext in the documentation (i.e., change the literal token ```text to
```plaintext for non-code text fences); search for occurrences of the string
"```text" (e.g., the top of the non-code blocks) and update them to
"```plaintext", then run markdown lint/format checks to ensure the file remains
valid.

In `@docs/users-guide.md`:
- Around line 290-291: Add an Oxford comma to the sentence "With `rstest`, place
the doc comment before all attributes including the test annotation:" by
inserting a comma after "attributes" so it reads "With `rstest`, place the doc
comment before all attributes, including the test annotation:" to improve
clarity.
🪄 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

Run ID: 5d944f0f-4849-49fc-9ee4-e1c59635f94e

📥 Commits

Reviewing files that changed from the base of the PR and between 21ac517 and 4d2ca95.

📒 Files selected for processing (7)
  • common/tests/rstest_span_recovery_behaviour.rs
  • crates/function_attrs_follow_docs/src/driver.rs
  • crates/function_attrs_follow_docs/src/tests/order_detection.rs
  • docs/developers-guide.md
  • docs/documentation-style-guide.md
  • docs/users-guide.md
  • src/hir.rs

### Roadmap example

```markdown
```text
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use plaintext instead of text for non-code fenced blocks.

The coding guidelines explicitly require plaintext as the language identifier for non-code text blocks. Change text to plaintext to comply with the project's documentation standards.

Triage: [type:syntax/md]

📝 Proposed fix
-```text
+```plaintext

As per coding guidelines: "Always provide a language identifier for fenced code blocks; use plaintext for non-code text."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/documentation-style-guide.md` at line 187, Replace the incorrect
fenced-block language identifier `text` with `plaintext` in the documentation
(change the opening fence that currently reads ```text to ```plaintext) so the
non-code text blocks comply with the project's documentation-style-guide; update
any other occurrences of the `text` fenced code marker in the file to
`plaintext` to ensure consistency.

### ADR template

```markdown
```text
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use plaintext instead of text for non-code fenced blocks.

The coding guidelines explicitly require plaintext as the language identifier for non-code text blocks. Change text to plaintext to comply with the project's documentation standards.

Triage: [type:syntax/md]

📝 Proposed fix
-```text
+```plaintext
🤖 AI coding-agent prompt to apply both fixes
Apply the following changes to docs/documentation-style-guide.md:

1. On line 187, replace ```text with ```plaintext
2. On line 269, replace ```text with ```plaintext

After applying the changes, validate by running:
- make markdownlint to verify Markdown syntax
- make fmt to format the file
- Visually inspect lines 187 and 269 to confirm the changes

As per coding guidelines: "Always provide a language identifier for fenced code blocks; use plaintext for non-code text."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/documentation-style-guide.md` at line 269, Replace any fenced-code block
language identifier of ```text with ```plaintext in the documentation (i.e.,
change the literal token ```text to ```plaintext for non-code text fences);
search for occurrences of the string "```text" (e.g., the top of the non-code
blocks) and update them to "```plaintext", then run markdown lint/format checks
to ensure the file remains valid.

Comment thread docs/users-guide.md
Comment on lines +290 to +291
With `rstest`, place the doc comment before all attributes including the test
annotation:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add Oxford comma for clarity.

Insert a comma after "attributes" to improve readability.

Triage: [type:grammar]

📝 Proposed fix
-With `rstest`, place the doc comment before all attributes including the test
+With `rstest`, place the doc comment before all attributes, including the test
 annotation:
🧰 Tools
🪛 LanguageTool

[uncategorized] ~290-~290: Possible missing comma found.
Context: ...test`, place the doc comment before all attributes including the test annotation: ```rust...

(AI_HYDRA_LEO_MISSING_COMMA)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/users-guide.md` around lines 290 - 291, Add an Oxford comma to the
sentence "With `rstest`, place the doc comment before all attributes including
the test annotation:" by inserting a comma after "attributes" so it reads "With
`rstest`, place the doc comment before all attributes, including the test
annotation:" to improve clarity.

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.

1 participant