[ruby-core:102888] [Ruby master Bug#17519] set_visibility fails when a prepended module and a refinement both exist
From:
dbfeldman@...
Date:
2021-03-16 19:12:52 UTC
List:
ruby-core #102888
Issue #17519 has been updated by fledman (David Feldman).
do you plan to backport the fix to any of the supported 2.x versions?
----------------------------------------
Bug #17519: set_visibility fails when a prepended module and a refinement both exist
https://bugs.ruby-lang.org/issues/17519#change-90949
* Author: fledman (David Feldman)
* Status: Closed
* Priority: Normal
* ruby -v: ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
* Backport: 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN
----------------------------------------
the set_visibility functions (aka public/private/protected) fail with NameError when:
* called on a specific object's singleton class
* for a specific method
* and both of the following are true
* any module has been prepended to the object's class
* a refinement exists for the specific method
note that the refinement does not need to ever be used
I have reproduced this on 3.0.0, 2.7.2, and 2.6.6 (those were the only 3 version I tested)
``` ruby
def test_visibility(function)
h1 = {x:1, y:1}
h2 = {x:2, y:2}
h1.singleton_class.send(:private, function)
h2.singleton_class.send(:public, function)
begin
puts h1.public_send(function, :x)
rescue NoMethodError => err
puts "hit NoMethodError as expected: #{err.inspect}"
end
puts h2.public_send(function, :x)
end
```
succeeds without any prepended modules or refinements:
```ruby
irb> test_visibility :except
hit NoMethodError as expected: #<NoMethodError: private method `except' called for {:x=>1, :y=>1}:Hash>
{:y=>2}
=> nil
```
succeeds with only a prepended module:
```ruby
irb> module Nothing; end; Hash.prepend(Nothing); Hash.ancestors
=> [Nothing, Hash, Enumerable, Object, PP::ObjectMixin, Kernel, BasicObject]
irb> test_visibility :except
hit NoMethodError as expected: #<NoMethodError: private method `except' called for {:x=>1, :y=>1}:Hash>
{:y=>2}
=> nil
```
succeeds with only a refinement:
```ruby
irb> module NeverUsed
refine Hash do
def except(*keys)
{never: 'used'}
end
end
end
=> #<refinement:Hash@NeverUsed>
irb> test_visibility :except
hit NoMethodError as expected: #<NoMethodError: private method `except' called for {:x=>1, :y=>1}:Hash>
{:y=>2}
=> nil
```
fails with both a refinement and a prepended module:
```ruby
irb> module Nothing; end; Hash.prepend(Nothing); Hash.ancestors
=> [Nothing, Hash, Enumerable, Object, PP::ObjectMixin, Kernel, BasicObject]
irb> module NeverUsed
refine Hash do
def except(*keys)
{never: 'used'}
end
end
end
=> #<refinement:Hash@NeverUsed>
irb> test_visibility :except
Traceback (most recent call last):
6: from .rubies/ruby-3.0.0/bin/irb:23:in `<main>'
5: from .rubies/ruby-3.0.0/bin/irb:23:in `load'
4: from .rubies/ruby-3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>'
3: from (irb):25:in `<main>'
2: from (irb):5:in `test_visibility'
1: from (irb):5:in `private'
NameError (undefined method `except' for class `#<Class:#<Hash:0x00007ffd6b176f18>>')
Did you mean? exec
# non-refined method still works
irb> test_visibility :fetch
hit NoMethodError as expected: #<NoMethodError: private method `fetch' called for {:x=>1, :y=>1}:Hash>
2
=> nil
```
--
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>