Bug #21125
openKernel is called first
Description
Kernel#system is being called when there is a column or method name named system.
The other methods of Kernel reproduce the problem.
Ex.Rand returns a random value, and exit terminates the program.
This problem did not occur until Ruby 3.3 series, but has occurred since Ruby 3.4.
Reproducible script:¶
class ObjectifiedHash
def initialize(hash)
@hash = hash
end
private
attr_reader :hash
def method_missing(method_name, *args, &block)
if hash.key?(method_name.to_sym)
hash[method_name.to_sym]
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
hash.key?(method_name.to_sym) || super
end
end
class Foo
def system(...)
# Kernel#system is called
raw.system(...)
# (...) passes if there is no
# raw.system
end
def initialize(raw)
@raw = raw
end
def get_system
system
end
private
attr_reader :raw
end
class Test
def self.run
obj = ObjectifiedHash.new({
system: 'system'
})
foo = Foo.new(obj)
# foo.system would pass.
# After executing foo.system, hoge.get_system also passes.
# p foo.system
p foo.get_system
end
end
Test.run
Execution Results¶
hoge.rb:26:in 'Kernel#system': wrong number of arguments (given 0, expected 1+) (ArgumentError)
raw.system(...)
^^^
from ruby.rb:26:in 'Foo#system'
from ruby.rb:36:in 'Foo#get_system'
from ruby.rb:54:in 'Test.run'
from ruby.rb:58:in '<main>'
Expected results¶
"system"
PR that may be relevant: https://github.com/ruby/ruby/pull/11028
Updated by jeremyevans0 (Jeremy Evans) 13 days ago
- Status changed from Open to Rejected
This isn't a bug. If you want method_missing
to be called instead of a method in Kernel, you need to undefine the existing method:
class ObjectifiedHash
undef_method :system
# rest of your example
You could consider the following instead, so that methods in Kernel are not included in the class:
class ObjectifiedHash < BasicObject
# rest of your example
Updated by mikik0 (Hashino Mikiko) 13 days ago
@jeremyevans0 (Jeremy Evans) san
It works with Ruby 3.3 series, but not from Ruby 3.4. Is it a specification?
Updated by jeremyevans0 (Jeremy Evans) 13 days ago
- Status changed from Rejected to Open
mikik0 (Hashino Mikiko) wrote in #note-2:
@jeremyevans0 (Jeremy Evans) san
It works with Ruby 3.3 series, but not from Ruby 3.4. Is it a specification?
You are correct. It also works on Ruby 3.4 when using --parser=parse.y
, so this appears to be related to prism. I'll have to do more analysis to determine which behavior is correct, but it's certainly a bug that the behavior is not the same between prism and parse.y.
Updated by jeremyevans0 (Jeremy Evans) 13 days ago
jeremyevans0 (Jeremy Evans) wrote in #note-3:
mikik0 (Hashino Mikiko) wrote in #note-2:
@jeremyevans0 (Jeremy Evans) san
It works with Ruby 3.3 series, but not from Ruby 3.4. Is it a specification?You are correct. It also works on Ruby 3.4 when using
--parser=parse.y
, so this appears to be related to prism. I'll have to do more analysis to determine which behavior is correct, but it's certainly a bug that the behavior is not the same between prism and parse.y.
Sorry, that was me testing the wrong version. The behavior is the same between prism and parse.y, so this is a difference in behavior between Ruby 3.3 and 3.4. I still need to more analysis to determine which behavior is correct.
Updated by jeremyevans0 (Jeremy Evans) 13 days ago
- Assignee set to tenderlovemaking (Aaron Patterson)
The reason this works in Ruby 3.3 and below is that system
is a private method and not a public method, and calls to private methods where the receiver is not self
call method_missing
. raw.system
is a method call where the receiver is not self
, but Kernel#system
is a private method, so this should be calling method_missing
. The fact that method_missing
is not called in Ruby 3.4 is a bug. I've determined the bug is in generic argument forwarding. If you switch ...
(generic argument forwarding) to *, **, &
(anonymous splat usage), things work correctly. Assigning to @tenderlovemaking (Aaron Patterson).
Updated by byroot (Jean Boussier) 13 days ago
- Backport changed from 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN to 3.1: DONTNEED, 3.2: DONTNEED, 3.3: DONTNEED, 3.4: REQUIRED