sorbet-runtime 0.5.10782 → 0.5.10993
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 +13 -1
- data/lib/types/enum.rb +1 -1
- data/lib/types/private/abstract/declare.rb +10 -9
- data/lib/types/private/class_utils.rb +13 -6
- data/lib/types/private/methods/_methods.rb +27 -9
- data/lib/types/private/methods/call_validation.rb +1 -1
- data/lib/types/private/methods/call_validation_2_6.rb +68 -60
- data/lib/types/private/methods/call_validation_2_7.rb +68 -60
- data/lib/types/private/methods/decl_builder.rb +4 -2
- data/lib/types/private/methods/signature.rb +63 -38
- data/lib/types/private/methods/signature_validation.rb +55 -6
- data/lib/types/private/types/not_typed.rb +2 -0
- data/lib/types/props/decorator.rb +38 -35
- data/lib/types/props/has_lazily_specialized_methods.rb +1 -1
- data/lib/types/props/private/setter_factory.rb +1 -1
- data/lib/types/props/type_validation.rb +5 -2
- data/lib/types/struct.rb +2 -2
- data/lib/types/types/class_of.rb +11 -0
- data/lib/types/types/simple.rb +12 -1
- data/lib/types/types/type_parameter.rb +19 -0
- data/lib/types/types/typed_class.rb +85 -0
- data/lib/types/types/typed_enumerable.rb +2 -0
- metadata +5 -4
@@ -8,17 +8,20 @@ class T::Private::Methods::Signature
|
|
8
8
|
:check_level, :parameters, :on_failure, :override_allow_incompatible,
|
9
9
|
:defined_raw
|
10
10
|
|
11
|
+
UNNAMED_REQUIRED_PARAMETERS = [[:req]].freeze
|
12
|
+
|
11
13
|
def self.new_untyped(method:, mode: T::Private::Methods::Modes.untyped, parameters: method.parameters)
|
12
|
-
# Using `
|
13
|
-
not_typed = T::Private::Types::NotTyped
|
14
|
+
# Using `NotTyped` ensures we'll get an error if we ever try validation on these.
|
15
|
+
not_typed = T::Private::Types::NotTyped::INSTANCE
|
14
16
|
raw_return_type = not_typed
|
15
17
|
# Map missing parameter names to "argN" positionally
|
16
18
|
parameters = parameters.each_with_index.map do |(param_kind, param_name), index|
|
17
19
|
[param_kind, param_name || "arg#{index}"]
|
18
20
|
end
|
19
|
-
raw_arg_types =
|
20
|
-
|
21
|
-
|
21
|
+
raw_arg_types = {}
|
22
|
+
parameters.each do |_, param_name|
|
23
|
+
raw_arg_types[param_name] = not_typed
|
24
|
+
end
|
22
25
|
|
23
26
|
self.new(
|
24
27
|
method: method,
|
@@ -36,8 +39,6 @@ class T::Private::Methods::Signature
|
|
36
39
|
def initialize(method:, method_name:, raw_arg_types:, raw_return_type:, bind:, mode:, check_level:, on_failure:, parameters: method.parameters, override_allow_incompatible: false, defined_raw: false)
|
37
40
|
@method = method
|
38
41
|
@method_name = method_name
|
39
|
-
@arg_types = []
|
40
|
-
@kwarg_types = {}
|
41
42
|
@block_type = nil
|
42
43
|
@block_name = nil
|
43
44
|
@rest_type = nil
|
@@ -48,8 +49,6 @@ class T::Private::Methods::Signature
|
|
48
49
|
@bind = bind ? T::Utils.coerce(bind) : bind
|
49
50
|
@mode = mode
|
50
51
|
@check_level = check_level
|
51
|
-
@req_arg_count = 0
|
52
|
-
@req_kwarg_names = []
|
53
52
|
@has_rest = false
|
54
53
|
@has_keyrest = false
|
55
54
|
@parameters = parameters
|
@@ -57,28 +56,40 @@ class T::Private::Methods::Signature
|
|
57
56
|
@override_allow_incompatible = override_allow_incompatible
|
58
57
|
@defined_raw = defined_raw
|
59
58
|
|
60
|
-
|
59
|
+
# Use T.untyped in lieu of T.nilable to try to avoid unnecessary allocations.
|
60
|
+
arg_types = T.let(nil, T.untyped)
|
61
|
+
kwarg_types = T.let(nil, T.untyped)
|
62
|
+
req_arg_count = 0
|
63
|
+
req_kwarg_names = T.let(nil, T.untyped)
|
64
|
+
|
61
65
|
# If sig params are declared but there is a single parameter with a missing name
|
62
66
|
# **and** the method ends with a "=", assume it is a writer method generated
|
63
67
|
# by attr_writer or attr_accessor
|
64
|
-
writer_method =
|
68
|
+
writer_method = !(raw_arg_types.size == 1 && raw_arg_types.key?(nil)) && parameters == UNNAMED_REQUIRED_PARAMETERS && method_name[-1] == "="
|
65
69
|
# For writer methods, map the single parameter to the method name without the "=" at the end
|
66
70
|
parameters = [[:req, method_name[0...-1].to_sym]] if writer_method
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
+
is_name_missing = parameters.any? {|_, name| !raw_arg_types.key?(name)}
|
72
|
+
if is_name_missing
|
73
|
+
param_names = parameters.map {|_, name| name}
|
74
|
+
missing_names = param_names - raw_arg_types.keys
|
71
75
|
raise "The declaration for `#{method.name}` is missing parameter(s): #{missing_names.join(', ')}"
|
72
|
-
|
73
|
-
|
74
|
-
|
76
|
+
elsif parameters.length == raw_arg_types.size
|
77
|
+
else
|
78
|
+
param_names = parameters.map {|_, name| name}
|
79
|
+
has_extra_names = parameters.count {|_, name| raw_arg_types.key?(name)} < raw_arg_types.size
|
80
|
+
if has_extra_names
|
81
|
+
extra_names = raw_arg_types.keys - param_names
|
82
|
+
raise "The declaration for `#{method.name}` has extra parameter(s): #{extra_names.join(', ')}"
|
83
|
+
end
|
75
84
|
end
|
76
85
|
|
77
86
|
if parameters.size != raw_arg_types.size
|
78
87
|
raise "The declaration for `#{method.name}` has arguments with duplicate names"
|
79
88
|
end
|
89
|
+
i = 0
|
90
|
+
raw_arg_types.each do |type_name, raw_type|
|
91
|
+
param_kind, param_name = parameters[i]
|
80
92
|
|
81
|
-
parameters.zip(raw_arg_types) do |(param_kind, param_name), (type_name, raw_type)|
|
82
93
|
if type_name != param_name
|
83
94
|
hint = ""
|
84
95
|
# Ruby reorders params so that required keyword arguments
|
@@ -92,15 +103,15 @@ class T::Private::Methods::Signature
|
|
92
103
|
end
|
93
104
|
|
94
105
|
raise "Parameter `#{type_name}` is declared out of order (declared as arg number " \
|
95
|
-
"#{
|
96
|
-
"#{
|
106
|
+
"#{i + 1}, defined in the method as arg number " \
|
107
|
+
"#{parameters.index {|_, name| name == type_name} + 1}).#{hint}\nMethod: #{method_desc}"
|
97
108
|
end
|
98
109
|
|
99
110
|
type = T::Utils.coerce(raw_type)
|
100
111
|
|
101
112
|
case param_kind
|
102
113
|
when :req
|
103
|
-
if
|
114
|
+
if (arg_types ? arg_types.length : 0) > req_arg_count
|
104
115
|
# Note that this is actually is supported by Ruby, but it would add complexity to
|
105
116
|
# support it here, and I'm happy to discourage its use anyway.
|
106
117
|
#
|
@@ -111,14 +122,14 @@ class T::Private::Methods::Signature
|
|
111
122
|
# see this error. The simplest resolution is to rename your method.
|
112
123
|
raise "Required params after optional params are not supported in method declarations. Method: #{method_desc}"
|
113
124
|
end
|
114
|
-
|
115
|
-
|
125
|
+
(arg_types ||= []) << [param_name, type]
|
126
|
+
req_arg_count += 1
|
116
127
|
when :opt
|
117
|
-
|
128
|
+
(arg_types ||= []) << [param_name, type]
|
118
129
|
when :key, :keyreq
|
119
|
-
|
130
|
+
(kwarg_types ||= {})[param_name] = type
|
120
131
|
if param_kind == :keyreq
|
121
|
-
|
132
|
+
(req_kwarg_names ||= []) << param_name
|
122
133
|
end
|
123
134
|
when :block
|
124
135
|
@block_name = param_name
|
@@ -134,7 +145,14 @@ class T::Private::Methods::Signature
|
|
134
145
|
else
|
135
146
|
raise "Unexpected param_kind: `#{param_kind}`. Method: #{method_desc}"
|
136
147
|
end
|
148
|
+
|
149
|
+
i += 1
|
137
150
|
end
|
151
|
+
|
152
|
+
@arg_types = arg_types || EMPTY_LIST
|
153
|
+
@kwarg_types = kwarg_types || EMPTY_HASH
|
154
|
+
@req_arg_count = req_arg_count
|
155
|
+
@req_kwarg_names = req_kwarg_names || EMPTY_LIST
|
138
156
|
end
|
139
157
|
|
140
158
|
attr_writer :method_name
|
@@ -179,15 +197,7 @@ class T::Private::Methods::Signature
|
|
179
197
|
kwargs = EMPTY_HASH
|
180
198
|
end
|
181
199
|
|
182
|
-
|
183
|
-
|
184
|
-
if @has_rest
|
185
|
-
rest_count = args_length - @arg_types.length
|
186
|
-
rest_count = 0 if rest_count.negative?
|
187
|
-
|
188
|
-
arg_types += [[@rest_name, @rest_type]] * rest_count
|
189
|
-
|
190
|
-
elsif (args_length < @req_arg_count) || (args_length > @arg_types.length)
|
200
|
+
if !@has_rest && ((args_length < @req_arg_count) || (args_length > @arg_types.length))
|
191
201
|
expected_str = @req_arg_count.to_s
|
192
202
|
if @arg_types.length != @req_arg_count
|
193
203
|
expected_str += "..#{@arg_types.length}"
|
@@ -197,10 +207,23 @@ class T::Private::Methods::Signature
|
|
197
207
|
|
198
208
|
begin
|
199
209
|
it = 0
|
200
|
-
|
201
|
-
|
210
|
+
|
211
|
+
# Process given pre-rest args. When there are no rest args,
|
212
|
+
# this is just the given number of args.
|
213
|
+
while it < args_length && it < @arg_types.length
|
214
|
+
yield @arg_types[it][0], args[it], @arg_types[it][1]
|
202
215
|
it += 1
|
203
216
|
end
|
217
|
+
|
218
|
+
if @has_rest
|
219
|
+
rest_count = args_length - @arg_types.length
|
220
|
+
rest_count = 0 if rest_count.negative?
|
221
|
+
|
222
|
+
rest_count.times do
|
223
|
+
yield @rest_name, args[it], @rest_type
|
224
|
+
it += 1
|
225
|
+
end
|
226
|
+
end
|
204
227
|
end
|
205
228
|
|
206
229
|
kwargs.each do |name, val|
|
@@ -208,6 +231,7 @@ class T::Private::Methods::Signature
|
|
208
231
|
if !type && @has_keyrest
|
209
232
|
type = @keyrest_type
|
210
233
|
end
|
234
|
+
|
211
235
|
yield name, val, type if type
|
212
236
|
end
|
213
237
|
end
|
@@ -221,5 +245,6 @@ class T::Private::Methods::Signature
|
|
221
245
|
"#{@method} at #{loc}"
|
222
246
|
end
|
223
247
|
|
248
|
+
EMPTY_LIST = [].freeze
|
224
249
|
EMPTY_HASH = {}.freeze
|
225
250
|
end
|
@@ -6,14 +6,63 @@ module T::Private::Methods::SignatureValidation
|
|
6
6
|
Modes = Methods::Modes
|
7
7
|
|
8
8
|
def self.validate(signature)
|
9
|
+
# Constructors in any language are always a bit weird: they're called in a
|
10
|
+
# static context, but their bodies are implemented by instance methods. So
|
11
|
+
# a mix of the rules that apply to instance methods and class methods
|
12
|
+
# apply.
|
13
|
+
#
|
14
|
+
# In languages like Java and Scala, static methods/companion object methods
|
15
|
+
# are never inherited. (In Java it almost looks like you can inherit them,
|
16
|
+
# because `Child.static_parent_method` works, but this method is simply
|
17
|
+
# resolved statically to `Parent.static_parent_method`). Even though most
|
18
|
+
# instance methods overrides have variance checking done, constructors are
|
19
|
+
# not treated like this, because static methods are never
|
20
|
+
# inherited/overridden, and the constructor can only ever be called
|
21
|
+
# indirectly by way of the static method. (Note: this is only a mental
|
22
|
+
# model--there's not actually a static method for the constructor in Java,
|
23
|
+
# there's an `invokespecial` JVM instruction that handles this).
|
24
|
+
#
|
25
|
+
# But Ruby is not like Java: singleton class methods in Ruby *are*
|
26
|
+
# inherited, unlike static methods in Java. In fact, this is similar to how
|
27
|
+
# JavaScript works. TypeScript simply then sidesteps the issue with
|
28
|
+
# structural typing: `typeof Parent` is not compatible with `typeof Child`
|
29
|
+
# if their constructors are different. (In a nominal type system, simply
|
30
|
+
# having Child descend from Parent should be the only factor in determining
|
31
|
+
# whether those types are compatible).
|
32
|
+
#
|
33
|
+
# Flow has nominal subtyping for classes. When overriding (static and
|
34
|
+
# instance) methods in a child class, the overrides must satisfy variance
|
35
|
+
# constraints. But it still carves out an exception for constructors,
|
36
|
+
# because then literally every class would have to have the same
|
37
|
+
# constructor. This is simply unsound. Hack does a similar thing--static
|
38
|
+
# method overrides are checked, but not constructors. Though what Hack
|
39
|
+
# *does* have is a way to opt into override checking for constructors with
|
40
|
+
# a special annotation.
|
41
|
+
#
|
42
|
+
# It turns out, Sorbet already has this special annotation: either
|
43
|
+
# `abstract` or `overridable`. At time of writing, *no* static override
|
44
|
+
# checking happens unless marked with these keywords (though at runtime, it
|
45
|
+
# always happens). Getting the static system to parity with the runtime by
|
46
|
+
# always checking overrides would be a great place to get to one day, but
|
47
|
+
# for now we can take advantage of it by only doing override checks for
|
48
|
+
# constructors if they've opted in.
|
49
|
+
#
|
50
|
+
# (When we get around to more widely checking overrides statically, we will
|
51
|
+
# need to build a matching special case for constructors statically.)
|
52
|
+
#
|
53
|
+
# Note that this breaks with tradition: normally, constructors are not
|
54
|
+
# allowed to be abstract. But that's kind of a side-effect of everything
|
55
|
+
# above: in Java/Scala, singleton class methods are never abstract because
|
56
|
+
# they're not inherited, and this extends to constructors. TypeScript
|
57
|
+
# simply rejects `new klass()` entirely if `klass` is
|
58
|
+
# `typeof AbstractClass`, requiring instead that you write
|
59
|
+
# `{ new(): AbstractClass }`. We may want to consider building some
|
60
|
+
# analogue to `T.class_of` in the future that works like this `{new():
|
61
|
+
# ...}` type.
|
9
62
|
if signature.method_name == :initialize && signature.method.owner.is_a?(Class)
|
10
|
-
|
11
|
-
|
12
|
-
# methods (this is consistent with how they're treated in other languages, e.g. Java)
|
13
|
-
if signature.mode != Modes.standard
|
14
|
-
raise "`initialize` should not use `.abstract` or `.implementation` or any other inheritance modifiers."
|
63
|
+
if signature.mode == Modes.standard
|
64
|
+
return
|
15
65
|
end
|
16
|
-
return
|
17
66
|
end
|
18
67
|
|
19
68
|
super_method = signature.method.super_method
|
@@ -17,7 +17,7 @@ class T::Props::Decorator
|
|
17
17
|
|
18
18
|
class NoRulesError < StandardError; end
|
19
19
|
|
20
|
-
EMPTY_PROPS = T.let({}.freeze, T::Hash[Symbol, Rules])
|
20
|
+
EMPTY_PROPS = T.let({}.freeze, T::Hash[Symbol, Rules], checked: false)
|
21
21
|
private_constant :EMPTY_PROPS
|
22
22
|
|
23
23
|
sig {params(klass: T.untyped).void.checked(:never)}
|
@@ -26,7 +26,7 @@ class T::Props::Decorator
|
|
26
26
|
@class.plugins.each do |mod|
|
27
27
|
T::Props::Plugin::Private.apply_decorator_methods(mod, self)
|
28
28
|
end
|
29
|
-
@props = T.let(EMPTY_PROPS, T::Hash[Symbol, Rules])
|
29
|
+
@props = T.let(EMPTY_PROPS, T::Hash[Symbol, Rules], checked: false)
|
30
30
|
end
|
31
31
|
|
32
32
|
# checked(:never) - O(prop accesses)
|
@@ -50,9 +50,9 @@ class T::Props::Decorator
|
|
50
50
|
override = rules.delete(:override)
|
51
51
|
|
52
52
|
if props.include?(prop) && !override
|
53
|
-
raise ArgumentError.new("Attempted to redefine prop #{prop.inspect} that's already defined without specifying :override => true: #{prop_rules(prop)}")
|
53
|
+
raise ArgumentError.new("Attempted to redefine prop #{prop.inspect} on class #{@class} that's already defined without specifying :override => true: #{prop_rules(prop)}")
|
54
54
|
elsif !props.include?(prop) && override
|
55
|
-
raise ArgumentError.new("Attempted to override a prop #{prop.inspect} that doesn't already exist")
|
55
|
+
raise ArgumentError.new("Attempted to override a prop #{prop.inspect} on class #{@class} that doesn't already exist")
|
56
56
|
end
|
57
57
|
|
58
58
|
@props = @props.merge(prop => rules.freeze).freeze
|
@@ -79,7 +79,7 @@ class T::Props::Decorator
|
|
79
79
|
extra
|
80
80
|
setter_validate
|
81
81
|
_tnilable
|
82
|
-
].
|
82
|
+
].to_h {|k| [k, true]}.freeze, T::Hash[Symbol, T::Boolean], checked: false)
|
83
83
|
private_constant :VALID_RULE_KEYS
|
84
84
|
|
85
85
|
sig {params(key: Symbol).returns(T::Boolean).checked(:never)}
|
@@ -205,7 +205,7 @@ class T::Props::Decorator
|
|
205
205
|
end
|
206
206
|
|
207
207
|
# TODO: we should really be checking all the methods on `cls`, not just Object
|
208
|
-
BANNED_METHOD_NAMES = T.let(Object.instance_methods.each_with_object({}) {|x, acc| acc[x] = true}.freeze, T::Hash[Symbol, TrueClass])
|
208
|
+
BANNED_METHOD_NAMES = T.let(Object.instance_methods.each_with_object({}) {|x, acc| acc[x] = true}.freeze, T::Hash[Symbol, TrueClass], checked: false)
|
209
209
|
|
210
210
|
# checked(:never) - Rules hash is expensive to check
|
211
211
|
sig do
|
@@ -247,10 +247,10 @@ class T::Props::Decorator
|
|
247
247
|
nil
|
248
248
|
end
|
249
249
|
|
250
|
-
SAFE_NAME = T.let(/\A[A-Za-z_][A-Za-z0-9_-]*\z/.freeze, Regexp)
|
250
|
+
SAFE_NAME = T.let(/\A[A-Za-z_][A-Za-z0-9_-]*\z/.freeze, Regexp, checked: false)
|
251
251
|
|
252
252
|
# Used to validate both prop names and serialized forms
|
253
|
-
sig {params(name: T.any(Symbol, String)).void}
|
253
|
+
sig {params(name: T.any(Symbol, String)).void.checked(:never)}
|
254
254
|
private def validate_prop_name(name)
|
255
255
|
if !name.match?(SAFE_NAME)
|
256
256
|
raise ArgumentError.new("Invalid prop name in #{@class.name}: #{name}")
|
@@ -258,7 +258,7 @@ class T::Props::Decorator
|
|
258
258
|
end
|
259
259
|
|
260
260
|
# This converts the type from a T::Type to a regular old ruby class.
|
261
|
-
sig {params(type: T::Types::Base).returns(Module)}
|
261
|
+
sig {params(type: T::Types::Base).returns(Module).checked(:never)}
|
262
262
|
private def convert_type_to_class(type)
|
263
263
|
case type
|
264
264
|
when T::Types::TypedArray, T::Types::FixedArray
|
@@ -338,34 +338,34 @@ class T::Props::Decorator
|
|
338
338
|
# Retrive the possible underlying object with T.nilable.
|
339
339
|
type = T::Utils::Nilable.get_underlying_type(type)
|
340
340
|
|
341
|
-
|
342
|
-
|
343
|
-
if
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
341
|
+
rules_sensitivity = rules[:sensitivity]
|
342
|
+
sensitivity_and_pii = {sensitivity: rules_sensitivity}
|
343
|
+
if !rules_sensitivity.nil?
|
344
|
+
normalize = T::Configuration.normalize_sensitivity_and_pii_handler
|
345
|
+
if normalize
|
346
|
+
sensitivity_and_pii = normalize.call(sensitivity_and_pii)
|
347
|
+
|
348
|
+
# We check for Class so this is only applied on concrete
|
349
|
+
# documents/models; We allow mixins containing props to not
|
350
|
+
# specify their PII nature, as long as every class into which they
|
351
|
+
# are ultimately included does.
|
352
|
+
#
|
353
|
+
if sensitivity_and_pii[:pii] && @class.is_a?(Class) && !T.unsafe(@class).contains_pii?
|
354
|
+
raise ArgumentError.new(
|
355
|
+
'Cannot include a pii prop in a class that declares `contains_no_pii`'
|
356
|
+
)
|
357
|
+
end
|
355
358
|
end
|
356
359
|
end
|
357
360
|
|
358
|
-
rules =
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
# extra arbitrary metadata attached by the code defining this property
|
367
|
-
extra: rules[:extra]&.freeze,
|
368
|
-
)
|
361
|
+
rules[:type] = type
|
362
|
+
rules[:type_object] = type_object
|
363
|
+
rules[:accessor_key] = "@#{name}".to_sym
|
364
|
+
rules[:sensitivity] = sensitivity_and_pii[:sensitivity]
|
365
|
+
rules[:pii] = sensitivity_and_pii[:pii]
|
366
|
+
rules[:extra] = rules[:extra]&.freeze
|
367
|
+
|
368
|
+
# extra arbitrary metadata attached by the code defining this property
|
369
369
|
|
370
370
|
validate_not_missing_sensitivity(name, rules)
|
371
371
|
|
@@ -419,6 +419,7 @@ class T::Props::Decorator
|
|
419
419
|
sig do
|
420
420
|
params(type: PropTypeOrClass, enum: T.untyped)
|
421
421
|
.returns(T::Types::Base)
|
422
|
+
.checked(:never)
|
422
423
|
end
|
423
424
|
private def smart_coerce(type, enum:)
|
424
425
|
# Backwards compatibility for pre-T::Types style
|
@@ -471,6 +472,7 @@ class T::Props::Decorator
|
|
471
472
|
redaction: T.untyped,
|
472
473
|
)
|
473
474
|
.void
|
475
|
+
.checked(:never)
|
474
476
|
end
|
475
477
|
private def handle_redaction_option(prop_name, redaction)
|
476
478
|
redacted_method = "#{prop_name}_redacted"
|
@@ -492,6 +494,7 @@ class T::Props::Decorator
|
|
492
494
|
valid_type_msg: String,
|
493
495
|
)
|
494
496
|
.void
|
497
|
+
.checked(:never)
|
495
498
|
end
|
496
499
|
private def validate_foreign_option(option_sym, foreign, valid_type_msg:)
|
497
500
|
if foreign.is_a?(Symbol) || foreign.is_a?(String)
|
@@ -523,8 +526,8 @@ class T::Props::Decorator
|
|
523
526
|
# here, but we're baking in `allow_direct_mutation` since we
|
524
527
|
# *haven't* allowed additional options in the past and want to
|
525
528
|
# default to keeping this interface narrow.
|
529
|
+
foreign = T.let(foreign, T.untyped, checked: false)
|
526
530
|
@class.send(:define_method, fk_method) do |allow_direct_mutation: nil|
|
527
|
-
foreign = T.let(foreign, T.untyped)
|
528
531
|
if foreign.is_a?(Proc)
|
529
532
|
resolved_foreign = foreign.call
|
530
533
|
if !resolved_foreign.respond_to?(:load)
|
@@ -117,7 +117,7 @@ module T::Props
|
|
117
117
|
def eagerly_define_lazy_methods!
|
118
118
|
return if lazily_defined_methods.empty?
|
119
119
|
|
120
|
-
source = lazily_defined_methods.values.map(&:call).map(&:to_s).join("\n\n")
|
120
|
+
source = "# frozen_string_literal: true\n" + lazily_defined_methods.values.map(&:call).map(&:to_s).join("\n\n")
|
121
121
|
|
122
122
|
cls = decorated_class
|
123
123
|
cls.class_eval(source)
|
@@ -176,7 +176,7 @@ module T::Props
|
|
176
176
|
base_message = "Can't set #{klass.name}.#{prop} to #{val.inspect} (instance of #{val.class}) - need a #{type}"
|
177
177
|
|
178
178
|
pretty_message = "Parameter '#{prop}': #{base_message}\n"
|
179
|
-
caller_loc = caller_locations
|
179
|
+
caller_loc = caller_locations.find {|l| !l.to_s.include?('sorbet-runtime/lib/types/props')}
|
180
180
|
if caller_loc
|
181
181
|
pretty_message += "Caller: #{caller_loc.path}:#{caller_loc.lineno}\n"
|
182
182
|
end
|
@@ -16,6 +16,7 @@ module T::Props::TypeValidation
|
|
16
16
|
super || key == :DEPRECATED_underspecified_type
|
17
17
|
end
|
18
18
|
|
19
|
+
# checked(:never) - Rules hash is expensive to check
|
19
20
|
sig do
|
20
21
|
params(
|
21
22
|
name: T.any(Symbol, String),
|
@@ -24,12 +25,13 @@ module T::Props::TypeValidation
|
|
24
25
|
type: T.any(T::Types::Base, Module)
|
25
26
|
)
|
26
27
|
.void
|
28
|
+
.checked(:never)
|
27
29
|
end
|
28
30
|
def prop_validate_definition!(name, _cls, rules, type)
|
29
31
|
super
|
30
32
|
|
31
33
|
if !rules[:DEPRECATED_underspecified_type]
|
32
|
-
validate_type(type,
|
34
|
+
validate_type(type, name)
|
33
35
|
elsif rules[:DEPRECATED_underspecified_type] && find_invalid_subtype(type).nil?
|
34
36
|
raise ArgumentError.new("DEPRECATED_underspecified_type set unnecessarily for #{@class.name}.#{name} - #{type} is a valid type")
|
35
37
|
end
|
@@ -41,8 +43,9 @@ module T::Props::TypeValidation
|
|
41
43
|
field_name: T.any(Symbol, String),
|
42
44
|
)
|
43
45
|
.void
|
46
|
+
.checked(:never)
|
44
47
|
end
|
45
|
-
private def validate_type(type, field_name
|
48
|
+
private def validate_type(type, field_name)
|
46
49
|
if (invalid_subtype = find_invalid_subtype(type))
|
47
50
|
raise UnderspecifiedType.new(type_error_message(invalid_subtype, field_name, type))
|
48
51
|
end
|
data/lib/types/struct.rb
CHANGED
@@ -10,7 +10,7 @@ end
|
|
10
10
|
class T::Struct < T::InexactStruct
|
11
11
|
def self.inherited(subclass)
|
12
12
|
super(subclass)
|
13
|
-
T::Private::ClassUtils.replace_method(subclass.singleton_class, :inherited) do |s|
|
13
|
+
T::Private::ClassUtils.replace_method(subclass.singleton_class, :inherited, true) do |s|
|
14
14
|
super(s)
|
15
15
|
raise "#{self.name} is a subclass of T::Struct and cannot be subclassed"
|
16
16
|
end
|
@@ -23,7 +23,7 @@ class T::ImmutableStruct < T::InexactStruct
|
|
23
23
|
def self.inherited(subclass)
|
24
24
|
super(subclass)
|
25
25
|
|
26
|
-
T::Private::ClassUtils.replace_method(subclass.singleton_class, :inherited) do |s|
|
26
|
+
T::Private::ClassUtils.replace_method(subclass.singleton_class, :inherited, true) do |s|
|
27
27
|
super(s)
|
28
28
|
raise "#{self.name} is a subclass of T::ImmutableStruct and cannot be subclassed"
|
29
29
|
end
|
data/lib/types/types/class_of.rb
CHANGED
@@ -27,6 +27,8 @@ module T::Types
|
|
27
27
|
@type <= other.type
|
28
28
|
when Simple
|
29
29
|
@type.is_a?(other.raw_type)
|
30
|
+
when TypedClass
|
31
|
+
true
|
30
32
|
else
|
31
33
|
false
|
32
34
|
end
|
@@ -36,5 +38,14 @@ module T::Types
|
|
36
38
|
def describe_obj(obj)
|
37
39
|
obj.inspect
|
38
40
|
end
|
41
|
+
|
42
|
+
# So that `T.class_of(...)[...]` syntax is valid.
|
43
|
+
# Mirrors the definition of T::Generic#[] (generics are erased).
|
44
|
+
#
|
45
|
+
# We avoid simply writing `include T::Generic` because we don't want any of
|
46
|
+
# the other methods to appear (`T.class_of(A).type_member` doesn't make sense)
|
47
|
+
def [](*types)
|
48
|
+
self
|
49
|
+
end
|
39
50
|
end
|
40
51
|
end
|
data/lib/types/types/simple.rb
CHANGED
@@ -4,6 +4,9 @@
|
|
4
4
|
module T::Types
|
5
5
|
# Validates that an object belongs to the specified class.
|
6
6
|
class Simple < Base
|
7
|
+
NAME_METHOD = Module.instance_method(:name)
|
8
|
+
private_constant(:NAME_METHOD)
|
9
|
+
|
7
10
|
attr_reader :raw_type
|
8
11
|
|
9
12
|
def initialize(raw_type)
|
@@ -16,7 +19,7 @@ module T::Types
|
|
16
19
|
#
|
17
20
|
# `name` isn't normally a hot path for types, but it is used in initializing a T::Types::Union,
|
18
21
|
# and so in `T.nilable`, and so in runtime constructions like `x = T.let(nil, T.nilable(Integer))`.
|
19
|
-
@name ||= @raw_type.name.freeze
|
22
|
+
@name ||= (NAME_METHOD.bind(@raw_type).call || @raw_type.name).freeze
|
20
23
|
end
|
21
24
|
|
22
25
|
# overrides Base
|
@@ -29,6 +32,11 @@ module T::Types
|
|
29
32
|
case other
|
30
33
|
when Simple
|
31
34
|
@raw_type <= other.raw_type
|
35
|
+
when TypedClass
|
36
|
+
# This case is a bit odd--we would have liked to solve this like we do
|
37
|
+
# for `T::Array` et al., but don't for backwards compatibility.
|
38
|
+
# See `type_for_module` below.
|
39
|
+
@raw_type <= other.underlying_class
|
32
40
|
else
|
33
41
|
false
|
34
42
|
end
|
@@ -86,6 +94,9 @@ module T::Types
|
|
86
94
|
elsif !Object.autoload?(:Set) && Object.const_defined?(:Set) && mod == ::Set
|
87
95
|
T::Set[T.untyped]
|
88
96
|
else
|
97
|
+
# ideally we would have a case mapping from ::Class -> T::Class here
|
98
|
+
# but for backwards compatibility we don't have that, and instead
|
99
|
+
# have a special case in subtype_of_single?
|
89
100
|
Simple.new(mod)
|
90
101
|
end
|
91
102
|
|
@@ -3,11 +3,30 @@
|
|
3
3
|
|
4
4
|
module T::Types
|
5
5
|
class TypeParameter < Base
|
6
|
+
module Private
|
7
|
+
@pool = {}
|
8
|
+
|
9
|
+
def self.cached_entry(name)
|
10
|
+
@pool[name]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.set_entry_for(name, type)
|
14
|
+
@pool[name] = type
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
6
18
|
def initialize(name)
|
7
19
|
raise ArgumentError.new("not a symbol: #{name}") unless name.is_a?(Symbol)
|
8
20
|
@name = name
|
9
21
|
end
|
10
22
|
|
23
|
+
def self.make(name)
|
24
|
+
cached = Private.cached_entry(name)
|
25
|
+
return cached if cached
|
26
|
+
|
27
|
+
Private.set_entry_for(name, new(name))
|
28
|
+
end
|
29
|
+
|
11
30
|
def valid?(obj)
|
12
31
|
true
|
13
32
|
end
|