Skip to content

fix(ruby): keep a wrapped chain indented inside a block that is a hash value - #43

Merged
amritk merged 3 commits into
mainfrom
claude/epic-sagan-jvrszn
Sep 30, 2026
Merged

amritk merged 3 commits into
mainfrom
claude/epic-sagan-jvrszn

Conversation

@amritk

@amritk amritk commented Sep 26, 2026

Copy link
Copy Markdown
Member

What & why

The RuboCop 1.91.0 bump (#40) introduced a regression in the Layout pass. A wrapped method chain inside a block that is a hash value lost the indentation on every continuation line:

# syntax_tree's output, and what RuboCop 1.84.0 left alone
run: -> do
  client
    .beta
    .messages
end

# what stock RuboCop 1.84.1 through 1.91.0 correct it to
run: -> do
  client
  .beta
  .messages
end

The cause is upstream. Since 1.84.1, Layout/MultilineMethodCallIndentation searches upward from a chain for an enclosing hash pair, and that search doesn't stop at a block body. So the chain inside the lambda is treated as the value of run: and aligned with its receiver. It still reproduces on RuboCop's current master. It's reported as rubocop/rubocop#15761, with the same fix proposed upstream.

This PR adds packages/ruby/src/rubocop-patch.ts, which is applied at VM boot the same way as the existing syntax_tree fixes. The artifact itself is not rebuilt.

  • The patch stops that search where a block, begin, if/else or loop body starts. Those chains go back to the indentation RuboCop 1.84.0 gave them.
  • A chain that is itself the hash value (key: foo.bar wrapped onto the next line) is still aligned exactly as stock 1.91.0 aligns it.
  • case and def bodies are deliberately left to stock. On the fallback path RuboCop has an older, separate misalignment there, so routing them to it would look worse.

Which packages

packages/ruby, plus the root README.md.

Exactness

The reference tool is still RuboCop 1.91.0. This adds one output-changing fix, so the README status moves from exact +3 fixes to exact +4 fixes, and both READMEs describe what diverges and why. The divergence is held narrow in three ways:

  • test/rubocop-conformance.test.ts still asserts byte-identical output against the native rubocop 1.91.0 binary for every other sample. That includes two new ones where the chain is the hash value, which the patch must leave alone.
  • A new test asserts that native RuboCop still flattens each patched shape, and that our output differs from it only in indentation. When RuboCop ships a fix, that test fails and the patch can be deleted.
  • src/rubocop-patch.test.ts fails if the VM reports any RuboCop other than 1.91.0. That way a pin bump can't leave a stale copy of the method overriding the new gem.

I also ran syntax_tree's output of 1,238 files through native rubocop --autocorrect --only Layout with and without the patch: rubocop 1.91's lib and specs, rubocop-ast, syntax_tree, parser and regexp_parser. Every file came out the same both ways. The patch only fires on the nested-body shapes above.

How it was validated

  • bun run check (the only warnings are 3 that were already on main)
  • bun run types:check
  • bun run test: all packages pass. Ruby's 89 tests include the native syntax_tree and RuboCop conformance tests against native gems at the pinned versions.
  • bun run test:node: ran for packages/ruby only. Its entry points and exports haven't changed, but the boot now evaluates one more patch.
  • Rebuilt the wasm artifact: not applicable, since the patch is applied at boot and the artifact is unchanged.

Each new test fails with the patch disabled, except the one that checks a chain that is itself the hash value, which passes either way by design.

Changeset

  • Added a changeset (@scalar/ruby-fmt: patch)

Generated by Claude Code

…h value

RuboCop 1.84 through 1.91 make Layout/MultilineMethodCallIndentation search
upward for an enclosing hash pair without stopping at a block, so a chain
inside a lambda, proc, do block, begin, if or loop that is a hash value is
aligned as if it were the value itself - flush under its receiver. A
boot-time patch stops that search where the body starts, which restores the
indent RuboCop 1.82 gave these chains, and leaves a chain that is itself the
hash value aligned exactly as stock 1.91 aligns it.

Tests cover the reported shape, each body the patch handles, the case it must
leave alone, a version guard on the RuboCop it was derived from, and a
conformance check that native RuboCop still flattens these shapes while we
differ from it only in that indentation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XSYkhEutUrPqAdCqYSZ9vv
…oCop 1.84.1

RuboCop 1.84.0 still indents a chain inside a block that is a hash value;
the regression arrived in 1.84.1 with the fix for rubocop/rubocop#11513.
The MultilineMethodCallIndentation regression the patch works around is
reported as rubocop/rubocop#15761, which is what to watch before retiring it.
@amritk
amritk merged commit 5c9e6c8 into main Sep 30, 2026
8 checks passed
@amritk
amritk deleted the claude/epic-sagan-jvrszn branch September 30, 2026 00:49
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.

3 participants