aboutsummaryrefslogtreecommitdiffstats
path: root/error.c
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2020-09-03 08:00:10 -0700
committerJeremy Evans <code@jeremyevans.net>2020-09-28 08:38:06 -0700
commit346301e2329c46362a6089311d0a64b8734b35ec (patch)
tree4b74d3d91bc7cf52cc4e1f50f8d62c3918aff26c /error.c
parent92c3ad9c276d048bf6fba1145ffccf8678fe8af1 (diff)
downloadruby-346301e2329c46362a6089311d0a64b8734b35ec.tar.gz
Add rb_category_warn{,ing} for warning messages with categories
This adds the following C-API functions that can be used to emit warnings with categories included: ```c void rb_category_warn(const char *, const char*, ...) void rb_category_warning(const char*, const char*, ...) ``` Internally in error.c, there is an rb_warn_category function that will call Warning.warn with the string and the category keyword if it doesn't have an arity of 1, and will call Warning.warn with just the string if it has an arity of 1. This refactors the rb_warn_deprecated{,_to_remove} functions to use rb_warn_category. This makes Kernel#warn accept a category keyword and pass it to Warning.warn, so that Ruby methods can more easily emit warnings with categories. rb_warn_category makes sure that the passed category is a already defined category symbol before calling Warning.warn. The only currently defined warning category is :deprecated, since that is what is already used. More categories can be added in later commits.
Diffstat (limited to 'error.c')
-rw-r--r--error.c86
1 files changed, 63 insertions, 23 deletions
diff --git a/error.c b/error.c
index e064eef4bc..fff9b4a5e1 100644
--- a/error.c
+++ b/error.c
@@ -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