Skip to content

Commit 8c4783f

Browse files
committed
[GR-19220] Fix Module#undef_method error message and Module#{to_s,inspect}.
PullRequest: truffleruby/1163
2 parents e5c0619 + 060b213 commit 8c4783f

File tree

9 files changed

+83
-36
lines changed

9 files changed

+83
-36
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Bug fixes:
2929
* Fixed missing partial evaluation boundary in `Array#{sort,sort!}` (#1727).
3030
* Fixed the class of `self` and the wrapping `Module` for `Kernel#load(path, wrap=true)` (#1739).
3131
* Fixed missing polyglot type declaration for `RSTRING_PTR` to help with native/managed interop.
32+
* Fixed `Module#to_s` and `Module#inspect` to not return an extra `#<Class:` for singleton classes.
3233

3334
Compatibility:
3435

spec/ruby/core/class/to_s_spec.rb

Lines changed: 0 additions & 23 deletions
This file was deleted.

spec/ruby/core/module/fixtures/classes.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ def self.without_test_modules(modules)
66

77
CONST = :plain_constant
88

9+
class NamedClass
10+
end
11+
912
module PrivConstModule
1013
PRIVATE_CONSTANT = 1
1114
private_constant :PRIVATE_CONSTANT

spec/ruby/core/module/to_s_spec.rb

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,44 @@
22
require_relative 'fixtures/classes'
33

44
describe "Module#to_s" do
5+
it 'returns the name of the module if it has a name' do
6+
Enumerable.to_s.should == 'Enumerable'
7+
String.to_s.should == 'String'
8+
end
9+
510
it "returns the full constant path leading to the module" do
611
ModuleSpecs::LookupMod.to_s.should == "ModuleSpecs::LookupMod"
712
end
813

914
it "works with an anonymous module" do
1015
m = Module.new
11-
m.to_s.should =~ /#<Module:0x[0-9a-f]+>/
16+
m.to_s.should =~ /\A#<Module:0x\h+>\z/
1217
end
1318

1419
it "works with an anonymous class" do
1520
c = Class.new
16-
c.to_s.should =~ /#<Class:0x[0-9a-f]+>/
21+
c.to_s.should =~ /\A#<Class:0x\h+>\z/
22+
end
23+
24+
it 'for the singleton class of an object of an anonymous class' do
25+
klass = Class.new
26+
obj = klass.new
27+
sclass = obj.singleton_class
28+
sclass.to_s.should == "#<Class:#{obj}>"
29+
sclass.to_s.should =~ /\A#<Class:#<#{klass}:0x\h+>>\z/
30+
sclass.to_s.should =~ /\A#<Class:#<#<Class:0x\h+>:0x\h+>>\z/
31+
end
32+
33+
it 'for a singleton class of a module includes the module name' do
34+
ModuleSpecs.singleton_class.to_s.should == '#<Class:ModuleSpecs>'
35+
end
36+
37+
it 'for a metaclass includes the class name' do
38+
ModuleSpecs::NamedClass.singleton_class.to_s.should == '#<Class:ModuleSpecs::NamedClass>'
39+
end
40+
41+
it 'for objects includes class name and object ID' do
42+
obj = ModuleSpecs::NamedClass.new
43+
obj.singleton_class.to_s.should =~ /\A#<Class:#<ModuleSpecs::NamedClass:0x\h+>>\z/
1744
end
1845
end

spec/ruby/core/module/undef_method_spec.rb

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,37 @@ def another_method_to_undef() 1 end
5656
@module.send(:undef_method, :method_to_undef).should equal(@module)
5757
end
5858

59-
it "raises a NameError when passed a missing name" do
60-
-> { @module.send :undef_method, :not_exist }.should raise_error(NameError) { |e|
59+
it "raises a NameError when passed a missing name for a module" do
60+
-> { @module.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for module `#{@module}'/) { |e|
61+
# a NameError and not a NoMethodError
62+
e.class.should == NameError
63+
}
64+
end
65+
66+
it "raises a NameError when passed a missing name for a class" do
67+
klass = Class.new
68+
-> { klass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for class `#{klass}'/) { |e|
69+
# a NameError and not a NoMethodError
70+
e.class.should == NameError
71+
}
72+
end
73+
74+
it "raises a NameError when passed a missing name for a singleton class" do
75+
klass = Class.new
76+
obj = klass.new
77+
sclass = obj.singleton_class
78+
79+
-> { sclass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for class `#{sclass}'/) { |e|
80+
e.message.should include('`#<Class:#<#<Class:')
81+
82+
# a NameError and not a NoMethodError
83+
e.class.should == NameError
84+
}
85+
end
86+
87+
it "raises a NameError when passed a missing name for a metaclass" do
88+
klass = String.singleton_class
89+
-> { klass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for class `String'/) { |e|
6190
# a NameError and not a NoMethodError
6291
e.class.should == NameError
6392
}

spec/tags/core/module/undef_method_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/main/java/org/truffleruby/core/exception/CoreExceptions.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,11 @@ public DynamicObject nameErrorInstanceVariableNotDefined(String name, Object rec
646646
public DynamicObject nameErrorUndefinedMethod(String name, DynamicObject module, Node currentNode) {
647647
assert RubyGuards.isRubyModule(module);
648648
return nameError(
649-
StringUtils.format("undefined method `%s' for %s", name, Layouts.MODULE.getFields(module).getName()),
649+
StringUtils.format(
650+
"undefined method `%s' for %s `%s'",
651+
name,
652+
Layouts.CLASS.isClass(module) ? "class" : "module",
653+
Layouts.MODULE.getFields(module).getName()),
650654
module,
651655
name,
652656
currentNode);

src/main/java/org/truffleruby/core/module/ModuleFields.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,9 +446,16 @@ public boolean removeMethod(String methodName) {
446446
public void undefMethod(RubyContext context, Node currentNode, String methodName) {
447447
final InternalMethod method = ModuleOperations.lookupMethodUncached(rubyModuleObject, methodName, null);
448448
if (method == null || method.isUndefined()) {
449+
final DynamicObject moduleForError;
450+
if (RubyGuards.isMetaClass(rubyModuleObject)) {
451+
moduleForError = Layouts.CLASS.getAttached(rubyModuleObject);
452+
} else {
453+
moduleForError = rubyModuleObject;
454+
}
455+
449456
throw new RaiseException(context, context.getCoreExceptions().nameErrorUndefinedMethod(
450457
methodName,
451-
rubyModuleObject,
458+
moduleForError,
452459
currentNode));
453460
} else {
454461
addMethod(context, currentNode, method.undefined());

src/main/java/org/truffleruby/core/module/ModuleNodes.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,19 +1854,19 @@ protected DynamicObject toS(DynamicObject module) {
18541854
final ModuleFields fields = Layouts.MODULE.getFields(module);
18551855
if (RubyGuards.isSingletonClass(module)) {
18561856
final DynamicObject attached = Layouts.CLASS.getAttached(module);
1857-
final String name;
1858-
if (Layouts.CLASS.isClass(attached) || Layouts.MODULE.isModule(attached)) {
1857+
final String attachedName;
1858+
if (Layouts.MODULE.isModule(attached)) {
1859+
attachedName = Layouts.MODULE.getFields(attached).getName();
1860+
} else {
18591861
if (callRbInspect == null) {
18601862
CompilerDirectives.transferToInterpreterAndInvalidate();
18611863
callRbInspect = insert(CallDispatchHeadNode.createPrivate());
18621864
}
18631865
final Object inspectResult = callRbInspect
18641866
.call(coreLibrary().getTruffleTypeModule(), "rb_inspect", attached);
1865-
name = StringOperations.getString((DynamicObject) inspectResult);
1866-
} else {
1867-
name = fields.getName();
1867+
attachedName = StringOperations.getString((DynamicObject) inspectResult);
18681868
}
1869-
moduleName = "#<Class:" + name + ">";
1869+
moduleName = "#<Class:" + attachedName + ">";
18701870
} else if (fields.isRefinement()) {
18711871
final String refinedClass = Layouts.MODULE.getFields(fields.getRefinedClass()).getName();
18721872
final String refinementNamespace = Layouts.MODULE.getFields(fields.getRefinementNamespace()).getName();

0 commit comments

Comments
 (0)