Add FrozenError#receiver

Similar to NameError#receiver, this returns the object on which
the modification was attempted.  This is useful as it can pinpoint
exactly what is frozen.  In many cases when a FrozenError is
raised, you cannot determine from the context which object is
frozen that you attempted to modify.

Users of the current rb_error_frozen C function will have to switch
to using rb_error_frozen_object or the new rb_frozen_error_raise
in order to set the receiver of the FrozenError.

To allow the receiver to be set from Ruby, support an optional
second argument to FrozenError#initialize.

Implements [Feature #15751]
This commit is contained in:
Jeremy Evans 2019-04-06 00:02:11 -07:00
parent 897901283c
commit 39eadca76b
7 changed files with 121 additions and 7 deletions

View file

@ -853,6 +853,33 @@ end.join
alias inspect pretty_inspect
end
def test_frozen_error_receiver
obj = Object.new.freeze
(obj.foo = 1) rescue (e = $!)
assert_same(obj, e.receiver)
obj.singleton_class.const_set(:A, 2) rescue (e = $!)
assert_same(obj.singleton_class, e.receiver)
end
def test_frozen_error_initialize
obj = Object.new
exc = FrozenError.new("bar", obj)
assert_equal("bar", exc.message)
assert_same(obj, exc.receiver)
exc = FrozenError.new("bar")
assert_equal("bar", exc.message)
assert_raise_with_message(ArgumentError, "no receiver is available") {
exc.receiver
}
exc = FrozenError.new
assert_equal("FrozenError", exc.message)
assert_raise_with_message(ArgumentError, "no receiver is available") {
exc.receiver
}
end
def test_name_error_new_default
error = NameError.new
assert_equal("NameError", error.message)