From: nobu@... Date: 2014-04-29T01:59:07+00:00 Subject: [ruby-core:62206] [ruby-trunk - Feature #9781] Feature Proposal: Method#super_method Issue #9781 has been updated by Nobuyoshi Nakada. Description updated A patch. No tests yet. ~~~diff diff --git a/proc.c b/proc.c index 8153cc9..d1db478 100644 --- a/proc.c +++ b/proc.c @@ -1481,11 +1481,17 @@ method_owner(VALUE obj) return defined_class; } -void -rb_method_name_error(VALUE klass, VALUE str) +struct method_name_error { + VALUE class_name; + const char *type; +}; + +static struct method_name_error +prepare_method_name_error(VALUE klass) { const char *s0 = " class"; VALUE c = klass; + struct method_name_error e; if (FL_TEST(c, FL_SINGLETON)) { VALUE obj = rb_ivar_get(klass, attached); @@ -1500,8 +1506,22 @@ rb_method_name_error(VALUE klass, VALUE str) else if (RB_TYPE_P(c, T_MODULE)) { s0 = " module"; } - rb_name_error_str(str, "undefined method `%"PRIsVALUE"' for%s `%"PRIsVALUE"'", - QUOTE(str), s0, rb_class_name(c)); + e.class_name = rb_class_name(c); + e.type = s0; + return e; +} + +#define method_name_error(klass, str, t) do { \ + struct method_name_error e = prepare_method_name_error(klass); \ + rb_name_error_str(str, t" method `%"PRIsVALUE"' for%s `%"PRIsVALUE"'", \ + QUOTE(str), e.type, e.class_name); \ + } while (0) + + +void +rb_method_name_error(VALUE klass, VALUE str) +{ + method_name_error(klass, str, "undefined"); } /* @@ -2430,6 +2450,23 @@ method_proc(VALUE method) return procval; } +static VALUE +method_super_method(VALUE method) +{ + struct METHOD *data; + VALUE defined_class, super_class; + + TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); + defined_class = data->defined_class; + if (BUILTIN_TYPE(defined_class) == T_MODULE) defined_class = data->rclass; + super_class = RCLASS_SUPER(defined_class); + if (!super_class) { + method_name_error(defined_class, rb_id2str(data->id), "no superclass"); + } + return mnew(super_class, data->recv, data->id, + rb_obj_class(method), FALSE); +} + /* * call-seq: * local_jump_error.exit_value -> obj @@ -2735,6 +2772,7 @@ Init_Proc(void) rb_define_method(rb_cMethod, "unbind", method_unbind, 0); rb_define_method(rb_cMethod, "source_location", rb_method_location, 0); rb_define_method(rb_cMethod, "parameters", rb_method_parameters, 0); + rb_define_method(rb_cMethod, "super_method", method_super_method, 0); rb_define_method(rb_mKernel, "method", rb_obj_method, 1); rb_define_method(rb_mKernel, "public_method", rb_obj_public_method, 1); rb_define_method(rb_mKernel, "singleton_method", rb_obj_singleton_method, 1); @@ -2756,6 +2794,7 @@ Init_Proc(void) rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1); rb_define_method(rb_cUnboundMethod, "source_location", rb_method_location, 0); rb_define_method(rb_cUnboundMethod, "parameters", rb_method_parameters, 0); + rb_define_method(rb_cUnboundMethod, "super_method", method_super_method, 0); /* Module#*_method */ rb_define_method(rb_cModule, "instance_method", rb_mod_instance_method, 1); ~~~ ---------------------------------------- Feature #9781: Feature Proposal: Method#super_method https://bugs.ruby-lang.org/issues/9781#change-46358 * Author: Richard Schneeman * Status: Open * Priority: Normal * Assignee: * Category: core * Target version: ---------------------------------------- When `super` is called in a method the Ruby VM knows how to find the next ancestor that has that method and call it. It is difficult to do this manually, so I propose we expose this information in Method#super_location. Ruby Method class (http://www.ruby-doc.org/core-2.1.1/Method.html) is returned by calling Object.method and passing in a method name (http://www.ruby-doc.org/core-2.1.1/Object.html#method-i-method). This is useful for debugging: ```ruby # /tmp/code.rb class Foo def bar end end puts Foo.new.method(:bar).source_location # => ["/tmp/code.rb", 3] ``` The Object#method allows a ruby developer to easily track the source location of the method and makes debugging very easy. However if the code is being invoked by a call to `super` it is difficult to track down: ```ruby # /tmp/code.rb class BigFoo def bar end end class Foo < BigFoo def bar super end end ``` In this code sample it is easy to find the method definition inside of Foo but it is very difficult in large projects to find what code exactly `super` is calling. This simple example is easy, but it can be hard when there are many ancestors. Currently if I wanted to find this we can inspect ancestors ```ruby Foo.ancestors[1..-1].map do |ancestor| next unless ancestor.method_defined?(:bar) ancestor.instance_method(:bar) end.compact.first.source_location ``` To make this process simpler I am proposing a method on the Method class that would return the result of `super` It could be called like this: ```ruby Foo.new.method(:bar).super_method ``` I believe adding Method#super_method, or exposing this same information somewhere else, could greatly help developers to debug large systems easily. -- https://bugs.ruby-lang.org/