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.
- checksums.yaml +4 -4
- data/lib/types/_types.rb +9 -8
- data/lib/types/configuration.rb +2 -2
- data/lib/types/enum.rb +12 -1
- data/lib/types/generic.rb +2 -2
- data/lib/types/interface_wrapper.rb +4 -4
- data/lib/types/non_forcing_constants.rb +9 -3
- data/lib/types/private/abstract/data.rb +2 -2
- data/lib/types/private/casts.rb +27 -0
- data/lib/types/private/class_utils.rb +5 -5
- data/lib/types/private/methods/_methods.rb +73 -23
- data/lib/types/private/methods/signature.rb +16 -6
- data/lib/types/private/sealed.rb +1 -1
- data/lib/types/private/types/type_alias.rb +5 -0
- data/lib/types/profile.rb +5 -1
- data/lib/types/props/constructor.rb +9 -3
- data/lib/types/props/decorator.rb +1 -1
- data/lib/types/props/generated_code_validation.rb +2 -2
- data/lib/types/props/plugin.rb +2 -2
- data/lib/types/props/private/setter_factory.rb +12 -2
- data/lib/types/props/serializable.rb +2 -2
- data/lib/types/props/weak_constructor.rb +14 -9
- data/lib/types/sig.rb +3 -3
- data/lib/types/types/attached_class.rb +5 -1
- data/lib/types/types/base.rb +13 -0
- data/lib/types/types/fixed_array.rb +28 -2
- data/lib/types/types/fixed_hash.rb +10 -9
- data/lib/types/types/intersection.rb +5 -0
- data/lib/types/types/noreturn.rb +4 -0
- data/lib/types/types/self_type.rb +4 -0
- data/lib/types/types/simple.rb +22 -1
- data/lib/types/types/typed_array.rb +7 -2
- data/lib/types/types/typed_enumerable.rb +12 -6
- data/lib/types/types/typed_enumerator.rb +7 -2
- data/lib/types/types/typed_hash.rb +8 -3
- data/lib/types/types/typed_range.rb +7 -2
- data/lib/types/types/typed_set.rb +7 -2
- data/lib/types/types/union.rb +36 -5
- data/lib/types/types/untyped.rb +4 -0
- data/lib/types/utils.rb +7 -7
- metadata +46 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4838c21772de1169f412a971cfe59e49fc482bda8de9fb70920fd4bcab5e654f
|
|
4
|
+
data.tar.gz: 5e27052a663a22e70e5aa3afee7563f43be650946ec4673df2fb246535a28bb6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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::
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
data/lib/types/configuration.rb
CHANGED
|
@@ -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}"
|
|
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}"
|
|
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)
|
|
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)
|
|
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)
|
|
86
|
+
def kind_of?(other)
|
|
87
87
|
is_a?(other)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
def is_a?(other)
|
|
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}")
|
|
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)
|
|
19
|
+
mod.instance_variable_set("@opus_abstract__#{key}", value)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def self.key?(mod, key)
|
data/lib/types/private/casts.rb
CHANGED
|
@@ -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)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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(
|
|
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
|
|
288
|
-
if hook_mod != 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(
|
|
326
|
-
maybe_wrapped_method = CallValidation.wrap_method_if_needed(
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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]] * (
|
|
169
|
+
arg_types += [[@rest_name, @rest_type]] * (args_length - @arg_types.length)
|
|
160
170
|
|
|
161
|
-
elsif (
|
|
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 #{
|
|
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 <
|
|
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
|
data/lib/types/private/sealed.rb
CHANGED
|
@@ -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(':')
|
|
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
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
data/lib/types/props/plugin.rb
CHANGED
|
@@ -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)
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
125
|
-
new_extra = new_val.instance_variable_get(:@_extra_props)
|
|
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
|
-
|
|
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
|
-
|
|
37
|
-
else
|
|
38
|
-
false
|
|
39
|
+
result += 1
|
|
39
40
|
end
|
|
40
|
-
end
|
|
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
|
-
|
|
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
|
-
|
|
60
|
+
result += 1
|
|
56
61
|
else
|
|
57
62
|
default_struct.set_default(instance)
|
|
58
|
-
false
|
|
59
63
|
end
|
|
60
|
-
end
|
|
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
|
|
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
|
|
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)
|
|
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
|
-
"
|
|
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
|
data/lib/types/types/base.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
obj.
|
|
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)}
|
data/lib/types/types/noreturn.rb
CHANGED
data/lib/types/types/simple.rb
CHANGED
|
@@ -12,7 +12,11 @@ module T::Types
|
|
|
12
12
|
|
|
13
13
|
# @override Base
|
|
14
14
|
def name
|
|
15
|
-
|
|
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
|
|
16
|
+
def recursively_valid?(obj)
|
|
17
17
|
obj.is_a?(Array) && super
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
|
|
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.
|
|
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.
|
|
46
|
-
return false if !key_type.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
18
|
+
def recursively_valid?(obj)
|
|
19
19
|
obj.is_a?(Enumerator) && super
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
|
|
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
|
|
25
|
+
def recursively_valid?(obj)
|
|
26
26
|
obj.is_a?(Hash) && super
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
18
|
+
def recursively_valid?(obj)
|
|
19
19
|
obj.is_a?(Range) && super
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
|
|
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
|
|
18
|
+
def recursively_valid?(obj)
|
|
19
19
|
obj.is_a?(Set) && super
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
|
|
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
|
|
data/lib/types/types/union.rb
CHANGED
|
@@ -43,12 +43,13 @@ module T::Types
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
# @override Base
|
|
46
|
-
def
|
|
47
|
-
@types.
|
|
48
|
-
|
|
49
|
-
end
|
|
46
|
+
def recursively_valid?(obj)
|
|
47
|
+
@types.any? {|type| type.recursively_valid?(obj)}
|
|
48
|
+
end
|
|
50
49
|
|
|
51
|
-
|
|
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
|
data/lib/types/types/untyped.rb
CHANGED
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.
|
|
24
|
+
T::Types::Simple::Private::Pool.type_for_module(val)
|
|
25
25
|
elsif val.is_a?(::Array)
|
|
26
|
-
T::Types::FixedArray.new(val)
|
|
26
|
+
T::Types::FixedArray.new(val)
|
|
27
27
|
elsif val.is_a?(::Hash)
|
|
28
|
-
T::Types::FixedHash.new(val)
|
|
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)
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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-
|
|
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:
|
|
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:
|
|
152
|
+
version: 1.5.3
|
|
111
153
|
description: Sorbet's runtime type checking component
|
|
112
154
|
email:
|
|
113
155
|
executables: []
|