summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoichi Sasada <[email protected]>2024-11-06 03:41:59 +0900
committerKoichi Sasada <[email protected]>2024-11-06 11:06:18 +0900
commitab7ab9e4508c24b998703824aa9576fb2e092065 ()
tree27baa0a69fbdc59f54bf0526dde4c8c299ccbf82
parent4203c70dfa96649bae305350817d7cc3c9bc5888 (diff)
`Warning[:strict_unused_block]`
to show unused block warning strictly. ```ruby class C def f = nil end class D def f = yield end [C.new, D.new].each{|obj| obj.f{}} ``` In this case, `D#f` accepts a block. However `C#f` doesn't accept a block. There are some cases passing a block with `obj.f{}` where `obj` is `C` or `D`. To avoid warnings on such cases, "unused block warning" will be warned only if there is not same name which accepts a block. On the above example, `C.new.f{}` doesn't show any warnings because there is a same name `D#f` which accepts a block. We call this default behavior as "relax mode". `strict_unused_block` new warning category changes from "relax mode" to "strict mode", we don't check same name methods and `C.new.f{}` will be warned. [Feature #15554]
Notes: Merged: https://.com/ruby/ruby/pull/12005
-rw-r--r--compile.c2
-rw-r--r--error.c4
-rw-r--r--include/ruby/internal/error.h4
-rw-r--r--ruby.c4
-rw-r--r--test/ruby/test_rubyoptions.rb2
-rw-r--r--vm.c6
-rw-r--r--vm_core.h1
-rw-r--r--vm_insnhelper.c11
8 files changed, 20 insertions, 14 deletions
@@ -2007,7 +2007,7 @@ iseq_set_use_block(rb_iseq_t *iseq)
rb_vm_t *vm = GET_VM();
- if (!vm->unused_block_warning_strict) {
st_data_t key = (st_data_t)rb_intern_str(body->location.label); // String -> ID
st_insert(vm->unused_block_warning_table, key, 1);
}
@@ -86,6 +86,7 @@ static ID id_category;
static ID id_deprecated;
static ID id_experimental;
static ID id_performance;
static VALUE sym_category;
static VALUE sym_highlight;
static struct {
@@ -3584,6 +3585,7 @@ Init_Exception(void)
id_deprecated = rb_intern_const("deprecated");
id_experimental = rb_intern_const("experimental");
id_performance = rb_intern_const("performance");
id_top = rb_intern_const("top");
id_bottom = rb_intern_const("bottom");
id_iseq = rb_make_internal_id();
@@ -3596,12 +3598,14 @@ Init_Exception(void)
st_add_direct(warning_categories.id2enum, id_deprecated, RB_WARN_CATEGORY_DEPRECATED);
st_add_direct(warning_categories.id2enum, id_experimental, RB_WARN_CATEGORY_EXPERIMENTAL);
st_add_direct(warning_categories.id2enum, id_performance, RB_WARN_CATEGORY_PERFORMANCE);
warning_categories.enum2id = rb_init_identtable();
st_add_direct(warning_categories.enum2id, RB_WARN_CATEGORY_NONE, 0);
st_add_direct(warning_categories.enum2id, RB_WARN_CATEGORY_DEPRECATED, id_deprecated);
st_add_direct(warning_categories.enum2id, RB_WARN_CATEGORY_EXPERIMENTAL, id_experimental);
st_add_direct(warning_categories.enum2id, RB_WARN_CATEGORY_PERFORMANCE, id_performance);
}
void
@@ -53,6 +53,9 @@ typedef enum {
/** Warning is for performance issues (not enabled by -w). */
RB_WARN_CATEGORY_PERFORMANCE,
RB_WARN_CATEGORY_DEFAULT_BITS = (
(1U << RB_WARN_CATEGORY_DEPRECATED) |
(1U << RB_WARN_CATEGORY_EXPERIMENTAL) |
@@ -62,6 +65,7 @@ typedef enum {
(1U << RB_WARN_CATEGORY_DEPRECATED) |
(1U << RB_WARN_CATEGORY_EXPERIMENTAL) |
(1U << RB_WARN_CATEGORY_PERFORMANCE) |
0)
} rb_warning_category_t;
@@ -398,6 +398,7 @@ usage(const char *name, int help, int highlight, int columns)
M("deprecated", "", "Deprecated features."),
M("experimental", "", "Experimental features."),
M("performance", "", "Performance issues."),
};
#if USE_RJIT
extern const struct ruby_opt_message rb_rjit_option_messages[];
@@ -1233,6 +1234,9 @@ proc_W_option(ruby_cmdline_options_t *opt, const char *s, int *warning)
else if (NAME_MATCH_P("performance", s, len)) {
bits = 1U << RB_WARN_CATEGORY_PERFORMANCE;
}
else {
rb_warn("unknown warning category: '%s'", s);
}
@@ -118,7 +118,7 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(%w(-We) + ['p $-W'], "", %w(2), [])
assert_in_out_err(%w(-w -W0 -e) + ['p $-W'], "", %w(0), [])
- categories = {deprecated: 1, experimental: 0, performance: 2}
assert_equal categories.keys.sort, Warning.categories.sort
categories.each do |category, level|
@@ -4271,12 +4271,6 @@ Init_BareVM(void)
vm->constant_cache = rb_id_table_create(0);
vm->unused_block_warning_table = st_init_numtable();
- // TODO: remove before Ruby 3.4.0 release
- const char *s = getenv("RUBY_TRY_UNUSED_BLOCK_WARNING_STRICT");
- if (s && strcmp(s, "1") == 0) {
- vm->unused_block_warning_strict = true;
- }
-
// setup main thread
th->nt = ZALLOC(struct rb_native_thread);
th->vm = vm;
@@ -799,7 +799,6 @@ typedef struct rb_vm_struct {
struct rb_id_table *negative_cme_table;
st_table *overloaded_cme_table; // cme -> overloaded_cme
st_table *unused_block_warning_table;
- bool unused_block_warning_strict;
// 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
@@ -3035,6 +3035,7 @@ warn_unused_block(const rb_callable_method_entry_t *cme, const rb_iseq_t *iseq,
rb_vm_t *vm = GET_VM();
st_table *dup_check_table = vm->unused_block_warning_table;
st_data_t key;
union {
VALUE v;
@@ -3046,7 +3047,7 @@ warn_unused_block(const rb_callable_method_entry_t *cme, const rb_iseq_t *iseq,
};
// relax check
- if (!vm->unused_block_warning_strict) {
key = (st_data_t)cme->def->original_id;
if (st_lookup(dup_check_table, key, NULL)) {
@@ -3072,16 +3073,16 @@ warn_unused_block(const rb_callable_method_entry_t *cme, const rb_iseq_t *iseq,
if (st_insert(dup_check_table, key, 1)) {
// already shown
}
- else {
VALUE m_loc = rb_method_entry_location((const rb_method_entry_t *)cme);
VALUE name = rb_gen_method_name(cme->defined_class, ISEQ_BODY(iseq)->location.base_label);
if (!NIL_P(m_loc)) {
- rb_warning("the block passed to '%"PRIsVALUE"' defined at %"PRIsVALUE":%"PRIsVALUE" may be ignored",
- name, RARRAY_AREF(m_loc, 0), RARRAY_AREF(m_loc, 1));
}
else {
- rb_warning("the block may be ignored because '%"PRIsVALUE"' does not use a block", name);
}
}
}