aboutsummaryrefslogtreecommitdiffstats
path: root/vm_args.c
diff options
context:
space:
mode:
authorYusuke Endoh <mame@ruby-lang.org>2019-09-13 17:02:08 +0900
committerYusuke Endoh <mame@ruby-lang.org>2019-11-29 17:32:27 +0900
commit191ce5344ec42c91571f8f47c85be9138262b1c7 (patch)
tree5f9f222cc5bfe870208673ceb3eba95c7a823239 /vm_args.c
parent3a87826d0c3dd4c42e327e0cd4fb0806d898497f (diff)
downloadruby-191ce5344ec42c91571f8f47c85be9138262b1c7.tar.gz
Reduce duplicated warnings for the change of Ruby 3 keyword arguments
By this change, the following code prints only one warning. ``` def foo(**opt); end 100.times { foo({kw:1}) } ``` A global variable `st_table *caller_to_callees` is a map from caller to a set of callee methods. It remembers that a warning is already printed for each pair of caller and callee. [Feature #16289]
Diffstat (limited to 'vm_args.c')
-rw-r--r--vm_args.c71
1 files changed, 61 insertions, 10 deletions
diff --git a/vm_args.c b/vm_args.c
index 6729d54af7..c46b3cf2a4 100644
--- a/vm_args.c
+++ b/vm_args.c
@@ -585,9 +585,56 @@ ignore_keyword_hash_p(VALUE keyword_hash, const rb_iseq_t * const iseq) {
VALUE rb_iseq_location(const rb_iseq_t *iseq);
+/* -- Remove In 3.0 -- */
+
+/* This is a map from caller PC to a set of callee methods.
+ * When a warning about keyword argument change is printed,
+ * it keeps the pair of callee and caller.
+ */
+static st_table *caller_to_callees = 0;
+
+static VALUE rb_warn_check(const rb_execution_context_t * const ec, const void *const callee)
+{
+ const rb_control_frame_t * const cfp = rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
+
+ if (!cfp) return 0;
+
+ const void *const caller = cfp->pc;
+
+ if (!caller_to_callees) {
+ caller_to_callees = st_init_numtable();
+ }
+
+ st_data_t val;
+ if (st_lookup(caller_to_callees, (st_data_t) caller, &val)) {
+ st_table *callees;
+
+ if (val & 1) {
+ val &= ~(st_data_t)1;
+ if (val == (st_data_t) callee) return 1; /* already warned */
+
+ callees = st_init_numtable();
+ st_insert(callees, val, 1);
+ }
+ else {
+ callees = (st_table *) val;
+ if (st_is_member(callees, (st_data_t) callee)) return 1; /* already warned */
+ }
+ st_insert(callees, (st_data_t) callee, 1);
+ st_insert(caller_to_callees, (st_data_t) caller, (st_data_t) callees);
+ }
+ else {
+ st_insert(caller_to_callees, (st_data_t) caller, ((st_data_t) callee) | 1);
+ }
+
+ return 0; /* not warned yet for the pair of caller and callee */
+}
+
static inline void
-rb_warn_keyword_to_last_hash(struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t * const iseq)
+rb_warn_keyword_to_last_hash(rb_execution_context_t * const ec, struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t * const iseq)
{
+ if (rb_warn_check(ec, iseq)) return;
+
VALUE name, loc;
if (calling->recv == Qundef) {
rb_warn("The keyword argument is passed as the last hash parameter");
@@ -613,8 +660,10 @@ rb_warn_keyword_to_last_hash(struct rb_calling_info *calling, const struct rb_ca
}
static inline void
-rb_warn_split_last_hash_to_keyword(struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t * const iseq)
+rb_warn_split_last_hash_to_keyword(rb_execution_context_t * const ec, struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t * const iseq)
{
+ if (rb_warn_check(ec, iseq)) return;
+
VALUE name, loc;
name = rb_id2str(ci->mid);
loc = rb_iseq_location(iseq);
@@ -636,8 +685,10 @@ rb_warn_split_last_hash_to_keyword(struct rb_calling_info *calling, const struct
}
static inline void
-rb_warn_last_hash_to_keyword(struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t * const iseq)
+rb_warn_last_hash_to_keyword(rb_execution_context_t * const ec, struct rb_calling_info *calling, const struct rb_call_info *ci, const rb_iseq_t * const iseq)
{
+ if (rb_warn_check(ec, iseq)) return;
+
VALUE name, loc;
name = rb_id2str(ci->mid);
loc = rb_iseq_location(iseq);
@@ -766,7 +817,7 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
}
}
else {
- rb_warn_keyword_to_last_hash(calling, ci, iseq);
+ rb_warn_keyword_to_last_hash(ec, calling, ci, iseq);
}
}
else if (!remove_empty_keyword_hash && rest_last) {
@@ -789,7 +840,7 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
}
}
else {
- rb_warn_keyword_to_last_hash(calling, ci, iseq);
+ rb_warn_keyword_to_last_hash(ec, calling, ci, iseq);
}
}
else if (!remove_empty_keyword_hash) {
@@ -856,10 +907,10 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
}
else if (check_only_symbol) {
if (keyword_hash != Qnil) {
- rb_warn_split_last_hash_to_keyword(calling, ci, iseq);
+ rb_warn_split_last_hash_to_keyword(ec, calling, ci, iseq);
}
else {
- rb_warn_keyword_to_last_hash(calling, ci, iseq);
+ rb_warn_keyword_to_last_hash(ec, calling, ci, iseq);
}
}
}
@@ -868,15 +919,15 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
* def foo(k:1) p [k]; end
* foo({k:42}) #=> 42
*/
- rb_warn_last_hash_to_keyword(calling, ci, iseq);
+ rb_warn_last_hash_to_keyword(ec, calling, ci, iseq);
given_argc--;
}
else if (keyword_hash != Qnil) {
- rb_warn_split_last_hash_to_keyword(calling, ci, iseq);
+ rb_warn_split_last_hash_to_keyword(ec, calling, ci, iseq);
}
}
else if (given_argc == min_argc && kw_flag) {
- rb_warn_keyword_to_last_hash(calling, ci, iseq);
+ rb_warn_keyword_to_last_hash(ec, calling, ci, iseq);
}
}