Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ New features:

Bug fixes:

* Fix error message when the method name is not a Symbol or String for `Kernel#respond_to?` (#2132, @ssnickolay)


Compatibility:

Expand Down
3 changes: 1 addition & 2 deletions spec/ruby/core/kernel/respond_to_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
end

it "throws a type error if argument can't be coerced into a Symbol" do
-> { @a.respond_to?(Object.new) }.should raise_error(TypeError)
-> { @a.respond_to?(Object.new) }.should raise_error(TypeError, /is not a symbol nor a string/)
end

it "returns false if obj responds to the given protected method" do
Expand Down Expand Up @@ -69,5 +69,4 @@ class KernelSpecs::Foo; def bar; 'done'; end; end
KernelSpecs::Foo.new.respond_to?(:bar).should == true
KernelSpecs::Foo.new.respond_to?(:invalid_and_silly_method_name).should == false
end

end
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,14 @@ public RubyException typeErrorIsNotA(String value, String expectedType, Node cur
return typeError(value + " is not a " + expectedType, currentNode);
}

@TruffleBoundary
public RubyException typeErrorIsNotAOrB(Object value, String expectedTypeA, String expectedTypeB,
Node currentNode) {
return typeError(
StringUtils.format("%s is not a %s nor a %s", inspectReceiver(value), expectedTypeA, expectedTypeB),
currentNode);
}

@TruffleBoundary
public RubyException typeErrorIsNotAClassModule(Object value, Node currentNode) {
return typeError(inspectReceiver(value) + " is not a class/module", currentNode);
Expand Down
39 changes: 11 additions & 28 deletions src/main/java/org/truffleruby/core/kernel/KernelNodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.truffleruby.core.cast.DurationToMillisecondsNodeGen;
import org.truffleruby.core.cast.NameToJavaStringNode;
import org.truffleruby.core.cast.ToStringOrSymbolNodeGen;
import org.truffleruby.core.cast.ToSymbolNode;
import org.truffleruby.core.exception.GetBacktraceException;
import org.truffleruby.core.format.BytesResult;
import org.truffleruby.core.format.FormatExceptionTranslator;
Expand Down Expand Up @@ -1584,38 +1585,20 @@ protected RubyNode coerceToBoolean(RubyNode includeProtectedAndPrivate) {
}

@Specialization
protected boolean doesRespondToString(
protected boolean doesRespondTo(
VirtualFrame frame,
Object object,
RubyString name,
Object name,
boolean includeProtectedAndPrivate,
@Cached ToJavaStringNode toJavaString) {
final boolean ret;
useCallerRefinements(frame);

if (ignoreVisibilityProfile.profile(includeProtectedAndPrivate)) {
ret = dispatchIgnoreVisibility.execute(frame, object, toJavaString.executeToJavaString(name));
} else {
ret = dispatch.execute(frame, object, toJavaString.executeToJavaString(name));
}

if (isTrueProfile.profile(ret)) {
return true;
} else if (respondToMissingProfile
.profile(dispatchRespondToMissing.execute(frame, object, "respond_to_missing?"))) {
return respondToMissing(object, getSymbol(name.rope), includeProtectedAndPrivate);
} else {
return false;
@Cached ConditionProfile notSymbolOrStringProfile,
@Cached ToJavaStringNode toJavaString,
@Cached ToSymbolNode toSymbolNode) {
if (notSymbolOrStringProfile.profile(!RubyGuards.isRubySymbolOrString(name))) {
throw new RaiseException(
getContext(),
coreExceptions().typeErrorIsNotAOrB(object, "symbol", "string", this));
}
}

@Specialization
protected boolean doesRespondToSymbol(
VirtualFrame frame,
Object object,
RubySymbol name,
boolean includeProtectedAndPrivate,
@Cached ToJavaStringNode toJavaString) {
final boolean ret;
useCallerRefinements(frame);

Expand All @@ -1629,7 +1612,7 @@ protected boolean doesRespondToSymbol(
return true;
} else if (respondToMissingProfile
.profile(dispatchRespondToMissing.execute(frame, object, "respond_to_missing?"))) {
return respondToMissing(object, name, includeProtectedAndPrivate);
return respondToMissing(object, toSymbolNode.execute(name), includeProtectedAndPrivate);
} else {
return false;
}
Expand Down