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.