Issue #16982 has been reported by pocke (Masataka Kuwabara).

----------------------------------------
Bug #16982: Define instance_methods and so on explicitly to class generated by DelegateClass
https://bugs.ruby-lang.org/issues/16982

* Author: pocke (Masataka Kuwabara)
* Status: Open
* Priority: Normal
* ruby -v: ruby 2.8.0dev (2020-06-23T13:58:26Z master dc351ff984) [x86_64-linux]
* Backport: 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN
----------------------------------------

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 a method name that added after the class is defined.
For example


```ruby
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 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 a method that added after the class is defined.
And it 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:


```ruby
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) # return a 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 these all methods.
So I think `instance_method` method should return a method object.



I actually got the error when I try 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


```ruby
# test.rb
require 'delegate'

class Parent
end
class Child < DelegateClass(Parent)
end
class Parent
  def foo() end
end
```


```bash
$ 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, for `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.







-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>