Project

General

Profile

Actions

Bug #10969

closed

public_send in combination with method_missing raises NameError instead of NoMethodError

Added by senny (Yves Senn) over 9 years ago. Updated over 8 years ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-darwin14]
[ruby-core:<unknown>]

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.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0