summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Evans <[email protected]>2021-01-14 11:59:25 -0800
committerJeremy Evans <[email protected]>2021-01-14 20:43:30 -0800
commite09094546a19d6b62b3e21d0b061b103cf21f760 ()
tree3b0cacf97950307ef79bbef1abf445e213f1820a
parent1cb0c5ac49e180cca060e3a17eba6c561f5330c2 (diff)
Make Module#prepend affect ancestor chain even if argument already included in receiver
Previously, if a class included a module and then prepended the same module, the prepend had no effect. This changes the behavior so that the prepend has an effect unless the module is already prepended the receiver. While here, rename the origin_seen variable in include_modules_at, since it is misleading. The variable tracks whether c has been seen, not whether the origin of klass has been. Fixes [Bug #17423]
Notes: Merged: https://.com/ruby/ruby/pull/4072
-rw-r--r--NEWS.md8
-rw-r--r--class.c51
-rw-r--r--test/ruby/test_module.rb37
3 files changed, 73 insertions, 23 deletions
@@ -21,6 +21,13 @@ Outstanding ones only.
* Enumerator::Lazy#compact is added. [[Feature #17312]]
## Stdlib updates
Outstanding ones only.
@@ -49,3 +56,4 @@ Excluding feature bug fixes.
[Feature #17312]: https://bugs.ruby-lang.org/issues/17312
@@ -1010,36 +1010,43 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super)
VALUE p, iclass, origin_stack = 0;
int method_changed = 0, constant_changed = 0, add_subclass;
long origin_len;
- struct rb_id_table *const klass_m_tbl = RCLASS_M_TBL(RCLASS_ORIGIN(klass));
VALUE original_klass = klass;
while (module) {
- int origin_seen = FALSE;
int superclass_seen = FALSE;
struct rb_id_table *tbl;
- if (klass == c)
- origin_seen = TRUE;
if (klass_m_tbl && klass_m_tbl == RCLASS_M_TBL(module))
return -1;
- /* ignore if the module included already in superclasses */
- for (p = RCLASS_SUPER(klass); p; p = RCLASS_SUPER(p)) {
- int type = BUILTIN_TYPE(p);
- if (c == p)
- origin_seen = TRUE;
- if (type == T_ICLASS) {
- if (RCLASS_M_TBL(p) == RCLASS_M_TBL(module)) {
- if (!superclass_seen && origin_seen) {
- c = p; /* move insertion point */
- }
- goto skip;
- }
- }
- else if (type == T_CLASS) {
- if (!search_super) break;
- superclass_seen = TRUE;
- }
- }
VALUE super_class = RCLASS_SUPER(c);
@@ -641,6 +641,41 @@ class TestModule < Test::Unit::TestCase
assert_equal([:p, :a, :s, :q, :r, :c], a.new.m)
end
def test_instance_methods
assert_equal([:user, :user2], User.instance_methods(false).sort)
assert_equal([:user, :user2, :mixin].sort, User.instance_methods(true).sort)
@@ -2158,7 +2193,7 @@ class TestModule < Test::Unit::TestCase
assert_equal([:c2, :m0, :m1, :m2, :c0], c2.new.x)
m3 = labeled_module("m3") {include m1; prepend m1}
- assert_equal([m3, m0, m1], m3.ancestors)
m3 = labeled_module("m3") {prepend m1; include m1}
assert_equal([m0, m1, m3], m3.ancestors)
m3 = labeled_module("m3") {prepend m1; prepend m1}