From: "jeremyevans0 (Jeremy Evans)" Date: 2021-12-27T16:23:29+00:00 Subject: [ruby-core:106839] [Ruby master Bug#18435] Calling `protected` on ancestor method changes result of `instance_methods(false)` Issue #18435 has been updated by jeremyevans0 (Jeremy Evans). ufuk (Ufuk Kayserilioglu) wrote in #note-2: > I understand why the difference in behaviour is happening, but I respectfully disagree that this is not a bug. That's fair. It's not 100% clear that this isn't a bug. Which I why I would like to get input from other committers. > Moreover, as I stated in my original report `private` does not have a similar problem, either. This is incorrect, `private` has exactly the same issue, it's just that `instance_methods` doesn't include private methods: ```ruby module A def method1() end end class B include A private :method1 private def method2() end end p B.private_instance_methods(false) #=> [:method1, :method2] p B.instance_method(:method1).owner #=> A ``` `public` has the same issue: ```ruby module A private def method1() end end class B include A public :method1 def method2() end end p B.instance_methods(false) #=> [:method1, :method2] p B.instance_method(:method1).owner #=> A ``` > Basically the documentation of `instance_methods` explicitly states: > > > If the optional parameter is `false`, the methods of any ancestors are not included. > > and, in this case, that statement is not correct. This depends on your definition of "methods of any ancestors". As I mentioned, `public`/`private`/`protected` create method entries in the current class if the method whose visibility they are affecting is defined in the parent class. I think that makes them methods of the current class, even if the definition occurs in the ancestor. After doing some more testing, I think there is a bug here, but it's related to `instance_method`/`method` returning the wrong information. Evidence of this behavior can be found via the newly introduced methods for checking method visibility: ```ruby module A def method1() end end class B include A protected :method1 end p A.instance_method(:method1).public? #=> true p B.instance_method(:method1).public? #=> true (should be false) p A.instance_method(:method1).protected? #=> false p B.instance_method(:method1).protected? #=> false (should be true) p B.new.method(:method1).public? #=> true (should be false) p B.new.method(:method1).protected? #=> false (should be true) ``` I'll see if I can work on a patch to fix this. ---------------------------------------- Bug #18435: Calling `protected` on ancestor method changes result of `instance_methods(false)` https://bugs.ruby-lang.org/issues/18435#change-95651 * Author: ufuk (Ufuk Kayserilioglu) * Status: Open * Priority: Normal * ruby -v: ruby 2.7.5p203 (2021-11-24 revision f69aeb8314) [x86_64-darwin20] * Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- As documented `instance_methods(false)` works as follows: ```ruby module A def method1() end end class B include A def method2() end end p B.instance_methods(false) #=> [:method2] ``` However, calling `protected` on the method defined by `A`, unexpectedly changes the result of `instance_methods(false)` on `B`, even though the owner of the method is still `A`: ```ruby module A def method1() end end class B include A protected :method1 def method2() end end p B.instance_methods(false) #=> [:method1, :method2] p B.instance_method(:method1).owner #=> A ``` In contrast, calling `private` or `public` on the same method does not cause any changes on the result of `B.instance_methods(false)`. This feels like a bug in the implementation of `instance_methods(false)`, but, if it is by design, it should at least be documented on `Module#instance_methods`. This reproduction script gives the same output all the way from Ruby 2.0 up to Ruby-HEAD: https://wandbox.org/permlink/LqbXMBTYxURRZmDz -- https://bugs.ruby-lang.org/ Unsubscribe: