Неожиданное значение __callee__ при включении модуля - это ошибка Ruby?

При вызове с помощью метода, созданного alias_method, __callee__ игнорирует имя старого метода (здесь xxx) и возвращает имя нового метода, как показано ниже:

class Foo
  def xxx() __callee__ end
  alias_method :foo, :xxx
end

Foo.new.foo # => :foo

Это поведение сохраняется даже тогда, когда xxx наследуется от суперкласса:

class Sup
  def xxx() __callee__ end
end

class Bar < Sup
  alias_method :bar, :xxx
end

Bar.new.bar # => :bar

Учитывая оба вышеизложенного, я ожидал бы, что такое же поведение сохранится, если xxx будет включено через модуль. Однако это не так:

module Mod
  def xxx() __callee__ end
end

class Baz
  include Mod
  alias_method :baz, :xxx
end

Baz.new.baz # => :xxx

Я ожидаю, что возвращаемое значение будет :baz, а не :xxx.


Вышеупомянутый код был выполнен с использованием Ruby 2.3.1p112. Это ошибка в реализации __callee__? Или, может быть, alias_method? А если нет, может кто-нибудь объяснить, почему включение модуля ведет себя по-другому?


ОБНОВЛЕНИЕ 1

Я отправил это в трекер ошибок Ruby, чтобы попытаться ответить на вопрос.


ОБНОВЛЕНИЕ 2

По-видимому, я не единственный, чтобы удивиться этой проблеме. Интересно, был ли Revision 50728 (который должен был решить Ошибка 11046: __callee__ возвращает неправильное имя метода в orphan proc) может быть связано.

Ответы

Ответ 1

Вы можете увидеть разницу между __callee__ и __method__ в модуле ядра Ruby.

Разница заключается в вызовах prev_frame_callee() и prev_frame_func(), соответственно. Я нашел эти определения функций в http://rxr.whitequark.org/mri/source/eval.c

Короче говоря, Foo и Bar немедленно вызывают псевдонимы foo и bar (которые являются именами для xxx), а Baz должен найти Mod и вызывать xxx из Mod. __method__ ищет оригинал с именем method id, а __callee__ ищет ближайший вызываемый метод id к вызову __callee__. Это лучше видно в eval.c на строках 848 - 906: найдите разницу в двух методах при обратных вызовах, похожих на <something> -> called_id vs <something> -> def->original_id.

Кроме того, если вы посмотрите на Ядро из версии 1.9.3, вы увидите, что оба метода изначально были одинаковыми. Итак, в какой-то момент произошли целенаправленные изменения между ними.