Bug #22276
openalias in a module falls back to Object even in classes not inheriting from Object
Description
When alias (or alias_method) is used in a module and the method is not found in the module, alias searches the method from Object. So a module can alias a method of Kernel or Object, and the alias works even if the module is later included in a class that does not have Object and Kernel as ancestors:
module M
alias foo puts
public :foo
end
class X < BasicObject
include ::M
end
X.new.foo("hello") #=> hello
X does not include Kernel, but X.new.foo calls Kernel#puts.
This fallback comes from Ruby 1.8, where Object was the root class, so every class that includes a module always had Object and Kernel as ancestors. Since Ruby 1.9 introduced BasicObject, this is no longer true, but the fallback is unchanged. As discussed in #22273, a module should be able to alias only its own methods and its ancestors' methods, and Object is not an ancestor of a module.
This behavior is intentional in the current implementation. There are tests for it (test_alias_in_module in test/ruby/test_alias.rb for #9663, and "accesses a method defined on Object from Kernel" in spec/ruby/core/module/alias_method_spec.rb), and the documentation of Module#alias_method has an example module Mod; alias_method :orig_exit, :exit; end. So code like module M; alias orig_to_s to_s; end exists, and simply raising NameError will break it.
Possible fixes:
- Remove the fallback and raise
NameError. This breaks existing code. - Do not resolve the method at alias time. Instead, resolve it at call time from the ancestors of the receiver's class, like ZSUPER methods. Existing code that includes the module in a subclass of
Objectkeeps working, andX.new.fooabove raisesNoMethodError. - Keep the current behavior and document it.
I think 2 is the best choice for compatibility.