sorbet-runtime 0.5.10439 → 0.5.11120
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sorbet-runtime.rb +7 -1
- data/lib/types/_types.rb +57 -3
- data/lib/types/compatibility_patches.rb +4 -2
- data/lib/types/enum.rb +6 -1
- data/lib/types/generic.rb +2 -0
- data/lib/types/private/abstract/declare.rb +10 -9
- data/lib/types/private/casts.rb +4 -1
- data/lib/types/private/class_utils.rb +13 -6
- data/lib/types/private/methods/_methods.rb +37 -12
- data/lib/types/private/methods/call_validation.rb +104 -5
- data/lib/types/private/methods/call_validation_2_6.rb +68 -60
- data/lib/types/private/methods/call_validation_2_7.rb +68 -60
- data/lib/types/private/methods/decl_builder.rb +21 -6
- data/lib/types/private/methods/signature.rb +63 -38
- data/lib/types/private/methods/signature_validation.rb +68 -6
- data/lib/types/private/runtime_levels.rb +19 -0
- data/lib/types/private/types/not_typed.rb +2 -0
- data/lib/types/private/types/simple_pair_union.rb +55 -0
- data/lib/types/private/types/void.rb +29 -23
- data/lib/types/props/_props.rb +2 -2
- data/lib/types/props/custom_type.rb +2 -2
- data/lib/types/props/decorator.rb +41 -36
- data/lib/types/props/has_lazily_specialized_methods.rb +2 -2
- data/lib/types/props/pretty_printable.rb +45 -83
- data/lib/types/props/private/setter_factory.rb +1 -1
- data/lib/types/props/serializable.rb +17 -9
- data/lib/types/props/type_validation.rb +5 -2
- data/lib/types/struct.rb +2 -2
- data/lib/types/types/anything.rb +31 -0
- data/lib/types/types/base.rb +15 -1
- data/lib/types/types/class_of.rb +11 -0
- data/lib/types/types/enum.rb +1 -1
- data/lib/types/types/fixed_array.rb +13 -0
- data/lib/types/types/fixed_hash.rb +22 -0
- data/lib/types/types/intersection.rb +1 -1
- data/lib/types/types/noreturn.rb +0 -1
- data/lib/types/types/simple.rb +27 -4
- data/lib/types/types/type_parameter.rb +19 -0
- data/lib/types/types/typed_array.rb +29 -0
- data/lib/types/types/typed_class.rb +85 -0
- data/lib/types/types/typed_enumerable.rb +7 -0
- data/lib/types/types/typed_enumerator_chain.rb +41 -0
- data/lib/types/types/union.rb +50 -14
- data/lib/types/utils.rb +41 -33
- metadata +14 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a7367e214fde40d68e9647c33185623fd1c0a6ec2328c43ba06036338014225
|
4
|
+
data.tar.gz: ef0bef28019ff3385b8d7a21b73bbeb51a4e7482c3190fcf00112f7b711b7164
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7411735478f902033b8ba4258ab00f7ed3e2869b1775fc3090d02e455f012850f804a97d3e005f4fe297b9477a24c94d31a300d82307373c0f9c492b89de7b0
|
7
|
+
data.tar.gz: 3b1c758b422685ba62e20070c0fd33274de920a0d8618c58a57b017b84e1e32723106a0e7996b15b0c2ef50fa0bf94f33559dcd88bbbcf01173ddb02949237dd
|
data/lib/sorbet-runtime.rb
CHANGED
@@ -37,14 +37,15 @@ require_relative 'types/types/fixed_array'
|
|
37
37
|
require_relative 'types/types/fixed_hash'
|
38
38
|
require_relative 'types/types/intersection'
|
39
39
|
require_relative 'types/types/noreturn'
|
40
|
+
require_relative 'types/types/anything'
|
40
41
|
require_relative 'types/types/proc'
|
41
42
|
require_relative 'types/types/attached_class'
|
42
43
|
require_relative 'types/types/self_type'
|
43
44
|
require_relative 'types/types/simple'
|
44
45
|
require_relative 'types/types/t_enum'
|
45
46
|
require_relative 'types/types/type_parameter'
|
46
|
-
require_relative 'types/types/typed_array'
|
47
47
|
require_relative 'types/types/typed_enumerator'
|
48
|
+
require_relative 'types/types/typed_enumerator_chain'
|
48
49
|
require_relative 'types/types/typed_enumerator_lazy'
|
49
50
|
require_relative 'types/types/typed_hash'
|
50
51
|
require_relative 'types/types/typed_range'
|
@@ -55,6 +56,7 @@ require_relative 'types/private/types/not_typed'
|
|
55
56
|
require_relative 'types/private/types/void'
|
56
57
|
require_relative 'types/private/types/string_holder'
|
57
58
|
require_relative 'types/private/types/type_alias'
|
59
|
+
require_relative 'types/private/types/simple_pair_union'
|
58
60
|
|
59
61
|
require_relative 'types/types/type_variable'
|
60
62
|
require_relative 'types/types/type_member'
|
@@ -81,6 +83,10 @@ require_relative 'types/private/retry'
|
|
81
83
|
require_relative 'types/utils'
|
82
84
|
require_relative 'types/boolean'
|
83
85
|
|
86
|
+
# Depends on types/utils
|
87
|
+
require_relative 'types/types/typed_array'
|
88
|
+
require_relative 'types/types/typed_class'
|
89
|
+
|
84
90
|
# Props dependencies
|
85
91
|
require_relative 'types/private/abstract/data'
|
86
92
|
require_relative 'types/private/mixins/mixins'
|
data/lib/types/_types.rb
CHANGED
@@ -47,6 +47,10 @@ module T
|
|
47
47
|
T::Types::NoReturn::Private::INSTANCE
|
48
48
|
end
|
49
49
|
|
50
|
+
def self.anything
|
51
|
+
T::Types::Anything::Private::INSTANCE
|
52
|
+
end
|
53
|
+
|
50
54
|
# T.all(<Type>, <Type>, ...) -- matches an object that has all of the types listed
|
51
55
|
def self.all(type_a, type_b, *types)
|
52
56
|
T::Types::Intersection.new([type_a, type_b] + types)
|
@@ -119,7 +123,7 @@ module T
|
|
119
123
|
# .returns(T::Array[T.type_parameter(:U)])
|
120
124
|
# def map(&blk); end
|
121
125
|
def self.type_parameter(name)
|
122
|
-
T::Types::TypeParameter.
|
126
|
+
T::Types::TypeParameter.make(name)
|
123
127
|
end
|
124
128
|
|
125
129
|
# Tells the typechecker that `value` is of type `type`. Use this to get additional checking after
|
@@ -221,6 +225,34 @@ module T
|
|
221
225
|
end
|
222
226
|
end
|
223
227
|
|
228
|
+
# A convenience method to `raise` with a provided error reason when the argument
|
229
|
+
# is `nil` and return it otherwise.
|
230
|
+
#
|
231
|
+
# Intended to be used as:
|
232
|
+
#
|
233
|
+
# needs_foo(T.must_because(maybe_gives_foo) {"reason_foo_should_not_be_nil"})
|
234
|
+
#
|
235
|
+
# Equivalent to:
|
236
|
+
#
|
237
|
+
# foo = maybe_gives_foo
|
238
|
+
# raise "reason_foo_should_not_be_nil" if foo.nil?
|
239
|
+
# needs_foo(foo)
|
240
|
+
#
|
241
|
+
# Intended to be used to promise sorbet that a given nilable value happens
|
242
|
+
# to contain a non-nil value at this point.
|
243
|
+
#
|
244
|
+
# `sig {params(arg: T.nilable(A), reason_blk: T.proc.returns(String)).returns(A)}`
|
245
|
+
def self.must_because(arg)
|
246
|
+
return arg if arg
|
247
|
+
return arg if arg == false
|
248
|
+
|
249
|
+
begin
|
250
|
+
raise TypeError.new("Unexpected `nil` because #{yield}")
|
251
|
+
rescue TypeError => e # raise into rescue to ensure e.backtrace is populated
|
252
|
+
T::Configuration.inline_type_error_handler(e, {kind: 'T.must_because', value: arg, type: nil})
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
224
256
|
# A way to ask Sorbet to show what type it thinks an expression has.
|
225
257
|
# This can be useful for debugging and checking assumptions.
|
226
258
|
# In the runtime, merely returns the value passed in.
|
@@ -251,9 +283,9 @@ module T
|
|
251
283
|
module Array
|
252
284
|
def self.[](type)
|
253
285
|
if type.is_a?(T::Types::Untyped)
|
254
|
-
T::Types::TypedArray::Untyped
|
286
|
+
T::Types::TypedArray::Untyped::Private::INSTANCE
|
255
287
|
else
|
256
|
-
T::Types::TypedArray.
|
288
|
+
T::Types::TypedArray::Private::Pool.type_for_module(type)
|
257
289
|
end
|
258
290
|
end
|
259
291
|
end
|
@@ -296,6 +328,16 @@ module T
|
|
296
328
|
end
|
297
329
|
end
|
298
330
|
end
|
331
|
+
|
332
|
+
module Chain
|
333
|
+
def self.[](type)
|
334
|
+
if type.is_a?(T::Types::Untyped)
|
335
|
+
T::Types::TypedEnumeratorChain::Untyped.new
|
336
|
+
else
|
337
|
+
T::Types::TypedEnumeratorChain.new(type)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
299
341
|
end
|
300
342
|
|
301
343
|
module Range
|
@@ -313,4 +355,16 @@ module T
|
|
313
355
|
end
|
314
356
|
end
|
315
357
|
end
|
358
|
+
|
359
|
+
module Class
|
360
|
+
def self.[](type)
|
361
|
+
if type.is_a?(T::Types::Untyped)
|
362
|
+
T::Types::TypedClass::Untyped::Private::INSTANCE
|
363
|
+
elsif type.is_a?(T::Types::Anything)
|
364
|
+
T::Types::TypedClass::Anything::Private::INSTANCE
|
365
|
+
else
|
366
|
+
T::Types::TypedClass::Private::Pool.type_for_module(type)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
316
370
|
end
|
@@ -27,8 +27,10 @@ if defined? ::RSpec::Mocks
|
|
27
27
|
module RSpecCompatibility
|
28
28
|
module RecorderExtensions
|
29
29
|
def observe!(method_name)
|
30
|
-
|
31
|
-
|
30
|
+
if @klass.method_defined?(method_name.to_sym)
|
31
|
+
method = @klass.instance_method(method_name.to_sym)
|
32
|
+
T::Private::Methods.maybe_run_sig_block_for_method(method)
|
33
|
+
end
|
32
34
|
super(method_name)
|
33
35
|
end
|
34
36
|
end
|
data/lib/types/enum.rb
CHANGED
@@ -357,11 +357,16 @@ class T::Enum
|
|
357
357
|
@fully_initialized = true
|
358
358
|
end
|
359
359
|
|
360
|
-
sig {params(child_class:
|
360
|
+
sig {params(child_class: T::Class[T.anything]).void}
|
361
361
|
def self.inherited(child_class)
|
362
362
|
super
|
363
363
|
|
364
364
|
raise "Inheriting from children of T::Enum is prohibited" if self != T::Enum
|
365
|
+
|
366
|
+
# "oj" gem JSON support
|
367
|
+
if Object.const_defined?(:Oj)
|
368
|
+
Object.const_get(:Oj).register_odd(child_class, child_class, :try_deserialize, :serialize)
|
369
|
+
end
|
365
370
|
end
|
366
371
|
|
367
372
|
# Marshal support
|
data/lib/types/generic.rb
CHANGED
@@ -27,26 +27,27 @@ module T::Private::Abstract::Declare
|
|
27
27
|
raise "Classes can't be interfaces. Use `abstract!` instead of `interface!`."
|
28
28
|
end
|
29
29
|
|
30
|
-
if
|
31
|
-
raise "You must call `abstract!` *before* defining
|
30
|
+
if Object.instance_method(:method).bind_call(mod, :new).owner == mod
|
31
|
+
raise "You must call `abstract!` *before* defining a `new` method"
|
32
32
|
end
|
33
33
|
|
34
34
|
# Don't need to silence warnings via without_ruby_warnings when calling
|
35
35
|
# define_method because of the guard above
|
36
36
|
|
37
|
-
mod.send(:
|
38
|
-
|
39
|
-
|
37
|
+
mod.send(:define_singleton_method, :new) do |*args, &blk|
|
38
|
+
super(*args, &blk).tap do |result|
|
39
|
+
if result.instance_of?(mod)
|
40
|
+
raise "#{mod} is declared as abstract; it cannot be instantiated"
|
41
|
+
end
|
40
42
|
end
|
41
|
-
super(*args, &blk)
|
42
43
|
end
|
43
44
|
|
44
45
|
# Ruby doesn not emit "method redefined" warnings for aliased methods
|
45
46
|
# (more robust than undef_method that would create a small window in which the method doesn't exist)
|
46
|
-
mod.send(:alias_method, :
|
47
|
+
mod.singleton_class.send(:alias_method, :new, :new)
|
47
48
|
|
48
|
-
if mod.respond_to?(:ruby2_keywords, true)
|
49
|
-
mod.send(:ruby2_keywords, :
|
49
|
+
if mod.singleton_class.respond_to?(:ruby2_keywords, true)
|
50
|
+
mod.singleton_class.send(:ruby2_keywords, :new)
|
50
51
|
end
|
51
52
|
end
|
52
53
|
end
|
data/lib/types/private/casts.rb
CHANGED
@@ -5,7 +5,10 @@ module T::Private
|
|
5
5
|
module Casts
|
6
6
|
def self.cast(value, type, cast_method)
|
7
7
|
begin
|
8
|
-
|
8
|
+
coerced_type = T::Utils::Private.coerce_and_check_module_types(type, value, true)
|
9
|
+
return value unless coerced_type
|
10
|
+
|
11
|
+
error = coerced_type.error_message_for_obj(value)
|
9
12
|
return value unless error
|
10
13
|
|
11
14
|
caller_loc = T.must(caller_locations(2..2)).first
|
@@ -91,10 +91,13 @@ module T::Private::ClassUtils
|
|
91
91
|
end
|
92
92
|
|
93
93
|
# Replaces a method, either by overwriting it (if it is defined directly on `mod`) or by
|
94
|
-
# overriding it (if it is defined by one of mod's ancestors).
|
95
|
-
# on which you can call `bind(...).call(...)`
|
96
|
-
#
|
97
|
-
|
94
|
+
# overriding it (if it is defined by one of mod's ancestors). If `original_only` is
|
95
|
+
# false, returns a ReplacedMethod instance on which you can call `bind(...).call(...)`
|
96
|
+
# to call the original method, or `restore` to restore the original method (by
|
97
|
+
# overwriting or removing the override).
|
98
|
+
#
|
99
|
+
# If `original_only` is true, return the `UnboundMethod` representing the original method.
|
100
|
+
def self.replace_method(mod, name, original_only=false, &blk)
|
98
101
|
original_method = mod.instance_method(name)
|
99
102
|
original_visibility = visibility_method_name(mod, name)
|
100
103
|
original_owner = original_method.owner
|
@@ -120,8 +123,12 @@ module T::Private::ClassUtils
|
|
120
123
|
def_with_visibility(mod, name, original_visibility, &blk)
|
121
124
|
end
|
122
125
|
end
|
123
|
-
new_method = mod.instance_method(name)
|
124
126
|
|
125
|
-
|
127
|
+
if original_only
|
128
|
+
original_method
|
129
|
+
else
|
130
|
+
new_method = mod.instance_method(name)
|
131
|
+
ReplacedMethod.new(mod, original_method, new_method, overwritten, original_visibility)
|
132
|
+
end
|
126
133
|
end
|
127
134
|
end
|
@@ -83,7 +83,7 @@ module T::Private::Methods
|
|
83
83
|
raise "Procs cannot have override/abstract modifiers"
|
84
84
|
end
|
85
85
|
if decl.mod != PROC_TYPE
|
86
|
-
raise "You are passing a DeclBuilder as a type. Did you accidentally use `self` inside a `sig` block?"
|
86
|
+
raise "You are passing a DeclBuilder as a type. Did you accidentally use `self` inside a `sig` block? Perhaps you wanted the `T.self_type` instead: https://sorbet.org/docs/self-type"
|
87
87
|
end
|
88
88
|
if decl.returns == ARG_NOT_PROVIDED
|
89
89
|
raise "Procs must specify a return type"
|
@@ -117,6 +117,11 @@ module T::Private::Methods
|
|
117
117
|
@signatures_by_method[key]
|
118
118
|
end
|
119
119
|
|
120
|
+
# Fetch the directory name of the file that defines the `T::Private` constant and
|
121
|
+
# add a trailing slash to allow us to match it as a directory prefix.
|
122
|
+
SORBET_RUNTIME_LIB_PATH = File.dirname(T.const_source_location(:Private).first) + File::SEPARATOR
|
123
|
+
private_constant :SORBET_RUNTIME_LIB_PATH
|
124
|
+
|
120
125
|
# when target includes a module with instance methods source_method_names, ensure there is zero intersection between
|
121
126
|
# the final instance methods of target and source_method_names. so, for every m in source_method_names, check if there
|
122
127
|
# is already a method defined on one of target_ancestors with the same name that is final.
|
@@ -158,7 +163,7 @@ module T::Private::Methods
|
|
158
163
|
|
159
164
|
definition_file, definition_line = T::Private::Methods.signature_for_method(ancestor.instance_method(method_name)).method.source_location
|
160
165
|
is_redefined = target == ancestor
|
161
|
-
caller_loc = caller_locations
|
166
|
+
caller_loc = caller_locations.find {|l| !l.to_s.start_with?(SORBET_RUNTIME_LIB_PATH)}
|
162
167
|
extra_info = "\n"
|
163
168
|
if caller_loc
|
164
169
|
extra_info = (is_redefined ? "Redefined" : "Overridden") + " here: #{caller_loc.path}:#{caller_loc.lineno}\n"
|
@@ -247,7 +252,7 @@ module T::Private::Methods
|
|
247
252
|
# (or unwrap back to the original method).
|
248
253
|
key = method_owner_and_name_to_key(mod, method_name)
|
249
254
|
unless current_declaration.raw
|
250
|
-
T::Private::ClassUtils.replace_method(mod, method_name) do |*args, &blk|
|
255
|
+
T::Private::ClassUtils.replace_method(mod, method_name, true) do |*args, &blk|
|
251
256
|
method_sig = T::Private::Methods.maybe_run_sig_block_for_key(key)
|
252
257
|
method_sig ||= T::Private::Methods._handle_missing_method_signature(
|
253
258
|
self,
|
@@ -334,9 +339,11 @@ module T::Private::Methods
|
|
334
339
|
nil
|
335
340
|
end
|
336
341
|
|
342
|
+
declaration_block.loc = nil
|
343
|
+
|
337
344
|
signature =
|
338
345
|
if current_declaration
|
339
|
-
build_sig(hook_mod, method_name, original_method, current_declaration
|
346
|
+
build_sig(hook_mod, method_name, original_method, current_declaration)
|
340
347
|
else
|
341
348
|
Signature.new_untyped(method: original_method)
|
342
349
|
end
|
@@ -353,7 +360,7 @@ module T::Private::Methods
|
|
353
360
|
.decl
|
354
361
|
end
|
355
362
|
|
356
|
-
def self.build_sig(hook_mod, method_name, original_method, current_declaration
|
363
|
+
def self.build_sig(hook_mod, method_name, original_method, current_declaration)
|
357
364
|
begin
|
358
365
|
# We allow `sig` in the current module's context (normal case) and
|
359
366
|
if hook_mod != current_declaration.mod &&
|
@@ -494,19 +501,37 @@ module T::Private::Methods
|
|
494
501
|
return
|
495
502
|
end
|
496
503
|
if is_enabled
|
497
|
-
|
504
|
+
# A cut-down version of T::Private::ClassUtils::ReplacedMethod#restore, because we
|
505
|
+
# should only be resetting final hooks during tests.
|
506
|
+
T::Configuration.without_ruby_warnings do
|
507
|
+
Module.define_method(:included, @old_hooks[0])
|
508
|
+
Module.define_method(:extended, @old_hooks[1])
|
509
|
+
Class.define_method(:inherited, @old_hooks[2])
|
510
|
+
end
|
498
511
|
@old_hooks = nil
|
499
512
|
else
|
500
|
-
old_included = T::Private::ClassUtils.replace_method(Module, :included) do |arg|
|
501
|
-
|
513
|
+
old_included = T::Private::ClassUtils.replace_method(Module, :included, true) do |arg|
|
514
|
+
if T::Configuration::AT_LEAST_RUBY_2_7
|
515
|
+
old_included.bind_call(self, arg)
|
516
|
+
else
|
517
|
+
old_included.bind(self).call(arg)
|
518
|
+
end
|
502
519
|
::T::Private::Methods._hook_impl(arg, false, self)
|
503
520
|
end
|
504
|
-
old_extended = T::Private::ClassUtils.replace_method(Module, :extended) do |arg|
|
505
|
-
|
521
|
+
old_extended = T::Private::ClassUtils.replace_method(Module, :extended, true) do |arg|
|
522
|
+
if T::Configuration::AT_LEAST_RUBY_2_7
|
523
|
+
old_extended.bind_call(self, arg)
|
524
|
+
else
|
525
|
+
old_extended.bind(self).call(arg)
|
526
|
+
end
|
506
527
|
::T::Private::Methods._hook_impl(arg, true, self)
|
507
528
|
end
|
508
|
-
old_inherited = T::Private::ClassUtils.replace_method(Class, :inherited) do |arg|
|
509
|
-
|
529
|
+
old_inherited = T::Private::ClassUtils.replace_method(Class, :inherited, true) do |arg|
|
530
|
+
if T::Configuration::AT_LEAST_RUBY_2_7
|
531
|
+
old_inherited.bind_call(self, arg)
|
532
|
+
else
|
533
|
+
old_inherited.bind(self).call(arg)
|
534
|
+
end
|
510
535
|
::T::Private::Methods._hook_impl(arg, false, self)
|
511
536
|
end
|
512
537
|
@old_hooks = [old_included, old_extended, old_inherited]
|
@@ -14,7 +14,7 @@ module T::Private::Methods::CallValidation
|
|
14
14
|
def self.wrap_method_if_needed(mod, method_sig, original_method)
|
15
15
|
original_visibility = visibility_method_name(mod, method_sig.method_name)
|
16
16
|
if method_sig.mode == T::Private::Methods::Modes.abstract
|
17
|
-
T::Private::ClassUtils.replace_method(mod, method_sig.method_name) do |*args, &blk|
|
17
|
+
T::Private::ClassUtils.replace_method(mod, method_sig.method_name, true) do |*args, &blk|
|
18
18
|
# TODO: write a cop to ensure that abstract methods have an empty body
|
19
19
|
#
|
20
20
|
# We allow abstract methods to be implemented by things further down the ancestor chain.
|
@@ -59,8 +59,9 @@ module T::Private::Methods::CallValidation
|
|
59
59
|
|
60
60
|
def self.create_validator_method(mod, original_method, method_sig, original_visibility)
|
61
61
|
has_fixed_arity = method_sig.kwarg_types.empty? && !method_sig.has_rest && !method_sig.has_keyrest &&
|
62
|
-
original_method.parameters.all? {|(kind, _name)| kind == :req}
|
63
|
-
|
62
|
+
original_method.parameters.all? {|(kind, _name)| kind == :req || kind == :block}
|
63
|
+
can_skip_block_type = method_sig.block_type.nil? || method_sig.block_type.valid?(nil)
|
64
|
+
ok_for_fast_path = has_fixed_arity && can_skip_block_type && !method_sig.bind && method_sig.arg_types.length < 5 && is_allowed_to_have_fast_path
|
64
65
|
|
65
66
|
all_args_are_simple = ok_for_fast_path && method_sig.arg_types.all? {|_name, type| type.is_a?(T::Types::Simple)}
|
66
67
|
simple_method = all_args_are_simple && method_sig.return_type.is_a?(T::Types::Simple)
|
@@ -76,6 +77,11 @@ module T::Private::Methods::CallValidation
|
|
76
77
|
create_validator_procedure_medium(mod, original_method, method_sig, original_visibility)
|
77
78
|
elsif ok_for_fast_path
|
78
79
|
create_validator_method_medium(mod, original_method, method_sig, original_visibility)
|
80
|
+
elsif can_skip_block_type
|
81
|
+
# The Ruby VM already validates that any block passed to a method
|
82
|
+
# must be either `nil` or a `Proc` object, so there's no need to also
|
83
|
+
# have sorbet-runtime check that.
|
84
|
+
create_validator_slow_skip_block_type(mod, original_method, method_sig, original_visibility)
|
79
85
|
else
|
80
86
|
create_validator_slow(mod, original_method, method_sig, original_visibility)
|
81
87
|
end
|
@@ -84,6 +90,88 @@ module T::Private::Methods::CallValidation
|
|
84
90
|
end
|
85
91
|
end
|
86
92
|
|
93
|
+
def self.create_validator_slow_skip_block_type(mod, original_method, method_sig, original_visibility)
|
94
|
+
T::Private::ClassUtils.def_with_visibility(mod, method_sig.method_name, original_visibility) do |*args, &blk|
|
95
|
+
CallValidation.validate_call_skip_block_type(self, original_method, method_sig, args, blk)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.validate_call_skip_block_type(instance, original_method, method_sig, args, blk)
|
100
|
+
# This method is called for every `sig`. It's critical to keep it fast and
|
101
|
+
# reduce number of allocations that happen here.
|
102
|
+
|
103
|
+
if method_sig.bind
|
104
|
+
message = method_sig.bind.error_message_for_obj(instance)
|
105
|
+
if message
|
106
|
+
CallValidation.report_error(
|
107
|
+
method_sig,
|
108
|
+
message,
|
109
|
+
'Bind',
|
110
|
+
nil,
|
111
|
+
method_sig.bind,
|
112
|
+
instance
|
113
|
+
)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# NOTE: We don't bother validating for missing or extra kwargs;
|
118
|
+
# the method call itself will take care of that.
|
119
|
+
method_sig.each_args_value_type(args) do |name, arg, type|
|
120
|
+
message = type.error_message_for_obj(arg)
|
121
|
+
if message
|
122
|
+
CallValidation.report_error(
|
123
|
+
method_sig,
|
124
|
+
message,
|
125
|
+
'Parameter',
|
126
|
+
name,
|
127
|
+
type,
|
128
|
+
arg,
|
129
|
+
caller_offset: 2
|
130
|
+
)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# The original method definition allows passing `nil` for the `&blk`
|
135
|
+
# argument, so we do not have to do any method_sig.block_type type checks
|
136
|
+
# of our own.
|
137
|
+
|
138
|
+
# The following line breaks are intentional to show nice pry message
|
139
|
+
|
140
|
+
|
141
|
+
|
142
|
+
|
143
|
+
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
# PRY note:
|
150
|
+
# this code is sig validation code.
|
151
|
+
# Please issue `finish` to step out of it
|
152
|
+
|
153
|
+
return_value = T::Configuration::AT_LEAST_RUBY_2_7 ? original_method.bind_call(instance, *args, &blk) : original_method.bind(instance).call(*args, &blk)
|
154
|
+
|
155
|
+
# The only type that is allowed to change the return value is `.void`.
|
156
|
+
# It ignores what you returned and changes it to be a private singleton.
|
157
|
+
if method_sig.return_type.is_a?(T::Private::Types::Void)
|
158
|
+
T::Private::Types::Void::VOID
|
159
|
+
else
|
160
|
+
message = method_sig.return_type.error_message_for_obj(return_value)
|
161
|
+
if message
|
162
|
+
CallValidation.report_error(
|
163
|
+
method_sig,
|
164
|
+
message,
|
165
|
+
'Return value',
|
166
|
+
nil,
|
167
|
+
method_sig.return_type,
|
168
|
+
return_value,
|
169
|
+
)
|
170
|
+
end
|
171
|
+
return_value
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
87
175
|
def self.create_validator_slow(mod, original_method, method_sig, original_visibility)
|
88
176
|
T::Private::ClassUtils.def_with_visibility(mod, method_sig.method_name, original_visibility) do |*args, &blk|
|
89
177
|
CallValidation.validate_call(self, original_method, method_sig, args, blk)
|
@@ -125,8 +213,19 @@ module T::Private::Methods::CallValidation
|
|
125
213
|
end
|
126
214
|
end
|
127
215
|
|
128
|
-
|
129
|
-
|
216
|
+
# The Ruby VM already checks that `&blk` is either a `Proc` type or `nil`:
|
217
|
+
# https://github.com/ruby/ruby/blob/v2_7_6/vm_args.c#L1150-L1154
|
218
|
+
# And `T.proc` types don't (can't) do any runtime arg checking, so we can
|
219
|
+
# save work by simply checking that `blk` is non-nil (if the method allows
|
220
|
+
# `nil` for the block, it would not have used this validate_call path).
|
221
|
+
unless blk
|
222
|
+
# Have to use `&.` here, because it's technically a public API that
|
223
|
+
# people can _always_ call `validate_call` to validate any signature
|
224
|
+
# (i.e., the faster validators are merely optimizations).
|
225
|
+
# In practice, this only affects the first call to the method (before the
|
226
|
+
# optimized validators have a chance to replace the initial, slow
|
227
|
+
# wrapper).
|
228
|
+
message = method_sig.block_type&.error_message_for_obj(blk)
|
130
229
|
if message
|
131
230
|
CallValidation.report_error(
|
132
231
|
method_sig,
|