Bug #21816
closediseq code_location of proc changed
Description
TBH, I'm not really sure if that title matches the problem here, but I can produce this issue with the following test:
# bug.rb
# note the indentation of the callable
callable = -> { puts "Hello, World!" }
iseq = RubyVM::InstructionSequence.of(callable)
pp iseq.to_a
pp iseq.to_a[4]
pp iseq.to_a[4][:code_location][1]
Ruby 4.0.0:
$ ruby -v bug.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [x86_64-linux]
["YARVInstructionSequence/SimpleDataFormat",
4,
0,
1,
{arg_size: 0, local_size: 0, stack_max: 2, node_id: 7, code_location: [1, 15, 1, 40], node_ids: [4, 5, 4, 7], parser: :prism},
"block in <main>",
"bug.rb",
"/tmp/tmp.Fbq5WA0GnO/bug.rb",
1,
:block,
[],
{},
[],
[1,
:RUBY_EVENT_LINE,
:RUBY_EVENT_B_CALL,
[:putself],
[:putchilledstring, "Hello, World!"],
[:opt_send_without_block, {mid: :puts, flag: 20, orig_argc: 1}],
:RUBY_EVENT_B_RETURN,
[:leave]]]
{arg_size: 0, local_size: 0, stack_max: 2, node_id: 7, code_location: [1, 15, 1, 40], node_ids: [4, 5, 4, 7], parser: :prism}
15
Ruby 4.1.0:
$ ruby -v bug.rb
ruby 4.1.0dev (2025-12-30T18:02:00Z master 9d37155cfc) +PRISM [x86_64-linux]
["YARVInstructionSequence/SimpleDataFormat",
4,
1,
1,
{arg_size: 0,
local_size: 0,
stack_max: 2,
node_id: 7,
code_location: [1, 13, 1, 40],
node_ids: [4, 5, 4, 7],
parser: :prism},
"block in <main>",
"bug.rb",
"/tmp/tmp.Fbq5WA0GnO/bug.rb",
1,
:block,
[],
{},
[],
[1,
:RUBY_EVENT_LINE,
:RUBY_EVENT_B_CALL,
[:putself],
[:putchilledstring, "Hello, World!"],
[:opt_send_without_block, {mid: :puts, flag: 20, orig_argc: 1}],
:RUBY_EVENT_B_RETURN,
[:leave]]]
{arg_size: 0,
local_size: 0,
stack_max: 2,
node_id: 7,
code_location: [1, 13, 1, 40],
node_ids: [4, 5, 4, 7],
parser: :prism}
13
For context:
This second value being 13 instead of 15 is causing Rails tests to fail:
https://buildkite.com/rails/rails-nightly/builds/3447#019b710f-e78a-405f-8297-20d2e26b0912/L3277
We want:
{ @object.num }
But we get:
-> { @object.num }
I am pretty sure using the same version of prism:
/tmp/tmp.Fbq5WA0GnO => rbenv shell 4.1.0
/tmp/tmp.Fbq5WA0GnO => gem list | grep prism
prism (1.7.0)
/tmp/tmp.Fbq5WA0GnO => rbenv shell 4.0.0
/tmp/tmp.Fbq5WA0GnO => gem list | grep prism
prism (1.7.0)
We use this data to get the original source string of a block we use to decorate the assert_difference assertion:
https://github.com/rails/rails/blob/3d31d593e6cf0f82fa9bd0338b635af2f30d627b/activesupport/lib/active_support/testing/assertions.rb#L331-L363
https://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_difference
Updated by nobu (Nobuyoshi Nakada) about 2 months ago
· Edited
- Backport changed from 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN to 3.2: DONTNEED, 3.3: DONTNEED, 3.4: DONTNEED, 4.0: UNKNOWN
Probably it's because of c970d2941d56a862bb9bb3b808cb588c2982f436.
That is the indent is not related, but the behavior change.
Updated by nobu (Nobuyoshi Nakada) about 2 months ago
- Subject changed from iseq code_location miss calculated indent to iseq code_location of proc changed
Updated by Eregon (Benoit Daloze) about 1 month ago
- Related to Bug #21784: Proc#source_location start column seems strange for -> {} added
Updated by Eregon (Benoit Daloze) about 1 month ago
· Edited
It is intentional change of #21784.
The source of the lambda in ...; -> { @object.num }; ... is -> { @object.num }, isn't it?
I think the code should be adapted to handle that.
BTW https://github.com/rails/rails/blob/3d31d593e6cf0f82fa9bd0338b635af2f30d627b/activesupport/lib/active_support/testing/assertions.rb#L331-L363
seems to already not support parameters, e.g.:
$ RUBYOPT=--parser=parse.y irb
irb(main):005> RUBY_VERSION
=> "3.4.7"
irb(main):006> RubyVM::AbstractSyntaxTree.of(-> (a, b) { puts "Hello, World!" }).source
=> "(a, b) { puts \"Hello, World!\" }"
$ RUBYOPT=--parser=parse.y irb
irb(main):003> RUBY_VERSION
=> "4.0.0"
irb(main):004> RubyVM::AbstractSyntaxTree.of(-> (a, b) { puts "Hello, World!" }).source
=> "-> (a, b) { puts \"Hello, World!\" }"
seems more correct/intuitive.
Updated by Eregon (Benoit Daloze) about 1 month ago
What is strange is we can see this in 4.0.0, while it should only be in master.
https://github.com/ruby/ruby/pull/15568 was not merged.
And https://github.com/ruby/ruby/pull/15580 was merged after 4.0.0.
Updated by Earlopain (Earlopain _) about 1 month ago
The change in RubyVM::AST.of seems to have been introduced in http://github.com/ruby/ruby/commit/2ccb2de677849732181224cb9fd1a831dbaac4c0. There is a test update where the source for the proc Proc.new { 1 + 2 } changes from { 1 + 2 } to Proc.new { 1 + 2 } so that seems intentional. The same is also true for stabby lambdas.
So I guess you could say that the change in 4.1 more closely matches the behaviour you get from RubyVM::AST. On the other hand, RubyVM::IS.of doesn't include the block receiver. That was briefly brought up in https://bugs.ruby-lang.org/issues/21784 but I don't think it got mentioned again.
Updated by zzak (zzak _) about 1 month ago
If I understand correctly, this is intended behavior to include the "->" stabby part in the full source definition.
I've sent a PR to rails to work around this: https://github.com/rails/rails/pull/56624
If that makes sense this can be closed, thank you!
Updated by Eregon (Benoit Daloze) about 1 month ago
- Status changed from Open to Closed