sorbet-runtime 0.5.5835 → 0.5.5848

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c78c486ad11e0c1425ad311f09498c7932e29d5dc95ecd3b39d497706d918ba
4
- data.tar.gz: c465da35e7c4a3327b4f8b2b29854f45aecfb2dfabc7863245cd82028371101a
3
+ metadata.gz: 4e36df8a78c0d43e49da28e22b8b3a8db6094d50227f3be131ba05133b09cfd7
4
+ data.tar.gz: 1bfe28320c26f5749aff854bfbf9616d540a5f4d458dd35bc0e1add778a7e653
5
5
  SHA512:
6
- metadata.gz: f92f74be1bb881101fab6440467307a370d063d458da5014ebfed3f768cfac7c829bcbb4200492d48d49d0d1c03d4652c4cfc9fb67edd64fc54643972a0ff332
7
- data.tar.gz: 0ff90148441a61f9cf1911fac9c6e91e065b927bb3d68a423c0371f765e11c00e4bbe3eef1b639c443603ea62a0a79ee6807ce5d54a1ab35d5bda38c23675b66
6
+ metadata.gz: a8d0946d74e2a13eb6ffad4eb44633161da79d42b1a0e39a099a126f081e3346e6b7eecba85c50dfd333b890530a6d75bc85e7676a889fe9a1d0043198e502f6
7
+ data.tar.gz: 8e44416ca05dd56362fc4a36457885713dc02ac4b9367cddabfefd33f80c0ec087befd957cc2c5dd4a66361313c82a87dc4e86157415c50e1ede2851fceb2d91
@@ -26,12 +26,15 @@
26
26
  module T
27
27
  # T.any(<Type>, <Type>, ...) -- matches any of the types listed
28
28
  def self.any(type_a, type_b, *types)
29
- T::Types::Union.new([type_a, type_b] + types)
29
+ type_a = T::Utils.coerce(type_a)
30
+ type_b = T::Utils.coerce(type_b)
31
+ types = types.map {|t| T::Utils.coerce(t)} if !types.empty?
32
+ T::Types::Union::Private::Pool.union_of_types(type_a, type_b, types)
30
33
  end
31
34
 
32
35
  # Shorthand for T.any(type, NilClass)
33
36
  def self.nilable(type)
34
- T::Types::Union.new([type, NilClass])
37
+ T::Types::Union::Private::Pool.union_of_types(T::Utils.coerce(type), T::Utils::Nilable::NIL_TYPE)
35
38
  end
36
39
 
37
40
  # Matches any object. In the static checker, T.untyped allows any
@@ -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
@@ -206,22 +206,8 @@ 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(mod, original_method, __callee__)
225
211
 
226
212
  # Should be the same logic as CallValidation.wrap_method_if_needed but we
227
213
  # don't want that extra layer of indirection in the callstack
@@ -252,6 +238,31 @@ module T::Private::Methods
252
238
  end
253
239
  end
254
240
 
241
+ def self._handle_missing_method_signature(mod, original_method, callee)
242
+ method_sig = T::Private::Methods.signature_for_method(original_method)
243
+ aliasing_method = mod.instance_method(callee)
244
+
245
+ if method_sig && aliasing_method != original_method && aliasing_method.original_name == original_method.name
246
+ # We're handling a case where `alias` or `alias_method` was called for a
247
+ # method which had already had a `sig` applied.
248
+ #
249
+ # Note, this logic is duplicated above, make sure to keep changes in sync.
250
+ if method_sig.check_level == :always || (method_sig.check_level == :tests && T::Private::RuntimeLevels.check_tests?)
251
+ # Checked, so copy the original signature to the aliased copy.
252
+ T::Private::Methods.unwrap_method(mod, method_sig, aliasing_method)
253
+ else
254
+ # Unchecked, so just make `alias_method` behave as if it had been called pre-sig.
255
+ mod.send(:alias_method, callee, original_method.name)
256
+ end
257
+ else
258
+ raise "`sig` not present for method `#{aliasing_method.name}` but you're trying to run it anyways. " \
259
+ "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. " \
260
+ "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."
261
+ end
262
+
263
+ method_sig
264
+ end
265
+
255
266
  # Executes the `sig` block, and converts the resulting Declaration
256
267
  # to a Signature.
257
268
  def self.run_sig(hook_mod, method_name, original_method, declaration_block)
@@ -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)
@@ -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
@@ -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
@@ -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
@@ -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)}
@@ -29,5 +29,22 @@ module T::Types
29
29
  false
30
30
  end
31
31
  end
32
+
33
+ def to_nilable
34
+ @nilable ||= T::Types::Union.new([self, T::Utils::Nilable::NIL_TYPE])
35
+ end
36
+
37
+ module Private
38
+ module Pool
39
+ def self.type_for_module(mod)
40
+ cached = mod.instance_variable_get(:@__as_sorbet_simple_type)
41
+ return cached if cached
42
+
43
+ type = Simple.new(mod)
44
+ mod.instance_variable_set(:@__as_sorbet_simple_type, type) unless mod.frozen?
45
+ type
46
+ end
47
+ end
48
+ end
32
49
  end
33
50
  end
@@ -13,10 +13,15 @@ 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
+ # @override Base
21
+ def valid?(obj)
22
+ obj.is_a?(Array)
23
+ end
24
+
20
25
  def new(*args) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
21
26
  Array.new(*T.unsafe(args))
22
27
  end
@@ -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
@@ -15,10 +15,15 @@ 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
+ # @override Base
23
+ def valid?(obj)
24
+ obj.is_a?(Enumerator)
25
+ end
26
+
22
27
  def new(*args, &blk) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
23
28
  T.unsafe(Enumerator).new(*args, &blk)
24
29
  end
@@ -22,10 +22,15 @@ 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
+ # @override Base
30
+ def valid?(obj)
31
+ obj.is_a?(Hash)
32
+ end
33
+
29
34
  def new(*args, &blk) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
30
35
  Hash.new(*T.unsafe(args), &blk) # rubocop:disable PrisonGuard/RestrictHashDefaults
31
36
  end
@@ -15,10 +15,15 @@ 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
+ # @override Base
23
+ def valid?(obj)
24
+ obj.is_a?(Range)
25
+ end
26
+
22
27
  def new(*args) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
23
28
  T.unsafe(Range).new(*args)
24
29
  end
@@ -15,10 +15,15 @@ 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
+ # @override Base
23
+ def valid?(obj)
24
+ obj.is_a?(Set)
25
+ end
26
+
22
27
  def new(*args) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
23
28
  Set.new(*T.unsafe(args))
24
29
  end
@@ -42,6 +42,11 @@ module T::Types
42
42
  end
43
43
  end
44
44
 
45
+ # @override Base
46
+ def recursively_valid?(obj)
47
+ @types.any? {|type| type.recursively_valid?(obj)}
48
+ end
49
+
45
50
  # @override Base
46
51
  def valid?(obj)
47
52
  @types.any? {|type| type.valid?(obj)}
@@ -52,5 +57,35 @@ module T::Types
52
57
  raise "This should never be reached if you're going through `subtype_of?` (and you should be)"
53
58
  end
54
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
55
90
  end
56
91
  end
@@ -21,7 +21,7 @@ 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
26
  T::Types::FixedArray.new(val) # rubocop:disable PrisonGuard/UseOpusTypesShortcut
27
27
  elsif val.is_a?(::Hash)
@@ -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.5835
4
+ version: 0.5.5848
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-21 00:00:00.000000000 Z
11
+ date: 2020-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest