Use more accurate source location in keyword argument separation warnings

This shows locations in places it didn't before, such as for
proc calls, and fixes the location for super calls.

This requires making iseq_location non-static and MJIT exported,
which I hope will not cause problems.
This commit is contained in:
Jeremy Evans 2019-08-30 21:56:50 -07:00
parent 3463e83192
commit ec6206a81a
3 changed files with 17 additions and 21 deletions

2
proc.c
View File

@ -1183,7 +1183,7 @@ rb_proc_get_iseq(VALUE self, int *is_proc)
return NULL; return NULL;
} }
static VALUE MJIT_FUNC_EXPORTED VALUE
iseq_location(const rb_iseq_t *iseq) iseq_location(const rb_iseq_t *iseq)
{ {
VALUE loc[2]; VALUE loc[2];

View File

@ -308,18 +308,18 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(expect, rest_keyrest(*expect), bug7665) assert_equal(expect, rest_keyrest(*expect), bug7665)
end end
pr = proc {|*args, **opt| next *args, opt} pr = proc {|*args, **opt| next *args, opt}
assert_warn(/The last argument for `call' is used as the keyword parameter/) do assert_warn(/The last argument for `call' .* is used as the keyword parameter/) do
assert_equal(expect, pr.call(*expect), bug7665) assert_equal(expect, pr.call(*expect), bug7665)
end end
assert_warn(/The last argument for `call' is used as the keyword parameter/) do assert_warn(/The last argument for `call' .* is used as the keyword parameter/) do
assert_equal(expect, pr.call(expect), bug8463) assert_equal(expect, pr.call(expect), bug8463)
end end
pr = proc {|a, *b, **opt| next a, *b, opt} pr = proc {|a, *b, **opt| next a, *b, opt}
assert_warn(/The last argument for `call' is used as the keyword parameter/) do assert_warn(/The last argument for `call' .* is used as the keyword parameter/) do
assert_equal(expect, pr.call(expect), bug8463) assert_equal(expect, pr.call(expect), bug8463)
end end
pr = proc {|a, **opt| next a, opt} pr = proc {|a, **opt| next a, opt}
assert_warn(/The last argument for `call' is used as the keyword parameter/) do assert_warn(/The last argument for `call' .* is used as the keyword parameter/) do
assert_equal(expect.values_at(0, -1), pr.call(expect), bug8463) assert_equal(expect.values_at(0, -1), pr.call(expect), bug8463)
end end
end end

View File

@ -579,17 +579,13 @@ ignore_keyword_hash_p(VALUE keyword_hash, const rb_iseq_t * const iseq) {
return 0; return 0;
} }
static inline VALUE VALUE iseq_location(const rb_iseq_t *iseq);
get_loc(struct rb_calling_info *calling, const struct rb_call_info *ci)
{
return rb_obj_method_location(calling->recv, ci->mid);
}
static inline void static inline void
rb_warn_keyword_to_last_hash(struct rb_calling_info *calling, const struct rb_call_info *ci) rb_warn_keyword_to_last_hash(struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t * const iseq)
{ {
if (calling->recv == Qundef) return; if (calling->recv == Qundef) return;
VALUE loc = get_loc(calling, ci); VALUE loc = iseq_location(iseq);
if (NIL_P(loc)) { if (NIL_P(loc)) {
rb_warn("The keyword argument for `%s' is passed as the last hash parameter", rb_id2name(ci->mid)); rb_warn("The keyword argument for `%s' is passed as the last hash parameter", rb_id2name(ci->mid));
} }
@ -600,10 +596,10 @@ rb_warn_keyword_to_last_hash(struct rb_calling_info *calling, const struct rb_ca
} }
static inline void static inline void
rb_warn_split_last_hash_to_keyword(struct rb_calling_info *calling, const struct rb_call_info *ci) rb_warn_split_last_hash_to_keyword(struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t * const iseq)
{ {
if (calling->recv == Qundef) return; if (calling->recv == Qundef) return;
VALUE loc = get_loc(calling, ci); VALUE loc = iseq_location(iseq);
if (NIL_P(loc)) { if (NIL_P(loc)) {
rb_warn("The last argument for `%s' is split into positional and keyword parameters", rb_id2name(ci->mid)); rb_warn("The last argument for `%s' is split into positional and keyword parameters", rb_id2name(ci->mid));
} }
@ -614,10 +610,10 @@ rb_warn_split_last_hash_to_keyword(struct rb_calling_info *calling, const struct
} }
static inline void static inline void
rb_warn_last_hash_to_keyword(struct rb_calling_info *calling, const struct rb_call_info *ci) rb_warn_last_hash_to_keyword(struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t * const iseq)
{ {
if (calling->recv == Qundef) return; if (calling->recv == Qundef) return;
VALUE loc = get_loc(calling, ci); VALUE loc = iseq_location(iseq);
if (NIL_P(loc)) { if (NIL_P(loc)) {
rb_warn("The last argument for `%s' is used as the keyword parameter", rb_id2name(ci->mid)); rb_warn("The last argument for `%s' is used as the keyword parameter", rb_id2name(ci->mid));
} }
@ -770,10 +766,10 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
} }
else if (check_only_symbol) { else if (check_only_symbol) {
if (keyword_hash != Qnil) { if (keyword_hash != Qnil) {
rb_warn_split_last_hash_to_keyword(calling, ci); rb_warn_split_last_hash_to_keyword(calling, ci, iseq);
} }
else { else {
rb_warn_keyword_to_last_hash(calling, ci); rb_warn_keyword_to_last_hash(calling, ci, iseq);
} }
} }
} }
@ -784,16 +780,16 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
*/ */
if (ec->cfp->iseq) { if (ec->cfp->iseq) {
/* called from Ruby level */ /* called from Ruby level */
rb_warn_last_hash_to_keyword(calling, ci); rb_warn_last_hash_to_keyword(calling, ci, iseq);
} }
given_argc--; given_argc--;
} }
else if (keyword_hash != Qnil && ec->cfp->iseq) { else if (keyword_hash != Qnil && ec->cfp->iseq) {
rb_warn_split_last_hash_to_keyword(calling, ci); rb_warn_split_last_hash_to_keyword(calling, ci, iseq);
} }
} }
else if (given_argc == min_argc && kw_flag) { else if (given_argc == min_argc && kw_flag) {
rb_warn_keyword_to_last_hash(calling, ci); rb_warn_keyword_to_last_hash(calling, ci, iseq);
} }
} }