From: nobu@... Date: 2015-07-29T06:37:18+00:00 Subject: [ruby-core:70165] [Ruby trunk - Bug #10969] public_send in combination with method_missing raises NameError instead of NoMethodError Issue #10969 has been updated by Nobuyoshi Nakada. Description updated Backport set to 2.0.0: DONTNEED, 2.1: DONTNEED, 2.2: REQUIRED ---------------------------------------- Bug #10969: public_send in combination with method_missing raises NameError instead of NoMethodError https://bugs.ruby-lang.org/issues/10969#change-53593 * Author: Yves Senn * Status: Closed * Priority: Normal * Assignee: * ruby -v: ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-darwin14] * Backport: 2.0.0: DONTNEED, 2.1: DONTNEED, 2.2: REQUIRED ---------------------------------------- While working on the Rails project, specifically this issue https://github.com/rails/rails/issues/19297 I discovered that `public_send` can raise a `NameError` instead of a `NoMethodError`. Following is a minimal reproduction scenario to trigger the bug. A more detailed example can be found in this Gist: https://gist.github.com/senny/9864a138defa322ed807 ~~~ruby class Person def implicit_assignment nope rescue nil public_send "nope=" end def method_missing(*args) super end end a = Person.new a.implicit_assignment # test.rb:13:in `method_missing': undefined local variable or method `nope=' for # (NameError) # from test.rb:4:in `public_send' # from test.rb:4:in `implicit_assignment' # from test.rb:24:in `
' ~~~ ### What a found out during debugging: I am not a C programmer and have very little experience in that field. While debugging the issue I could make some observations what's going on. The error is being raised in `raise_method_missing` (https://github.com/ruby/ruby/blob/7790f37efdd8dd42a0a43c3206f6afdd43f8e86a/vm_eval.c#L704-L706): ~~~c else if (last_call_status & NOEX_VCALL) { format = "undefined local variable or method `%s' for %s"; exc = rb_eNameError; ~~~ `last_call_status` is stored on the current thread (https://github.com/ruby/ruby/blob/7790f37efdd8dd42a0a43c3206f6afdd43f8e86a/vm_eval.c#L655): The thread struct is modified in `vm_call_method_missing` (https://github.com/ruby/ruby/blob/7790f37efdd8dd42a0a43c3206f6afdd43f8e86a/vm_insnhelper.c#L1668): ~~~c th->method_missing_reason = ci->aux.missing_reason; ~~~ Now the problem is, that the call to `public_send` with the method name containing an `=` sign, does not modify the thread struct. This means that it still contains the value assigned from the previous call. That's what `nope rescue nil` in the reproduction is used for. It assigns `NOEX_VCALL` to that struct. -- https://bugs.ruby-lang.org/