sorbet-runtime 0.5.5794 → 0.5.5942

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/types/_types.rb +9 -8
  3. data/lib/types/configuration.rb +2 -2
  4. data/lib/types/enum.rb +12 -1
  5. data/lib/types/generic.rb +2 -2
  6. data/lib/types/interface_wrapper.rb +4 -4
  7. data/lib/types/non_forcing_constants.rb +9 -3
  8. data/lib/types/private/abstract/data.rb +2 -2
  9. data/lib/types/private/casts.rb +27 -0
  10. data/lib/types/private/class_utils.rb +5 -5
  11. data/lib/types/private/methods/_methods.rb +73 -23
  12. data/lib/types/private/methods/signature.rb +16 -6
  13. data/lib/types/private/sealed.rb +1 -1
  14. data/lib/types/private/types/type_alias.rb +5 -0
  15. data/lib/types/profile.rb +5 -1
  16. data/lib/types/props/constructor.rb +9 -3
  17. data/lib/types/props/decorator.rb +1 -1
  18. data/lib/types/props/generated_code_validation.rb +2 -2
  19. data/lib/types/props/plugin.rb +2 -2
  20. data/lib/types/props/private/setter_factory.rb +12 -2
  21. data/lib/types/props/serializable.rb +2 -2
  22. data/lib/types/props/weak_constructor.rb +14 -9
  23. data/lib/types/sig.rb +3 -3
  24. data/lib/types/types/attached_class.rb +5 -1
  25. data/lib/types/types/base.rb +13 -0
  26. data/lib/types/types/fixed_array.rb +28 -2
  27. data/lib/types/types/fixed_hash.rb +10 -9
  28. data/lib/types/types/intersection.rb +5 -0
  29. data/lib/types/types/noreturn.rb +4 -0
  30. data/lib/types/types/self_type.rb +4 -0
  31. data/lib/types/types/simple.rb +22 -1
  32. data/lib/types/types/typed_array.rb +7 -2
  33. data/lib/types/types/typed_enumerable.rb +12 -6
  34. data/lib/types/types/typed_enumerator.rb +7 -2
  35. data/lib/types/types/typed_hash.rb +8 -3
  36. data/lib/types/types/typed_range.rb +7 -2
  37. data/lib/types/types/typed_set.rb +7 -2
  38. data/lib/types/types/union.rb +36 -5
  39. data/lib/types/types/untyped.rb +4 -0
  40. data/lib/types/utils.rb +7 -7
  41. metadata +46 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ac9a48252c9529d0ca91f96108b141c85388c88180281e5c8a682b16c11a9d5a
4
- data.tar.gz: 31dc95eabc00327b1672a8fc142da7dc47f0ee0699fd4112358042248628675b
3
+ metadata.gz: 4838c21772de1169f412a971cfe59e49fc482bda8de9fb70920fd4bcab5e654f
4
+ data.tar.gz: 5e27052a663a22e70e5aa3afee7563f43be650946ec4673df2fb246535a28bb6
5
5
  SHA512:
6
- metadata.gz: 21b1ef4ffdd76365effafe0c6a3eea4b15c1f9c36f2f402a1c8ba382d2f03d89f2202c1b8ac2aa82c315c7471f0989f7501d1888a49f67d401d33d26ac8e4d3a
7
- data.tar.gz: 0f4c110725910ae452ea72fe6852538aa1c31cbb576dc242fe3631bd318e3b2bfda7b0fe3b05362584bd53885c9788a4f3eb2d5920d285c1c39ce727fb0ebd14
6
+ metadata.gz: 87abc9d8a3a9a753d96577a52856c4e04b2dfaaefb1908e696d9882c04a593ab6cdd9112660afa887d57fd3df62dbf99fded5a697b176ac1c043011b3fd43db1
7
+ data.tar.gz: 6f10e93404924fbf7752eb01786e717d585472df7b2c77aaead247df1d170998ff46b8d3d8e5b51bad6773fc950280ebc3c922651078a249fef2d29929d75aef
data/lib/types/_types.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  # typed: true
3
3
  # This is where we define the shortcuts, so we can't use them here
4
- # rubocop:disable PrisonGuard/UseOpusTypesShortcut
5
4
 
6
5
  # _____
7
6
  # |_ _| _ _ __ ___ ___
@@ -26,23 +25,26 @@
26
25
  module T
27
26
  # T.any(<Type>, <Type>, ...) -- matches any of the types listed
28
27
  def self.any(type_a, type_b, *types)
29
- T::Types::Union.new([type_a, type_b] + types)
28
+ type_a = T::Utils.coerce(type_a)
29
+ type_b = T::Utils.coerce(type_b)
30
+ types = types.map {|t| T::Utils.coerce(t)} if !types.empty?
31
+ T::Types::Union::Private::Pool.union_of_types(type_a, type_b, types)
30
32
  end
31
33
 
32
34
  # Shorthand for T.any(type, NilClass)
33
35
  def self.nilable(type)
34
- T::Types::Union.new([type, NilClass])
36
+ T::Types::Union::Private::Pool.union_of_types(T::Utils.coerce(type), T::Utils::Nilable::NIL_TYPE)
35
37
  end
36
38
 
37
39
  # Matches any object. In the static checker, T.untyped allows any
38
40
  # method calls or operations.
39
41
  def self.untyped
40
- T::Types::Untyped.new
42
+ T::Types::Untyped::Private::INSTANCE
41
43
  end
42
44
 
43
45
  # Indicates a function never returns (e.g. "Kernel#raise")
44
46
  def self.noreturn
45
- T::Types::NoReturn.new
47
+ T::Types::NoReturn::Private::INSTANCE
46
48
  end
47
49
 
48
50
  # T.all(<Type>, <Type>, ...) -- matches an object that has all of the types listed
@@ -62,12 +64,12 @@ module T
62
64
 
63
65
  # Matches `self`:
64
66
  def self.self_type
65
- T::Types::SelfType.new
67
+ T::Types::SelfType::Private::INSTANCE
66
68
  end
67
69
 
68
70
  # Matches the instance type in a singleton-class context
69
71
  def self.attached_class
70
- T::Types::AttachedClassType.new
72
+ T::Types::AttachedClassType::Private::INSTANCE
71
73
  end
72
74
 
73
75
  # Matches any class that subclasses or includes the provided class
@@ -282,4 +284,3 @@ module T
282
284
  end
283
285
  end
284
286
  end
285
- # rubocop:enable PrisonGuard/UseOpusTypesShortcut
@@ -248,7 +248,7 @@ module T::Configuration
248
248
  end
249
249
 
250
250
  private_class_method def self.log_info_handler_default(str, extra)
251
- puts "#{str}, extra: #{extra}" # rubocop:disable PrisonGuard/NoBarePuts
251
+ puts "#{str}, extra: #{extra}"
252
252
  end
253
253
 
254
254
  def self.log_info_handler(str, extra)
@@ -282,7 +282,7 @@ module T::Configuration
282
282
  end
283
283
 
284
284
  private_class_method def self.soft_assert_handler_default(str, extra)
285
- puts "#{str}, extra: #{extra}" # rubocop:disable PrisonGuard/NoBarePuts
285
+ puts "#{str}, extra: #{extra}"
286
286
  end
287
287
 
288
288
  def self.soft_assert_handler(str, extra)
data/lib/types/enum.rb CHANGED
@@ -57,6 +57,17 @@ class T::Enum
57
57
  @values
58
58
  end
59
59
 
60
+ # This exists for compatibility with the interface of `Hash` & mostly to support
61
+ # the HashEachMethods Rubocop.
62
+ sig {params(blk: T.nilable(T.proc.params(arg0: T.attached_class).void)).returns(T.any(T::Enumerator[T.attached_class], T::Array[T.attached_class]))}
63
+ def self.each_value(&blk)
64
+ if blk
65
+ values.each(&blk)
66
+ else
67
+ values.each
68
+ end
69
+ end
70
+
60
71
  # Convert from serialized value to enum instance
61
72
  #
62
73
  # Note: It would have been nice to make this method final before people started overriding it.
@@ -88,7 +99,7 @@ class T::Enum
88
99
 
89
100
  # Note: It would have been nice to make this method final before people started overriding it.
90
101
  # @return [Boolean] Does the given serialized value correspond with any of this enum's values.
91
- sig {overridable.params(serialized_val: SerializedVal).returns(T::Boolean)}
102
+ sig {overridable.params(serialized_val: SerializedVal).returns(T::Boolean).checked(:never)}
92
103
  def self.has_serialized?(serialized_val)
93
104
  if @mapping.nil?
94
105
  raise "Attempting to access serialization map of #{self.class} before it has been initialized." \
data/lib/types/generic.rb CHANGED
@@ -14,10 +14,10 @@ module T::Generic
14
14
  end
15
15
 
16
16
  def type_member(variance=:invariant, fixed: nil, lower: T.untyped, upper: BasicObject)
17
- T::Types::TypeMember.new(variance) # rubocop:disable PrisonGuard/UseOpusTypesShortcut
17
+ T::Types::TypeMember.new(variance)
18
18
  end
19
19
 
20
20
  def type_template(variance=:invariant, fixed: nil, lower: T.untyped, upper: BasicObject)
21
- T::Types::TypeTemplate.new(variance) # rubocop:disable PrisonGuard/UseOpusTypesShortcut
21
+ T::Types::TypeTemplate.new(variance)
22
22
  end
23
23
  end
@@ -83,11 +83,11 @@ class T::InterfaceWrapper
83
83
  end
84
84
  end
85
85
 
86
- def kind_of?(other) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
86
+ def kind_of?(other)
87
87
  is_a?(other)
88
88
  end
89
89
 
90
- def is_a?(other) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
90
+ def is_a?(other)
91
91
  if !other.is_a?(Module)
92
92
  raise TypeError.new("class or module required")
93
93
  end
@@ -99,13 +99,13 @@ class T::InterfaceWrapper
99
99
 
100
100
  # Prefixed because we're polluting the namespace of the interface we're wrapping, and we don't
101
101
  # want anyone else (besides dynamic_cast) calling it.
102
- def __target_obj_DO_NOT_USE
102
+ def __target_obj_DO_NOT_USE # rubocop:disable Naming/MethodName
103
103
  @target_obj
104
104
  end
105
105
 
106
106
  # Prefixed because we're polluting the namespace of the interface we're wrapping, and we don't
107
107
  # want anyone else (besides wrapped_dynamic_cast) calling it.
108
- def __interface_mod_DO_NOT_USE
108
+ def __interface_mod_DO_NOT_USE # rubocop:disable Naming/MethodName
109
109
  @interface_mod
110
110
  end
111
111
 
@@ -4,8 +4,11 @@
4
4
  module T::NonForcingConstants
5
5
  # NOTE: This method is documented on the RBI in Sorbet's payload, so that it
6
6
  # shows up in the hover/completion documentation via LSP.
7
- T::Sig::WithoutRuntime.sig {params(val: BasicObject, klass: String).returns(T::Boolean)}
8
- def self.non_forcing_is_a?(val, klass)
7
+ T::Sig::WithoutRuntime.sig {params(val: BasicObject, klass: String, package: T.nilable(String)).returns(T::Boolean)}
8
+ def self.non_forcing_is_a?(val, klass, package: nil)
9
+ # TODO(gdritter): once we have a runtime implementation of
10
+ # packages, we'll need to actually handle the `package` argument
11
+ # here.
9
12
  method_name = "T::NonForcingConstants.non_forcing_is_a?"
10
13
  if klass.empty?
11
14
  raise ArgumentError.new("The string given to `#{method_name}` must not be empty")
@@ -18,7 +21,10 @@ module T::NonForcingConstants
18
21
  parts.each do |part|
19
22
  if current_klass.nil?
20
23
  # First iteration
21
- if part != ""
24
+ if part != "" && package.nil?
25
+ # if we've supplied a package, we're probably running in
26
+ # package mode, which means absolute references are
27
+ # meaningless
22
28
  raise ArgumentError.new("The string given to `#{method_name}` must be an absolute constant reference that starts with `::`")
23
29
  end
24
30
 
@@ -12,11 +12,11 @@
12
12
  # modules that override the `hash` method with something completely broken.
13
13
  module T::Private::Abstract::Data
14
14
  def self.get(mod, key)
15
- mod.instance_variable_get("@opus_abstract__#{key}") # rubocop:disable PrisonGuard/NoLurkyInstanceVariableAccess
15
+ mod.instance_variable_get("@opus_abstract__#{key}")
16
16
  end
17
17
 
18
18
  def self.set(mod, key, value)
19
- mod.instance_variable_set("@opus_abstract__#{key}", value) # rubocop:disable PrisonGuard/NoLurkyInstanceVariableAccess
19
+ mod.instance_variable_set("@opus_abstract__#{key}", value)
20
20
  end
21
21
 
22
22
  def self.key?(mod, key)
@@ -2,6 +2,14 @@
2
2
  # typed: false
3
3
 
4
4
  module T::Private
5
+ # Dynamically confirm that `value` is recursively a valid value of
6
+ # type `type`, including recursively through collections. Note that
7
+ # in some cases this runtime check can be very expensive, especially
8
+ # with large collections of objects.
9
+ def self.check_type_recursive!(value, type)
10
+ T::Private::Casts.cast_recursive(value, type, cast_method: "T.check_type_recursive!")
11
+ end
12
+
5
13
  module Casts
6
14
  def self.cast(value, type, cast_method:)
7
15
  begin
@@ -18,5 +26,24 @@ module T::Private
18
26
  value
19
27
  end
20
28
  end
29
+
30
+ # there's a lot of shared logic with the above one, but factoring
31
+ # it out like this makes it easier to hopefully one day delete
32
+ # this one
33
+ def self.cast_recursive(value, type, cast_method:)
34
+ begin
35
+ error = T::Utils.coerce(type).error_message_for_obj_recursive(value)
36
+ return value unless error
37
+
38
+ caller_loc = T.must(caller_locations(2..2)).first
39
+
40
+ suffix = "Caller: #{T.must(caller_loc).path}:#{T.must(caller_loc).lineno}"
41
+
42
+ raise TypeError.new("#{cast_method}: #{error}\n#{suffix}")
43
+ rescue TypeError => e # raise into rescue to ensure e.backtrace is populated
44
+ T::Configuration.inline_type_error_handler(e)
45
+ value
46
+ end
47
+ end
21
48
  end
22
49
  end
@@ -33,16 +33,16 @@ module T::Private::ClassUtils
33
33
  if @overwritten
34
34
  # The original method was overwritten. Overwrite again to restore it.
35
35
  T::Configuration.without_ruby_warnings do
36
- @mod.send(:define_method, @old_method.name, @old_method) # rubocop:disable PrisonGuard/UsePublicSend
36
+ @mod.send(:define_method, @old_method.name, @old_method)
37
37
  end
38
38
  else
39
39
  # The original method was in an ancestor. Restore it by removing the overriding method.
40
- @mod.send(:remove_method, @old_method.name) # rubocop:disable PrisonGuard/UsePublicSend
40
+ @mod.send(:remove_method, @old_method.name)
41
41
  end
42
42
 
43
43
  # Restore the visibility. Note that we need to do this even when we call remove_method
44
44
  # above, because the module may have set custom visibility for a method it inherited.
45
- @mod.send(@visibility, @old_method.name) # rubocop:disable PrisonGuard/UsePublicSend
45
+ @mod.send(@visibility, @old_method.name)
46
46
 
47
47
  nil
48
48
  end
@@ -97,13 +97,13 @@ module T::Private::ClassUtils
97
97
  overwritten = original_owner == mod
98
98
  T::Configuration.without_ruby_warnings do
99
99
  T::Private::DeclState.current.without_on_method_added do
100
- mod.send(:define_method, name, &blk) # rubocop:disable PrisonGuard/UsePublicSend
100
+ mod.send(:define_method, name, &blk)
101
101
  if blk.arity < 0 && mod.respond_to?(:ruby2_keywords, true)
102
102
  mod.send(:ruby2_keywords, name)
103
103
  end
104
104
  end
105
105
  end
106
- mod.send(original_visibility, name) # rubocop:disable PrisonGuard/UsePublicSend
106
+ mod.send(original_visibility, name)
107
107
  new_method = mod.instance_method(name)
108
108
 
109
109
  ReplacedMethod.new(mod, original_method, new_method, overwritten, original_visibility)
@@ -72,7 +72,7 @@ module T::Private::Methods
72
72
  decl.params = {}
73
73
  end
74
74
 
75
- T::Types::Proc.new(decl.params, decl.returns) # rubocop:disable PrisonGuard/UseOpusTypesShortcut
75
+ T::Types::Proc.new(decl.params, decl.returns)
76
76
  end
77
77
 
78
78
  # See docs at T::Utils.register_forwarder.
@@ -206,22 +206,12 @@ module T::Private::Methods
206
206
  # (or unwrap back to the original method).
207
207
  new_method = nil
208
208
  T::Private::ClassUtils.replace_method(mod, method_name) do |*args, &blk|
209
- if !T::Private::Methods.has_sig_block_for_method(new_method)
210
- # This should only happen if the user used alias_method to grab a handle
211
- # to the original pre-unwound `sig` method. I guess we'll just proxy the
212
- # call forever since we don't know who is holding onto this handle to
213
- # replace it.
214
- new_new_method = mod.instance_method(method_name)
215
- if new_method == new_new_method
216
- raise "`sig` not present for method `#{method_name}` but you're trying to run it anyways. " \
217
- "This should only be executed if you used `alias_method` to grab a handle to a method after `sig`ing it, but that clearly isn't what you are doing. " \
218
- "Maybe look to see if an exception was thrown in your `sig` lambda or somehow else your `sig` wasn't actually applied to the method. " \
219
- "Contact #dev-productivity if you're really stuck."
220
- end
221
- return new_new_method.bind(self).call(*args, &blk)
222
- end
223
-
224
- method_sig = T::Private::Methods.run_sig_block_for_method(new_method)
209
+ method_sig = T::Private::Methods.maybe_run_sig_block_for_method(new_method)
210
+ method_sig ||= T::Private::Methods._handle_missing_method_signature(
211
+ self,
212
+ original_method,
213
+ __callee__,
214
+ )
225
215
 
226
216
  # Should be the same logic as CallValidation.wrap_method_if_needed but we
227
217
  # don't want that extra layer of indirection in the callstack
@@ -252,6 +242,43 @@ module T::Private::Methods
252
242
  end
253
243
  end
254
244
 
245
+ def self._handle_missing_method_signature(receiver, original_method, callee)
246
+ method_sig = T::Private::Methods.signature_for_method(original_method)
247
+ if !method_sig
248
+ raise "`sig` not present for method `#{callee}` on #{receiver.inspect} but you're trying to run it anyways. " \
249
+ "This should only be executed if you used `alias_method` to grab a handle to a method after `sig`ing it, but that clearly isn't what you are doing. " \
250
+ "Maybe look to see if an exception was thrown in your `sig` lambda or somehow else your `sig` wasn't actually applied to the method."
251
+ end
252
+
253
+ if receiver.class <= original_method.owner
254
+ receiving_class = receiver.class
255
+ elsif receiver.singleton_class <= original_method.owner
256
+ receiving_class = receiver.singleton_class
257
+ elsif receiver.is_a?(Module) && receiver <= original_method.owner
258
+ receiving_class = receiver
259
+ else
260
+ raise "#{receiver} is not related to #{original_method} - how did we get here?"
261
+ end
262
+
263
+ # Check for a case where `alias` or `alias_method` was called for a
264
+ # method which had already had a `sig` applied. In that case, we want
265
+ # to avoid hitting this slow path again, by moving to a faster validator
266
+ # just like we did or will for the original method.
267
+ #
268
+ # If this isn't an `alias` or `alias_method` case, we're probably in the
269
+ # middle of some metaprogramming using a Method object, e.g. a pattern like
270
+ # `arr.map(&method(:foo))`. There's nothing really we can do to optimize
271
+ # that here.
272
+ receiving_method = receiving_class.instance_method(callee)
273
+ if receiving_method != original_method && receiving_method.original_name == original_method.name
274
+ aliasing_mod = receiving_method.owner
275
+ method_sig = method_sig.as_alias(callee)
276
+ unwrap_method(aliasing_mod, method_sig, original_method)
277
+ end
278
+
279
+ method_sig
280
+ end
281
+
255
282
  # Executes the `sig` block, and converts the resulting Declaration
256
283
  # to a Signature.
257
284
  def self.run_sig(hook_mod, method_name, original_method, declaration_block)
@@ -270,7 +297,7 @@ module T::Private::Methods
270
297
  Signature.new_untyped(method: original_method)
271
298
  end
272
299
 
273
- unwrap_method(hook_mod, signature, original_method)
300
+ unwrap_method(signature.method.owner, signature, original_method)
274
301
  signature
275
302
  end
276
303
 
@@ -284,8 +311,12 @@ module T::Private::Methods
284
311
 
285
312
  def self.build_sig(hook_mod, method_name, original_method, current_declaration, loc)
286
313
  begin
287
- # We allow `sig` in the current module's context (normal case) and inside `class << self`
288
- if hook_mod != current_declaration.mod && hook_mod.singleton_class != current_declaration.mod
314
+ # We allow `sig` in the current module's context (normal case) and
315
+ if hook_mod != current_declaration.mod &&
316
+ # inside `class << self`, and
317
+ hook_mod.singleton_class != current_declaration.mod &&
318
+ # on `self` at the top level of a file
319
+ current_declaration.mod != TOP_SELF
289
320
  raise "A method (#{method_name}) is being added on a different class/module (#{hook_mod}) than the " \
290
321
  "last call to `sig` (#{current_declaration.mod}). Make sure each call " \
291
322
  "to `sig` is immediately followed by a method definition on the same " \
@@ -322,8 +353,8 @@ module T::Private::Methods
322
353
  end
323
354
  end
324
355
 
325
- def self.unwrap_method(hook_mod, signature, original_method)
326
- maybe_wrapped_method = CallValidation.wrap_method_if_needed(signature.method.owner, signature, original_method)
356
+ def self.unwrap_method(mod, signature, original_method)
357
+ maybe_wrapped_method = CallValidation.wrap_method_if_needed(mod, signature, original_method)
327
358
  @signatures_by_method[method_to_key(maybe_wrapped_method)] = signature
328
359
  end
329
360
 
@@ -435,6 +466,20 @@ module T::Private::Methods
435
466
  return if @installed_hooks.include?(mod)
436
467
  @installed_hooks << mod
437
468
 
469
+ if mod == TOP_SELF
470
+ # self at the top-level of a file is weirdly special in Ruby
471
+ # The Ruby VM on startup creates an `Object.new` and stashes it.
472
+ # Unlike when we're using sig inside a module, `self` is actually a
473
+ # normal object, not an instance of Module.
474
+ #
475
+ # Thus we can't ask things like mod.singleton_class? (since that's
476
+ # defined only on Module, not on Object) and even if we could, the places
477
+ # where we need to install the hooks are special.
478
+ mod.extend(SingletonMethodHooks) # def self.foo; end (at top level)
479
+ Object.extend(MethodHooks) # def foo; end (at top level)
480
+ return
481
+ end
482
+
438
483
  if mod.singleton_class?
439
484
  mod.include(SingletonMethodHooks)
440
485
  else
@@ -454,7 +499,12 @@ module T::Private::Methods
454
499
 
455
500
  private_class_method def self.key_to_method(key)
456
501
  id, name = key.split("#")
457
- obj = ObjectSpace._id2ref(id.to_i) # rubocop:disable PrisonGuard/NoDynamicConstAccess
502
+ obj = ObjectSpace._id2ref(id.to_i)
458
503
  obj.instance_method(name)
459
504
  end
460
505
  end
506
+
507
+ # This has to be here, and can't be nested inside `T::Private::Methods`,
508
+ # because the value of `self` depends on lexical (nesting) scope, and we
509
+ # specifically need a reference to the file-level self, i.e. `main:Object`
510
+ T::Private::Methods::TOP_SELF = self
@@ -121,6 +121,15 @@ class T::Private::Methods::Signature
121
121
  end
122
122
  end
123
123
 
124
+ attr_writer :method_name
125
+ protected :method_name=
126
+
127
+ def as_alias(alias_name)
128
+ new_sig = clone
129
+ new_sig.method_name = alias_name
130
+ new_sig
131
+ end
132
+
124
133
  def arg_count
125
134
  @arg_types.length
126
135
  end
@@ -146,9 +155,10 @@ class T::Private::Methods::Signature
146
155
  # can't) match the definition of the method we're validating. In addition, Ruby has a bug that
147
156
  # causes forwarding **kwargs to do the wrong thing: see https://bugs.ruby-lang.org/issues/10708
148
157
  # and https://bugs.ruby-lang.org/issues/11860.
149
- if (args.length > @req_arg_count) && (!@kwarg_types.empty? || @has_keyrest) && args[-1].is_a?(Hash)
158
+ args_length = args.length
159
+ if (args_length > @req_arg_count) && (!@kwarg_types.empty? || @has_keyrest) && args[-1].is_a?(Hash)
150
160
  kwargs = args[-1]
151
- args = args[0...-1]
161
+ args_length -= 1
152
162
  else
153
163
  kwargs = EMPTY_HASH
154
164
  end
@@ -156,19 +166,19 @@ class T::Private::Methods::Signature
156
166
  arg_types = @arg_types
157
167
 
158
168
  if @has_rest
159
- arg_types += [[@rest_name, @rest_type]] * (args.length - @arg_types.length)
169
+ arg_types += [[@rest_name, @rest_type]] * (args_length - @arg_types.length)
160
170
 
161
- elsif (args.length < @req_arg_count) || (args.length > @arg_types.length)
171
+ elsif (args_length < @req_arg_count) || (args_length > @arg_types.length)
162
172
  expected_str = @req_arg_count.to_s
163
173
  if @arg_types.length != @req_arg_count
164
174
  expected_str += "..#{@arg_types.length}"
165
175
  end
166
- raise ArgumentError.new("wrong number of arguments (given #{args.length}, expected #{expected_str})")
176
+ raise ArgumentError.new("wrong number of arguments (given #{args_length}, expected #{expected_str})")
167
177
  end
168
178
 
169
179
  begin
170
180
  it = 0
171
- while it < args.length
181
+ while it < args_length
172
182
  yield arg_types[it][0], args[it], arg_types[it][1]
173
183
  it += 1
174
184
  end
@@ -46,7 +46,7 @@ module T::Private::Sealed
46
46
  end
47
47
 
48
48
  def self.validate_inheritance(this_line, parent, verb)
49
- this_file = this_line&.split(':').first
49
+ this_file = this_line&.split(':')&.first
50
50
  decl_file = parent.instance_variable_get(:@sorbet_sealed_module_decl_file)
51
51
 
52
52
  if !this_file || !decl_file
@@ -18,6 +18,11 @@ module T::Private::Types
18
18
  aliased_type.name
19
19
  end
20
20
 
21
+ # @override Base
22
+ def recursively_valid?(obj)
23
+ aliased_type.recursively_valid?(obj)
24
+ end
25
+
21
26
  # @override Base
22
27
  def valid?(obj)
23
28
  aliased_type.valid?(obj)
data/lib/types/profile.rb CHANGED
@@ -10,7 +10,11 @@ module T::Profile
10
10
  def typecheck_duration_estimate
11
11
  total_typechecks = typecheck_samples * SAMPLE_RATE + (SAMPLE_RATE - typecheck_sample_attempts)
12
12
  typechecks_measured = typecheck_samples * SAMPLE_RATE
13
- typecheck_duration * SAMPLE_RATE * 1.0 * total_typechecks / typechecks_measured
13
+ if typechecks_measured.positive?
14
+ typecheck_duration * SAMPLE_RATE * 1.0 * total_typechecks / typechecks_measured
15
+ else
16
+ 0.0
17
+ end
14
18
  end
15
19
 
16
20
  def typecheck_count_estimate
@@ -17,11 +17,16 @@ module T::Props::Constructor::DecoratorMethods
17
17
  # checked(:never) - O(runtime object construction)
18
18
  sig {params(instance: T::Props::Constructor, hash: T::Hash[Symbol, T.untyped]).returns(Integer).checked(:never)}
19
19
  def construct_props_without_defaults(instance, hash)
20
- @props_without_defaults&.count do |p, setter_proc|
20
+ # Use `each_pair` rather than `count` because, as of Ruby 2.6, the latter delegates to Enumerator
21
+ # and therefore allocates for each entry.
22
+ result = 0
23
+ @props_without_defaults&.each_pair do |p, setter_proc|
21
24
  begin
22
25
  val = hash[p]
23
26
  instance.instance_exec(val, &setter_proc)
24
- val || hash.key?(p)
27
+ if val || hash.key?(p)
28
+ result += 1
29
+ end
25
30
  rescue TypeError, T::Props::InvalidValueError
26
31
  if !hash.key?(p)
27
32
  raise ArgumentError.new("Missing required prop `#{p}` for class `#{instance.class.name}`")
@@ -29,6 +34,7 @@ module T::Props::Constructor::DecoratorMethods
29
34
  raise
30
35
  end
31
36
  end
32
- end || 0
37
+ end
38
+ result
33
39
  end
34
40
  end
@@ -427,7 +427,7 @@ class T::Props::Decorator
427
427
  # TODO(PRIVACYENG-982) Ideally we'd also check for 'password' and possibly
428
428
  # other terms, but this interacts badly with ProtoDefinedDocument because
429
429
  # the proto syntax currently can't declare "sensitivity: []"
430
- if prop_name =~ /\bsecret\b/
430
+ if /\bsecret\b/.match?(prop_name)
431
431
  T::Configuration.hard_assert_handler(
432
432
  "#{@class}##{prop_name} has the word 'secret' in its name, but no " \
433
433
  "'sensitivity:' annotation. This is probably wrong, because if a " \
@@ -170,7 +170,7 @@ module T::Props
170
170
 
171
171
  private_class_method def self.validate_deserialize_handle_nil(node)
172
172
  case node.type
173
- when :hash, :array, :str, :sym, :int, :float, :true, :false, :nil, :const
173
+ when :hash, :array, :str, :sym, :int, :float, :true, :false, :nil, :const # rubocop:disable Lint/BooleanSymbol
174
174
  # Primitives and constants are safe
175
175
  when :send
176
176
  receiver, method, arg = node.children
@@ -209,7 +209,7 @@ module T::Props
209
209
  when :const
210
210
  # This is ok, because we'll have validated what method has been called
211
211
  # if applicable
212
- when :hash, :array, :str, :sym, :int, :float, :true, :false, :nil, :self
212
+ when :hash, :array, :str, :sym, :int, :float, :true, :false, :nil, :self # rubocop:disable Lint/BooleanSymbol
213
213
  # Primitives & self are ok
214
214
  when :lvar, :arg, :ivar
215
215
  # Reading local & instance variables & arguments is ok
@@ -22,7 +22,7 @@ module T::Props::Plugin
22
22
  if plugin.const_defined?('ClassMethods')
23
23
  # FIXME: This will break preloading, selective test execution, etc if `mod::ClassMethods`
24
24
  # is ever defined in a separate file from `mod`.
25
- target.extend(plugin::ClassMethods) # rubocop:disable PrisonGuard/NoDynamicConstAccess
25
+ target.extend(plugin::ClassMethods)
26
26
  end
27
27
  end
28
28
 
@@ -30,7 +30,7 @@ module T::Props::Plugin
30
30
  if plugin.const_defined?('DecoratorMethods')
31
31
  # FIXME: This will break preloading, selective test execution, etc if `mod::DecoratorMethods`
32
32
  # is ever defined in a separate file from `mod`.
33
- target.extend(plugin::DecoratorMethods) # rubocop:disable PrisonGuard/NoDynamicConstAccess
33
+ target.extend(plugin::DecoratorMethods)
34
34
  end
35
35
  end
36
36
  end
@@ -52,7 +52,12 @@ module T::Props
52
52
  end
53
53
  private_class_method def self.non_nil_proc(prop, accessor_key, non_nil_type, klass, validate)
54
54
  proc do |val|
55
- if non_nil_type.valid?(val)
55
+ # this use of recursively_valid? is intentional: unlike for
56
+ # methods, we want to make sure data at the 'edge'
57
+ # (e.g. models that go into databases or structs serialized
58
+ # from disk) are correct, so we use more thorough runtime
59
+ # checks there
60
+ if non_nil_type.recursively_valid?(val)
56
61
  if validate
57
62
  validate.call(prop, val)
58
63
  end
@@ -83,7 +88,12 @@ module T::Props
83
88
  proc do |val|
84
89
  if val.nil?
85
90
  instance_variable_set(accessor_key, nil)
86
- elsif non_nil_type.valid?(val)
91
+ # this use of recursively_valid? is intentional: unlike for
92
+ # methods, we want to make sure data at the 'edge'
93
+ # (e.g. models that go into databases or structs serialized
94
+ # from disk) are correct, so we use more thorough runtime
95
+ # checks there
96
+ elsif non_nil_type.recursively_valid?(val)
87
97
  if validate
88
98
  validate.call(prop, val)
89
99
  end
@@ -121,8 +121,8 @@ module T::Props::Serializable
121
121
  private def with_existing_hash(changed_props, existing_hash:)
122
122
  serialized = existing_hash
123
123
  new_val = self.class.from_hash(serialized.merge(recursive_stringify_keys(changed_props)))
124
- old_extra = self.instance_variable_get(:@_extra_props) # rubocop:disable PrisonGuard/NoLurkyInstanceVariableAccess
125
- new_extra = new_val.instance_variable_get(:@_extra_props) # rubocop:disable PrisonGuard/NoLurkyInstanceVariableAccess
124
+ old_extra = self.instance_variable_get(:@_extra_props)
125
+ new_extra = new_val.instance_variable_get(:@_extra_props)
126
126
  if old_extra != new_extra
127
127
  difference =
128
128
  if old_extra
@@ -30,14 +30,16 @@ module T::Props::WeakConstructor::DecoratorMethods
30
30
  # checked(:never) - O(runtime object construction)
31
31
  sig {params(instance: T::Props::WeakConstructor, hash: T::Hash[Symbol, T.untyped]).returns(Integer).checked(:never)}
32
32
  def construct_props_without_defaults(instance, hash)
33
- @props_without_defaults&.count do |p, setter_proc|
33
+ # Use `each_pair` rather than `count` because, as of Ruby 2.6, the latter delegates to Enumerator
34
+ # and therefore allocates for each entry.
35
+ result = 0
36
+ @props_without_defaults&.each_pair do |p, setter_proc|
34
37
  if hash.key?(p)
35
38
  instance.instance_exec(hash[p], &setter_proc)
36
- true
37
- else
38
- false
39
+ result += 1
39
40
  end
40
- end || 0
41
+ end
42
+ result
41
43
  end
42
44
 
43
45
  # Set values for all props that have defaults. Use the default if and only if
@@ -49,14 +51,17 @@ module T::Props::WeakConstructor::DecoratorMethods
49
51
  # checked(:never) - O(runtime object construction)
50
52
  sig {params(instance: T::Props::WeakConstructor, hash: T::Hash[Symbol, T.untyped]).returns(Integer).checked(:never)}
51
53
  def construct_props_with_defaults(instance, hash)
52
- @props_with_defaults&.count do |p, default_struct|
54
+ # Use `each_pair` rather than `count` because, as of Ruby 2.6, the latter delegates to Enumerator
55
+ # and therefore allocates for each entry.
56
+ result = 0
57
+ @props_with_defaults&.each_pair do |p, default_struct|
53
58
  if hash.key?(p)
54
59
  instance.instance_exec(hash[p], &default_struct.setter_proc)
55
- true
60
+ result += 1
56
61
  else
57
62
  default_struct.set_default(instance)
58
- false
59
63
  end
60
- end || 0
64
+ end
65
+ result
61
66
  end
62
67
  end
data/lib/types/sig.rb CHANGED
@@ -7,7 +7,7 @@ module T::Sig
7
7
  module WithoutRuntime
8
8
  # At runtime, does nothing, but statically it is treated exactly the same
9
9
  # as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
10
- def self.sig(arg0=nil, &blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
10
+ def self.sig(arg0=nil, &blk); end
11
11
 
12
12
  original_verbose = $VERBOSE
13
13
  $VERBOSE = false
@@ -15,7 +15,7 @@ module T::Sig
15
15
  # At runtime, does nothing, but statically it is treated exactly the same
16
16
  # as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
17
17
  T::Sig::WithoutRuntime.sig {params(arg0: T.nilable(Symbol), blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
18
- def self.sig(arg0=nil, &blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride, Lint/DuplicateMethods
18
+ def self.sig(arg0=nil, &blk); end # rubocop:disable Lint/DuplicateMethods
19
19
 
20
20
  $VERBOSE = original_verbose
21
21
  end
@@ -24,7 +24,7 @@ module T::Sig
24
24
  # abstract/override/... helpers. See the documentation URL on
25
25
  # {T::Helpers}
26
26
  T::Sig::WithoutRuntime.sig {params(arg0: T.nilable(Symbol), blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
27
- def sig(arg0=nil, &blk) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
27
+ def sig(arg0=nil, &blk)
28
28
  T::Private::Methods.declare_sig(self, arg0, &blk)
29
29
  end
30
30
  end
@@ -12,7 +12,7 @@ module T::Types
12
12
 
13
13
  # @override Base
14
14
  def name
15
- "AttachedClass"
15
+ "T.attached_class"
16
16
  end
17
17
 
18
18
  # @override Base
@@ -29,5 +29,9 @@ module T::Types
29
29
  false
30
30
  end
31
31
  end
32
+
33
+ module Private
34
+ INSTANCE = AttachedClassType.new.freeze
35
+ end
32
36
  end
33
37
  end
@@ -16,6 +16,11 @@ module T::Types
16
16
  end
17
17
  end
18
18
 
19
+ # this will be redefined in certain subclasses
20
+ def recursively_valid?(obj)
21
+ valid?(obj)
22
+ end
23
+
19
24
  def valid?(obj)
20
25
  raise NotImplementedError
21
26
  end
@@ -130,6 +135,14 @@ module T::Types
130
135
  end
131
136
  end
132
137
 
138
+ def error_message_for_obj_recursive(obj)
139
+ if recursively_valid?(obj)
140
+ nil
141
+ else
142
+ "Expected type #{self.name}, got #{describe_obj(obj)}"
143
+ end
144
+ end
145
+
133
146
  def validate!(obj)
134
147
  err = error_message_for_obj(obj)
135
148
  raise TypeError.new(err) if err
@@ -17,10 +17,36 @@ module T::Types
17
17
  "[#{@types.join(', ')}]"
18
18
  end
19
19
 
20
+ # @override Base
21
+ def recursively_valid?(obj)
22
+ if obj.is_a?(Array) && obj.length == @types.length
23
+ i = 0
24
+ while i < @types.length
25
+ if !@types[i].recursively_valid?(obj[i])
26
+ return false
27
+ end
28
+ i += 1
29
+ end
30
+ true
31
+ else
32
+ false
33
+ end
34
+ end
35
+
20
36
  # @override Base
21
37
  def valid?(obj)
22
- obj.is_a?(Array) && obj.length == @types.length &&
23
- obj.zip(@types).all? {|item, type| type.valid?(item)}
38
+ if obj.is_a?(Array) && obj.length == @types.length
39
+ i = 0
40
+ while i < @types.length
41
+ if !@types[i].valid?(obj[i])
42
+ return false
43
+ end
44
+ i += 1
45
+ end
46
+ true
47
+ else
48
+ false
49
+ end
24
50
  end
25
51
 
26
52
  # @override Base
@@ -17,17 +17,18 @@ module T::Types
17
17
  end
18
18
 
19
19
  # @override Base
20
- def valid?(obj)
20
+ def recursively_valid?(obj)
21
21
  return false unless obj.is_a?(Hash)
22
+ return false if @types.any? {|key, type| !type.recursively_valid?(obj[key])}
23
+ return false if obj.any? {|key, _| !@types[key]}
24
+ true
25
+ end
22
26
 
23
- @types.each do |key, type|
24
- return false unless type.valid?(obj[key])
25
- end
26
-
27
- obj.each_key do |key|
28
- return false unless @types[key]
29
- end
30
-
27
+ # @override Base
28
+ def valid?(obj)
29
+ return false unless obj.is_a?(Hash)
30
+ return false if @types.any? {|key, type| !type.valid?(obj[key])}
31
+ return false if obj.any? {|key, _| !@types[key]}
31
32
  true
32
33
  end
33
34
 
@@ -23,6 +23,11 @@ module T::Types
23
23
  "T.all(#{@types.map(&:name).sort.join(', ')})"
24
24
  end
25
25
 
26
+ # @override Base
27
+ def recursively_valid?(obj)
28
+ @types.all? {|type| type.recursively_valid?(obj)}
29
+ end
30
+
26
31
  # @override Base
27
32
  def valid?(obj)
28
33
  @types.all? {|type| type.valid?(obj)}
@@ -21,5 +21,9 @@ module T::Types
21
21
  private def subtype_of_single?(other)
22
22
  true
23
23
  end
24
+
25
+ module Private
26
+ INSTANCE = NoReturn.new.freeze
27
+ end
24
28
  end
25
29
  end
@@ -27,5 +27,9 @@ module T::Types
27
27
  false
28
28
  end
29
29
  end
30
+
31
+ module Private
32
+ INSTANCE = SelfType.new.freeze
33
+ end
30
34
  end
31
35
  end
@@ -12,7 +12,11 @@ module T::Types
12
12
 
13
13
  # @override Base
14
14
  def name
15
- @raw_type.name
15
+ # Memoize to mitigate pathological performance with anonymous modules (https://bugs.ruby-lang.org/issues/11119)
16
+ #
17
+ # `name` isn't normally a hot path for types, but it is used in initializing a T::Types::Union,
18
+ # and so in `T.nilable`, and so in runtime constructions like `x = T.let(nil, T.nilable(Integer))`.
19
+ @name ||= @raw_type.name.freeze
16
20
  end
17
21
 
18
22
  # @override Base
@@ -29,5 +33,22 @@ module T::Types
29
33
  false
30
34
  end
31
35
  end
36
+
37
+ def to_nilable
38
+ @nilable ||= T::Types::Union.new([self, T::Utils::Nilable::NIL_TYPE])
39
+ end
40
+
41
+ module Private
42
+ module Pool
43
+ def self.type_for_module(mod)
44
+ cached = mod.instance_variable_get(:@__as_sorbet_simple_type)
45
+ return cached if cached
46
+
47
+ type = Simple.new(mod)
48
+ mod.instance_variable_set(:@__as_sorbet_simple_type, type) unless mod.frozen?
49
+ type
50
+ end
51
+ end
52
+ end
32
53
  end
33
54
  end
@@ -13,11 +13,16 @@ module T::Types
13
13
  end
14
14
 
15
15
  # @override Base
16
- def valid?(obj)
16
+ def recursively_valid?(obj)
17
17
  obj.is_a?(Array) && super
18
18
  end
19
19
 
20
- def new(*args) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
20
+ # @override Base
21
+ def valid?(obj)
22
+ obj.is_a?(Array)
23
+ end
24
+
25
+ def new(*args)
21
26
  Array.new(*T.unsafe(args))
22
27
  end
23
28
 
@@ -23,13 +23,18 @@ module T::Types
23
23
 
24
24
  # @override Base
25
25
  def valid?(obj)
26
+ obj.is_a?(Enumerable)
27
+ end
28
+
29
+ # @override Base
30
+ def recursively_valid?(obj)
26
31
  return false unless obj.is_a?(Enumerable)
27
32
  case obj
28
33
  when Array
29
34
  begin
30
35
  it = 0
31
36
  while it < obj.count
32
- return false unless @type.valid?(obj[it])
37
+ return false unless @type.recursively_valid?(obj[it])
33
38
  it += 1
34
39
  end
35
40
  return true
@@ -42,8 +47,8 @@ module T::Types
42
47
  value_type = types[1]
43
48
  obj.each_pair do |key, val|
44
49
  # Some objects (I'm looking at you Rack::Utils::HeaderHash) don't
45
- # iterate over a [key, value] array, so we can't juse use the @type.valid?(v)
46
- return false if !key_type.valid?(key) || !value_type.valid?(val)
50
+ # iterate over a [key, value] array, so we can't juse use the @type.recursively_valid?(v)
51
+ return false if !key_type.recursively_valid?(key) || !value_type.recursively_valid?(val)
47
52
  end
48
53
  return true
49
54
  when Enumerator
@@ -54,10 +59,10 @@ module T::Types
54
59
  # boundlessness, it does not express a type. For example `(nil...nil)` is not a T::Range[NilClass], its a range
55
60
  # of unknown types (T::Range[T.untyped]).
56
61
  # Similarly, `(nil...1)` is not a `T::Range[T.nilable(Integer)]`, it's a boundless range of Integer.
57
- (obj.begin.nil? || @type.valid?(obj.begin)) && (obj.end.nil? || @type.valid?(obj.end))
62
+ (obj.begin.nil? || @type.recursively_valid?(obj.begin)) && (obj.end.nil? || @type.recursively_valid?(obj.end))
58
63
  when Set
59
64
  obj.each do |item|
60
- return false unless @type.valid?(item)
65
+ return false unless @type.recursively_valid?(item)
61
66
  end
62
67
 
63
68
  return true
@@ -144,7 +149,8 @@ module T::Types
144
149
  # enumerating the object is a destructive operation and might hang.
145
150
  obj.class
146
151
  else
147
- self.class.new(type_from_instances(obj))
152
+ # This is a specialized enumerable type, just return the class.
153
+ Object.instance_method(:class).bind(obj).call
148
154
  end
149
155
  end
150
156
 
@@ -15,11 +15,16 @@ module T::Types
15
15
  end
16
16
 
17
17
  # @override Base
18
- def valid?(obj)
18
+ def recursively_valid?(obj)
19
19
  obj.is_a?(Enumerator) && super
20
20
  end
21
21
 
22
- def new(*args, &blk) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
22
+ # @override Base
23
+ def valid?(obj)
24
+ obj.is_a?(Enumerator)
25
+ end
26
+
27
+ def new(*args, &blk)
23
28
  T.unsafe(Enumerator).new(*args, &blk)
24
29
  end
25
30
 
@@ -22,12 +22,17 @@ module T::Types
22
22
  end
23
23
 
24
24
  # @override Base
25
- def valid?(obj)
25
+ def recursively_valid?(obj)
26
26
  obj.is_a?(Hash) && super
27
27
  end
28
28
 
29
- def new(*args, &blk) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
30
- Hash.new(*T.unsafe(args), &blk) # rubocop:disable PrisonGuard/RestrictHashDefaults
29
+ # @override Base
30
+ def valid?(obj)
31
+ obj.is_a?(Hash)
32
+ end
33
+
34
+ def new(*args, &blk)
35
+ Hash.new(*T.unsafe(args), &blk)
31
36
  end
32
37
 
33
38
  class Untyped < TypedHash
@@ -15,11 +15,16 @@ module T::Types
15
15
  end
16
16
 
17
17
  # @override Base
18
- def valid?(obj)
18
+ def recursively_valid?(obj)
19
19
  obj.is_a?(Range) && super
20
20
  end
21
21
 
22
- def new(*args) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
22
+ # @override Base
23
+ def valid?(obj)
24
+ obj.is_a?(Range)
25
+ end
26
+
27
+ def new(*args)
23
28
  T.unsafe(Range).new(*args)
24
29
  end
25
30
  end
@@ -15,11 +15,16 @@ module T::Types
15
15
  end
16
16
 
17
17
  # @override Base
18
- def valid?(obj)
18
+ def recursively_valid?(obj)
19
19
  obj.is_a?(Set) && super
20
20
  end
21
21
 
22
- def new(*args) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
22
+ # @override Base
23
+ def valid?(obj)
24
+ obj.is_a?(Set)
25
+ end
26
+
27
+ def new(*args)
23
28
  Set.new(*T.unsafe(args))
24
29
  end
25
30
 
@@ -43,12 +43,13 @@ module T::Types
43
43
  end
44
44
 
45
45
  # @override Base
46
- def valid?(obj)
47
- @types.each do |type|
48
- return true if type.valid?(obj)
49
- end
46
+ def recursively_valid?(obj)
47
+ @types.any? {|type| type.recursively_valid?(obj)}
48
+ end
50
49
 
51
- false
50
+ # @override Base
51
+ def valid?(obj)
52
+ @types.any? {|type| type.valid?(obj)}
52
53
  end
53
54
 
54
55
  # @override Base
@@ -56,5 +57,35 @@ module T::Types
56
57
  raise "This should never be reached if you're going through `subtype_of?` (and you should be)"
57
58
  end
58
59
 
60
+ module Private
61
+ module Pool
62
+ EMPTY_ARRAY = [].freeze
63
+ private_constant :EMPTY_ARRAY
64
+
65
+ # @param type_a [T::Types::Base]
66
+ # @param type_b [T::Types::Base]
67
+ # @param types [Array] optional array of additional T::Types::Base instances
68
+ def self.union_of_types(type_a, type_b, types=EMPTY_ARRAY)
69
+ if types.empty?
70
+ # We aren't guaranteed to detect a simple `T.nilable(<Module>)` type here
71
+ # in cases where there are duplicate types, nested unions, etc.
72
+ #
73
+ # That's ok, because this is an optimization which isn't necessary for
74
+ # correctness.
75
+ if type_b == T::Utils::Nilable::NIL_TYPE && type_a.is_a?(T::Types::Simple)
76
+ type_a.to_nilable
77
+ elsif type_a == T::Utils::Nilable::NIL_TYPE && type_b.is_a?(T::Types::Simple)
78
+ type_b.to_nilable
79
+ else
80
+ Union.new([type_a, type_b])
81
+ end
82
+ else
83
+ # This can't be a `T.nilable(<Module>)` case unless there are duplicates,
84
+ # which is possible but unexpected.
85
+ Union.new([type_a, type_b] + types)
86
+ end
87
+ end
88
+ end
89
+ end
59
90
  end
60
91
  end
@@ -21,5 +21,9 @@ module T::Types
21
21
  private def subtype_of_single?(other)
22
22
  true
23
23
  end
24
+
25
+ module Private
26
+ INSTANCE = Untyped.new.freeze
27
+ end
24
28
  end
25
29
  end
data/lib/types/utils.rb CHANGED
@@ -21,15 +21,15 @@ module T::Utils
21
21
  elsif val == ::Range
22
22
  T::Range[T.untyped]
23
23
  elsif val.is_a?(Module)
24
- T::Types::Simple.new(val) # rubocop:disable PrisonGuard/UseOpusTypesShortcut
24
+ T::Types::Simple::Private::Pool.type_for_module(val)
25
25
  elsif val.is_a?(::Array)
26
- T::Types::FixedArray.new(val) # rubocop:disable PrisonGuard/UseOpusTypesShortcut
26
+ T::Types::FixedArray.new(val)
27
27
  elsif val.is_a?(::Hash)
28
- T::Types::FixedHash.new(val) # rubocop:disable PrisonGuard/UseOpusTypesShortcut
28
+ T::Types::FixedHash.new(val)
29
29
  elsif val.is_a?(T::Private::Methods::DeclBuilder)
30
30
  T::Private::Methods.finalize_proc(val.decl)
31
31
  elsif val.is_a?(::T::Enum)
32
- T::Types::TEnum.new(val) # rubocop:disable PrisonGuard/UseOpusTypesShortcut
32
+ T::Types::TEnum.new(val)
33
33
  elsif val.is_a?(::String)
34
34
  raise "Invalid String literal for type constraint. Must be an #{T::Types::Base}, a " \
35
35
  "class/module, or an array. Got a String with value `#{val}`."
@@ -109,10 +109,10 @@ module T::Utils
109
109
 
110
110
  # Returns the arity of a method, unwrapping the sig if needed
111
111
  def self.arity(method)
112
- arity = method.arity # rubocop:disable PrisonGuard/NoArity
112
+ arity = method.arity
113
113
  return arity if arity != -1 || method.is_a?(Proc)
114
114
  sig = T::Private::Methods.signature_for_method(method)
115
- sig ? sig.method.arity : arity # rubocop:disable PrisonGuard/NoArity
115
+ sig ? sig.method.arity : arity
116
116
  end
117
117
 
118
118
  # Elide the middle of a string as needed and replace it with an ellipsis.
@@ -162,7 +162,7 @@ module T::Utils
162
162
  elsif classes.length > 1
163
163
  T::Types::Union.new(classes)
164
164
  else
165
- T::Types::Simple.new(classes.first)
165
+ T::Types::Simple::Private::Pool.type_for_module(classes.first)
166
166
  end
167
167
  end
168
168
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sorbet-runtime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.5794
4
+ version: 0.5.5942
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stripe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-01 00:00:00.000000000 Z
11
+ date: 2020-10-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.90.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.90.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-performance
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.8.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.8.0
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: concurrent-ruby
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -100,14 +128,28 @@ dependencies:
100
128
  requirements:
101
129
  - - "~>"
102
130
  - !ruby/object:Gem::Version
103
- version: '2.7'
131
+ version: 2.7.1
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 2.7.1
139
+ - !ruby/object:Gem::Dependency
140
+ name: subprocess
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 1.5.3
104
146
  type: :development
105
147
  prerelease: false
106
148
  version_requirements: !ruby/object:Gem::Requirement
107
149
  requirements:
108
150
  - - "~>"
109
151
  - !ruby/object:Gem::Version
110
- version: '2.7'
152
+ version: 1.5.3
111
153
  description: Sorbet's runtime type checking component
112
154
  email:
113
155
  executables: []