From e7b8d32e166815f2e7edebf32aa178915d191b8c Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 16 Dec 2022 07:31:27 +0900 Subject: [PATCH] Stop using receiver#inspect for "undefined method" errors ``` 42.time #=> undefined method `time' for object Integer (NoMethodError) class Foo privatee #=> undefined local variable or method 'privatee' for class Foo (NoMethodError) end s = "" def s.foo = nil s.bar #=> undefined method `bar' for extended object String (NoMethodError) ``` [Feature #18285] --- error.c | 71 ++++++++++++++++++++++++++++++++++++++++--------------- vm_eval.c | 10 ++++---- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/error.c b/error.c index 8aa081c198..affee7d828 100644 --- a/error.c +++ b/error.c @@ -2066,43 +2066,76 @@ name_err_mesg_to_str(VALUE obj) if (NIL_P(mesg)) return Qnil; else { struct RString s_str, d_str; - VALUE c, s, d = 0, args[4]; - int state = 0, singleton = 0; + VALUE c, s, d = 0, args[4], c2; + int state = 0; rb_encoding *usascii = rb_usascii_encoding(); #define FAKE_CSTR(v, str) rb_setup_fake_str((v), (str), rb_strlen_lit(str), usascii) + c = s = FAKE_CSTR(&s_str, ""); obj = ptr[NAME_ERR_MESG__RECV]; switch (obj) { case Qnil: - d = FAKE_CSTR(&d_str, "nil"); + c = d = FAKE_CSTR(&d_str, "nil"); break; case Qtrue: - d = FAKE_CSTR(&d_str, "true"); + c = d = FAKE_CSTR(&d_str, "true"); break; case Qfalse: - d = FAKE_CSTR(&d_str, "false"); + c = d = FAKE_CSTR(&d_str, "false"); break; default: - d = rb_protect(name_err_mesg_receiver_name, obj, &state); - if (state || NIL_OR_UNDEF_P(d)) - d = rb_protect(rb_inspect, obj, &state); + if (strstr(RSTRING_PTR(mesg), "%2$s")) { + d = rb_protect(name_err_mesg_receiver_name, obj, &state); + if (state || NIL_OR_UNDEF_P(d)) + d = rb_protect(rb_inspect, obj, &state); + if (state) { + rb_set_errinfo(Qnil); + } + d = rb_check_string_type(d); + if (NIL_P(d)) { + d = rb_any_to_s(obj); + } + } + + if (!RB_SPECIAL_CONST_P(obj)) { + switch (RB_BUILTIN_TYPE(obj)) { + case T_MODULE: + s = FAKE_CSTR(&s_str, "module "); + c = obj; + break; + case T_CLASS: + s = FAKE_CSTR(&s_str, "class "); + c = obj; + break; + default: + goto object; + } + } + else { + VALUE klass; +object: + klass = CLASS_OF(obj); + if (RB_TYPE_P(klass, T_CLASS) && FL_TEST(klass, FL_SINGLETON)) { + s = FAKE_CSTR(&s_str, "extended object "); + } + else { + s = FAKE_CSTR(&s_str, "object "); + } + c = rb_class_real(klass); + } + c2 = rb_protect(name_err_mesg_receiver_name, c, &state); + if (state || NIL_OR_UNDEF_P(c2)) + c2 = rb_protect(rb_inspect, c, &state); if (state) { rb_set_errinfo(Qnil); } - d = rb_check_string_type(d); - if (NIL_P(d)) { - d = rb_any_to_s(obj); + c2 = rb_check_string_type(c2); + if (NIL_P(c2)) { + c2 = rb_any_to_s(c); } - singleton = (RSTRING_LEN(d) > 0 && RSTRING_PTR(d)[0] == '#'); + c = c2; break; } - if (!singleton) { - s = FAKE_CSTR(&s_str, ":"); - c = rb_class_name(CLASS_OF(obj)); - } - else { - c = s = FAKE_CSTR(&s_str, ""); - } args[0] = rb_obj_as_string(ptr[NAME_ERR_MESG__NAME]); args[1] = d; args[2] = s; diff --git a/vm_eval.c b/vm_eval.c index 1caa76000b..f91d7d75cd 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -933,7 +933,7 @@ rb_make_no_method_exception(VALUE exc, VALUE format, VALUE obj, VALUE name = argv[0]; if (!format) { - format = rb_fstring_lit("undefined method `%s' for %s%s%s"); + format = rb_fstring_lit("undefined method `%1$s' for %3$s%4$s"); } if (exc == rb_eNoMethodError) { VALUE args = rb_ary_new4(argc - 1, argv + 1); @@ -965,17 +965,17 @@ raise_method_missing(rb_execution_context_t *ec, int argc, const VALUE *argv, VA stack_check(ec); if (last_call_status & MISSING_PRIVATE) { - format = rb_fstring_lit("private method `%s' called for %s%s%s"); + format = rb_fstring_lit("private method `%1$s' called for %3$s%4$s"); } else if (last_call_status & MISSING_PROTECTED) { - format = rb_fstring_lit("protected method `%s' called for %s%s%s"); + format = rb_fstring_lit("protected method `%1$s' called for %3$s%4$s"); } else if (last_call_status & MISSING_VCALL) { - format = rb_fstring_lit("undefined local variable or method `%s' for %s%s%s"); + format = rb_fstring_lit("undefined local variable or method `%1$s' for %3$s%4$s"); exc = rb_eNameError; } else if (last_call_status & MISSING_SUPER) { - format = rb_fstring_lit("super: no superclass method `%s' for %s%s%s"); + format = rb_fstring_lit("super: no superclass method `%1$s' for %3$s%4$s"); } {