diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb index f10412e6cd..4c99a3212d 100644 --- a/test/ruby/test_keyword.rb +++ b/test/ruby/test_keyword.rb @@ -641,4 +641,17 @@ class TestKeywordArguments < Test::Unit::TestCase assert_equal(x, result) assert_kind_of(klass, result, bug12884) end + + def test_arity_error_message + obj = Object.new + def obj.t(x:) end + assert_raise_with_message(ArgumentError, /required keyword: x\)/) do + obj.t(42) + end + obj = Object.new + def obj.t(x:, y:, z: nil) end + assert_raise_with_message(ArgumentError, /required keywords: x, y\)/) do + obj.t(42) + end + end end diff --git a/vm_args.c b/vm_args.c index 59bd87ebf0..898301ce49 100644 --- a/vm_args.c +++ b/vm_args.c @@ -719,7 +719,26 @@ raise_argument_error(rb_thread_t *th, const rb_iseq_t *iseq, const VALUE exc) static void argument_arity_error(rb_thread_t *th, const rb_iseq_t *iseq, const int miss_argc, const int min_argc, const int max_argc) { - raise_argument_error(th, iseq, rb_arity_error_new(miss_argc, min_argc, max_argc)); + VALUE exc = rb_arity_error_new(miss_argc, min_argc, max_argc); + if (iseq->body->param.flags.has_kw) { + const struct rb_iseq_param_keyword *const kw = iseq->body->param.keyword; + const ID *keywords = kw->table; + int req_key_num = kw->required_num; + if (req_key_num > 0) { + static const char required[] = "; required keywords"; + VALUE mesg = rb_attr_get(exc, idMesg); + rb_str_resize(mesg, RSTRING_LEN(mesg)-1); + rb_str_cat(mesg, required, sizeof(required) - 1 - (req_key_num == 1)); + rb_str_cat_cstr(mesg, ":"); + do { + rb_str_cat_cstr(mesg, " "); + rb_str_append(mesg, rb_id2str(*keywords++)); + rb_str_cat_cstr(mesg, ","); + } while (--req_key_num); + RSTRING_PTR(mesg)[RSTRING_LEN(mesg)-1] = ')'; + } + } + raise_argument_error(th, iseq, exc); } static void