Project

General

Profile

Bug #16982

Define instance_methods and so on explicitly on classes generated by DelegateClass

Added by pocke (Masataka Kuwabara) about 1 month ago. Updated 29 days ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Target version:
-
ruby -v:
ruby 2.8.0dev (2020-06-23T13:58:26Z master dc351ff984) [x86_64-linux]
[ruby-core:98929]

Description

I found two problems with DelegateClass.
The patch is available on GitHub. https://github.com/ruby/ruby/pull/3221

instance_methods

First, instance_methods method's result does not contain the names of methods that are added after the class is defined.
For example

require 'delegate'

class Parent
  def parent_public; end

  protected

  def parent_protected; end
end

class Child < DelegateClass(Parent)
end

class Parent
  def parent_public_added; end

  protected

  def parent_protected_added; end
end

# They're ok.
p Child.instance_methods.include? :parent_public    # => true
p Child.instance_methods.include? :parent_protected # => true

# They're wrong. :parent_public_added and :parent_protected_added should be included, but they are not.
p Child.instance_methods.include? :parent_public_added    # => false
p Child.instance_methods.include? :parent_protected_added # => false

So I think DelegateClass should define instance_methods method explicitly, like public_instance_methods and protected_instance_methods.

I've added instance_methods in this commit https://github.com/ruby/ruby/pull/3221/commits/3f49e3ff094288e41b5629fd07aa8fa858abb196

instance_method and public_instance_method

Second, instance_method and public_instance_method cannot get a method object for methods that are added after the class is defined.
And they cannot get a method object for :to_s and so on because of https://github.com/ruby/ruby/blob/fbb32b1f483925987d225b4dc08efd197775bcae/lib/delegate.rb#L390.

For example:

require 'delegate'

class Parent
  def parent() end
end

class Child < DelegateClass(Parent)
end

class Parent
  def parent_added() end
end

p Child.instance_method(:parent) # returns an UnboundMethod
p Child.instance_method(:parent_added) # undefined method `parent_added' for class `Child' (NameError)
p Child.instance_method(:to_s) # undefined method `to_s' for class `Child' (NameError)

But actually a Child instance has all these methods.
So I think instance_method method should return a method object.

I actually got the error when I tried to use rbs prototype runtime.
https://github.com/ruby/rbs/blob/150bc6e65838eab2be088a5cc456b6f4b04bc81c/lib/rbs/prototype/runtime.rb#L172
This code raises an error when it analyses a class that inherits a class generated by DelegateClass method.
For example

# test.rb
require 'delegate'

class Parent
end
class Child < DelegateClass(Parent)
end
class Parent
  def foo() end
end
$ rbs prototype runtime --require-relative test.rb  Child 
W, [2020-06-24T21:17:45.628527 #890363]  WARN -- rbs: Skipping anonymous superclass #<Class:0x000056098f280750> of Child
/home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:172:in `instance_method': undefined method `foo' for class `Child' (NameError)
    from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:172:in `target_method?'
    from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:210:in `block in generate_methods'
    from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:210:in `select'
    from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:210:in `generate_methods'
    from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:351:in `generate_class'
    from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:43:in `block in decls'
    from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:40:in `each'
    from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:40:in `decls'
    from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/cli.rb:455:in `run_prototype'
    from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/cli.rb:85:in `run'
    from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/exe/rbs:7:in `<top (required)>'
    from /home/pocke/.rbenv/versions/trunk/bin/rbs:23:in `load'
    from /home/pocke/.rbenv/versions/trunk/bin/rbs:23:in `<main>'

And I also got the same error when I applied the first patch to instance_methods method. The first patch broke ruby/test/ruby/test_method.rb.
https://github.com/ruby/ruby/blob/d1fb446b62b1e114252606dcf040dd9392f25170/test/ruby/test_method.rb#L1300

I fixed the problem with this commit https://github.com/ruby/ruby/pull/3221/commits/d1fb446b62b1e114252606dcf040dd9392f25170.

#1

Updated by sawa (Tsuyoshi Sawada) about 1 month ago

  • Description updated (diff)
  • Subject changed from Define instance_methods and so on explicitly to class generated by DelegateClass to Define instance_methods and so on explicitly on classes generated by DelegateClass
#2

Updated by pocke (Masataka Kuwabara) 29 days ago

  • Status changed from Open to Closed

Applied in changeset git|ba81bc24e62d03cc0f4c05cf4e2b378696631568.


Add instance_methods to class generated by DelegateClass

Also, make DelegateClass.instance_method fallback to superclass.

Fixes [Bug #16982]

Updated by jeremyevans0 (Jeremy Evans) 29 days ago

I agree that this is a bug, and your pull request is a good way to fix it. I have squash merged your pull request. Thank you pocke (Masataka Kuwabara)!

Also available in: Atom PDF