Skip to content

Commit c6706bd

Browse files
committed
[Kernel] Tweak signatures for conversion methods, and add unit tests in
1 parent 4482ed2 commit c6706bd

File tree

5 files changed

+195
-47
lines changed

5 files changed

+195
-47
lines changed

core/kernel.rbs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -480,9 +480,12 @@ module Kernel : BasicObject
480480
#
481481
def self?.Complex: (_ToC complex_like, ?exception: true) -> Complex
482482
| (_ToC complex_like, exception: bool) -> Complex?
483-
| (Numeric | String real, ?Numeric | String imag, ?exception: true) -> Complex
484-
| (Numeric | String real, ?Numeric | String imag, exception: bool) -> Complex?
485-
| (untyped, ?untyped, ?exception: bool) -> Complex?
483+
| (Numeric numeric, ?exception: bool) -> Complex
484+
| (String real_or_both, ?exception: true) -> Complex
485+
| (untyped real_or_both, exception: bool) -> Complex?
486+
| (Numeric | String real, Numeric | String imag, ?exception: true) -> Complex
487+
| (Numeric | String real, Integer | Float | Rational | Complex imag, exception: bool) -> Complex
488+
| (Numeric | String real, untyped imag, exception: bool) -> Complex?
486489

487490
# <!--
488491
# rdoc-file=kernel.rb
@@ -502,7 +505,7 @@ module Kernel : BasicObject
502505
#
503506
def self?.Float: (_ToF float_like, ?exception: true) -> Float
504507
| (_ToF float_like, exception: bool) -> Float?
505-
| (untyped, ?exception: bool) -> Float?
508+
| (untyped, exception: bool) -> Float?
506509

507510
# <!--
508511
# rdoc-file=object.c
@@ -524,7 +527,7 @@ module Kernel : BasicObject
524527
# Hash(nil) # => {}
525528
# Hash([]) # => {}
526529
#
527-
def self?.Hash: [K, V] (nil | [] _empty) -> Hash[K, V]
530+
def self?.Hash: [K, V] (nil | []) -> Hash[K, V]
528531
| [K, V] (hash[K, V] hash_like) -> Hash[K, V]
529532

530533
# <!--
@@ -614,7 +617,7 @@ module Kernel : BasicObject
614617
| (int | _ToI int_like, exception: bool) -> Integer?
615618
| (string str, int base, ?exception: true) -> Integer
616619
| (string str, int base, exception: bool) -> Integer?
617-
| (untyped, ?untyped, ?exception: bool) -> Integer?
620+
| (untyped, ?int base, exception: bool) -> Integer?
618621

619622
# <!--
620623
# rdoc-file=rational.c
@@ -655,14 +658,19 @@ module Kernel : BasicObject
655658
#
656659
def self?.Rational: (int | _ToR rational_like, ?exception: true) -> Rational
657660
| (int | _ToR rational_like, exception: bool) -> Rational?
658-
| (int | _ToR numer, ?int | _ToR denom, ?exception: true) -> Rational
659-
| (int | _ToR numer, ?int | _ToR denom, exception: bool) -> Rational?
660-
| [T] (Numeric & _RationalDiv[T] numer, Numeric denom, ?exception: bool) -> T
661+
| (int | _ToR numer, int | _ToR denom, ?exception: true) -> Rational
662+
| (int | _ToR numer, int | _ToR denom, exception: bool) -> Rational?
661663
| [T < Numeric] (T value, 1, ?exception: bool) -> T
662-
| (untyped, ?untyped, ?exception: bool) -> Rational?
664+
| [T] (Numeric & _RationalDiv[T] numer, Numeric denom, ?exception: bool) -> T
665+
| (untyped, ?untyped, exception: bool) -> Rational?
663666

667+
# An interface used in `Kernel.Rational` when both arguments are `Numeric`s,
668+
# but don't define `to_r` or `to_int`.
669+
#
670+
# The return type of the division is the return type of `Rational(numer, denom)`.
664671
interface _RationalDiv[T]
665-
def /: (Numeric) -> T
672+
# Divide the numerator by `denom`
673+
def /: (Numeric denom) -> T
666674
end
667675

668676
# <!--

lib/rbs/test/type_check.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,12 @@ def value(val, type)
340340
when Types::Variable
341341
true
342342
when Types::Literal
343-
type.literal == val
343+
begin
344+
type.literal == val
345+
rescue NoMethodError
346+
raise if defined?(val.==)
347+
false
348+
end
344349
when Types::Union
345350
type.types.any? {|type| value(val, type) }
346351
when Types::Intersection

lib/rbs/unit_test/type_assertions.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,11 @@ def send_setup(method_type, receiver, method, args, proc)
180180
)
181181
errors = typecheck.method_call(method, method_type, trace, errors: [])
182182

183-
assert_empty errors.map {|x| RBS::Test::Errors.to_string(x) }, "Call trace does not match with given method type: #{trace.inspect}"
183+
assert_empty errors.map {|x| RBS::Test::Errors.to_string(x) }, proc { "Call trace does not match with given method type: #{trace.inspect}" }
184184

185185
method_defs = method_defs(method)
186186
all_errors = method_defs.map {|t| typecheck.method_call(method, t.type, trace, errors: [], annotations: t.each_annotation.to_a) }
187-
assert all_errors.any? {|es| es.empty? }, "Call trace does not match one of method definitions:\n #{trace.inspect}\n #{method_defs.map(&:type).join(" | ")}"
187+
assert all_errors.any? {|es| es.empty? }, proc { "Call trace does not match one of method definitions:\n #{trace.inspect}\n #{method_defs.map(&:type).join(" | ")}" }
188188

189189
raise exception if exception
190190

sig/unit_test/type_assertions.rbs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,19 @@ module RBS
3030
type target_type = Types::ClassInstance | Types::ClassSingleton
3131

3232
interface _BaseAssertions
33-
def assert: (untyped, ?String?) -> void
33+
def assert: (untyped, ?String? | Proc) -> void
3434

35-
def refute: (untyped, ?String?) -> void
35+
def refute: (untyped, ?String? | Proc) -> void
3636

37-
def assert_empty: (untyped, ?String?) -> void
37+
def assert_empty: (untyped, ?String | nil | Proc) -> void
3838

3939
def assert_operator: (untyped, Symbol, *untyped) -> void
4040

4141
def notify: (untyped) -> void
4242

43-
def assert_predicate: (untyped, Symbol, ?String?) -> void
43+
def assert_predicate: (untyped, Symbol, ?String? | Proc) -> void
4444

45-
def refute_predicate: (untyped, Symbol, ?String?) -> void
45+
def refute_predicate: (untyped, Symbol, ?String? | Proc) -> void
4646
end
4747

4848
module ClassMethods

test/stdlib/Kernel_test.rb

Lines changed: 163 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,73 +11,208 @@ def test_Array
1111
assert_send_type "(nil) -> []",
1212
Kernel, :Array, nil
1313

14-
with_array(1r, 2r).chain([ToA.new(1r,2r)]).each do |ary|
15-
assert_send_type "(::array[Rational] | ::_ToA[Rational]) -> Array[Rational]",
16-
Kernel, :Array, ary
14+
with_untyped do |ele|
15+
with_array(ele, ele).and ToA.new(ele, ele) do |ary|
16+
assert_send_type "[T] (array[T] | _ToA[T]) -> Array[T]",
17+
Kernel, :Array, ary
18+
end
19+
20+
next if defined?(ele.to_a) || defined?(ele.to_ary)
21+
assert_send_type "[T] (T) -> [T]",
22+
Kernel, :Array, ele
1723
end
24+
end
25+
26+
def test_Complex
27+
# (_ToC complex_like, ?exception: true) -> Complex
28+
assert_send_type "(_ToC) -> Complex",
29+
Kernel, :Complex, ToC.new
30+
assert_send_type "(_ToC, exception: true) -> Complex",
31+
Kernel, :Complex, ToC.new, exception: true
32+
33+
# (_ToC complex_like, exception: bool) -> Complex?
34+
assert_send_type "(_ToC, exception: bool) -> Complex",
35+
Kernel, :Complex, ToC.new, exception: false
36+
assert_send_type "(_ToC, exception: bool) -> nil",
37+
Kernel, :Complex, Class.new(BlankSlate){ def to_c = fail }.new, exception: false
1838

19-
assert_send_type "(Rational) -> [Rational]",
20-
Kernel, :Array, 1r
39+
numeric = Class.new(Numeric).new
40+
41+
# (Numeric numeric, ?exception: bool) -> Complex
42+
with 1, 1r, 1.0, (1+0i), numeric do |real|
43+
assert_send_type "(Numeric) -> Complex",
44+
Kernel, :Complex, real
45+
46+
# Single `Numeric`s can never fail
47+
with_bool do |exception|
48+
assert_send_type "(Numeric, exception: bool) -> Complex",
49+
Kernel, :Complex, real, exception: exception
50+
end
51+
end
52+
53+
# (String real_or_both, ?exception: true) -> Complex
54+
assert_send_type "(String) -> Complex",
55+
Kernel, :Complex, '1'
56+
assert_send_type "(String, exception: true) -> Complex",
57+
Kernel, :Complex, '1', exception: true
58+
59+
# (untyped real_or_both, exception: bool) -> Complex?
60+
with_untyped.and 'oops' do |real_untype|
61+
assert_send_type '(untyped, exception: bool) -> Complex?',
62+
Kernel, :Complex, real_untype, exception: false
63+
end
64+
65+
with '1', 1, 1r, 1.0, (1+0i), numeric do |real|
66+
with '2', 2, 2r, 2.0, (2+0i), numeric do |imag|
67+
# (Numeric | String real, Numeric | String imag, ?exception: true) -> Complex
68+
assert_send_type "(Numeric | String, Numeric | String) -> Complex",
69+
Kernel, :Complex, real, imag
70+
assert_send_type "(Numeric | String, Numeric | String, exception: true) -> Complex",
71+
Kernel, :Complex, real, imag, exception: true
72+
73+
# Complex has an awkward edgecase where `exception: false` will unconditionally return `nil`
74+
# if the imaginary argument is not one of the builtin `Numeric`s. Oddly enough, it's not for
75+
# the `real` one...
76+
case imag
77+
when Integer, Float, Rational, Complex
78+
# (Numeric | String real, Integer | Float | Rational | Complex imag, exception: bool) -> Complex
79+
assert_send_type "(Numeric | String, Integer | Float | Rational | Complex, exception: bool) -> Complex",
80+
Kernel, :Complex, real, imag, exception: false
81+
end
82+
end
83+
84+
# (Numeric | String real, untyped, exception: bool) -> Complex?
85+
with_untyped.and 'oops', numeric do |imag|
86+
next if [Integer, Float, Rational, Complex].any? { _1 === imag }
87+
assert_send_type "(Numeric | String, untyped, exception: bool) -> nil",
88+
Kernel, :Complex, real, imag, exception: false
89+
end
90+
end
2191
end
2292

93+
2394
def test_Float
24-
with_float 1.0 do |float|
25-
assert_send_type "(::float) -> Float",
26-
Kernel, :Float, float
27-
assert_send_type "(::float, exception: true) -> Float",
28-
Kernel, :Float, float, exception: true
29-
assert_send_type "(::float, exception: bool) -> Float?",
30-
Kernel, :Float, float, exception: false
95+
with 1, 1.0, ToF.new(1.0), '1e3' do |float_like|
96+
assert_send_type "(_ToF) -> Float",
97+
Kernel, :Float, float_like
98+
assert_send_type "(_ToF, exception: true) -> Float",
99+
Kernel, :Float, float_like, exception: true
100+
assert_send_type "(_ToF, exception: bool) -> Float",
101+
Kernel, :Float, float_like, exception: false
31102
end
32103

33-
assert_send_type "(untyped, ?exception: bool) -> Float?",
34-
Kernel, :Float, :hello, exception: false
104+
with_untyped do |untyped|
105+
next if defined? untyped.to_f
106+
assert_send_type "(untyped, exception: bool) -> nil",
107+
Kernel, :Float, untyped, exception: false
108+
end
35109
end
36110

37111
def test_Hash
38-
assert_send_type "(nil) -> Hash[untyped, untyped]",
112+
assert_send_type "[K, V] (nil) -> Hash[K, V]",
39113
Kernel, :Hash, nil
40-
assert_send_type "([]) -> Hash[untyped, untyped]",
114+
assert_send_type "[K, V] ([]) -> Hash[K, V]",
41115
Kernel, :Hash, []
42116

43117
with_hash 'a' => 3 do |hash|
44-
assert_send_type "(::hash[String, Integer]) -> Hash[String, Integer]",
118+
assert_send_type "[K, V] (hash[K, V]) -> Hash[K, V]",
45119
Kernel, :Hash, hash
46120
end
47121
end
48122

49123
def test_Integer
50-
with_int(1).chain([ToI.new(1)]).each do |int|
51-
assert_send_type "(::int | ::_ToI) -> Integer",
124+
with_int.and ToI.new do |int|
125+
assert_send_type "(int | _ToI) -> Integer",
52126
Kernel, :Integer, int
53-
assert_send_type "(::int | ::_ToI, exception: true) -> Integer",
127+
assert_send_type "(int | _ToI, exception: true) -> Integer",
54128
Kernel, :Integer, int, exception: true
55-
assert_send_type "(::int | ::_ToI, exception: bool) -> Integer?",
129+
assert_send_type "(int | _ToI, exception: bool) -> Integer?",
56130
Kernel, :Integer, int, exception: false
57131
end
58132

59133
with_string "123" do |string|
60134
with_int 8 do |base|
61-
assert_send_type "(::string, ::int) -> Integer",
135+
assert_send_type "(string, int) -> Integer",
62136
Kernel, :Integer, string, base
63-
assert_send_type "(::string, ::int, exception: true) -> Integer",
137+
assert_send_type "(string, int, exception: true) -> Integer",
64138
Kernel, :Integer, string, base, exception: true
65-
assert_send_type "(::string, ::int, exception: bool) -> Integer?",
139+
assert_send_type "(string, int, exception: bool) -> Integer?",
66140
Kernel, :Integer, string, base, exception: false
67141
end
68142
end
69143

70-
assert_send_type "(untyped, ?exception: bool) -> Integer?",
71-
Kernel, :Integer, :hello, exception: false
144+
with_untyped do |untyped|
145+
assert_send_type "(untyped, exception: bool) -> Integer?",
146+
Kernel, :Integer, untyped, exception: false
147+
148+
with_int 10 do |base|
149+
assert_send_type "(untyped, int, exception: bool) -> Integer?",
150+
Kernel, :Integer, untyped, base, exception: false
151+
end
152+
end
153+
end
154+
155+
def test_Rational
156+
with_int(1).and ToR.new(1r) do |numer|
157+
assert_send_type "(int | _ToR) -> Rational",
158+
Kernel, :Rational, numer
159+
assert_send_type "(int | _ToR, exception: true) -> Rational",
160+
Kernel, :Rational, numer, exception: true
161+
assert_send_type "(int | _ToR, exception: bool) -> Rational",
162+
Kernel, :Rational, numer, exception: false
163+
164+
with_int(2).and ToR.new(2r) do |denom|
165+
assert_send_type "(int | _ToR, int | _ToR) -> Rational",
166+
Kernel, :Rational, numer, denom
167+
assert_send_type "(int | _ToR, int | _ToR, exception: true) -> Rational",
168+
Kernel, :Rational, numer, denom, exception: true
169+
assert_send_type "(int | _ToR, int | _ToR, exception: bool) -> Rational",
170+
Kernel, :Rational, numer, denom, exception: false
171+
end
172+
end
173+
174+
bad_int = Class.new(BlankSlate){ def to_int = fail }.new
175+
bad_rat = Class.new(BlankSlate){ def to_r = fail }.new
176+
with bad_int, bad_rat do |bad_numer|
177+
assert_send_type "(int | _ToR, exception: bool) -> nil",
178+
Kernel, :Rational, bad_numer, exception: false
179+
assert_send_type "(int | _ToR, int | _ToR, exception: bool) -> nil",
180+
Kernel, :Rational, bad_numer, bad_numer, exception: false
181+
end
182+
183+
184+
numeric = Class.new(Numeric).new
185+
assert_send_type "[T < _Numeric] (T numer, 1) -> T",
186+
Kernel, :Rational, numeric, 1
187+
assert_send_type "[T < _Numeric] (T numer, 1, exception: bool) -> T",
188+
Kernel, :Rational, numeric, 1, exception: true
189+
assert_send_type "[T < _Numeric] (T numer, 1, exception: bool) -> T",
190+
Kernel, :Rational, numeric, 1, exception: false
191+
192+
numeric_div = Class.new(Numeric){ def /(other) = :hello }.new
193+
194+
assert_send_type "[T] (Numeric & Kernel::_RationalDiv[T] numer, Numeric denom) -> T",
195+
Kernel, :Rational, numeric_div, numeric
196+
assert_send_type "[T] (Numeric & Kernel::_RationalDiv[T] numer, Numeric denom, exception: bool) -> T",
197+
Kernel, :Rational, numeric_div, numeric, exception: true
198+
assert_send_type "[T] (Numeric & Kernel::_RationalDiv[T] numer, Numeric denom, exception: bool) -> T",
199+
Kernel, :Rational, numeric_div, numeric, exception: false
200+
201+
with_untyped do |numer|
202+
with_untyped do |denom|
203+
assert_send_type "(untyped, untyped, exception: bool) -> Rational?",
204+
Kernel, :Rational, numer, denom, exception: false
205+
end
206+
end
72207
end
73208

74209
def test_String
75210
with_string do |string|
76-
assert_send_type "(::string) -> String",
211+
assert_send_type "(string) -> String",
77212
Kernel, :String, string
78213
end
79214

80-
assert_send_type "(::_ToS) -> String",
215+
assert_send_type "(_ToS) -> String",
81216
Kernel, :String, ToS.new
82217
end
83218

0 commit comments

Comments
 (0)