diff options
-rw-r--r-- | error.c | 86 | ||||
-rw-r--r-- | include/ruby/internal/error.h | 2 | ||||
-rw-r--r-- | spec/ruby/core/kernel/warn_spec.rb | 34 | ||||
-rw-r--r-- | spec/ruby/core/warning/warn_spec.rb | 43 | ||||
-rw-r--r-- | warning.rb | 4 |
5 files changed, 135 insertions, 34 deletions
@@ -73,6 +73,8 @@ static VALUE rb_cWarningBuffer; static ID id_warn; static ID id_category; +static VALUE sym_category; +static VALUE warning_categories; extern const char ruby_description[]; @@ -276,6 +278,34 @@ rb_warning_warn(VALUE mod, VALUE str) return rb_funcallv(mod, id_warn, 1, &str); } + +static int +rb_warning_warn_arity(void) { + return rb_method_entry_arity(rb_method_entry(rb_singleton_class(rb_mWarning), id_warn)); +} + +static VALUE +rb_warn_category(VALUE str, VALUE category) +{ + if (category != Qnil) { + category = rb_to_symbol_type(category); + if (rb_hash_aref(warning_categories, category) != Qtrue) { + rb_raise(rb_eArgError, "invalid warning category used: %s", rb_id2name(SYM2ID(category))); + } + } + + if (rb_warning_warn_arity() == 1) { + return rb_warning_warn(rb_mWarning, str); + } + else { + VALUE args[2]; + args[0] = str; + args[1] = rb_hash_new(); + rb_hash_aset(args[1], sym_category, category); + return rb_funcallv_kw(rb_mWarning, id_warn, 2, args, RB_PASS_KEYWORDS); + } +} + static void rb_write_warning_str(VALUE str) { @@ -345,6 +375,16 @@ rb_warn(const char *fmt, ...) } void +rb_category_warn(const char *category, const char *fmt, ...) +{ + if (!NIL_P(ruby_verbose)) { + with_warning_string(mesg, 0, fmt) { + rb_warn_category(mesg, ID2SYM(rb_intern(category))); + } + } +} + +void rb_enc_warn(rb_encoding *enc, const char *fmt, ...) { if (!NIL_P(ruby_verbose)) { @@ -365,6 +405,17 @@ rb_warning(const char *fmt, ...) } } +/* rb_category_warning() reports only in verbose mode */ +void +rb_category_warning(const char *category, const char *fmt, ...) +{ + if (RTEST(ruby_verbose)) { + with_warning_string(mesg, 0, fmt) { + rb_warn_category(mesg, ID2SYM(rb_intern(category))); + } + } +} + VALUE rb_warning_string(const char *fmt, ...) { @@ -385,8 +436,6 @@ rb_enc_warning(rb_encoding *enc, const char *fmt, ...) } #endif -static void warn_deprecated(VALUE mesg); - void rb_warn_deprecated(const char *fmt, const char *suggest, ...) { @@ -400,7 +449,7 @@ rb_warn_deprecated(const char *fmt, const char *suggest, ...) rb_str_cat_cstr(mesg, " is deprecated"); if (suggest) rb_str_catf(mesg, "; use %s instead", suggest); rb_str_cat_cstr(mesg, "\n"); - warn_deprecated(mesg); + rb_warn_category(mesg, ID2SYM(rb_intern("deprecated"))); } void @@ -414,24 +463,7 @@ rb_warn_deprecated_to_remove(const char *fmt, const char *removal, ...) va_end(args); rb_str_set_len(mesg, RSTRING_LEN(mesg) - 1); rb_str_catf(mesg, " is deprecated and will be removed in Ruby %s\n", removal); - warn_deprecated(mesg); -} - -static void -warn_deprecated(VALUE mesg) -{ - VALUE warn_args[2] = {mesg}; - int kwd = 0; - const rb_method_entry_t *me = rb_method_entry(rb_singleton_class(rb_mWarning), id_warn); - - if (rb_method_entry_arity(me) != 1) { - VALUE kwargs = rb_hash_new(); - rb_hash_aset(kwargs, ID2SYM(rb_intern("category")), ID2SYM(rb_intern("deprecated"))); - warn_args[1] = kwargs; - kwd = 1; - } - - rb_funcallv_kw(rb_mWarning, id_warn, 1 + kwd, warn_args, kwd); + rb_warn_category(mesg, ID2SYM(rb_intern("deprecated"))); } static inline int @@ -453,7 +485,7 @@ warning_write(int argc, VALUE *argv, VALUE buf) VALUE rb_ec_backtrace_location_ary(rb_execution_context_t *ec, long lev, long n); static VALUE -rb_warn_m(rb_execution_context_t *ec, VALUE exc, VALUE msgs, VALUE uplevel) +rb_warn_m(rb_execution_context_t *ec, VALUE exc, VALUE msgs, VALUE uplevel, VALUE category) { VALUE location = Qnil; int argc = RARRAY_LENINT(msgs); @@ -489,12 +521,13 @@ rb_warn_m(rb_execution_context_t *ec, VALUE exc, VALUE msgs, VALUE uplevel) rb_io_puts(argc, argv, str); RBASIC_SET_CLASS(str, rb_cString); } + if (exc == rb_mWarning) { rb_must_asciicompat(str); rb_write_error_str(str); } else { - rb_write_warning_str(str); + rb_warn_category(str, category); } } return Qnil; @@ -2758,6 +2791,13 @@ Init_Exception(void) id_bottom = rb_intern_const("bottom"); id_iseq = rb_make_internal_id(); id_recv = rb_make_internal_id(); + + sym_category = ID2SYM(id_category); + + warning_categories = rb_hash_new(); + rb_gc_register_mark_object(warning_categories); + rb_hash_aset(warning_categories, ID2SYM(rb_intern("deprecated")), Qtrue); + rb_obj_freeze(warning_categories); } void diff --git a/include/ruby/internal/error.h b/include/ruby/internal/error.h index 98f016d995..dc842cc6f6 100644 --- a/include/ruby/internal/error.h +++ b/include/ruby/internal/error.h @@ -63,10 +63,12 @@ VALUE *rb_ruby_debug_ptr(void); /* reports if `-W' specified */ PRINTF_ARGS(void rb_warning(const char*, ...), 1, 2); +PRINTF_ARGS(void rb_category_warning(const char*, const char*, ...), 2, 3); PRINTF_ARGS(void rb_compile_warning(const char *, int, const char*, ...), 3, 4); PRINTF_ARGS(void rb_sys_warning(const char*, ...), 1, 2); /* reports always */ COLDFUNC PRINTF_ARGS(void rb_warn(const char*, ...), 1, 2); +COLDFUNC PRINTF_ARGS(void rb_category_warn(const char *, const char*, ...), 2, 3); PRINTF_ARGS(void rb_compile_warn(const char *, int, const char*, ...), 3, 4); RBIMPL_SYMBOL_EXPORT_END() diff --git a/spec/ruby/core/kernel/warn_spec.rb b/spec/ruby/core/kernel/warn_spec.rb index 79e78dd4a3..de08cd8cff 100644 --- a/spec/ruby/core/kernel/warn_spec.rb +++ b/spec/ruby/core/kernel/warn_spec.rb @@ -17,7 +17,7 @@ describe "Kernel#warn" do Kernel.should have_private_instance_method(:warn) end - it "requires multiple arguments" do + it "accepts multiple arguments" do Kernel.method(:warn).arity.should < 0 end @@ -114,6 +114,38 @@ describe "Kernel#warn" do end end + ruby_version_is "3.0" do + it "accepts :category keyword with a symbol" do + -> { + $VERBOSE = true + warn("message", category: :deprecated) + }.should output(nil, "message\n") + end + + it "accepts :category keyword with nil" do + -> { + $VERBOSE = true + warn("message", category: nil) + }.should output(nil, "message\n") + end + + it "accepts :category keyword with object convertible to symbol" do + o = Object.new + def o.to_sym; :deprecated; end + -> { + $VERBOSE = true + warn("message", category: o) + }.should output(nil, "message\n") + end + + it "raises if :category keyword is not nil and not convertible to symbol" do + -> { + $VERBOSE = true + warn("message", category: Object.new) + }.should raise_error(TypeError) + end + end + it "converts first arg using to_s" do w = KernelSpecs::WarnInNestedCall.new diff --git a/spec/ruby/core/warning/warn_spec.rb b/spec/ruby/core/warning/warn_spec.rb index 21424c6c76..2ded6a109d 100644 --- a/spec/ruby/core/warning/warn_spec.rb +++ b/spec/ruby/core/warning/warn_spec.rb @@ -51,14 +51,41 @@ describe "Warning.warn" do end end - it "is called by Kernel.warn" do - Warning.should_receive(:warn).with("Chunky bacon!\n") - verbose = $VERBOSE - $VERBOSE = false - begin - Kernel.warn("Chunky bacon!") - ensure - $VERBOSE = verbose + + ruby_version_is '3.0' do + it "is called by Kernel.warn with nil category keyword" do + Warning.should_receive(:warn).with("Chunky bacon!\n", category: nil) + verbose = $VERBOSE + $VERBOSE = false + begin + Kernel.warn("Chunky bacon!") + ensure + $VERBOSE = verbose + end + end + + it "is called by Kernel.warn with given category keyword converted to a symbol" do + Warning.should_receive(:warn).with("Chunky bacon!\n", category: :deprecated) + verbose = $VERBOSE + $VERBOSE = false + begin + Kernel.warn("Chunky bacon!", category: 'deprecated') + ensure + $VERBOSE = verbose + end + end + end + + ruby_version_is ''...'3.0' do + it "is called by Kernel.warn" do + Warning.should_receive(:warn).with("Chunky bacon!\n") + verbose = $VERBOSE + $VERBOSE = false + begin + Kernel.warn("Chunky bacon!") + ensure + $VERBOSE = verbose + end end end end diff --git a/warning.rb b/warning.rb index f81ad77629..22acd3424e 100644 --- a/warning.rb +++ b/warning.rb @@ -39,7 +39,7 @@ module Kernel # # baz.rb:6: warning: invalid call to foo # - def warn(*msgs, uplevel: nil) - Primitive.rb_warn_m(msgs, uplevel) + def warn(*msgs, uplevel: nil, category: nil) + Primitive.rb_warn_m(msgs, uplevel, category) end end |