sorbet-runtime 0.5.10346 → 0.5.10597
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sorbet-runtime.rb +1 -0
- data/lib/types/_types.rb +32 -4
- data/lib/types/interface_wrapper.rb +4 -0
- data/lib/types/private/casts.rb +6 -3
- data/lib/types/private/methods/call_validation.rb +103 -4
- data/lib/types/private/types/simple_pair_union.rb +42 -0
- data/lib/types/props/decorator.rb +3 -1
- data/lib/types/props/has_lazily_specialized_methods.rb +1 -1
- data/lib/types/props/pretty_printable.rb +45 -83
- data/lib/types/props/serializable.rb +14 -7
- data/lib/types/types/base.rb +5 -1
- data/lib/types/types/enum.rb +1 -1
- data/lib/types/types/intersection.rb +1 -1
- data/lib/types/types/simple.rb +4 -1
- data/lib/types/types/union.rb +39 -14
- data/lib/types/utils.rb +34 -22
- metadata +9 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5569102ba150ab771b791b9265fa4e5472793cb90b4d6066b66fbbf180a6b591
|
4
|
+
data.tar.gz: 64e6645c8a45b5623763b43f5141febbbe5623c8de3868d4842086874984aad0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3db0e5871eab9001aaaa336cda62ae6d2f951168beb393e8f60b1868ebe4d901753ae191cabb54753b3f85d50e6f2fb85d861352537a286e257b37870e89bd2e
|
7
|
+
data.tar.gz: 70ed5dba4f054c4a68362df8b65060d623762530f18522696eca9ed17ba076cc7ea92c474350ee99acb53db4f856a53088c904ff757ea2e687d1910e82c0cd34
|
data/lib/sorbet-runtime.rb
CHANGED
@@ -55,6 +55,7 @@ require_relative 'types/private/types/not_typed'
|
|
55
55
|
require_relative 'types/private/types/void'
|
56
56
|
require_relative 'types/private/types/string_holder'
|
57
57
|
require_relative 'types/private/types/type_alias'
|
58
|
+
require_relative 'types/private/types/simple_pair_union'
|
58
59
|
|
59
60
|
require_relative 'types/types/type_variable'
|
60
61
|
require_relative 'types/types/type_member'
|
data/lib/types/_types.rb
CHANGED
@@ -130,7 +130,7 @@ module T
|
|
130
130
|
def self.cast(value, type, checked: true)
|
131
131
|
return value unless checked
|
132
132
|
|
133
|
-
Private::Casts.cast(value, type,
|
133
|
+
Private::Casts.cast(value, type, "T.cast")
|
134
134
|
end
|
135
135
|
|
136
136
|
# Tells the typechecker to declare a variable of type `type`. Use
|
@@ -145,7 +145,7 @@ module T
|
|
145
145
|
def self.let(value, type, checked: true)
|
146
146
|
return value unless checked
|
147
147
|
|
148
|
-
Private::Casts.cast(value, type,
|
148
|
+
Private::Casts.cast(value, type, "T.let")
|
149
149
|
end
|
150
150
|
|
151
151
|
# Tells the type checker to treat `self` in the current block as `type`.
|
@@ -164,7 +164,7 @@ module T
|
|
164
164
|
def self.bind(value, type, checked: true)
|
165
165
|
return value unless checked
|
166
166
|
|
167
|
-
Private::Casts.cast(value, type,
|
167
|
+
Private::Casts.cast(value, type, "T.bind")
|
168
168
|
end
|
169
169
|
|
170
170
|
# Tells the typechecker to ensure that `value` is of type `type` (if not, the typechecker will
|
@@ -174,7 +174,7 @@ module T
|
|
174
174
|
def self.assert_type!(value, type, checked: true)
|
175
175
|
return value unless checked
|
176
176
|
|
177
|
-
Private::Casts.cast(value, type,
|
177
|
+
Private::Casts.cast(value, type, "T.assert_type!")
|
178
178
|
end
|
179
179
|
|
180
180
|
# For the static type checker, strips all type information from a value
|
@@ -221,6 +221,34 @@ module T
|
|
221
221
|
end
|
222
222
|
end
|
223
223
|
|
224
|
+
# A convenience method to `raise` with a provided error reason when the argument
|
225
|
+
# is `nil` and return it otherwise.
|
226
|
+
#
|
227
|
+
# Intended to be used as:
|
228
|
+
#
|
229
|
+
# needs_foo(T.must_because(maybe_gives_foo) {"reason_foo_should_not_be_nil"})
|
230
|
+
#
|
231
|
+
# Equivalent to:
|
232
|
+
#
|
233
|
+
# foo = maybe_gives_foo
|
234
|
+
# raise "reason_foo_should_not_be_nil" if foo.nil?
|
235
|
+
# needs_foo(foo)
|
236
|
+
#
|
237
|
+
# Intended to be used to promise sorbet that a given nilable value happens
|
238
|
+
# to contain a non-nil value at this point.
|
239
|
+
#
|
240
|
+
# `sig {params(arg: T.nilable(A), reason_blk: T.proc.returns(String)).returns(A)}`
|
241
|
+
def self.must_because(arg)
|
242
|
+
return arg if arg
|
243
|
+
return arg if arg == false
|
244
|
+
|
245
|
+
begin
|
246
|
+
raise TypeError.new("Unexpected `nil` because #{yield}")
|
247
|
+
rescue TypeError => e # raise into rescue to ensure e.backtrace is populated
|
248
|
+
T::Configuration.inline_type_error_handler(e, {kind: 'T.must_because', value: arg, type: nil})
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
224
252
|
# A way to ask Sorbet to show what type it thinks an expression has.
|
225
253
|
# This can be useful for debugging and checking assumptions.
|
226
254
|
# In the runtime, merely returns the value passed in.
|
@@ -71,6 +71,10 @@ class T::InterfaceWrapper
|
|
71
71
|
target_obj.send(method_name, *args, &blk)
|
72
72
|
end
|
73
73
|
|
74
|
+
if singleton_class.respond_to?(:ruby2_keywords, true)
|
75
|
+
singleton_class.send(:ruby2_keywords, method_name)
|
76
|
+
end
|
77
|
+
|
74
78
|
if target_obj.singleton_class.public_method_defined?(method_name)
|
75
79
|
# no-op, it's already public
|
76
80
|
elsif target_obj.singleton_class.protected_method_defined?(method_name)
|
data/lib/types/private/casts.rb
CHANGED
@@ -3,9 +3,12 @@
|
|
3
3
|
|
4
4
|
module T::Private
|
5
5
|
module Casts
|
6
|
-
def self.cast(value, type, cast_method
|
6
|
+
def self.cast(value, type, cast_method)
|
7
7
|
begin
|
8
|
-
|
8
|
+
coerced_type = T::Utils::Private.coerce_and_check_module_types(type, value, true)
|
9
|
+
return value unless coerced_type
|
10
|
+
|
11
|
+
error = coerced_type.error_message_for_obj(value)
|
9
12
|
return value unless error
|
10
13
|
|
11
14
|
caller_loc = T.must(caller_locations(2..2)).first
|
@@ -22,7 +25,7 @@ module T::Private
|
|
22
25
|
# there's a lot of shared logic with the above one, but factoring
|
23
26
|
# it out like this makes it easier to hopefully one day delete
|
24
27
|
# this one
|
25
|
-
def self.cast_recursive(value, type, cast_method
|
28
|
+
def self.cast_recursive(value, type, cast_method)
|
26
29
|
begin
|
27
30
|
error = T::Utils.coerce(type).error_message_for_obj_recursive(value)
|
28
31
|
return value unless error
|
@@ -59,8 +59,9 @@ module T::Private::Methods::CallValidation
|
|
59
59
|
|
60
60
|
def self.create_validator_method(mod, original_method, method_sig, original_visibility)
|
61
61
|
has_fixed_arity = method_sig.kwarg_types.empty? && !method_sig.has_rest && !method_sig.has_keyrest &&
|
62
|
-
original_method.parameters.all? {|(kind, _name)| kind == :req}
|
63
|
-
|
62
|
+
original_method.parameters.all? {|(kind, _name)| kind == :req || kind == :block}
|
63
|
+
can_skip_block_type = method_sig.block_type.nil? || method_sig.block_type.valid?(nil)
|
64
|
+
ok_for_fast_path = has_fixed_arity && can_skip_block_type && !method_sig.bind && method_sig.arg_types.length < 5 && is_allowed_to_have_fast_path
|
64
65
|
|
65
66
|
all_args_are_simple = ok_for_fast_path && method_sig.arg_types.all? {|_name, type| type.is_a?(T::Types::Simple)}
|
66
67
|
simple_method = all_args_are_simple && method_sig.return_type.is_a?(T::Types::Simple)
|
@@ -76,6 +77,11 @@ module T::Private::Methods::CallValidation
|
|
76
77
|
create_validator_procedure_medium(mod, original_method, method_sig, original_visibility)
|
77
78
|
elsif ok_for_fast_path
|
78
79
|
create_validator_method_medium(mod, original_method, method_sig, original_visibility)
|
80
|
+
elsif can_skip_block_type
|
81
|
+
# The Ruby VM already validates that any block passed to a method
|
82
|
+
# must be either `nil` or a `Proc` object, so there's no need to also
|
83
|
+
# have sorbet-runtime check that.
|
84
|
+
create_validator_slow_skip_block_type(mod, original_method, method_sig, original_visibility)
|
79
85
|
else
|
80
86
|
create_validator_slow(mod, original_method, method_sig, original_visibility)
|
81
87
|
end
|
@@ -84,6 +90,88 @@ module T::Private::Methods::CallValidation
|
|
84
90
|
end
|
85
91
|
end
|
86
92
|
|
93
|
+
def self.create_validator_slow_skip_block_type(mod, original_method, method_sig, original_visibility)
|
94
|
+
T::Private::ClassUtils.def_with_visibility(mod, method_sig.method_name, original_visibility) do |*args, &blk|
|
95
|
+
CallValidation.validate_call_skip_block_type(self, original_method, method_sig, args, blk)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.validate_call_skip_block_type(instance, original_method, method_sig, args, blk)
|
100
|
+
# This method is called for every `sig`. It's critical to keep it fast and
|
101
|
+
# reduce number of allocations that happen here.
|
102
|
+
|
103
|
+
if method_sig.bind
|
104
|
+
message = method_sig.bind.error_message_for_obj(instance)
|
105
|
+
if message
|
106
|
+
CallValidation.report_error(
|
107
|
+
method_sig,
|
108
|
+
message,
|
109
|
+
'Bind',
|
110
|
+
nil,
|
111
|
+
method_sig.bind,
|
112
|
+
instance
|
113
|
+
)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# NOTE: We don't bother validating for missing or extra kwargs;
|
118
|
+
# the method call itself will take care of that.
|
119
|
+
method_sig.each_args_value_type(args) do |name, arg, type|
|
120
|
+
message = type.error_message_for_obj(arg)
|
121
|
+
if message
|
122
|
+
CallValidation.report_error(
|
123
|
+
method_sig,
|
124
|
+
message,
|
125
|
+
'Parameter',
|
126
|
+
name,
|
127
|
+
type,
|
128
|
+
arg,
|
129
|
+
caller_offset: 2
|
130
|
+
)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# The original method definition allows passing `nil` for the `&blk`
|
135
|
+
# argument, so we do not have to do any method_sig.block_type type checks
|
136
|
+
# of our own.
|
137
|
+
|
138
|
+
# The following line breaks are intentional to show nice pry message
|
139
|
+
|
140
|
+
|
141
|
+
|
142
|
+
|
143
|
+
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
# PRY note:
|
150
|
+
# this code is sig validation code.
|
151
|
+
# Please issue `finish` to step out of it
|
152
|
+
|
153
|
+
return_value = T::Configuration::AT_LEAST_RUBY_2_7 ? original_method.bind_call(instance, *args, &blk) : original_method.bind(instance).call(*args, &blk)
|
154
|
+
|
155
|
+
# The only type that is allowed to change the return value is `.void`.
|
156
|
+
# It ignores what you returned and changes it to be a private singleton.
|
157
|
+
if method_sig.return_type.is_a?(T::Private::Types::Void)
|
158
|
+
T::Private::Types::Void::VOID
|
159
|
+
else
|
160
|
+
message = method_sig.return_type.error_message_for_obj(return_value)
|
161
|
+
if message
|
162
|
+
CallValidation.report_error(
|
163
|
+
method_sig,
|
164
|
+
message,
|
165
|
+
'Return value',
|
166
|
+
nil,
|
167
|
+
method_sig.return_type,
|
168
|
+
return_value,
|
169
|
+
)
|
170
|
+
end
|
171
|
+
return_value
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
87
175
|
def self.create_validator_slow(mod, original_method, method_sig, original_visibility)
|
88
176
|
T::Private::ClassUtils.def_with_visibility(mod, method_sig.method_name, original_visibility) do |*args, &blk|
|
89
177
|
CallValidation.validate_call(self, original_method, method_sig, args, blk)
|
@@ -125,8 +213,19 @@ module T::Private::Methods::CallValidation
|
|
125
213
|
end
|
126
214
|
end
|
127
215
|
|
128
|
-
|
129
|
-
|
216
|
+
# The Ruby VM already checks that `&blk` is either a `Proc` type or `nil`:
|
217
|
+
# https://github.com/ruby/ruby/blob/v2_7_6/vm_args.c#L1150-L1154
|
218
|
+
# And `T.proc` types don't (can't) do any runtime arg checking, so we can
|
219
|
+
# save work by simply checking that `blk` is non-nil (if the method allows
|
220
|
+
# `nil` for the block, it would not have used this validate_call path).
|
221
|
+
unless blk
|
222
|
+
# Have to use `&.` here, because it's technically a public API that
|
223
|
+
# people can _always_ call `validate_call` to validate any signature
|
224
|
+
# (i.e., the faster validators are merely optimizations).
|
225
|
+
# In practice, this only affects the first call to the method (before the
|
226
|
+
# optimized validators have a chance to replace the initial, slow
|
227
|
+
# wrapper).
|
228
|
+
message = method_sig.block_type&.error_message_for_obj(blk)
|
130
229
|
if message
|
131
230
|
CallValidation.report_error(
|
132
231
|
method_sig,
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
# Specialization of Union for the common case of the union of two simple types.
|
5
|
+
#
|
6
|
+
# This covers e.g. T.nilable(SomeModule), T.any(Integer, Float), and T::Boolean.
|
7
|
+
class T::Private::Types::SimplePairUnion < T::Types::Union
|
8
|
+
class DuplicateType < RuntimeError; end
|
9
|
+
|
10
|
+
# @param type_a [T::Types::Simple]
|
11
|
+
# @param type_b [T::Types::Simple]
|
12
|
+
def initialize(type_a, type_b)
|
13
|
+
if type_a == type_b
|
14
|
+
raise DuplicateType.new("#{type_a} == #{type_b}")
|
15
|
+
end
|
16
|
+
|
17
|
+
@raw_a = type_a.raw_type
|
18
|
+
@raw_b = type_b.raw_type
|
19
|
+
end
|
20
|
+
|
21
|
+
# @override Union
|
22
|
+
def recursively_valid?(obj)
|
23
|
+
obj.is_a?(@raw_a) || obj.is_a?(@raw_b)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @override Union
|
27
|
+
def valid?(obj)
|
28
|
+
obj.is_a?(@raw_a) || obj.is_a?(@raw_b)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @override Union
|
32
|
+
def types
|
33
|
+
# We reconstruct the simple types rather than just storing them because
|
34
|
+
# (1) this is normally not a hot path and (2) we want to keep the instance
|
35
|
+
# variable count <= 3 so that we can fit in a 40 byte heap entry along
|
36
|
+
# with object headers.
|
37
|
+
@types ||= [
|
38
|
+
T::Types::Simple::Private::Pool.type_for_module(@raw_a),
|
39
|
+
T::Types::Simple::Private::Pool.type_for_module(@raw_b),
|
40
|
+
]
|
41
|
+
end
|
42
|
+
end
|
@@ -300,7 +300,9 @@ class T::Props::Decorator
|
|
300
300
|
.checked(:never)
|
301
301
|
end
|
302
302
|
private def prop_nilable?(cls, rules)
|
303
|
-
T::Utils::
|
303
|
+
# NB: `prop` and `const` do not `T::Utils::coerce the type of the prop if it is a `Module`,
|
304
|
+
# hence the bare `NilClass` check.
|
305
|
+
T::Utils::Nilable.is_union_with_nilclass(cls) || ((cls == T.untyped || cls == NilClass) && rules.key?(:default) && rules[:default].nil?)
|
304
306
|
end
|
305
307
|
|
306
308
|
# checked(:never) - Rules hash is expensive to check
|
@@ -83,7 +83,7 @@ module T::Props
|
|
83
83
|
lazily_defined_methods[name] = blk
|
84
84
|
|
85
85
|
cls = decorated_class
|
86
|
-
if cls.method_defined?(name)
|
86
|
+
if cls.method_defined?(name) || cls.private_method_defined?(name)
|
87
87
|
# Ruby does not emit "method redefined" warnings for aliased methods
|
88
88
|
# (more robust than undef_method that would create a small window in which the method doesn't exist)
|
89
89
|
cls.send(:alias_method, name, name)
|
@@ -1,18 +1,49 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
# typed: true
|
3
|
+
require 'pp'
|
3
4
|
|
4
5
|
module T::Props::PrettyPrintable
|
5
6
|
include T::Props::Plugin
|
6
7
|
|
7
|
-
#
|
8
|
+
# Override the PP gem with something that's similar, but gives us a hook to do redaction and customization
|
9
|
+
def pretty_print(pp)
|
10
|
+
clazz = T.unsafe(T.cast(self, Object).class).decorator
|
11
|
+
multiline = pp.is_a?(PP)
|
12
|
+
pp.group(1, "<#{clazz.inspect_class_with_decoration(self)}", ">") do
|
13
|
+
clazz.all_props.sort.each do |prop|
|
14
|
+
pp.breakable
|
15
|
+
val = clazz.get(self, prop)
|
16
|
+
rules = clazz.prop_rules(prop)
|
17
|
+
pp.text("#{prop}=")
|
18
|
+
if (custom_inspect = rules[:inspect])
|
19
|
+
inspected = if T::Utils.arity(custom_inspect) == 1
|
20
|
+
custom_inspect.call(val)
|
21
|
+
else
|
22
|
+
custom_inspect.call(val, {multiline: multiline})
|
23
|
+
end
|
24
|
+
pp.text(inspected.nil? ? "nil" : inspected)
|
25
|
+
elsif rules[:sensitivity] && !rules[:sensitivity].empty? && !val.nil?
|
26
|
+
pp.text("<REDACTED #{rules[:sensitivity].join(', ')}>")
|
27
|
+
else
|
28
|
+
val.pretty_print(pp)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
clazz.pretty_print_extra(self, pp)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return a string representation of this object and all of its props in a single line
|
8
36
|
def inspect
|
9
|
-
|
37
|
+
string = +""
|
38
|
+
PP.singleline_pp(self, string)
|
39
|
+
string
|
10
40
|
end
|
11
41
|
|
12
|
-
#
|
13
|
-
# to do redaction
|
42
|
+
# Return a pretty string representation of this object and all of its props
|
14
43
|
def pretty_inspect
|
15
|
-
|
44
|
+
string = +""
|
45
|
+
PP.pp(self, string)
|
46
|
+
string
|
16
47
|
end
|
17
48
|
|
18
49
|
module DecoratorMethods
|
@@ -23,85 +54,16 @@ module T::Props::PrettyPrintable
|
|
23
54
|
super || key == :inspect
|
24
55
|
end
|
25
56
|
|
26
|
-
|
27
|
-
|
28
|
-
|
57
|
+
# Overridable method to specify how the first part of a `pretty_print`d object's class should look like
|
58
|
+
# NOTE: This is just to support Stripe's `PrettyPrintableModel` case, and not recommended to be overriden
|
59
|
+
sig {params(instance: T::Props::PrettyPrintable).returns(String)}
|
60
|
+
def inspect_class_with_decoration(instance)
|
61
|
+
T.unsafe(instance).class.to_s
|
29
62
|
end
|
30
|
-
def inspect_instance(instance, multiline: false, indent: ' ')
|
31
|
-
components =
|
32
|
-
inspect_instance_components(
|
33
|
-
instance,
|
34
|
-
multiline: multiline,
|
35
|
-
indent: indent
|
36
|
-
)
|
37
|
-
.reject(&:empty?)
|
38
|
-
|
39
|
-
# Not using #<> here as that makes pry highlight these objects
|
40
|
-
# as if they were all comments, whereas this makes them look
|
41
|
-
# like the structured thing they are.
|
42
|
-
if multiline
|
43
|
-
"#{components[0]}:\n" + T.must(components[1..-1]).join("\n")
|
44
|
-
else
|
45
|
-
"<#{components.join(' ')}>"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
sig do
|
50
|
-
params(instance: T::Props::PrettyPrintable, multiline: T::Boolean, indent: String)
|
51
|
-
.returns(T::Array[String])
|
52
|
-
end
|
53
|
-
private def inspect_instance_components(instance, multiline:, indent:)
|
54
|
-
pretty_props = T.unsafe(self).all_props.map do |prop|
|
55
|
-
[prop, inspect_prop_value(instance, prop, multiline: multiline, indent: indent)]
|
56
|
-
end
|
57
|
-
|
58
|
-
joined_props = join_props_with_pretty_values(
|
59
|
-
pretty_props,
|
60
|
-
multiline: multiline,
|
61
|
-
indent: indent
|
62
|
-
)
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
69
|
-
|
70
|
-
sig do
|
71
|
-
params(instance: T::Props::PrettyPrintable, prop: Symbol, multiline: T::Boolean, indent: String)
|
72
|
-
.returns(String)
|
73
|
-
.checked(:never)
|
74
|
-
end
|
75
|
-
private def inspect_prop_value(instance, prop, multiline:, indent:)
|
76
|
-
val = T.unsafe(self).get(instance, prop)
|
77
|
-
rules = T.unsafe(self).prop_rules(prop)
|
78
|
-
if (custom_inspect = rules[:inspect])
|
79
|
-
if T::Utils.arity(custom_inspect) == 1
|
80
|
-
custom_inspect.call(val)
|
81
|
-
else
|
82
|
-
custom_inspect.call(val, {multiline: multiline, indent: indent})
|
83
|
-
end
|
84
|
-
elsif rules[:sensitivity] && !rules[:sensitivity].empty? && !val.nil?
|
85
|
-
"<REDACTED #{rules[:sensitivity].join(', ')}>"
|
86
|
-
else
|
87
|
-
val.inspect
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
sig do
|
92
|
-
params(pretty_kvs: T::Array[[Symbol, String]], multiline: T::Boolean, indent: String)
|
93
|
-
.returns(String)
|
94
|
-
end
|
95
|
-
private def join_props_with_pretty_values(pretty_kvs, multiline:, indent: ' ')
|
96
|
-
pairs = pretty_kvs
|
97
|
-
.sort_by {|k, _v| k.to_s}
|
98
|
-
.map {|k, v| "#{k}=#{v}"}
|
99
|
-
|
100
|
-
if multiline
|
101
|
-
indent + pairs.join("\n#{indent}")
|
102
|
-
else
|
103
|
-
pairs.join(', ')
|
104
|
-
end
|
105
|
-
end
|
64
|
+
# Overridable method to add anything that is not a prop
|
65
|
+
# NOTE: This is to support cases like Serializable's `@_extra_props`, and Stripe's `PrettyPrintableModel#@_deleted`
|
66
|
+
sig {params(instance: T::Props::PrettyPrintable, pp: T.any(PrettyPrint, PP::SingleLine)).void}
|
67
|
+
def pretty_print_extra(instance, pp); end
|
106
68
|
end
|
107
69
|
end
|
@@ -338,14 +338,21 @@ module T::Props::Serializable::DecoratorMethods
|
|
338
338
|
end
|
339
339
|
end
|
340
340
|
|
341
|
-
#
|
342
|
-
|
341
|
+
# adds to the default result of T::Props::PrettyPrintable
|
342
|
+
def pretty_print_extra(instance, pp)
|
343
|
+
# This is to maintain backwards compatibility with Stripe's codebase, where only the single line (through `inspect`)
|
344
|
+
# version is expected to add anything extra
|
345
|
+
return if !pp.is_a?(PP::SingleLine)
|
343
346
|
if (extra_props = extra_props(instance)) && !extra_props.empty?
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
347
|
+
pp.breakable
|
348
|
+
pp.text("@_extra_props=")
|
349
|
+
pp.group(1, "<", ">") do
|
350
|
+
extra_props.each_with_index do |(prop, value), i|
|
351
|
+
pp.breakable unless i.zero?
|
352
|
+
pp.text("#{prop}=")
|
353
|
+
value.pretty_print(pp)
|
354
|
+
end
|
355
|
+
end
|
349
356
|
end
|
350
357
|
end
|
351
358
|
end
|
data/lib/types/types/base.rb
CHANGED
@@ -163,8 +163,12 @@ module T::Types
|
|
163
163
|
# Type equivalence, defined by serializing the type to a string (with
|
164
164
|
# `#name`) and comparing the resulting strings for equality.
|
165
165
|
def ==(other)
|
166
|
-
|
166
|
+
case other
|
167
|
+
when T::Types::Base
|
167
168
|
other.name == self.name
|
169
|
+
else
|
170
|
+
false
|
171
|
+
end
|
168
172
|
end
|
169
173
|
|
170
174
|
alias_method :eql?, :==
|
data/lib/types/types/enum.rb
CHANGED
data/lib/types/types/simple.rb
CHANGED
data/lib/types/types/union.rb
CHANGED
@@ -6,6 +6,8 @@ module T::Types
|
|
6
6
|
class Union < Base
|
7
7
|
attr_reader :types
|
8
8
|
|
9
|
+
# Don't use Union.new directly, use `Private::Pool.union_of_types`
|
10
|
+
# inside sorbet-runtime and `T.any` elsewhere.
|
9
11
|
def initialize(types)
|
10
12
|
@types = types.flat_map do |type|
|
11
13
|
type = T::Utils.coerce(type)
|
@@ -20,11 +22,16 @@ module T::Types
|
|
20
22
|
|
21
23
|
# overrides Base
|
22
24
|
def name
|
23
|
-
|
25
|
+
# Use the attr_reader here so we can override it in SimplePairUnion
|
26
|
+
type_shortcuts(types)
|
24
27
|
end
|
25
28
|
|
26
29
|
private def type_shortcuts(types)
|
27
30
|
if types.size == 1
|
31
|
+
# We shouldn't generally get here but it's possible if initializing the type
|
32
|
+
# evades Sorbet's static check and we end up on the slow path, or if someone
|
33
|
+
# is using the T:Types::Union constructor directly (the latter possibility
|
34
|
+
# is why we don't just move the `uniq` into `Private::Pool.union_of_types`).
|
28
35
|
return types[0].name
|
29
36
|
end
|
30
37
|
nilable = T::Utils.coerce(NilClass)
|
@@ -62,27 +69,45 @@ module T::Types
|
|
62
69
|
EMPTY_ARRAY = [].freeze
|
63
70
|
private_constant :EMPTY_ARRAY
|
64
71
|
|
72
|
+
# Try to use `to_nilable` on a type to get memoization, or failing that
|
73
|
+
# try to at least use SimplePairUnion to get faster init and typechecking.
|
74
|
+
#
|
75
|
+
# We aren't guaranteed to detect a simple `T.nilable(<Module>)` type here
|
76
|
+
# in cases where there are duplicate types, nested unions, etc.
|
77
|
+
#
|
78
|
+
# That's ok, because returning is SimplePairUnion an optimization which
|
79
|
+
# isn't necessary for correctness.
|
80
|
+
#
|
65
81
|
# @param type_a [T::Types::Base]
|
66
82
|
# @param type_b [T::Types::Base]
|
67
83
|
# @param types [Array] optional array of additional T::Types::Base instances
|
68
84
|
def self.union_of_types(type_a, type_b, types=EMPTY_ARRAY)
|
69
|
-
if types.empty?
|
70
|
-
#
|
71
|
-
|
72
|
-
|
73
|
-
#
|
74
|
-
|
75
|
-
|
85
|
+
if !types.empty?
|
86
|
+
# Slow path
|
87
|
+
return Union.new([type_a, type_b] + types)
|
88
|
+
elsif !type_a.is_a?(T::Types::Simple) || !type_b.is_a?(T::Types::Simple)
|
89
|
+
# Slow path
|
90
|
+
return Union.new([type_a, type_b])
|
91
|
+
end
|
92
|
+
|
93
|
+
begin
|
94
|
+
if type_b == T::Utils::Nilable::NIL_TYPE
|
76
95
|
type_a.to_nilable
|
77
|
-
elsif type_a == T::Utils::Nilable::NIL_TYPE
|
96
|
+
elsif type_a == T::Utils::Nilable::NIL_TYPE
|
78
97
|
type_b.to_nilable
|
79
98
|
else
|
80
|
-
|
99
|
+
T::Private::Types::SimplePairUnion.new(type_a, type_b)
|
81
100
|
end
|
82
|
-
|
83
|
-
#
|
84
|
-
#
|
85
|
-
|
101
|
+
rescue T::Private::Types::SimplePairUnion::DuplicateType
|
102
|
+
# Slow path
|
103
|
+
#
|
104
|
+
# This shouldn't normally be possible due to static checks,
|
105
|
+
# but we can get here if we're constructing a type dynamically.
|
106
|
+
#
|
107
|
+
# Relying on the duplicate check in the constructor has the
|
108
|
+
# advantage that we avoid it when we hit the memoized case
|
109
|
+
# of `to_nilable`.
|
110
|
+
type_a
|
86
111
|
end
|
87
112
|
end
|
88
113
|
end
|
data/lib/types/utils.rb
CHANGED
@@ -2,29 +2,41 @@
|
|
2
2
|
# typed: true
|
3
3
|
|
4
4
|
module T::Utils
|
5
|
+
module Private
|
6
|
+
def self.coerce_and_check_module_types(val, check_val, check_module_type)
|
7
|
+
if val.is_a?(T::Types::Base)
|
8
|
+
if val.is_a?(T::Private::Types::TypeAlias)
|
9
|
+
val.aliased_type
|
10
|
+
else
|
11
|
+
val
|
12
|
+
end
|
13
|
+
elsif val.is_a?(Module)
|
14
|
+
if check_module_type && check_val.is_a?(val)
|
15
|
+
nil
|
16
|
+
else
|
17
|
+
T::Types::Simple::Private::Pool.type_for_module(val)
|
18
|
+
end
|
19
|
+
elsif val.is_a?(::Array)
|
20
|
+
T::Types::FixedArray.new(val)
|
21
|
+
elsif val.is_a?(::Hash)
|
22
|
+
T::Types::FixedHash.new(val)
|
23
|
+
elsif val.is_a?(T::Private::Methods::DeclBuilder)
|
24
|
+
T::Private::Methods.finalize_proc(val.decl)
|
25
|
+
elsif val.is_a?(::T::Enum)
|
26
|
+
T::Types::TEnum.new(val)
|
27
|
+
elsif val.is_a?(::String)
|
28
|
+
raise "Invalid String literal for type constraint. Must be an #{T::Types::Base}, a " \
|
29
|
+
"class/module, or an array. Got a String with value `#{val}`."
|
30
|
+
else
|
31
|
+
raise "Invalid value for type constraint. Must be an #{T::Types::Base}, a " \
|
32
|
+
"class/module, or an array. Got a `#{val.class}`."
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
5
37
|
# Used to convert from a type specification to a `T::Types::Base`.
|
6
38
|
def self.coerce(val)
|
7
|
-
|
8
|
-
val.aliased_type
|
9
|
-
elsif val.is_a?(T::Types::Base)
|
10
|
-
val
|
11
|
-
elsif val.is_a?(Module)
|
12
|
-
T::Types::Simple::Private::Pool.type_for_module(val)
|
13
|
-
elsif val.is_a?(::Array)
|
14
|
-
T::Types::FixedArray.new(val)
|
15
|
-
elsif val.is_a?(::Hash)
|
16
|
-
T::Types::FixedHash.new(val)
|
17
|
-
elsif val.is_a?(T::Private::Methods::DeclBuilder)
|
18
|
-
T::Private::Methods.finalize_proc(val.decl)
|
19
|
-
elsif val.is_a?(::T::Enum)
|
20
|
-
T::Types::TEnum.new(val)
|
21
|
-
elsif val.is_a?(::String)
|
22
|
-
raise "Invalid String literal for type constraint. Must be an #{T::Types::Base}, a " \
|
23
|
-
"class/module, or an array. Got a String with value `#{val}`."
|
24
|
-
else
|
25
|
-
raise "Invalid value for type constraint. Must be an #{T::Types::Base}, a " \
|
26
|
-
"class/module, or an array. Got a `#{val.class}`."
|
27
|
-
end
|
39
|
+
Private.coerce_and_check_module_types(val, nil, false)
|
28
40
|
end
|
29
41
|
|
30
42
|
# Dynamically confirm that `value` is recursively a valid value of
|
@@ -32,7 +44,7 @@ module T::Utils
|
|
32
44
|
# in some cases this runtime check can be very expensive, especially
|
33
45
|
# with large collections of objects.
|
34
46
|
def self.check_type_recursive!(value, type)
|
35
|
-
T::Private::Casts.cast_recursive(value, type,
|
47
|
+
T::Private::Casts.cast_recursive(value, type, "T.check_type_recursive!")
|
36
48
|
end
|
37
49
|
|
38
50
|
# Returns the set of all methods (public, protected, private) defined on a module or its
|
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.10597
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stripe
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -151,7 +151,7 @@ dependencies:
|
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: 1.5.3
|
153
153
|
description: Sorbet's runtime type checking component
|
154
|
-
email:
|
154
|
+
email:
|
155
155
|
executables: []
|
156
156
|
extensions: []
|
157
157
|
extra_rdoc_files: []
|
@@ -189,6 +189,7 @@ files:
|
|
189
189
|
- lib/types/private/runtime_levels.rb
|
190
190
|
- lib/types/private/sealed.rb
|
191
191
|
- lib/types/private/types/not_typed.rb
|
192
|
+
- lib/types/private/types/simple_pair_union.rb
|
192
193
|
- lib/types/private/types/string_holder.rb
|
193
194
|
- lib/types/private/types/type_alias.rb
|
194
195
|
- lib/types/private/types/void.rb
|
@@ -245,7 +246,7 @@ licenses:
|
|
245
246
|
- Apache-2.0
|
246
247
|
metadata:
|
247
248
|
source_code_uri: https://github.com/sorbet/sorbet
|
248
|
-
post_install_message:
|
249
|
+
post_install_message:
|
249
250
|
rdoc_options: []
|
250
251
|
require_paths:
|
251
252
|
- lib
|
@@ -253,15 +254,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
253
254
|
requirements:
|
254
255
|
- - ">="
|
255
256
|
- !ruby/object:Gem::Version
|
256
|
-
version: 2.
|
257
|
+
version: 2.7.0
|
257
258
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
258
259
|
requirements:
|
259
260
|
- - ">="
|
260
261
|
- !ruby/object:Gem::Version
|
261
262
|
version: '0'
|
262
263
|
requirements: []
|
263
|
-
rubygems_version: 3.
|
264
|
-
signing_key:
|
264
|
+
rubygems_version: 3.3.7
|
265
|
+
signing_key:
|
265
266
|
specification_version: 4
|
266
267
|
summary: Sorbet runtime
|
267
268
|
test_files: []
|