Bug #16982
Updated by sawa (Tsuyoshi Sawada) over 4 years ago
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 a method name that are 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 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 a method that are added after the class is defined. And they 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) # returns an 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 these methods. So I think `instance_method` method should return a method object. I actually got the error when I tried 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 to 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.