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]
This commit is contained in:
Yusuke Endoh 2022-12-16 07:31:27 +09:00
parent 5c0298bf18
commit e7b8d32e16
Notes: git 2023-02-20 01:33:36 +00:00
2 changed files with 57 additions and 24 deletions

71
error.c
View File

@ -2066,43 +2066,76 @@ name_err_mesg_to_str(VALUE obj)
if (NIL_P(mesg)) return Qnil; if (NIL_P(mesg)) return Qnil;
else { else {
struct RString s_str, d_str; struct RString s_str, d_str;
VALUE c, s, d = 0, args[4]; VALUE c, s, d = 0, args[4], c2;
int state = 0, singleton = 0; int state = 0;
rb_encoding *usascii = rb_usascii_encoding(); rb_encoding *usascii = rb_usascii_encoding();
#define FAKE_CSTR(v, str) rb_setup_fake_str((v), (str), rb_strlen_lit(str), usascii) #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]; obj = ptr[NAME_ERR_MESG__RECV];
switch (obj) { switch (obj) {
case Qnil: case Qnil:
d = FAKE_CSTR(&d_str, "nil"); c = d = FAKE_CSTR(&d_str, "nil");
break; break;
case Qtrue: case Qtrue:
d = FAKE_CSTR(&d_str, "true"); c = d = FAKE_CSTR(&d_str, "true");
break; break;
case Qfalse: case Qfalse:
d = FAKE_CSTR(&d_str, "false"); c = d = FAKE_CSTR(&d_str, "false");
break; break;
default: default:
d = rb_protect(name_err_mesg_receiver_name, obj, &state); if (strstr(RSTRING_PTR(mesg), "%2$s")) {
if (state || NIL_OR_UNDEF_P(d)) d = rb_protect(name_err_mesg_receiver_name, obj, &state);
d = rb_protect(rb_inspect, 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) { if (state) {
rb_set_errinfo(Qnil); rb_set_errinfo(Qnil);
} }
d = rb_check_string_type(d); c2 = rb_check_string_type(c2);
if (NIL_P(d)) { if (NIL_P(c2)) {
d = rb_any_to_s(obj); c2 = rb_any_to_s(c);
} }
singleton = (RSTRING_LEN(d) > 0 && RSTRING_PTR(d)[0] == '#'); c = c2;
break; 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[0] = rb_obj_as_string(ptr[NAME_ERR_MESG__NAME]);
args[1] = d; args[1] = d;
args[2] = s; args[2] = s;

View File

@ -933,7 +933,7 @@ rb_make_no_method_exception(VALUE exc, VALUE format, VALUE obj,
VALUE name = argv[0]; VALUE name = argv[0];
if (!format) { 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) { if (exc == rb_eNoMethodError) {
VALUE args = rb_ary_new4(argc - 1, argv + 1); 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); stack_check(ec);
if (last_call_status & MISSING_PRIVATE) { 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) { 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) { 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; exc = rb_eNameError;
} }
else if (last_call_status & MISSING_SUPER) { 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");
} }
{ {