From 05313c914b29f7027b27a91021ae2662f0149e54 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@jeremyevans.net>
Date: Mon, 28 Sep 2020 10:10:31 -0700
Subject: [PATCH] Use category: :deprecated in warnings that are related to
 deprecation

Also document that both :deprecated and :experimental are supported
:category option values.

The locations where warnings were marked as deprecation warnings
was previously reviewed by shyouhei.

Comment a couple locations where deprecation warnings should probably
be used but are not currently used because deprecation warning
enablement has not occurred at the time they are called
(RUBY_FREE_MIN, RUBY_HEAP_MIN_SLOTS, -K).

Add assert_deprecated_warn to test assertions.  Use this to simplify
some tests, and fix failing tests after marking some warnings with
deprecated category.
---
 array.c                               |  2 +-
 io.c                                  |  7 ++++---
 object.c                              |  4 ++--
 re.c                                  |  6 +++---
 ruby.c                                |  5 ++++-
 string.c                              |  4 ++--
 test/ruby/test_array.rb               | 30 +++++++++++++--------------
 test/ruby/test_io.rb                  |  2 +-
 test/ruby/test_module.rb              |  4 ++--
 test/ruby/test_regexp.rb              |  6 +++---
 tool/lib/test/unit/core_assertions.rb |  7 +++++++
 variable.c                            |  4 ++--
 vm_method.c                           |  3 ++-
 warning.rb                            |  9 ++++++--
 14 files changed, 55 insertions(+), 38 deletions(-)

diff --git a/array.c b/array.c
index 207d5a337a..f4c3e27af7 100644
--- a/array.c
+++ b/array.c
@@ -2835,7 +2835,7 @@ rb_ary_join_m(int argc, VALUE *argv, VALUE ary)
     if (rb_check_arity(argc, 0, 1) == 0 || NIL_P(sep = argv[0])) {
         sep = rb_output_fs;
         if (!NIL_P(sep)) {
-            rb_warn("$, is set to non-nil value");
+            rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "$, is set to non-nil value");
         }
     }
 
diff --git a/io.c b/io.c
index 15032d72c2..e42c2dcc0a 100644
--- a/io.c
+++ b/io.c
@@ -1956,7 +1956,7 @@ rb_io_writev(VALUE io, int argc, const VALUE *argv)
 	if (io != rb_ractor_stderr() && RTEST(ruby_verbose)) {
 	    VALUE klass = CLASS_OF(io);
 	    char sep = FL_TEST(klass, FL_SINGLETON) ? (klass = io, '.') : '#';
-	    rb_warning("%+"PRIsVALUE"%c""write is outdated interface"
+            rb_category_warning(RB_WARN_CATEGORY_DEPRECATED, "%+"PRIsVALUE"%c""write is outdated interface"
 		       " which accepts just one argument",
 		       klass, sep);
 	}
@@ -7717,7 +7717,7 @@ rb_io_print(int argc, const VALUE *argv, VALUE out)
 	argv = &line;
     }
     if (argc > 1 && !NIL_P(rb_output_fs)) {
-        rb_warn("$, is set to non-nil value");
+        rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "$, is set to non-nil value");
     }
     for (i=0; i<argc; i++) {
 	if (!NIL_P(rb_output_fs) && i>0) {
@@ -10238,7 +10238,8 @@ rb_f_syscall(int argc, VALUE *argv, VALUE _)
     int i;
 
     if (RTEST(ruby_verbose)) {
-	rb_warning("We plan to remove a syscall function at future release. DL(Fiddle) provides safer alternative.");
+        rb_category_warning(RB_WARN_CATEGORY_DEPRECATED,
+            "We plan to remove a syscall function at future release. DL(Fiddle) provides safer alternative.");
     }
 
     if (argc == 0)
diff --git a/object.c b/object.c
index 552b10ba49..b987e13ae8 100644
--- a/object.c
+++ b/object.c
@@ -1590,7 +1590,7 @@ static VALUE
 rb_obj_match(VALUE obj1, VALUE obj2)
 {
     if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_DEPRECATED)) {
-        rb_warn("deprecated Object#=~ is called on %"PRIsVALUE
+        rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "deprecated Object#=~ is called on %"PRIsVALUE
                 "; it always returns nil", rb_obj_class(obj1));
     }
     return Qnil;
@@ -2295,7 +2295,7 @@ VALUE
 rb_mod_attr(int argc, VALUE *argv, VALUE klass)
 {
     if (argc == 2 && (argv[1] == Qtrue || argv[1] == Qfalse)) {
-	rb_warning("optional boolean argument is obsoleted");
+        rb_category_warning(RB_WARN_CATEGORY_DEPRECATED, "optional boolean argument is obsoleted");
 	rb_attr(klass, id_for_attr(klass, argv[0]), 1, RTEST(argv[1]), TRUE);
 	return Qnil;
     }
diff --git a/re.c b/re.c
index 2af617ea81..a56b483513 100644
--- a/re.c
+++ b/re.c
@@ -3467,7 +3467,7 @@ rb_reg_initialize_m(int argc, VALUE *argv, VALUE self)
 		flags |= ARG_ENCODING_NONE;
 	    }
 	    else {
-		rb_warn("encoding option is ignored - %s", kcode);
+                rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "encoding option is ignored - %s", kcode);
 	    }
 	}
 	str = StringValue(argv[0]);
@@ -3922,14 +3922,14 @@ rb_reg_regsub(VALUE str, VALUE src, struct re_registers *regs, VALUE regexp)
 static VALUE
 ignorecase_getter(ID _x, VALUE *_y)
 {
-    rb_warn("variable $= is no longer effective");
+    rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "variable $= is no longer effective");
     return Qfalse;
 }
 
 static void
 ignorecase_setter(VALUE val, ID id, VALUE *_)
 {
-    rb_warn("variable $= is no longer effective; ignored");
+    rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "variable $= is no longer effective; ignored");
 }
 
 static VALUE
diff --git a/ruby.c b/ruby.c
index aa6d4a2b9d..a19e08b798 100644
--- a/ruby.c
+++ b/ruby.c
@@ -1747,7 +1747,10 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
     }
 
     if (opt->src.enc.name)
-	rb_warning("-K is specified; it is for 1.8 compatibility and may cause odd behavior");
+        /* cannot set deprecated category, as enabling deprecation warnings based on flags
+         * has not happened yet.
+         */
+        rb_warning("-K is specified; it is for 1.8 compatibility and may cause odd behavior");
 
 #if USE_MJIT
     if (opt->features.set & FEATURE_BIT(jit)) {
diff --git a/string.c b/string.c
index 864a0909b2..dfb38e4e98 100644
--- a/string.c
+++ b/string.c
@@ -8187,7 +8187,7 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str)
 	rb_raise(rb_eTypeError, "value of $; must be String or Regexp");
     }
     else {
-        rb_warn("$; is set to non-nil value");
+        rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "$; is set to non-nil value");
     }
     if (split_type != SPLIT_TYPE_AWK) {
         switch (BUILTIN_TYPE(spat)) {
@@ -8413,7 +8413,7 @@ get_rs(void)
 	(!RB_TYPE_P(rs, T_STRING) ||
 	 RSTRING_LEN(rs) != 1 ||
 	 RSTRING_PTR(rs)[0] != '\n')) {
-        rb_warn("$/ is set to non-default value");
+        rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "$/ is set to non-default value");
     }
     return rs;
 }
diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb
index f2abb3d5bc..522b58e214 100644
--- a/test/ruby/test_array.rb
+++ b/test/ruby/test_array.rb
@@ -1112,40 +1112,40 @@ class TestArray < Test::Unit::TestCase
   def test_join
     assert_deprecated_warning {$, = ""}
     a = @cls[]
-    assert_equal("", assert_warn(/non-nil value/) {a.join})
+    assert_equal("", assert_deprecated_warn(/non-nil value/) {a.join})
     assert_equal("", a.join(','))
-    assert_equal(Encoding::US_ASCII, assert_warn(/non-nil value/) {a.join}.encoding)
+    assert_equal(Encoding::US_ASCII, assert_deprecated_warn(/non-nil value/) {a.join}.encoding)
 
     assert_deprecated_warning {$, = ""}
     a = @cls[1, 2]
-    assert_equal("12", assert_warn(/non-nil value/) {a.join})
-    assert_equal("12", assert_warn(/non-nil value/) {a.join(nil)})
+    assert_equal("12", assert_deprecated_warn(/non-nil value/) {a.join})
+    assert_equal("12", assert_deprecated_warn(/non-nil value/) {a.join(nil)})
     assert_equal("1,2", a.join(','))
 
     assert_deprecated_warning {$, = ""}
     a = @cls[1, 2, 3]
-    assert_equal("123", assert_warn(/non-nil value/) {a.join})
-    assert_equal("123", assert_warn(/non-nil value/) {a.join(nil)})
+    assert_equal("123", assert_deprecated_warn(/non-nil value/) {a.join})
+    assert_equal("123", assert_deprecated_warn(/non-nil value/) {a.join(nil)})
     assert_equal("1,2,3", a.join(','))
 
     assert_deprecated_warning {$, = ":"}
     a = @cls[1, 2, 3]
-    assert_equal("1:2:3", assert_warn(/non-nil value/) {a.join})
-    assert_equal("1:2:3", assert_warn(/non-nil value/) {a.join(nil)})
+    assert_equal("1:2:3", assert_deprecated_warn(/non-nil value/) {a.join})
+    assert_equal("1:2:3", assert_deprecated_warn(/non-nil value/) {a.join(nil)})
     assert_equal("1,2,3", a.join(','))
 
     assert_deprecated_warning {$, = ""}
 
     e = ''.force_encoding('EUC-JP')
     u = ''.force_encoding('UTF-8')
-    assert_equal(Encoding::US_ASCII, assert_warn(/non-nil value/) {[[]].join}.encoding)
-    assert_equal(Encoding::US_ASCII, assert_warn(/non-nil value/) {[1, [u]].join}.encoding)
-    assert_equal(Encoding::UTF_8, assert_warn(/non-nil value/) {[u, [e]].join}.encoding)
-    assert_equal(Encoding::UTF_8, assert_warn(/non-nil value/) {[u, [1]].join}.encoding)
-    assert_equal(Encoding::UTF_8, assert_warn(/non-nil value/) {[Struct.new(:to_str).new(u)].join}.encoding)
+    assert_equal(Encoding::US_ASCII, assert_deprecated_warn(/non-nil value/) {[[]].join}.encoding)
+    assert_equal(Encoding::US_ASCII, assert_deprecated_warn(/non-nil value/) {[1, [u]].join}.encoding)
+    assert_equal(Encoding::UTF_8, assert_deprecated_warn(/non-nil value/) {[u, [e]].join}.encoding)
+    assert_equal(Encoding::UTF_8, assert_deprecated_warn(/non-nil value/) {[u, [1]].join}.encoding)
+    assert_equal(Encoding::UTF_8, assert_deprecated_warn(/non-nil value/) {[Struct.new(:to_str).new(u)].join}.encoding)
     bug5379 = '[ruby-core:39776]'
-    assert_equal(Encoding::US_ASCII, assert_warn(/non-nil value/) {[[], u, nil].join}.encoding, bug5379)
-    assert_equal(Encoding::UTF_8, assert_warn(/non-nil value/) {[[], "\u3042", nil].join}.encoding, bug5379)
+    assert_equal(Encoding::US_ASCII, assert_deprecated_warn(/non-nil value/) {[[], u, nil].join}.encoding, bug5379)
+    assert_equal(Encoding::UTF_8, assert_deprecated_warn(/non-nil value/) {[[], "\u3042", nil].join}.encoding, bug5379)
   ensure
     $, = nil
   end
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index 9b409af319..9f9318eaf7 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -2621,7 +2621,7 @@ class TestIO < Test::Unit::TestCase
     end
 
     capture.clear
-    assert_warning(/[.#]write is outdated/) do
+    assert_deprecated_warning(/[.#]write is outdated/) do
       stdout, $stdout = $stdout, capture
       puts "hey"
     ensure
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index 55755a61a6..c45b88490a 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -906,10 +906,10 @@ class TestModule < Test::Unit::TestCase
         @foo = :foo
         @bar = :bar
       end
-      assert_warning(/optional boolean argument/) do
+      assert_deprecated_warning(/optional boolean argument/) do
         attr :foo, true
       end
-      assert_warning(/optional boolean argument/) do
+      assert_deprecated_warning(/optional boolean argument/) do
         attr :bar, false
       end
     end
diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb
index 6daae50718..194b8c355d 100644
--- a/test/ruby/test_regexp.rb
+++ b/test/ruby/test_regexp.rb
@@ -42,7 +42,7 @@ class TestRegexp < Test::Unit::TestCase
 
   def test_yoshidam_net_20041111_1
     s = "[\xC2\xA0-\xC3\xBE]"
-    r = assert_warning(/ignored/) {Regexp.new(s, nil, "u")}
+    r = assert_deprecated_warning(/ignored/) {Regexp.new(s, nil, "u")}
     assert_match(r, "\xC3\xBE")
   end
 
@@ -665,9 +665,9 @@ class TestRegexp < Test::Unit::TestCase
   end
 
   def test_ignorecase
-    v = assert_warning(/variable \$= is no longer effective/) { $= }
+    v = assert_deprecated_warning(/variable \$= is no longer effective/) { $= }
     assert_equal(false, v)
-    assert_warning(/variable \$= is no longer effective; ignored/) { $= = nil }
+    assert_deprecated_warning(/variable \$= is no longer effective; ignored/) { $= = nil }
   end
 
   def test_match_setter
diff --git a/tool/lib/test/unit/core_assertions.rb b/tool/lib/test/unit/core_assertions.rb
index c4c271403b..a5defeec75 100644
--- a/tool/lib/test/unit/core_assertions.rb
+++ b/tool/lib/test/unit/core_assertions.rb
@@ -594,6 +594,13 @@ eom
         end
       end
 
+      def assert_deprecated_warn(mesg = /deprecated/)
+        assert_warn(mesg) do
+          Warning[:deprecated] = true
+          yield
+        end
+      end
+
       class << (AssertFile = Struct.new(:failure_message).new)
         include CoreAssertions
         def assert_file_predicate(predicate, *args)
diff --git a/variable.c b/variable.c
index 5b430a7947..3b1462b9de 100644
--- a/variable.c
+++ b/variable.c
@@ -2530,10 +2530,10 @@ rb_const_warn_if_deprecated(const rb_const_entry_t *ce, VALUE klass, ID id)
     if (RB_CONST_DEPRECATED_P(ce) &&
         rb_warning_category_enabled_p(RB_WARN_CATEGORY_DEPRECATED)) {
 	if (klass == rb_cObject) {
-	    rb_warn("constant ::%"PRIsVALUE" is deprecated", QUOTE_ID(id));
+            rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "constant ::%"PRIsVALUE" is deprecated", QUOTE_ID(id));
 	}
 	else {
-	    rb_warn("constant %"PRIsVALUE"::%"PRIsVALUE" is deprecated",
+            rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "constant %"PRIsVALUE"::%"PRIsVALUE" is deprecated",
 		    rb_class_name(klass), QUOTE_ID(id));
 	}
     }
diff --git a/vm_method.c b/vm_method.c
index e526ee0130..f1d6c84875 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -2450,7 +2450,8 @@ vm_respond_to(rb_execution_context_t *ec, VALUE klass, VALUE obj, ID id, int pri
 	    }
 	    else if (!NIL_P(ruby_verbose)) {
 		VALUE location = rb_method_entry_location((const rb_method_entry_t *)cme);
-		rb_warn("%"PRIsVALUE"%c""respond_to?(:%"PRIsVALUE") uses"
+                rb_category_warn(RB_WARN_CATEGORY_DEPRECATED,
+                        "%"PRIsVALUE"%c""respond_to?(:%"PRIsVALUE") uses"
 			" the deprecated method signature, which takes one parameter",
 			(FL_TEST(klass, FL_SINGLETON) ? obj : klass),
 			(FL_TEST(klass, FL_SINGLETON) ? '.' : '#'),
diff --git a/warning.rb b/warning.rb
index 764e352296..0012ffb1d6 100644
--- a/warning.rb
+++ b/warning.rb
@@ -39,9 +39,14 @@ module Kernel
   #
   #    baz.rb:6: warning: invalid call to foo
   #
-  # If the +category+ keyword argument is given, it is passed to
-  # Warning.warn method.
+  # If <code>category</code> keyword argument is given, passes the category
+  # to <code>Warning.warn</code>.  The category given must be be one of the
+  # following categories:
   #
+  # :deprecated :: Used for warning for deprecated functionality that may
+  #                be removed in the future.
+  # :experimental :: Used for experimental features that may change in
+  #                  future releases.
   def warn(*msgs, uplevel: nil, category: nil)
     Primitive.rb_warn_m(msgs, uplevel, category)
   end