diff --git a/NEWS.md b/NEWS.md
index 7ab42697be..82eb55b877 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -26,6 +26,9 @@ Note that each entry is kept to a minimum, see links for details.
Note: We're only listing outstanding class updates.
+* Module
+ * Module.used_refinements has been added. [[Feature #14332]]
+
## Stdlib updates
* The following default gem are updated.
diff --git a/eval.c b/eval.c
index 9ba90ecdfa..8103f52fa5 100644
--- a/eval.c
+++ b/eval.c
@@ -1510,6 +1510,57 @@ rb_mod_s_used_modules(VALUE _)
return rb_funcall(ary, rb_intern("uniq"), 0);
}
+static int
+used_refinements_i(VALUE _, VALUE mod, VALUE ary)
+{
+ while (FL_TEST(rb_class_of(mod), RMODULE_IS_REFINEMENT)) {
+ rb_ary_push(ary, rb_class_of(mod));
+ mod = RCLASS_SUPER(mod);
+ }
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * used_refinements -> array
+ *
+ * Returns an array of all modules used in the current scope. The ordering
+ * of modules in the resulting array is not defined.
+ *
+ * module A
+ * refine Object do
+ * end
+ * end
+ *
+ * module B
+ * refine Object do
+ * end
+ * end
+ *
+ * using A
+ * using B
+ * p Module.used_refinements
+ *
+ * produces:
+ *
+ * [#, #]
+ */
+static VALUE
+rb_mod_s_used_refinements(VALUE _)
+{
+ const rb_cref_t *cref = rb_vm_cref();
+ VALUE ary = rb_ary_new();
+
+ while (cref) {
+ if (!NIL_P(CREF_REFINEMENTS(cref))) {
+ rb_hash_foreach(CREF_REFINEMENTS(cref), used_refinements_i, ary);
+ }
+ cref = CREF_NEXT(cref);
+ }
+
+ return ary;
+}
+
struct refinement_import_methods_arg {
rb_cref_t *cref;
VALUE refinement;
@@ -1944,6 +1995,8 @@ Init_eval(void)
rb_define_private_method(rb_cModule, "using", mod_using, 1);
rb_define_singleton_method(rb_cModule, "used_modules",
rb_mod_s_used_modules, 0);
+ rb_define_singleton_method(rb_cModule, "used_refinements",
+ rb_mod_s_used_refinements, 0);
rb_undef_method(rb_cClass, "refine");
rb_define_private_method(rb_cRefinement, "import_methods", refinement_import_methods, -1);
diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb
index 202da13645..c6de9ed958 100644
--- a/test/ruby/test_refinement.rb
+++ b/test/ruby/test_refinement.rb
@@ -1687,6 +1687,8 @@ class TestRefinement < Test::Unit::TestCase
refine Object do
def in_ref_a
end
+
+ RefA.const_set(:REF, self)
end
end
@@ -1694,6 +1696,8 @@ class TestRefinement < Test::Unit::TestCase
refine Object do
def in_ref_b
end
+
+ RefB.const_set(:REF, self)
end
end
@@ -1703,23 +1707,28 @@ class TestRefinement < Test::Unit::TestCase
refine Object do
def in_ref_c
end
+
+ RefC.const_set(:REF, self)
end
end
module Foo
using RefB
USED_MODS = Module.used_modules
+ USED_REFS = Module.used_refinements
end
module Bar
using RefC
USED_MODS = Module.used_modules
+ USED_REFS = Module.used_refinements
end
module Combined
using RefA
using RefB
USED_MODS = Module.used_modules
+ USED_REFS = Module.used_refinements
end
end
@@ -1731,6 +1740,14 @@ class TestRefinement < Test::Unit::TestCase
assert_equal [ref::RefB, ref::RefA], ref::Combined::USED_MODS
end
+ def test_used_refinements
+ ref = VisibleRefinements
+ assert_equal [], Module.used_refinements
+ assert_equal [ref::RefB::REF], ref::Foo::USED_REFS
+ assert_equal [ref::RefC::REF], ref::Bar::USED_REFS
+ assert_equal [ref::RefB::REF, ref::RefA::REF], ref::Combined::USED_REFS
+ end
+
def test_warn_setconst_in_refinmenet
bug10103 = '[ruby-core:64143] [Bug #10103]'
warnings = [