1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/test/ruby/test_super.rb
Jeremy Evans 55b7ba3686 Make super in instance_eval in method in module raise TypeError
This makes behavior the same as super in instance_eval in method
in class.  The reason this wasn't implemented before is that
there is a check to determine if the self in the current context
is of the expected class, and a module itself can be included
in multiple classes, so it doesn't have an expected class.

Implementing this requires giving iclasses knowledge of which
class created them, so that super call in the module method
knows the expected class for super calls.  This reference
is called includer, and should only be set for iclasses.

Note that the approach Ruby uses in this check is not robust. If
you instance_eval another object of the same class and call super,
instead of an TypeError, you get super called with the
instance_eval receiver instead of the method receiver.  Truly
fixing super would require keeping a reference to the super object
(method receiver) in each frame where scope has changed, and using
that instead of current self when calling super.

Fixes [Bug #11636]
2019-12-12 15:50:19 +09:00

586 lines
12 KiB
Ruby

# frozen_string_literal: false
require 'test/unit'
class TestSuper < Test::Unit::TestCase
class Base
def single(a) a end
def double(a, b) [a,b] end
def array(*a) a end
def optional(a = 0) a end
def keyword(**a) a end
end
class Single1 < Base
def single(*) super end
end
class Single2 < Base
def single(a,*) super end
end
class Double1 < Base
def double(*) super end
end
class Double2 < Base
def double(a,*) super end
end
class Double3 < Base
def double(a,b,*) super end
end
class Array1 < Base
def array(*) super end
end
class Array2 < Base
def array(a,*) super end
end
class Array3 < Base
def array(a,b,*) super end
end
class Array4 < Base
def array(a,b,c,*) super end
end
class Optional1 < Base
def optional(a = 1) super end
end
class Optional2 < Base
def optional(a, b = 1) super end
end
class Optional3 < Base
def single(a = 1) super end
end
class Optional4 < Base
def array(a = 1, *) super end
end
class Optional5 < Base
def array(a = 1, b = 2, *) super end
end
class Keyword1 < Base
def keyword(foo: "keyword1") super end
end
class Keyword2 < Base
def keyword(foo: "keyword2")
foo = "changed1"
x = super
foo = "changed2"
y = super
[x, y]
end
end
def test_single1
assert_equal(1, Single1.new.single(1))
end
def test_single2
assert_equal(1, Single2.new.single(1))
end
def test_double1
assert_equal([1, 2], Double1.new.double(1, 2))
end
def test_double2
assert_equal([1, 2], Double2.new.double(1, 2))
end
def test_double3
assert_equal([1, 2], Double3.new.double(1, 2))
end
def test_array1
assert_equal([], Array1.new.array())
assert_equal([1], Array1.new.array(1))
end
def test_array2
assert_equal([1], Array2.new.array(1))
assert_equal([1,2], Array2.new.array(1, 2))
end
def test_array3
assert_equal([1,2], Array3.new.array(1, 2))
assert_equal([1,2,3], Array3.new.array(1, 2, 3))
end
def test_array4
assert_equal([1,2,3], Array4.new.array(1, 2, 3))
assert_equal([1,2,3,4], Array4.new.array(1, 2, 3, 4))
end
def test_optional1
assert_equal(9, Optional1.new.optional(9))
assert_equal(1, Optional1.new.optional)
end
def test_optional2
assert_raise(ArgumentError) do
# call Base#optional with 2 arguments; the 2nd arg is supplied
Optional2.new.optional(9)
end
assert_raise(ArgumentError) do
# call Base#optional with 2 arguments
Optional2.new.optional(9, 2)
end
end
def test_optional3
assert_equal(9, Optional3.new.single(9))
# call Base#single with 1 argument; the arg is supplied
assert_equal(1, Optional3.new.single)
end
def test_optional4
assert_equal([1], Optional4.new.array)
assert_equal([9], Optional4.new.array(9))
assert_equal([9, 8], Optional4.new.array(9, 8))
end
def test_optional5
assert_equal([1, 2], Optional5.new.array)
assert_equal([9, 2], Optional5.new.array(9))
assert_equal([9, 8], Optional5.new.array(9, 8))
assert_equal([9, 8, 7], Optional5.new.array(9, 8, 7))
end
def test_keyword1
assert_equal({foo: "keyword1"}, Keyword1.new.keyword)
bug8008 = '[ruby-core:53114] [Bug #8008]'
assert_equal({foo: bug8008}, Keyword1.new.keyword(foo: bug8008))
end
def test_keyword2
assert_equal([{foo: "changed1"}, {foo: "changed2"}], Keyword2.new.keyword)
end
class A
def tt(aa)
"A#tt"
end
def uu(a)
class << self
define_method(:tt) do |sym|
super(sym)
end
end
end
end
def test_define_method
a = A.new
a.uu(12)
assert_equal("A#tt", a.tt(12), "[ruby-core:3856]")
assert_raise_with_message(RuntimeError, /implicit argument passing of super from method defined by define_method/, "[ruby-core:24244]") {
lambda {
Class.new {
define_method(:a) {super}
}.new.a
}.call
}
end
class SubSeq
def initialize
@first=11
@first or fail
end
def subseq
@first or fail
end
end
class Indexed
def subseq
SubSeq.new
end
end
Overlaid = proc do
class << self
def subseq
super.instance_eval(& Overlaid)
end
end
end
def test_overlaid
assert_nothing_raised('[ruby-dev:40959]') do
overlaid = proc do |obj|
def obj.reverse
super
end
end
overlaid.call(str = "123")
overlaid.call([1,2,3])
str.reverse
end
assert_nothing_raised('[ruby-core:27230]') do
mid=Indexed.new
mid.instance_eval(&Overlaid)
mid.subseq
mid.subseq
end
end
module DoubleInclude
class Base
def foo
[:Base]
end
end
module Override
def foo
super << :Override
end
end
class A < Base
end
class B < A
end
B.send(:include, Override)
A.send(:include, Override)
end
def test_double_include
assert_equal([:Base, :Override, :Override], DoubleInclude::B.new.foo, "[Bug #3351]")
end
module DoubleInclude2
class Base
def foo
[:Base]
end
end
module Override
def foo
super << :Override
end
end
class A < Base
def foo
super << :A
end
end
class B < A
def foo
super << :B
end
end
B.send(:include, Override)
A.send(:include, Override)
end
def test_double_include2
assert_equal([:Base, :Override, :A, :Override, :B],
DoubleInclude2::B.new.foo)
end
def test_super_in_instance_eval
super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
def foo
return [:super, self]
end
}
sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
def foo
x = Object.new
x.instance_eval do
super()
end
end
}
obj = sub_class.new
assert_raise_with_message(TypeError, /Sub\u{30af 30e9 30b9}/) do
obj.foo
end
end
def test_super_in_instance_eval_with_define_method
super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
def foo
return [:super, self]
end
}
sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
define_method(:foo) do
x = Object.new
x.instance_eval do
super()
end
end
}
obj = sub_class.new
assert_raise_with_message(TypeError, /Sub\u{30af 30e9 30b9}/) do
obj.foo
end
end
def test_super_in_instance_eval_in_module
super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
def foo
return [:super, self]
end
}
mod = EnvUtil.labeled_module("Mod\u{30af 30e9 30b9}") {
def foo
x = Object.new
x.instance_eval do
super()
end
end
}
sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
include mod
}
obj = sub_class.new
assert_raise_with_message(TypeError, /Sub\u{30af 30e9 30b9}/) do
obj.foo
end
end
def test_super_in_orphan_block
super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
def foo
return [:super, self]
end
}
sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
def foo
lambda { super() }
end
}
obj = sub_class.new
assert_equal([:super, obj], obj.foo.call)
end
def test_super_in_orphan_block_with_instance_eval
super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
def foo
return [:super, self]
end
}
sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
def foo
x = Object.new
x.instance_eval do
lambda { super() }
end
end
}
obj = sub_class.new
assert_raise_with_message(TypeError, /Sub\u{30af 30e9 30b9}/) do
obj.foo.call
end
end
def test_yielding_super
a = Class.new { def yielder; yield; end }
x = Class.new { define_singleton_method(:hello) { 'hi' } }
y = Class.new(x) {
define_singleton_method(:hello) {
m = a.new
m.yielder { super() }
}
}
assert_equal 'hi', y.hello
end
def test_super_in_thread
hoge = Class.new {
def bar; 'hoge'; end
}
foo = Class.new(hoge) {
def bar; Thread.new { super }.join.value; end
}
assert_equal 'hoge', foo.new.bar
end
def assert_super_in_block(type)
bug7064 = '[ruby-core:47680]'
assert_normal_exit "#{type} {super}", bug7064
end
def test_super_in_at_exit
assert_super_in_block("at_exit")
end
def test_super_in_END
assert_super_in_block("END")
end
def test_super_in_BEGIN
assert_super_in_block("BEGIN")
end
class X
def foo(*args)
args
end
end
class Y < X
define_method(:foo) do |*args|
super(*args)
end
end
def test_super_splat
# [ruby-list:49575]
y = Y.new
assert_equal([1, 2], y.foo(1, 2))
assert_equal([1, false], y.foo(1, false))
assert_equal([1, 2, 3, 4, 5], y.foo(1, 2, 3, 4, 5))
assert_equal([false, true], y.foo(false, true))
assert_equal([false, false], y.foo(false, false))
assert_equal([1, 2, 3, false, 5], y.foo(1, 2, 3, false, 5))
end
def test_missing_super
o = Class.new {def foo; super; end}.new
e = assert_raise(NoMethodError) {o.foo}
assert_same(o, e.receiver)
assert_equal(:foo, e.name)
end
def test_missing_super_in_method_module
bug9315 = '[ruby-core:59358] [Bug #9315]'
a = Module.new do
def foo
super
end
end
b = Class.new do
include a
end
assert_raise(NoMethodError, bug9315) do
b.new.method(:foo).call
end
end
def test_module_super_in_method_module
bug9315 = '[ruby-core:59589] [Bug #9315]'
a = Module.new do
def foo
super
end
end
c = Class.new do
def foo
:ok
end
end
o = c.new.extend(a)
assert_nothing_raised(NoMethodError, bug9315) do
assert_equal(:ok, o.method(:foo).call, bug9315)
end
end
def test_missing_super_in_module_unbound_method
bug9377 = '[ruby-core:59619] [Bug #9377]'
a = Module.new do
def foo; super end
end
m = a.instance_method(:foo).bind(Object.new)
assert_raise(NoMethodError, bug9377) do
m.call
end
end
def test_super_in_module_unbound_method
bug9721 = '[ruby-core:61936] [Bug #9721]'
a = Module.new do
def foo(result)
result << "A"
end
end
b = Module.new do
def foo(result)
result << "B"
super
end
end
um = b.instance_method(:foo)
m = um.bind(Object.new.extend(a))
result = []
assert_nothing_raised(NoMethodError, bug9721) do
m.call(result)
end
assert_equal(%w[B A], result, bug9721)
bug9740 = '[ruby-core:62017] [Bug #9740]'
b.module_eval do
define_method(:foo) do |res|
um.bind(self).call(res)
end
end
result.clear
o = Object.new.extend(a).extend(b)
assert_nothing_raised(NoMethodError, SystemStackError, bug9740) do
o.foo(result)
end
assert_equal(%w[B A], result, bug9721)
end
def test_from_eval
bug10263 = '[ruby-core:65122] [Bug #10263a]'
a = Class.new do
def foo
"A"
end
end
b = Class.new(a) do
def foo
binding.eval("super")
end
end
assert_equal("A", b.new.foo, bug10263)
end
def test_super_with_block
a = Class.new do
def foo
yield
end
end
b = Class.new(a) do
def foo
super{
"b"
}
end
end
assert_equal "b", b.new.foo{"c"}
end
def test_public_zsuper_with_prepend
bug12876 = '[ruby-core:77784] [Bug #12876]'
m = EnvUtil.labeled_module("M")
c = EnvUtil.labeled_class("C") {prepend m; public :initialize}
o = assert_nothing_raised(Timeout::Error, bug12876) {
Timeout.timeout(3) {c.new}
}
assert_instance_of(c, o)
m.module_eval {def initialize; raise "exception in M"; end}
assert_raise_with_message(RuntimeError, "exception in M") {
c.new
}
end
class TestFor_super_with_modified_rest_parameter_base
def foo *args
args
end
end
class TestFor_super_with_modified_rest_parameter < TestFor_super_with_modified_rest_parameter_base
def foo *args
args = 13
super
end
end
def test_super_with_modified_rest_parameter
assert_equal [13], TestFor_super_with_modified_rest_parameter.new.foo
end
end