Skip to content

Commit f69ad0e

Browse files
committed
[Bug #21094] Update nested module names when setting temporary name
1 parent 6670926 commit f69ad0e

File tree

3 files changed

+101
-9
lines changed

3 files changed

+101
-9
lines changed

spec/ruby/core/module/set_temporary_name_spec.rb

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,18 @@ module m::N; end
108108
m.name.should == "fake_name_2"
109109
end
110110

111-
it "does not affect a name of a module nested into an anonymous module with a temporary name" do
112-
m = Module.new
113-
m::N = Module.new
114-
m::N.name.should =~ /\A#<Module:0x\h+>::N\z/
115-
116-
m.set_temporary_name("foo")
117-
m::N.name.should =~ /\A#<Module:0x\h+>::N\z/
111+
ruby_bug "#21094", ""..."3.5" do
112+
it "also updates a name of a nested module" do
113+
m = Module.new
114+
m::N = Module.new
115+
m::N.name.should =~ /\A#<Module:0x\h+>::N\z/
116+
117+
m.set_temporary_name "m"
118+
m::N.name.should == "m::N"
119+
120+
m.set_temporary_name nil
121+
m::N.name.should == nil
122+
end
118123
end
119124

120125
it "keeps temporary name when assigned in an anonymous module" do

test/ruby/test_module.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3378,13 +3378,22 @@ def test_set_temporary_name
33783378
m::N.set_temporary_name(nil)
33793379
assert_nil(m::N.name)
33803380

3381+
m::N.const_set(:O, Module.new)
3382+
m.const_set(:Recursive, m)
3383+
m::N.const_set(:Recursive, m)
3384+
m.const_set(:A, 42)
3385+
33813386
m.set_temporary_name(name = "fake_name")
33823387
name.upcase!
33833388
assert_equal("fake_name", m.name)
33843389
assert_raise(FrozenError) {m.name.upcase!}
3390+
assert_equal("fake_name::N", m::N.name)
3391+
assert_equal("fake_name::N::O", m::N::O.name)
33853392

33863393
m.set_temporary_name(nil)
33873394
assert_nil m.name
3395+
assert_nil m::N.name
3396+
assert_nil m::N::O.name
33883397

33893398
assert_raise_with_message(ArgumentError, "empty class/module name") do
33903399
m.set_temporary_name("")

variable.c

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,80 @@ is_constant_path(VALUE name)
166166
return true;
167167
}
168168

169+
struct sub_temporary_name_args {
170+
VALUE names;
171+
ID last;
172+
};
173+
174+
static VALUE build_const_path(VALUE head, ID tail);
175+
static void set_sub_temporary_name_foreach(VALUE mod, struct sub_temporary_name_args *args, VALUE name);
176+
177+
static VALUE
178+
set_sub_temporary_name_recursive(VALUE mod, VALUE data, int recursive)
179+
{
180+
if (recursive) return Qfalse;
181+
182+
struct sub_temporary_name_args *args = (void *)data;
183+
VALUE name = 0;
184+
if (args->names) {
185+
name = build_const_path(rb_ary_last(0, 0, args->names), args->last);
186+
}
187+
set_sub_temporary_name_foreach(mod, args, name);
188+
return Qtrue;
189+
}
190+
191+
static VALUE
192+
set_sub_temporary_name_topmost(VALUE mod, VALUE data, int recursive)
193+
{
194+
if (recursive) return Qfalse;
195+
196+
struct sub_temporary_name_args *args = (void *)data;
197+
VALUE name = args->names;
198+
if (name) {
199+
args->names = rb_ary_hidden_new(0);
200+
}
201+
set_sub_temporary_name_foreach(mod, args, name);
202+
return Qtrue;
203+
}
204+
205+
static enum rb_id_table_iterator_result
206+
set_sub_temporary_name_i(ID id, VALUE val, void *data)
207+
{
208+
val = ((rb_const_entry_t *)val)->value;
209+
if (rb_namespace_p(val) && !RCLASS_EXT(val)->permanent_classpath) {
210+
VALUE arg = (VALUE)data;
211+
struct sub_temporary_name_args *args = data;
212+
args->last = id;
213+
rb_exec_recursive_paired(set_sub_temporary_name_recursive, val, arg, arg);
214+
}
215+
return ID_TABLE_CONTINUE;
216+
}
217+
218+
static void
219+
set_sub_temporary_name_foreach(VALUE mod, struct sub_temporary_name_args *args, VALUE name)
220+
{
221+
RCLASS_SET_CLASSPATH(mod, name, FALSE);
222+
struct rb_id_table *tbl = RCLASS_CONST_TBL(mod);
223+
if (!tbl) return;
224+
if (!name) {
225+
rb_id_table_foreach(tbl, set_sub_temporary_name_i, args);
226+
}
227+
else {
228+
long names_len = RARRAY_LEN(args->names); // paranoiac check?
229+
rb_ary_push(args->names, name);
230+
rb_id_table_foreach(tbl, set_sub_temporary_name_i, args);
231+
rb_ary_set_len(args->names, names_len);
232+
}
233+
}
234+
235+
static void
236+
set_sub_temporary_name(VALUE mod, VALUE name)
237+
{
238+
struct sub_temporary_name_args args = {name};
239+
VALUE arg = (VALUE)&args;
240+
rb_exec_recursive_paired(set_sub_temporary_name_topmost, mod, arg, arg);
241+
}
242+
169243
/*
170244
* call-seq:
171245
* mod.set_temporary_name(string) -> self
@@ -224,7 +298,9 @@ rb_mod_set_temporary_name(VALUE mod, VALUE name)
224298

225299
if (NIL_P(name)) {
226300
// Set the temporary classpath to NULL (anonymous):
227-
RCLASS_SET_CLASSPATH(mod, 0, FALSE);
301+
RB_VM_LOCK_ENTER();
302+
set_sub_temporary_name(mod, 0);
303+
RB_VM_LOCK_LEAVE();
228304
}
229305
else {
230306
// Ensure the name is a string:
@@ -241,7 +317,9 @@ rb_mod_set_temporary_name(VALUE mod, VALUE name)
241317
name = rb_str_new_frozen(name);
242318

243319
// Set the temporary classpath to the given name:
244-
RCLASS_SET_CLASSPATH(mod, name, FALSE);
320+
RB_VM_LOCK_ENTER();
321+
set_sub_temporary_name(mod, name);
322+
RB_VM_LOCK_LEAVE();
245323
}
246324

247325
return mod;

0 commit comments

Comments
 (0)