Bug #10969
closedpublic_send in combination with method_missing raises NameError instead of NoMethodError
Description
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
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 #<Person:0x007f91d1052ef8> (NameError)
# from test.rb:4:in `public_send'
# from test.rb:4:in `implicit_assignment'
# from test.rb:24:in `<main>'
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):
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):
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.
Updated by nobu (Nobuyoshi Nakada) over 9 years ago
- Status changed from Open to Closed
Applied in changeset r51427.
vm_eval.c: set method_missing_reason
- vm_eval.c (send_internal): set method_missing_reason before
invoking overriding method_missing method so that the default
method_missing can achieve it properly.
[ruby-core:68515] [Bug #10969]
Updated by nobu (Nobuyoshi Nakada) over 9 years ago
- Description updated (diff)
- Backport set to 2.0.0: DONTNEED, 2.1: DONTNEED, 2.2: REQUIRED
Updated by senny (Yves Senn) over 9 years ago
Thank you Nobuyoshi Nakada and Koichi Sasada! 💛
Updated by nagachika (Tomoyuki Chikanaga) almost 9 years ago
- Backport changed from 2.0.0: DONTNEED, 2.1: DONTNEED, 2.2: REQUIRED to 2.0.0: DONTNEED, 2.1: DONTNEED, 2.2: DONE
Backported into ruby_2_2
branch at r52771.
The MISSING_NOENTRY
was introduced at r50743. I replaced MISSING_NOENTRY
it to 0
to backport.