relax unused block warning for duck typing

if a method `foo` uses a block, other (unrelated) method `foo`
can receives a block. So try to relax the unused block warning
condition.

```ruby
      class C0
        def f = yield
      end

      class C1 < C0
        def f = nil
      end

      [C0, C1].f{ block } # do not warn
```
This commit is contained in:
Koichi Sasada 2024-04-17 19:46:48 +09:00
parent 6ade36c06b
commit e9d7478ded
5 changed files with 44 additions and 11 deletions

View File

@ -1998,6 +1998,19 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
return arg_size;
}
static void
iseq_set_use_block(rb_iseq_t *iseq)
{
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
if (!body->param.flags.use_block) {
body->param.flags.use_block = 1;
rb_vm_t *vm = GET_VM();
st_data_t key = (st_data_t)rb_intern_str(body->location.label); // String -> ID
st_insert(vm->unused_block_warning_table, key, 1);
}
}
static int
iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *const node_args)
{
@ -2099,7 +2112,7 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons
if (block_id) {
body->param.block_start = arg_size++;
body->param.flags.has_block = TRUE;
body->param.flags.use_block = 1;
iseq_set_use_block(iseq);
}
iseq_calc_param_size(iseq);
@ -5920,7 +5933,7 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
ADD_INSN(ret, line_node, putnil);
ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_YIELD), 0,
PUSH_VAL(DEFINED_YIELD));
ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.use_block = 1;
iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
return;
case NODE_BACK_REF:
@ -8632,7 +8645,7 @@ compile_builtin_attr(rb_iseq_t *iseq, const NODE *node)
ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_INLINE_BLOCK;
}
else if (strcmp(RSTRING_PTR(string), "use_block") == 0) {
ISEQ_BODY(iseq)->param.flags.use_block = 1;
iseq_set_use_block(iseq);
}
else {
goto unknown_arg;
@ -9481,7 +9494,7 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
}
if (use_block && parent_block == NULL) {
ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.use_block = 1;
iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
}
flag |= VM_CALL_SUPER | VM_CALL_FCALL;
@ -9527,7 +9540,7 @@ compile_yield(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
ADD_SEQ(ret, args);
ADD_INSN1(ret, node, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), flag, keywords, FALSE));
ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.use_block = 1;
iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq);
if (popped) {
ADD_INSN(ret, node, pop);

View File

@ -1692,5 +1692,19 @@ class TestMethod < Test::Unit::TestCase
assert_match(/-:23: warning.+f5/, err.join)
assert_match(/-:24: warning.+f6/, err.join)
end
assert_in_out_err '-w', <<-'RUBY' do |_out, err, _status|
class C0
def f = yield
end
class C1 < C0
def f = nil
end
C1.new.f{} # do not warn on duck typing
RUBY
assert_equal 0, err.size, err.join("\n")
end
end
end

1
vm.c
View File

@ -4254,6 +4254,7 @@ Init_BareVM(void)
vm->negative_cme_table = rb_id_table_create(16);
vm->overloaded_cme_table = st_init_numtable();
vm->constant_cache = rb_id_table_create(0);
vm->unused_block_warning_table = st_init_numtable();
// setup main thread
th->nt = ZALLOC(struct rb_native_thread);

View File

@ -773,6 +773,7 @@ typedef struct rb_vm_struct {
st_table *ci_table;
struct rb_id_table *negative_cme_table;
st_table *overloaded_cme_table; // cme -> overloaded_cme
st_table *unused_block_warning_table;
// This id table contains a mapping from ID to ICs. It does this with ID
// keys and nested st_tables as values. The nested tables have ICs as keys

View File

@ -2971,9 +2971,9 @@ VALUE rb_gen_method_name(VALUE owner, VALUE name); // in vm_backtrace.c
static void
warn_unused_block(const rb_callable_method_entry_t *cme, const rb_iseq_t *iseq, void *pc)
{
static st_table *dup_check_table = NULL;
rb_vm_t *vm = GET_VM();
st_table *dup_check_table = vm->unused_block_warning_table;
st_data_t key = 0;
union {
VALUE v;
unsigned char b[SIZEOF_VALUE];
@ -2983,6 +2983,14 @@ warn_unused_block(const rb_callable_method_entry_t *cme, const rb_iseq_t *iseq,
.v = (VALUE)cme->def,
};
// relax check
st_data_t key = (st_data_t)cme->def->original_id;
if (st_lookup(dup_check_table, key, NULL)) {
return;
}
// strict check
// make unique key from pc and me->def pointer
for (int i=0; i<SIZEOF_VALUE; i++) {
// fprintf(stderr, "k1:%3d k2:%3d\n", k1.b[i], k2.b[SIZEOF_VALUE-1-i]);
@ -2995,10 +3003,6 @@ warn_unused_block(const rb_callable_method_entry_t *cme, const rb_iseq_t *iseq,
fprintf(stderr, "key:%p\n", (void *)key);
}
if (!dup_check_table) {
dup_check_table = st_init_numtable();
}
// duplication check
if (st_insert(dup_check_table, key, 1)) {
// already shown