Skip to content

Adopt line-aligned ast_transform 3.0; delete the mapping apparatus (3.0.0)#13

Merged
JPDuchesne merged 26 commits into
mainfrom
jpd/line-aligned-emission
Jul 24, 2026
Merged

Adopt line-aligned ast_transform 3.0; delete the mapping apparatus (3.0.0)#13
JPDuchesne merged 26 commits into
mainfrom
jpd/line-aligned-emission

Conversation

@JPDuchesne

@JPDuchesne JPDuchesne commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What

Adopts ast_transform 3.0 line-aligned emission (rspockframework/ast-transform#12) and deletes RSpock's entire backtrace-mapping apparatus. Raw VM line numbers are now the source line numbers — backtraces, failure messages, breakpoints, and debugger display are source-true with zero runtime filtering.

Builds on the red acceptance tests from this branch's first commits; all five now pass: Then assertion failures, failing Where rows, interaction setups, and Cleanup failures cite their own source lines, and the generated test name still embeds the row line.

Simplification (Stage 2)

  • Node classes register on ASTTransform::Node — RSpock's Node::REGISTRY, Node.build, and the NodeBuilder s override are deleted; classes keep only accessors.
  • TestMethodTransformation rebuilt around source order: Then/Expect sections lower in place (parsers anchor IR nodes at the statements they classify via s_at), and the hoisted_setups accumulator threading is gone.
  • _test_index_ / _line_number_ removed (MethodCallToLVarTransformation deleted — a full extra AST pass per test). The row line still flows into the generated test NAME (uniqueness + -n selector target) through internal __rspock_row_index__ / __rspock_row_line__ block params.

Execution-order via deferral (Stage 2b)

  • Interactions: run_after(body, run: when_statements, after: last_setup) — the When body stays at its source position textually and executes after the last interaction setup. Only tests with interactions pay the lambda.
  • raises + interactions: low-level defer, with the deferred execution composed into the assert_raises block.
  • raises without interactions: the When body inlines directly into assert_raises (unchanged).

Deletions (Stage 3)

RSpock::BacktraceFilter, RSpock::Minitest::BacktraceFilter, the Minitest plugin, the Rails generator + initializer template, and the class-body rescue wrapper. The subprocess canary from #12 inverts: it now asserts source-true output with zero filter machinery.

Breaking (3.0.0)

  • Ruby 3.2 support dropped (EOL since March 2026): required_ruby_version >= 3.3, CI matrix now 3.3/4.0.
  • Direct parser and unparser runtime dependencies removed: rspock uses both only through ast_transform, which owns their floors.
  • _test_index_ / _line_number_, the Rails generator, and the backtrace filters (above) are the user-facing removals.

Docs

README loses the Rails-generator section and the magic-variables tips; Debugging documents "just works" plus the Where-row isolation triage (rerun command / -n by row line / conditional break on column locals). The skill (riding from #11) gets the same updates.

Suite: 232 tests, 0 failures. Supersedes the "plugin still needed" documentation test from #12.

Note: Gemfile temporarily pins ast_transform to the branch; swap to the released 3.0.0 before merge.

Made with Cursor

JPDuchesne and others added 6 commits July 21, 2026 11:46
Failing tests must report line numbers from the source the developer
wrote, not the transformed code that executes. Unit tests cover
RSpock::BacktraceFilter and the Minitest wrapper against a real
registered SourceMap; a subprocess integration test pins the end-to-end
behavior, including one test documenting that the Minitest plugin
filter is still required — ast-transform's compile-with-source-path fix
corrected file paths but line mapping still needs the filter.

Co-authored-by: Cursor <cursoragent@cursor.com>
Raw line numbers — no Minitest plugin, no BacktraceFilter, no SourceMap —
must be source line numbers once ast-transform emits line-aligned code.
Four angles through the full RSpock transformation:

- Then assertion failure cites the statement's source line
- failing Where row cites the failing statement's source line
- interaction setup executes at the interaction's own source line
  (today interactions hoist textually to the When position)
- Cleanup-block failure cites the cleanup statement's source line

Plus one green pin: the generated test name embeds the data row's source
line — the row-isolation selector that must survive _line_number_ removal.

All four alignment tests are red today.

Co-authored-by: Cursor <cursoragent@cursor.com>
Stage 2 + 2b + 3 of the line-aligned emission plan:

Adopt promoted machinery: node classes register on ASTTransform::Node
(REGISTRY/Node.build/NodeBuilder deleted); parsers anchor IR nodes
(s_at) at the statements they classify so lowered assertions and Mocha
setups emit at their own source lines.

TestMethodTransformation rebuilt around source order: Then/Expect
sections lower in place, and execution-order needs that source order
cannot express use ast-transform's deferral facility — interactions
defer the When body via run_after (the paved road); raises-with-
interactions composes the deferred execution into assert_raises via
low-level defer; raises-without-interactions inlines the When body. The
hoisted_setups accumulator is gone.

_test_index_/_line_number_ removed: MethodCallToLVarTransformation
(a full extra AST pass per test) deleted; the row line still flows into
the generated test NAME (uniqueness + -n selector target) through
internal __rspock_row_index__/__rspock_row_line__ block params.

Deleted: BacktraceFilter, its Minitest adapter and plugin, the Rails
generator + initializer template, and the source_map_rescue_wrapper —
line-aligned emission makes raw VM line numbers the source line numbers,
so there is nothing left to map. The subprocess canary inverts: it now
asserts source-true output with zero filter machinery.

All five line-alignment acceptance tests green; suite 232 tests,
0 failures. rspock now requires ast_transform ~> 3.0.

Co-authored-by: Cursor <cursoragent@cursor.com>
README: the Rails generator section is replaced by "no setup needed";
the Debugging chapter now documents source-true backtraces and debugger
display, the one interaction-stepping oddity, and the Where-row isolation
triage (rerun command / -n by row line / conditional break on column
locals) replacing the deleted _test_index_/_line_number_ magic variables.
Test-name interpolation documents the appended '<index> line <line>'
suffix as the row-isolation selector target.

Skill (from the PR #11 branch, riding this one): drops the Rails
generator setup, reframes tmp/ast_transform as line-matched, and
replaces the magic-variable debugging advice with the same triage.

Version 3.0.0; rspock requires ast_transform ~> 3.0.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Ruby 3.2 is EOL (March 2026) — required_ruby_version and the CI matrix
move to 3.3+, which also lifts the temporary unparser < 0.9 Gemfile pin.
The direct parser and unparser runtime dependencies are removed: rspock
only touches both through ast_transform, which owns their floors
(unparser >= 0.8, parser >= 3.3). Lockfile now on unparser 0.9.0.

Co-authored-by: Cursor <cursoragent@cursor.com>
@codecov-commenter

codecov-commenter commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

JPDuchesne and others added 20 commits July 23, 2026 08:49
Co-authored-by: Cursor <cursoragent@cursor.com>
dev.yml pins Ruby 4.0.6 (latest; converges with the org toolchain) and
declares up/test; dev up provisions the per-machine .shadowenv.d (now
gitignored) via rbenv, so the right Ruby activates without manual PATH
surgery. .ruby-version gives plain rbenv users the same pin. Inert for
external contributors — plain Bundler still works, README says so.

Co-authored-by: Cursor <cursoragent@cursor.com>
dev now single-sources the project Ruby from the dependencies.rb manifest;
dev.yml's ruby: key is removed. Toolchain-only manifest — gems stay
bundler-managed via the hand-written gemspec/Gemfile.

Co-authored-by: Cursor <cursoragent@cursor.com>
Updates to ast_transform's proc lowering (hidden closure is a
non-lambda proc with method-scope pre-declarations):

- Fixes the long-standing NameError when a Then expectation or an
  ensure-run Cleanup reads a local assigned in a When body deferred
  behind interaction setups.
- New subprocess acceptance fixtures pin both: When-result readable in
  Then, and Cleanup seeing the When local after a failing Then.
- Transformation expectations updated (proc do + result = result
  pre-declaration) and a new expectation pinning the emission shape.
- CI adds Ruby 3.4.

Co-authored-by: Cursor <cursoragent@cursor.com>
The When body is now wrapped in one thunk node placed at its execution
point (after the last interaction setup, or composed into the
assert_raises block); ast_transform's lowering re-emits the body at its
own source lines. The raises-with-interactions branch loses the manual
placement splice, and the hidden lvar is now __ast_thunk_N__.

Co-authored-by: Cursor <cursoragent@cursor.com>
ast_transform's line-aligned emitter now indents freshly placed
statements to their source column; transformation expectations pick up
the indented (source-shaped) layout.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
The &block tests asserted regex fragments on deterministic unparser
output. Replace them with exact expected source via assert_transforms,
and make assert_transforms compare exactly instead of massaging
trailing newlines (gsub(/\n$/, '') stripped two newlines from
multi-line expectations, since $ also matches before a final newline).

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Codecov flagged build_raises_body's interactions arm and its
insert_after helper as the only uncovered lines in
test_method_transformation.rb. Pin the transformed shape (assert_raises
wrapping the thunk call, inserted after the last Mocha setup) and add a
behavioral example test exercising the combination end to end.

Co-authored-by: Cursor <cursoragent@cursor.com>
Nothing Rails-specific remains: no generator, no backtrace cleaner to
configure. The railtie still loads the rake tasks automatically, which
is why the Rakefile snippet stays qualified as non-Rails.

Co-authored-by: Cursor <cursoragent@cursor.com>
The old name described the deleted mechanism, not the behavior the test
pins. Also spell out why the fixture's blank lines and split
expressions are load-bearing: without them a non-aligned emitter would
reproduce the source line numbers by coincidence.

Co-authored-by: Cursor <cursoragent@cursor.com>
Prep for a future rubocop-shopify adoption (Layout/LineLength: 120),
mirroring ast-transform. Expected-output heredocs keep their long lines
(AllowHeredoc exempts them, and they pin real emitted output).

Co-authored-by: Cursor <cursoragent@cursor.com>
The line-length wrap of validate_blocks' second raise pulled it into
the PR's patch, and Codecov showed it had never been exercised: no test
ended a test method on a block that requires a successor. Pin the
BlockError for a When with no Then.

Co-authored-by: Cursor <cursoragent@cursor.com>
@JPDuchesne
JPDuchesne merged commit 2021c60 into main Jul 24, 2026
3 checks passed
@JPDuchesne
JPDuchesne deleted the jpd/line-aligned-emission branch July 24, 2026 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants