sorbet-runtime 0.5.5843 → 0.5.5855

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: 4aa66cf13c6570f24fd5c7d394119fd49ce3bb595f85cc2cf2584232d3cb9ff4
4
- data.tar.gz: b223ba693cdb5e2795f3adbfab10a7b1fc127457821011b26344510ada3495d2
3
+ metadata.gz: 05211daadd19374c8196176c89361938ef05fd1c5fa4c02f9e9d581bcf4114bf
4
+ data.tar.gz: 33f7feb752c822c281276f990b99c3ceeab66a4dd63359c00cfad66643680231
5
5
  SHA512:
6
- metadata.gz: a58a518764c9fb4309a14f9174cf17080e4e5fca191d189c5562a3aad610f0c153e0a334b9ce6ce1aa78cd7906f52336a8795a1a3f1e513ce7c972a934605b33
7
- data.tar.gz: 6f638789b8c7cdece2c89b82d7f49687dcc7183b04b5a3be85e655a809b5f97f2a7f8969a34ddf12016d52de5444d882cf50a7dca61df728e32ed8b1855391d1
6
+ metadata.gz: 908de44e4d49035037a83f508074c273af88c8ff465147e569ecb062e6a19c818bdd0e8812830f5ce23b582fec0010e04dd9191e24095b024e04bcb07ed9ef69
7
+ data.tar.gz: 8a77c89234429113ac224c877a02a0fefa81fc4cc066e1c2a3b0e1b7b94129372396523e260e5280a6487f7cc41b8de2cc0efb1af63214108b664407657fa97b
@@ -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,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
+ is_singleton_method ? self.singleton_class : self.class,
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,33 @@ module T::Private::Methods
252
242
  end
253
243
  end
254
244
 
245
+ def self._handle_missing_method_signature(klass, original_method, callee)
246
+ method_sig = T::Private::Methods.signature_for_method(original_method)
247
+
248
+ aliasing_method = klass.instance_method(callee)
249
+ aliasing_mod = aliasing_method.owner
250
+
251
+ if method_sig && aliasing_method != original_method && aliasing_method.original_name == original_method.name
252
+ # We're handling a case where `alias` or `alias_method` was called for a
253
+ # method which had already had a `sig` applied.
254
+ #
255
+ # Note, this logic is duplicated above, make sure to keep changes in sync.
256
+ if method_sig.check_level == :always || (method_sig.check_level == :tests && T::Private::RuntimeLevels.check_tests?)
257
+ # Checked, so copy the original signature to the aliased copy.
258
+ T::Private::Methods.unwrap_method(aliasing_mod, method_sig, aliasing_method)
259
+ else
260
+ # Unchecked, so just make `alias_method` behave as if it had been called pre-sig.
261
+ aliasing_mod.send(:alias_method, callee, original_method.name)
262
+ end
263
+ else
264
+ raise "`sig` not present for method `#{aliasing_method.name}` but you're trying to run it anyways. " \
265
+ "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. " \
266
+ "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."
267
+ end
268
+
269
+ method_sig
270
+ end
271
+
255
272
  # Executes the `sig` block, and converts the resulting Declaration
256
273
  # to a Signature.
257
274
  def self.run_sig(hook_mod, method_name, original_method, declaration_block)
@@ -146,9 +146,10 @@ class T::Private::Methods::Signature
146
146
  # can't) match the definition of the method we're validating. In addition, Ruby has a bug that
147
147
  # causes forwarding **kwargs to do the wrong thing: see https://bugs.ruby-lang.org/issues/10708
148
148
  # 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)
149
+ args_length = args.length
150
+ if (args_length > @req_arg_count) && (!@kwarg_types.empty? || @has_keyrest) && args[-1].is_a?(Hash)
150
151
  kwargs = args[-1]
151
- args = args[0...-1]
152
+ args_length -= 1
152
153
  else
153
154
  kwargs = EMPTY_HASH
154
155
  end
@@ -156,19 +157,19 @@ class T::Private::Methods::Signature
156
157
  arg_types = @arg_types
157
158
 
158
159
  if @has_rest
159
- arg_types += [[@rest_name, @rest_type]] * (args.length - @arg_types.length)
160
+ arg_types += [[@rest_name, @rest_type]] * (args_length - @arg_types.length)
160
161
 
161
- elsif (args.length < @req_arg_count) || (args.length > @arg_types.length)
162
+ elsif (args_length < @req_arg_count) || (args_length > @arg_types.length)
162
163
  expected_str = @req_arg_count.to_s
163
164
  if @arg_types.length != @req_arg_count
164
165
  expected_str += "..#{@arg_types.length}"
165
166
  end
166
- raise ArgumentError.new("wrong number of arguments (given #{args.length}, expected #{expected_str})")
167
+ raise ArgumentError.new("wrong number of arguments (given #{args_length}, expected #{expected_str})")
167
168
  end
168
169
 
169
170
  begin
170
171
  it = 0
171
- while it < args.length
172
+ while it < args_length
172
173
  yield arg_types[it][0], args[it], arg_types[it][1]
173
174
  it += 1
174
175
  end
@@ -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)
@@ -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
@@ -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,6 +17,22 @@ 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
38
  if obj.is_a?(Array) && obj.length == @types.length
@@ -16,6 +16,14 @@ module T::Types
16
16
  "{#{@types.map {|(k, v)| "#{k}: #{v}"}.join(', ')}}"
17
17
  end
18
18
 
19
+ # @override Base
20
+ def recursively_valid?(obj)
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
26
+
19
27
  # @override Base
20
28
  def valid?(obj)
21
29
  return false unless obj.is_a?(Hash)
@@ -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)}
@@ -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,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.5843
4
+ version: 0.5.5855
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-23 00:00:00.000000000 Z
11
+ date: 2020-07-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest