Skip to content

Commit d59fa39

Browse files
committed
Allow nil if annotated implicitly-returns-nil
1 parent e3e0b1e commit d59fa39

File tree

5 files changed

+53
-20
lines changed

5 files changed

+53
-20
lines changed

lib/rbs/test/type_check.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ def initialize(self_class:, builder:, sample_size:, unchecked_classes:, instance
2222
end
2323

2424
def overloaded_call(method, method_name, call, errors:)
25-
es = method.method_types.map do |method_type|
26-
es = method_call(method_name, method_type, call, errors: [])
25+
es = method.defs.map do |type_def|
26+
es = method_call(method_name, type_def.type, call, errors: [], annotations: type_def.annotations)
2727

2828
if es.empty?
2929
return errors
@@ -58,11 +58,11 @@ def overloaded_call(method, method_name, call, errors:)
5858
errors
5959
end
6060

61-
def method_call(method_name, method_type, call, errors:)
61+
def method_call(method_name, method_type, call, errors:, annotations: [])
6262
return errors if method_type.type.is_a?(Types::UntypedFunction)
6363

6464
args(method_name, method_type, method_type.type, call.method_call, errors, type_error: Errors::ArgumentTypeError, argument_error: Errors::ArgumentError)
65-
self.return(method_name, method_type, method_type.type, call.method_call, errors, return_error: Errors::ReturnTypeError)
65+
self.return(method_name, method_type, method_type.type, call.method_call, errors, return_error: Errors::ReturnTypeError, annotations:)
6666

6767
if method_type.block
6868
case
@@ -106,8 +106,10 @@ def args(method_name, method_type, fun, call, errors, type_error:, argument_erro
106106
end
107107
end
108108

109-
def return(method_name, method_type, fun, call, errors, return_error:)
109+
def return(method_name, method_type, fun, call, errors, return_error:, annotations: [])
110110
if call.return?
111+
return if Test.call(call.return_value, IS_AP, NilClass) && annotations.find { |a| a.string == "implicitly-returns-nil" }
112+
111113
unless value(call.return_value, fun.return_type)
112114
errors << return_error.new(klass: self_class,
113115
method_name: method_name,

lib/rbs/unit_test/type_assertions.rb

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def send_setup(method_type, receiver, method, args, proc)
163163
end
164164
end
165165

166-
last_trace = trace.last or raise
166+
last_trace = trace.last or raise "empty trace"
167167

168168
yield(mt, last_trace, result, exception)
169169
end
@@ -182,9 +182,9 @@ def send_setup(method_type, receiver, method, args, proc)
182182

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

185-
method_types = method_types(method)
186-
all_errors = method_types.map {|t| typecheck.method_call(method, t, trace, errors: []) }
187-
assert all_errors.any? {|es| es.empty? }, "Call trace does not match one of method definitions:\n #{trace.inspect}\n #{method_types.join(" | ")}"
185+
method_defs = method_defs(method)
186+
all_errors = method_defs.map {|t| typecheck.method_call(method, t.type, trace, errors: [], annotations: t.annotations) }
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(" | ")}"
188188

189189
raise exception if exception
190190

@@ -219,30 +219,36 @@ def send_setup(method_type, receiver, method, args, proc)
219219
assert_operator exception, :is_a?, ::Exception
220220
assert_empty errors.map {|x| RBS::Test::Errors.to_string(x) }
221221

222-
method_types = method_types(method)
223-
all_errors = method_types.map {|t| typecheck.method_call(method, t, trace, errors: []) }
224-
assert all_errors.all? {|es| es.size > 0 }, "Call trace unexpectedly matches one of method definitions:\n #{trace.inspect}\n #{method_types.join(" | ")}"
222+
method_defs = method_defs(method)
223+
all_errors = method_defs.map {|t| typecheck.method_call(method, t.type, trace, errors: [], annotations: t.annotations) }
224+
assert all_errors.all? {|es| es.size > 0 }, "Call trace unexpectedly matches one of method definitions:\n #{trace.inspect}\n #{method_defs.map(&:type).join(" | ")}"
225225

226226
result
227227
end
228228
end
229229

230-
def method_types(method)
230+
def method_defs(method)
231231
type, definition = target
232232

233233
case type
234234
when Types::ClassInstance
235235
subst = RBS::Substitution.build(definition.type_params, type.args)
236-
definition.methods[method].method_types.map do |method_type|
237-
method_type.sub(subst)
236+
definition.methods[method].defs.map do |type_def|
237+
type_def.update(
238+
type: type_def.type.sub(subst)
239+
)
238240
end
239241
when Types::ClassSingleton
240-
definition.methods[method].method_types
242+
definition.methods[method].defs
241243
else
242244
raise
243245
end
244246
end
245247

248+
def method_types(method)
249+
method_defs(method).map(&:type)
250+
end
251+
246252
def allows_error(*errors)
247253
yield
248254
rescue *errors => exn

sig/test/type_check.rbs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ module RBS
77
#
88
# Returns an array with detected errors.
99
#
10-
def method_call: (Symbol, MethodType, CallTrace, errors: Array[Errors::t]) -> Array[Errors::t]
10+
def method_call: (Symbol, MethodType, CallTrace, errors: Array[Errors::t], ?annotations: Array[AST::Annotation]) -> Array[Errors::t]
1111

1212
# Test if given `value` is compatible to type
1313
#
1414
# Returns `true` if the value has the type.
15-
#
15+
#
1616
def value: (untyped value, Types::t) -> bool
1717
end
1818
end

sig/unit_test/type_assertions.rbs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ module RBS
104104
#
105105
def class_class: () -> Class
106106

107+
def method_defs: (Symbol) -> Array[Definition::Method::TypeDef]
108+
107109
def method_types: (Symbol) -> Array[MethodType]
108110

109111
def allows_error: (*Exception) { () -> void } -> void

test/stdlib/Array_test.rb

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ def test_lshift
8888
def test_aref
8989
assert_send_type "(Integer) -> Integer",
9090
[1,2,3], :[], 0
91+
assert_send_type "(Integer) -> nil",
92+
[1,2,3], :[], 1000
9193
assert_send_type "(Float) -> Integer",
9294
[1,2,3], :[], 0.1
9395
assert_send_type "(ToInt) -> Integer",
@@ -155,6 +157,8 @@ def test_at
155157
[1,2,3], :at, 0
156158
assert_send_type "(ToInt) -> Integer",
157159
[1,2,3], :at, ToInt.new(0)
160+
assert_send_type "(ToInt) -> nil",
161+
[1,2,3], :at, ToInt.new(-5)
158162
end
159163

160164
def test_bsearch
@@ -267,8 +271,11 @@ def test_delete
267271
def test_delete_at
268272
assert_send_type "(Integer) -> Integer",
269273
[1,2,3], :delete_at, 2
270-
assert_send_type "(ToInt) -> Integer",
271-
[1,2,3], :delete_at, ToInt.new(2)
274+
assert_send_type "(Integer) -> nil",
275+
[1,2,3], :delete_at, 100
276+
277+
assert_send_type "(ToInt) -> nil",
278+
[1,2,3], :delete_at, ToInt.new(300)
272279
end
273280

274281
def test_delete_if
@@ -422,6 +429,8 @@ def test_find_index
422429
def test_first
423430
assert_send_type "() -> Integer",
424431
[1,2,3], :first
432+
assert_send_type "() -> nil",
433+
[], :first
425434

426435
assert_send_type "(Integer) -> Array[Integer]",
427436
[1,2,3], :first, 2
@@ -522,6 +531,8 @@ def test_keep_if
522531
def test_last
523532
assert_send_type "() -> Integer",
524533
[1,2,3], :last
534+
assert_send_type "() -> nil",
535+
[], :last
525536

526537
assert_send_type "(Integer) -> Array[Integer]",
527538
[1,2,3], :last, 2
@@ -549,6 +560,8 @@ def test_map!
549560
def test_max
550561
assert_send_type "() -> Integer",
551562
[1,2,3], :max
563+
assert_send_type "() -> nil",
564+
[], :max
552565

553566
assert_send_type "(Integer) -> Array[Integer]",
554567
[1,2,3], :max, 1
@@ -557,6 +570,8 @@ def test_max
557570

558571
assert_send_type "() { (Integer, Integer) -> Integer } -> Integer",
559572
[1,2,3], :max do |_, _| 1 end
573+
assert_send_type "() { (Integer, Integer) -> Integer } -> nil",
574+
[], :max do |_, _| 0 end
560575

561576
assert_send_type "(ToInt) { (Integer, Integer) -> Integer } -> Array[Integer]",
562577
[1,2,3], :max, ToInt.new(2) do |_, _| 0 end
@@ -745,6 +760,8 @@ def test_rotate!
745760
def test_sample
746761
assert_send_type "() -> Integer",
747762
[1,2,3], :sample
763+
assert_send_type "() -> nil",
764+
[], :sample
748765
assert_send_type "(random: Random) -> Integer",
749766
[1,2,3], :sample, random: Random.new(1)
750767
assert_send_type "(random: Rand) -> Integer",
@@ -779,6 +796,8 @@ def test_shift
779796
[1,2,3], :shift
780797
assert_send_type "(ToInt) -> Array[Integer]",
781798
[1,2,3], :shift, ToInt.new(1)
799+
assert_send_type "() -> nil",
800+
[], :shift
782801
end
783802

784803
def test_shuffle
@@ -802,6 +821,8 @@ def test_shuffle!
802821
def test_slice
803822
assert_send_type "(Integer) -> Integer",
804823
[1,2,3], :slice, 1
824+
assert_send_type "(ToInt) -> nil",
825+
[1,2,3], :slice, ToInt.new(11)
805826

806827
assert_send_type "(Integer, Integer) -> Array[Integer]",
807828
[1,2,3], :slice, 1, 2
@@ -817,6 +838,8 @@ def test_slice
817838
def test_slice!
818839
assert_send_type "(Integer) -> Integer",
819840
[1,2,3], :slice!, 1
841+
assert_send_type "(ToInt) -> nil",
842+
[1,2,3], :slice!, ToInt.new(11)
820843

821844
assert_send_type "(Integer, Integer) -> Array[Integer]",
822845
[1,2,3], :slice!, 1, 2

0 commit comments

Comments
 (0)