ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
From: dbfeldman@gmail.com
To: ruby-core@ruby-lang.org
Subject: [ruby-core:102004] [Ruby master Bug#17519] set_visibility fails when a prepended module and a refinement both exist
Date: Mon, 11 Jan 2021 19:01:54 +0000 (UTC)	[thread overview]
Message-ID: <redmine.journal-89850.20210111190150.48474@ruby-lang.org> (raw)
In-Reply-To: redmine.issue-17519.20210108191508.48474@ruby-lang.org

Issue #17519 has been updated by fledman (David Feldman).


although the title makes it sound obscure, this bug is actually fairly easy to trigger when using rspec and rails:

* activesupport >= 5 prepends a module onto Hash
* i18n >= 1.3 refines several methods on Hash
* power_assert (used by test-unit and minitest) refines most of the basic operators of the core classes

e.g. try to [`expect(some_hash).to receive(:except)`](https://github.com/rspec/rspec-rails/issues/2394)


----------------------------------------
Bug #17519: set_visibility fails when a prepended module and a refinement both exist
https://bugs.ruby-lang.org/issues/17519#change-89850

* Author: fledman (David Feldman)
* Status: Open
* 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/

  parent reply	other threads:[~2021-01-11 19:02 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-08 19:15 [ruby-core:101981] [Ruby master Bug#17519] set_visibility fails when a prepended module and a refinement both exist dbfeldman
2021-01-08 19:29 ` [ruby-core:101982] " dbfeldman
2021-01-08 19:45 ` [ruby-core:101983] " dbfeldman
2021-01-11 19:01 ` dbfeldman [this message]
2021-03-16 19:12 ` [ruby-core:102888] " dbfeldman
2021-03-16 19:25 ` [ruby-core:102889] " merch-redmine
2021-03-20  7:30 ` [ruby-core:102956] " nagachika00
2021-04-02  7:01 ` [ruby-core:103169] " naruse
2021-04-05 21:52 ` [ruby-core:103244] " dbfeldman
2021-04-05 23:06 ` [ruby-core:103245] " merch-redmine
2021-04-24  9:57 ` [ruby-core:103583] " nagachika00
2021-05-29  5:56 ` [ruby-core:104095] " nagachika00

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.ruby-lang.org/en/community/mailing-lists/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=redmine.journal-89850.20210111190150.48474@ruby-lang.org \
    --to=ruby-core@ruby-lang.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).