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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sorbet-runtime.rb +7 -1
  3. data/lib/types/_types.rb +57 -3
  4. data/lib/types/compatibility_patches.rb +4 -2
  5. data/lib/types/enum.rb +6 -1
  6. data/lib/types/generic.rb +2 -0
  7. data/lib/types/private/abstract/declare.rb +10 -9
  8. data/lib/types/private/casts.rb +4 -1
  9. data/lib/types/private/class_utils.rb +13 -6
  10. data/lib/types/private/methods/_methods.rb +37 -12
  11. data/lib/types/private/methods/call_validation.rb +104 -5
  12. data/lib/types/private/methods/call_validation_2_6.rb +68 -60
  13. data/lib/types/private/methods/call_validation_2_7.rb +68 -60
  14. data/lib/types/private/methods/decl_builder.rb +21 -6
  15. data/lib/types/private/methods/signature.rb +63 -38
  16. data/lib/types/private/methods/signature_validation.rb +68 -6
  17. data/lib/types/private/runtime_levels.rb +19 -0
  18. data/lib/types/private/types/not_typed.rb +2 -0
  19. data/lib/types/private/types/simple_pair_union.rb +55 -0
  20. data/lib/types/private/types/void.rb +29 -23
  21. data/lib/types/props/_props.rb +2 -2
  22. data/lib/types/props/custom_type.rb +2 -2
  23. data/lib/types/props/decorator.rb +41 -36
  24. data/lib/types/props/has_lazily_specialized_methods.rb +2 -2
  25. data/lib/types/props/pretty_printable.rb +45 -83
  26. data/lib/types/props/private/setter_factory.rb +1 -1
  27. data/lib/types/props/serializable.rb +17 -9
  28. data/lib/types/props/type_validation.rb +5 -2
  29. data/lib/types/struct.rb +2 -2
  30. data/lib/types/types/anything.rb +31 -0
  31. data/lib/types/types/base.rb +15 -1
  32. data/lib/types/types/class_of.rb +11 -0
  33. data/lib/types/types/enum.rb +1 -1
  34. data/lib/types/types/fixed_array.rb +13 -0
  35. data/lib/types/types/fixed_hash.rb +22 -0
  36. data/lib/types/types/intersection.rb +1 -1
  37. data/lib/types/types/noreturn.rb +0 -1
  38. data/lib/types/types/simple.rb +27 -4
  39. data/lib/types/types/type_parameter.rb +19 -0
  40. data/lib/types/types/typed_array.rb +29 -0
  41. data/lib/types/types/typed_class.rb +85 -0
  42. data/lib/types/types/typed_enumerable.rb +7 -0
  43. data/lib/types/types/typed_enumerator_chain.rb +41 -0
  44. data/lib/types/types/union.rb +50 -14
  45. data/lib/types/utils.rb +41 -33
  46. metadata +14 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '043696f438ea0a2cfc233bec608d68d026c210142c080ddedb2964952e53e861'
4
- data.tar.gz: eb931df988ea8317cf6ca070ce4b340d9742f2fdc95b9f36d796c72e3add5e51
3
+ metadata.gz: 5a7367e214fde40d68e9647c33185623fd1c0a6ec2328c43ba06036338014225
4
+ data.tar.gz: ef0bef28019ff3385b8d7a21b73bbeb51a4e7482c3190fcf00112f7b711b7164
5
5
  SHA512:
6
- metadata.gz: 1063b0900bdd17f4f103544d85fbc9a288536202c18a39b1836087322bdfea659355e0d13f884febed0a1025e30b54a06ca549fd8e2ff212666dd1f5f55c77d9
7
- data.tar.gz: 8d0d2808f09d07a6895548106cca2e98baecb5bd6f6eda860cf7cf49b66890d9bdbe1575b105dd0ecf85e3e42fa08ffa480c0d2c90130d7465004ca9d36f49fd
6
+ metadata.gz: b7411735478f902033b8ba4258ab00f7ed3e2869b1775fc3090d02e455f012850f804a97d3e005f4fe297b9477a24c94d31a300d82307373c0f9c492b89de7b0
7
+ data.tar.gz: 3b1c758b422685ba62e20070c0fd33274de920a0d8618c58a57b017b84e1e32723106a0e7996b15b0c2ef50fa0bf94f33559dcd88bbbcf01173ddb02949237dd
@@ -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.new(name)
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.new
286
+ T::Types::TypedArray::Untyped::Private::INSTANCE
255
287
  else
256
- T::Types::TypedArray.new(type)
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
- method = @klass.instance_method(method_name.to_sym)
31
- T::Private::Methods.maybe_run_sig_block_for_method(method)
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: Module).void}
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
@@ -19,4 +19,6 @@ module T::Generic
19
19
  def type_template(variance=:invariant, &blk)
20
20
  T::Types::TypeTemplate.new(variance)
21
21
  end
22
+
23
+ def has_attached_class!(variance=:invariant, &blk); end
22
24
  end
@@ -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 mod.instance_method(:initialize).owner == mod
31
- raise "You must call `abstract!` *before* defining an initialize method"
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(:define_method, :initialize) do |*args, &blk|
38
- if self.class == mod
39
- raise "#{mod} is declared as abstract; it cannot be instantiated"
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, :initialize, :initialize)
47
+ mod.singleton_class.send(:alias_method, :new, :new)
47
48
 
48
- if mod.respond_to?(:ruby2_keywords, true)
49
- mod.send(:ruby2_keywords, :initialize)
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
@@ -5,7 +5,10 @@ module T::Private
5
5
  module Casts
6
6
  def self.cast(value, type, cast_method)
7
7
  begin
8
- error = T::Utils.coerce(type).error_message_for_obj(value)
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). Returns a ReplacedMethod instance
95
- # on which you can call `bind(...).call(...)` to call the original method, or `restore` to
96
- # restore the original method (by overwriting or removing the override).
97
- def self.replace_method(mod, name, &blk)
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
- ReplacedMethod.new(mod, original_method, new_method, overwritten, original_visibility)
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&.find {|l| !l.to_s.match?(%r{sorbet-runtime[^/]*/lib/})}
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, declaration_block.loc)
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, loc)
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
- @old_hooks.each(&:restore)
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
- old_included.bind(self).call(arg)
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
- old_extended.bind(self).call(arg)
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
- old_inherited.bind(self).call(arg)
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
- ok_for_fast_path = has_fixed_arity && !method_sig.bind && method_sig.arg_types.length < 5 && is_allowed_to_have_fast_path
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
- if method_sig.block_type
129
- message = method_sig.block_type.error_message_for_obj(blk)
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,