sorbet-runtime 0.5.12155 → 0.6.12632
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 +1 -1
- data/lib/types/boolean.rb +1 -1
- data/lib/types/configuration.rb +4 -14
- data/lib/types/enum.rb +35 -35
- data/lib/types/non_forcing_constants.rb +1 -1
- data/lib/types/private/abstract/data.rb +1 -1
- data/lib/types/private/methods/_methods.rb +21 -5
- data/lib/types/private/methods/call_validation.rb +3 -16
- data/lib/types/private/methods/decl_builder.rb +6 -1
- data/lib/types/private/methods/signature.rb +8 -8
- data/lib/types/private/methods/signature_validation.rb +42 -3
- data/lib/types/private/mixins/mixins.rb +1 -1
- data/lib/types/private/runtime_levels.rb +1 -1
- data/lib/types/private/sealed.rb +5 -5
- data/lib/types/props/_props.rb +6 -9
- data/lib/types/props/constructor.rb +1 -1
- data/lib/types/props/custom_type.rb +5 -5
- data/lib/types/props/decorator.rb +127 -64
- data/lib/types/props/generated_code_validation.rb +4 -4
- data/lib/types/props/has_lazily_specialized_methods.rb +13 -13
- data/lib/types/props/optional.rb +2 -2
- data/lib/types/props/pretty_printable.rb +6 -6
- data/lib/types/props/private/apply_default.rb +15 -15
- data/lib/types/props/private/deserializer_generator.rb +10 -6
- data/lib/types/props/private/serde_transform.rb +8 -8
- data/lib/types/props/private/serializer_generator.rb +9 -5
- data/lib/types/props/private/setter_factory.rb +4 -4
- data/lib/types/props/serializable.rb +14 -19
- data/lib/types/props/type_validation.rb +5 -5
- data/lib/types/props/utils.rb +1 -1
- data/lib/types/props/weak_constructor.rb +3 -3
- data/lib/types/sig.rb +2 -2
- data/lib/types/struct.rb +2 -2
- data/lib/types/types/base.rb +6 -6
- data/lib/types/types/fixed_array.rb +1 -1
- data/lib/types/types/fixed_hash.rb +6 -6
- data/lib/types/types/intersection.rb +2 -2
- data/lib/types/types/t_enum.rb +2 -0
- data/lib/types/types/union.rb +7 -7
- metadata +3 -3
data/lib/types/private/sealed.rb
CHANGED
@@ -5,7 +5,7 @@ module T::Private::Sealed
|
|
5
5
|
module NoInherit
|
6
6
|
def inherited(child)
|
7
7
|
super
|
8
|
-
caller_loc = T::Private::CallerUtils.find_caller {|loc| loc.base_label != 'inherited'}
|
8
|
+
caller_loc = T::Private::CallerUtils.find_caller { |loc| loc.base_label != 'inherited' }
|
9
9
|
T::Private::Sealed.validate_inheritance(caller_loc, self, child, 'inherited')
|
10
10
|
@sorbet_sealed_module_all_subclasses << child
|
11
11
|
end
|
@@ -22,14 +22,14 @@ module T::Private::Sealed
|
|
22
22
|
module NoIncludeExtend
|
23
23
|
def included(child)
|
24
24
|
super
|
25
|
-
caller_loc = T::Private::CallerUtils.find_caller {|loc| loc.base_label != 'included'}
|
25
|
+
caller_loc = T::Private::CallerUtils.find_caller { |loc| loc.base_label != 'included' }
|
26
26
|
T::Private::Sealed.validate_inheritance(caller_loc, self, child, 'included')
|
27
27
|
@sorbet_sealed_module_all_subclasses << child
|
28
28
|
end
|
29
29
|
|
30
30
|
def extended(child)
|
31
31
|
super
|
32
|
-
caller_loc = T::Private::CallerUtils.find_caller {|loc| loc.base_label != 'extended'}
|
32
|
+
caller_loc = T::Private::CallerUtils.find_caller { |loc| loc.base_label != 'extended' }
|
33
33
|
T::Private::Sealed.validate_inheritance(caller_loc, self, child, 'extended')
|
34
34
|
@sorbet_sealed_module_all_subclasses << child
|
35
35
|
end
|
@@ -70,7 +70,7 @@ module T::Private::Sealed
|
|
70
70
|
|
71
71
|
def self.validate_inheritance(caller_loc, parent, child, verb)
|
72
72
|
this_file = caller_loc&.path
|
73
|
-
decl_file = parent.instance_variable_get(:@sorbet_sealed_module_decl_file)
|
73
|
+
decl_file = parent.instance_variable_get(:@sorbet_sealed_module_decl_file)
|
74
74
|
|
75
75
|
if !this_file
|
76
76
|
raise "Could not use backtrace to determine file for #{verb} child #{child}"
|
@@ -81,7 +81,7 @@ module T::Private::Sealed
|
|
81
81
|
|
82
82
|
if !this_file.start_with?(decl_file)
|
83
83
|
whitelist = T::Configuration.sealed_violation_whitelist
|
84
|
-
if !whitelist.nil? && whitelist.any? {|pattern| this_file =~ pattern}
|
84
|
+
if !whitelist.nil? && whitelist.any? { |pattern| this_file =~ pattern }
|
85
85
|
return
|
86
86
|
end
|
87
87
|
|
data/lib/types/props/_props.rb
CHANGED
@@ -109,7 +109,7 @@ module T::Props
|
|
109
109
|
# form.
|
110
110
|
#
|
111
111
|
# @return [void]
|
112
|
-
sig {params(name: Symbol, cls: T.untyped, rules: T.untyped).void}
|
112
|
+
sig { params(name: Symbol, cls: T.untyped, rules: T.untyped).void }
|
113
113
|
def prop(name, cls, **rules)
|
114
114
|
cls = T::Utils.coerce(cls) if !cls.is_a?(Module)
|
115
115
|
decorator.prop_defined(name, cls, rules)
|
@@ -132,17 +132,14 @@ module T::Props
|
|
132
132
|
end
|
133
133
|
|
134
134
|
# Shorthand helper to define a `prop` with `immutable => true`
|
135
|
-
sig {params(name: Symbol,
|
136
|
-
def const(name,
|
137
|
-
if
|
135
|
+
sig { params(name: Symbol, cls: T.untyped, rules: T.untyped).void }
|
136
|
+
def const(name, cls, **rules)
|
137
|
+
if rules.key?(:immutable)
|
138
138
|
Kernel.raise ArgumentError.new("Cannot pass 'immutable' argument when using 'const' keyword to define a prop")
|
139
139
|
end
|
140
140
|
|
141
|
-
|
142
|
-
|
143
|
-
else
|
144
|
-
self.prop(name, cls_or_args, **args.merge(immutable: true))
|
145
|
-
end
|
141
|
+
rules[:immutable] = true
|
142
|
+
self.prop(name, cls, **rules)
|
146
143
|
end
|
147
144
|
|
148
145
|
def included(child)
|
@@ -15,7 +15,7 @@ module T::Props::Constructor::DecoratorMethods
|
|
15
15
|
# we'll use to check for any unrecognized input.)
|
16
16
|
#
|
17
17
|
# checked(:never) - O(runtime object construction)
|
18
|
-
sig {params(instance: T::Props::Constructor, hash: T::Hash[Symbol, T.untyped]).returns(Integer).checked(:never)}
|
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
21
|
# and therefore allocates for each entry.
|
@@ -39,7 +39,7 @@ module T::Props
|
|
39
39
|
#
|
40
40
|
# @param [Object] instance
|
41
41
|
# @return An instance of one of T::Configuration.scalar_types
|
42
|
-
sig {abstract.params(instance: T.untyped).returns(T.untyped).checked(:never)}
|
42
|
+
sig { abstract.params(instance: T.untyped).returns(T.untyped).checked(:never) }
|
43
43
|
def serialize(instance); end
|
44
44
|
|
45
45
|
# Given the serialized form of your type, this returns an instance
|
@@ -47,17 +47,17 @@ module T::Props
|
|
47
47
|
#
|
48
48
|
# @param scalar One of T::Configuration.scalar_types
|
49
49
|
# @return Object
|
50
|
-
sig {abstract.params(scalar: T.untyped).returns(T.untyped).checked(:never)}
|
50
|
+
sig { abstract.params(scalar: T.untyped).returns(T.untyped).checked(:never) }
|
51
51
|
def deserialize(scalar); end
|
52
52
|
|
53
|
-
sig {override.params(_base: Module).void}
|
53
|
+
sig { override.params(_base: Module).void }
|
54
54
|
def self.included(_base)
|
55
55
|
super
|
56
56
|
|
57
57
|
raise 'Please use "extend", not "include" to attach this module'
|
58
58
|
end
|
59
59
|
|
60
|
-
sig(:final) {params(val: T.untyped).returns(T::Boolean).checked(:never)}
|
60
|
+
sig(:final) { params(val: T.untyped).returns(T::Boolean).checked(:never) }
|
61
61
|
def self.scalar_type?(val)
|
62
62
|
# We don't need to check for val's included modules in
|
63
63
|
# T::Configuration.scalar_types, because T::Configuration.scalar_types
|
@@ -74,7 +74,7 @@ module T::Props
|
|
74
74
|
# implement set-like fields that store a unique-array, but forbid
|
75
75
|
# hashes; Custom hash types should be implemented via an emebdded
|
76
76
|
# T::Struct (or a subclass like Chalk::ODM::Document) or via T.
|
77
|
-
sig(:final) {params(val: Object).returns(T::Boolean).checked(:never)}
|
77
|
+
sig(:final) { params(val: Object).returns(T::Boolean).checked(:never) }
|
78
78
|
def self.valid_serialization?(val)
|
79
79
|
case val
|
80
80
|
when Array
|
@@ -10,17 +10,33 @@
|
|
10
10
|
class T::Props::Decorator
|
11
11
|
extend T::Sig
|
12
12
|
|
13
|
-
Rules = T.type_alias {T::Hash[Symbol, T.untyped]}
|
14
|
-
DecoratedInstance = T.type_alias {Object} # Would be T::Props, but that produces circular reference errors in some circumstances
|
15
|
-
PropType = T.type_alias {T::Types::Base}
|
16
|
-
PropTypeOrClass = T.type_alias {T.any(PropType, Module)}
|
13
|
+
Rules = T.type_alias { T::Hash[Symbol, T.untyped] }
|
14
|
+
DecoratedInstance = T.type_alias { Object } # Would be T::Props, but that produces circular reference errors in some circumstances
|
15
|
+
PropType = T.type_alias { T::Types::Base }
|
16
|
+
PropTypeOrClass = T.type_alias { T.any(PropType, Module) }
|
17
|
+
OverrideRules = T.type_alias { T::Hash[Symbol, {allow_incompatible: T::Boolean}] }
|
17
18
|
|
18
19
|
class NoRulesError < StandardError; end
|
19
20
|
|
20
21
|
EMPTY_PROPS = T.let({}.freeze, T::Hash[Symbol, Rules], checked: false)
|
21
22
|
private_constant :EMPTY_PROPS
|
22
23
|
|
23
|
-
|
24
|
+
OVERRIDE_TRUE = T.let({
|
25
|
+
reader: {allow_incompatible: false}.freeze,
|
26
|
+
writer: {allow_incompatible: false}.freeze,
|
27
|
+
}.freeze, OverrideRules)
|
28
|
+
|
29
|
+
OVERRIDE_READER = T.let({
|
30
|
+
reader: {allow_incompatible: false}.freeze,
|
31
|
+
}.freeze, OverrideRules)
|
32
|
+
|
33
|
+
OVERRIDE_WRITER = T.let({
|
34
|
+
writer: {allow_incompatible: false}.freeze,
|
35
|
+
}.freeze, OverrideRules)
|
36
|
+
|
37
|
+
OVERRIDE_EMPTY = T.let({}.freeze, OverrideRules)
|
38
|
+
|
39
|
+
sig { params(klass: T.untyped).void.checked(:never) }
|
24
40
|
def initialize(klass)
|
25
41
|
@class = T.let(klass, T.all(Module, T::Props::ClassMethods))
|
26
42
|
@class.plugins.each do |mod|
|
@@ -30,32 +46,30 @@ class T::Props::Decorator
|
|
30
46
|
end
|
31
47
|
|
32
48
|
# checked(:never) - O(prop accesses)
|
33
|
-
sig {returns(T::Hash[Symbol, Rules]).checked(:never)}
|
49
|
+
sig { returns(T::Hash[Symbol, Rules]).checked(:never) }
|
34
50
|
attr_reader :props
|
35
51
|
|
36
|
-
sig {returns(T::Array[Symbol])}
|
52
|
+
sig { returns(T::Array[Symbol]) }
|
37
53
|
def all_props
|
38
54
|
props.keys
|
39
55
|
end
|
40
56
|
|
41
57
|
# checked(:never) - O(prop accesses)
|
42
|
-
sig {params(prop: T.any(Symbol, String)).returns(Rules).checked(:never)}
|
58
|
+
sig { params(prop: T.any(Symbol, String)).returns(Rules).checked(:never) }
|
43
59
|
def prop_rules(prop)
|
44
60
|
props[prop.to_sym] || raise("No such prop: #{prop.inspect}")
|
45
61
|
end
|
46
62
|
|
47
63
|
# checked(:never) - Rules hash is expensive to check
|
48
|
-
sig {params(
|
49
|
-
def add_prop_definition(
|
64
|
+
sig { params(name: Symbol, rules: Rules).void.checked(:never) }
|
65
|
+
def add_prop_definition(name, rules)
|
50
66
|
override = rules.delete(:override)
|
51
67
|
|
52
|
-
if props.include?(
|
53
|
-
raise ArgumentError.new("Attempted to redefine prop #{
|
54
|
-
elsif !props.include?(prop) && override
|
55
|
-
raise ArgumentError.new("Attempted to override a prop #{prop.inspect} on class #{@class} that doesn't already exist")
|
68
|
+
if props.include?(name) && !override
|
69
|
+
raise ArgumentError.new("Attempted to redefine prop #{name.inspect} on class #{@class} that's already defined without specifying :override => true: #{prop_rules(name)}")
|
56
70
|
end
|
57
71
|
|
58
|
-
@props = @props.merge(
|
72
|
+
@props = @props.merge(name => rules.freeze).freeze
|
59
73
|
end
|
60
74
|
|
61
75
|
# Heads up!
|
@@ -79,16 +93,16 @@ class T::Props::Decorator
|
|
79
93
|
extra
|
80
94
|
setter_validate
|
81
95
|
_tnilable
|
82
|
-
].to_h {|k| [k, true]}.freeze, T::Hash[Symbol, T::Boolean], checked: false)
|
96
|
+
].to_h { |k| [k, true] }.freeze, T::Hash[Symbol, T::Boolean], checked: false)
|
83
97
|
private_constant :VALID_RULE_KEYS
|
84
98
|
|
85
|
-
sig {params(key: Symbol).returns(T::Boolean).checked(:never)}
|
99
|
+
sig { params(key: Symbol).returns(T::Boolean).checked(:never) }
|
86
100
|
def valid_rule_key?(key)
|
87
101
|
!!VALID_RULE_KEYS[key]
|
88
102
|
end
|
89
103
|
|
90
104
|
# checked(:never) - O(prop accesses)
|
91
|
-
sig {returns(T.all(Module, T::Props::ClassMethods)).checked(:never)}
|
105
|
+
sig { returns(T.all(Module, T::Props::ClassMethods)).checked(:never) }
|
92
106
|
def decorated_class
|
93
107
|
@class
|
94
108
|
end
|
@@ -98,7 +112,7 @@ class T::Props::Decorator
|
|
98
112
|
# Use this to validate that a value will validate for a given prop. Useful for knowing whether a value can be set on a model without setting it.
|
99
113
|
#
|
100
114
|
# checked(:never) - potentially O(prop accesses) depending on usage pattern
|
101
|
-
sig {params(prop: Symbol, val: T.untyped).void.checked(:never)}
|
115
|
+
sig { params(prop: Symbol, val: T.untyped).void.checked(:never) }
|
102
116
|
def validate_prop_value(prop, val)
|
103
117
|
prop_rules(prop).fetch(:value_validate_proc).call(val)
|
104
118
|
end
|
@@ -160,7 +174,9 @@ class T::Props::Decorator
|
|
160
174
|
.checked(:never)
|
161
175
|
end
|
162
176
|
def prop_get(instance, prop, rules=prop_rules(prop))
|
163
|
-
|
177
|
+
# `instance_variable_get` will return nil if the variable doesn't exist
|
178
|
+
# which is what we want to have happen for the logic below.
|
179
|
+
val = instance.instance_variable_get(rules[:accessor_key])
|
164
180
|
if !val.nil?
|
165
181
|
val
|
166
182
|
elsif (d = rules[:ifunset])
|
@@ -180,7 +196,9 @@ class T::Props::Decorator
|
|
180
196
|
.checked(:never)
|
181
197
|
end
|
182
198
|
def prop_get_if_set(instance, prop, rules=prop_rules(prop))
|
183
|
-
|
199
|
+
# `instance_variable_get` will return nil if the variable doesn't exist
|
200
|
+
# which is what we want to have happen for the return value here.
|
201
|
+
instance.instance_variable_get(rules[:accessor_key])
|
184
202
|
end
|
185
203
|
alias_method :get, :prop_get_if_set # Alias for backwards compatibility
|
186
204
|
|
@@ -202,7 +220,7 @@ class T::Props::Decorator
|
|
202
220
|
end
|
203
221
|
|
204
222
|
# TODO: we should really be checking all the methods on `cls`, not just Object
|
205
|
-
BANNED_METHOD_NAMES = T.let(Object.instance_methods.each_with_object({}) {|x, acc| acc[x] = true}.freeze, T::Hash[Symbol, TrueClass], checked: false)
|
223
|
+
BANNED_METHOD_NAMES = T.let(Object.instance_methods.each_with_object({}) { |x, acc| acc[x] = true }.freeze, T::Hash[Symbol, TrueClass], checked: false)
|
206
224
|
|
207
225
|
# checked(:never) - Rules hash is expensive to check
|
208
226
|
sig do
|
@@ -223,8 +241,10 @@ class T::Props::Decorator
|
|
223
241
|
"to 'sensitivity:' (in prop #{@class.name}.#{name})")
|
224
242
|
end
|
225
243
|
|
226
|
-
if rules.keys.any? {|k| !valid_rule_key?(k)}
|
227
|
-
|
244
|
+
if rules.keys.any? { |k| !valid_rule_key?(k) }
|
245
|
+
invalid_keys = rules.keys.reject { |k| valid_rule_key?(k) }
|
246
|
+
suffix = invalid_keys.size == 1 ? "" : "s"
|
247
|
+
raise ArgumentError.new("Invalid prop arg#{suffix} supplied in #{self}: #{invalid_keys.inspect}")
|
228
248
|
end
|
229
249
|
|
230
250
|
if !rules[:clobber_existing_method!] && !rules[:without_accessors] && BANNED_METHOD_NAMES.include?(name.to_sym)
|
@@ -245,9 +265,11 @@ class T::Props::Decorator
|
|
245
265
|
end
|
246
266
|
|
247
267
|
SAFE_NAME = T.let(/\A[A-Za-z_][A-Za-z0-9_-]*\z/.freeze, Regexp, checked: false)
|
268
|
+
# Should be exactly the same as `SAFE_NAME`, but with a leading `@`.
|
269
|
+
SAFE_ACCESSOR_KEY_NAME = T.let(/\A@[A-Za-z_][A-Za-z0-9_-]*\z/.freeze, Regexp, checked: false)
|
248
270
|
|
249
271
|
# Used to validate both prop names and serialized forms
|
250
|
-
sig {params(name: T.any(Symbol, String)).void.checked(:never)}
|
272
|
+
sig { params(name: T.any(Symbol, String)).void.checked(:never) }
|
251
273
|
private def validate_prop_name(name)
|
252
274
|
if !name.match?(SAFE_NAME)
|
253
275
|
raise ArgumentError.new("Invalid prop name in #{@class.name}: #{name}")
|
@@ -255,7 +277,7 @@ class T::Props::Decorator
|
|
255
277
|
end
|
256
278
|
|
257
279
|
# This converts the type from a T::Type to a regular old ruby class.
|
258
|
-
sig {params(type: T::Types::Base).returns(Module).checked(:never)}
|
280
|
+
sig { params(type: T::Types::Base).returns(Module).checked(:never) }
|
259
281
|
private def convert_type_to_class(type)
|
260
282
|
case type
|
261
283
|
when T::Types::TypedArray, T::Types::FixedArray
|
@@ -302,6 +324,30 @@ class T::Props::Decorator
|
|
302
324
|
T::Utils::Nilable.is_union_with_nilclass(cls) || ((cls == T.untyped || cls == NilClass) && rules.key?(:default) && rules[:default].nil?)
|
303
325
|
end
|
304
326
|
|
327
|
+
sig(:final) { params(name: Symbol).returns(T::Boolean).checked(:never) }
|
328
|
+
private def method_defined_on_ancestor?(name)
|
329
|
+
(@class.method_defined?(name) || @class.private_method_defined?(name)) &&
|
330
|
+
# Unfortunately, older versions of ruby don't allow the second parameter on
|
331
|
+
# `private_method_defined?`.
|
332
|
+
(!@class.method_defined?(name, false) && !@class.private_method_defined?(name, false))
|
333
|
+
end
|
334
|
+
|
335
|
+
sig(:final) { params(name: Symbol, rules: Rules).void.checked(:never) }
|
336
|
+
private def validate_overrides(name, rules)
|
337
|
+
override = elaborate_override(name, rules[:override])
|
338
|
+
|
339
|
+
return if rules[:without_accessors]
|
340
|
+
|
341
|
+
if override[:reader] && !method_defined_on_ancestor?(name) && !props.include?(name)
|
342
|
+
raise ArgumentError.new("You marked the getter for prop #{name.inspect} as `override`, but the method `#{name}` doesn't exist to be overridden.")
|
343
|
+
end
|
344
|
+
|
345
|
+
# Properly, we should also check whether `props[name]` is immutable, but the old code didn't either.
|
346
|
+
if !rules[:immutable] && override[:writer] && !method_defined_on_ancestor?("#{name}=".to_sym) && !props.include?(name)
|
347
|
+
raise ArgumentError.new("You marked the setter for prop #{name.inspect} as `override`, but the method `#{name}=` doesn't exist to be overridden.")
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
305
351
|
# checked(:never) - Rules hash is expensive to check
|
306
352
|
sig do
|
307
353
|
params(
|
@@ -349,7 +395,7 @@ class T::Props::Decorator
|
|
349
395
|
#
|
350
396
|
if sensitivity_and_pii[:pii] && @class.is_a?(Class) && !T.unsafe(@class).contains_pii?
|
351
397
|
raise ArgumentError.new(
|
352
|
-
|
398
|
+
"Cannot define pii prop `#{@class}##{name}` because `#{@class}` is `contains_no_pii`"
|
353
399
|
)
|
354
400
|
end
|
355
401
|
end
|
@@ -364,8 +410,6 @@ class T::Props::Decorator
|
|
364
410
|
|
365
411
|
# extra arbitrary metadata attached by the code defining this property
|
366
412
|
|
367
|
-
validate_not_missing_sensitivity(name, rules)
|
368
|
-
|
369
413
|
# for backcompat (the `:array` key is deprecated but because the name is
|
370
414
|
# so generic it's really hard to be sure it's not being relied on anymore)
|
371
415
|
if type.is_a?(T::Types::TypedArray)
|
@@ -381,6 +425,7 @@ class T::Props::Decorator
|
|
381
425
|
rules[:setter_proc] = setter_proc
|
382
426
|
rules[:value_validate_proc] = value_validate_proc
|
383
427
|
|
428
|
+
validate_overrides(name, rules)
|
384
429
|
add_prop_definition(name, rules)
|
385
430
|
|
386
431
|
# NB: using `without_accessors` doesn't make much sense unless you also define some other way to
|
@@ -392,7 +437,7 @@ class T::Props::Decorator
|
|
392
437
|
end
|
393
438
|
|
394
439
|
# checked(:never) - Rules hash is expensive to check
|
395
|
-
sig {params(name: Symbol, rules: Rules).void.checked(:never)}
|
440
|
+
sig { params(name: Symbol, rules: Rules).void.checked(:never) }
|
396
441
|
private def define_getter_and_setter(name, rules)
|
397
442
|
T::Configuration.without_ruby_warnings do
|
398
443
|
if !rules[:immutable]
|
@@ -405,6 +450,7 @@ class T::Props::Decorator
|
|
405
450
|
# Fast path (~4x faster as of Ruby 2.6)
|
406
451
|
@class.send(:define_method, "#{name}=", &rules.fetch(:setter_proc))
|
407
452
|
end
|
453
|
+
|
408
454
|
end
|
409
455
|
|
410
456
|
if method(:prop_get).owner != T::Props::Decorator || rules.key?(:ifunset)
|
@@ -439,35 +485,6 @@ class T::Props::Decorator
|
|
439
485
|
end
|
440
486
|
end
|
441
487
|
|
442
|
-
# checked(:never) - Rules hash is expensive to check
|
443
|
-
sig {params(prop_name: Symbol, rules: Rules).void.checked(:never)}
|
444
|
-
private def validate_not_missing_sensitivity(prop_name, rules)
|
445
|
-
if rules[:sensitivity].nil?
|
446
|
-
if rules[:redaction]
|
447
|
-
T::Configuration.hard_assert_handler(
|
448
|
-
"#{@class}##{prop_name} has a 'redaction:' annotation but no " \
|
449
|
-
"'sensitivity:' annotation. This is probably wrong, because if a " \
|
450
|
-
"prop needs redaction then it is probably sensitive. Add a " \
|
451
|
-
"sensitivity annotation like 'sensitivity: Opus::Sensitivity::PII." \
|
452
|
-
"whatever', or explicitly override this check with 'sensitivity: []'."
|
453
|
-
)
|
454
|
-
end
|
455
|
-
# TODO(PRIVACYENG-982) Ideally we'd also check for 'password' and possibly
|
456
|
-
# other terms, but this interacts badly with ProtoDefinedDocument because
|
457
|
-
# the proto syntax currently can't declare "sensitivity: []"
|
458
|
-
if /\bsecret\b/.match?(prop_name)
|
459
|
-
T::Configuration.hard_assert_handler(
|
460
|
-
"#{@class}##{prop_name} has the word 'secret' in its name, but no " \
|
461
|
-
"'sensitivity:' annotation. This is probably wrong, because if a " \
|
462
|
-
"prop is named 'secret' then it is probably sensitive. Add a " \
|
463
|
-
"sensitivity annotation like 'sensitivity: Opus::Sensitivity::NonPII." \
|
464
|
-
"security_token', or explicitly override this check with " \
|
465
|
-
"'sensitivity: []'."
|
466
|
-
)
|
467
|
-
end
|
468
|
-
end
|
469
|
-
end
|
470
|
-
|
471
488
|
# Create `#{prop_name}_redacted` method
|
472
489
|
sig do
|
473
490
|
params(
|
@@ -612,7 +629,7 @@ class T::Props::Decorator
|
|
612
629
|
#
|
613
630
|
# This gets called when a module or class that extends T::Props gets included, extended,
|
614
631
|
# prepended, or inherited.
|
615
|
-
sig {params(child: Module).void.checked(:never)}
|
632
|
+
sig { params(child: Module).void.checked(:never) }
|
616
633
|
def model_inherited(child)
|
617
634
|
child.extend(T::Props::ClassMethods)
|
618
635
|
child = T.cast(child, T.all(Module, T::Props::ClassMethods))
|
@@ -627,7 +644,7 @@ class T::Props::Decorator
|
|
627
644
|
|
628
645
|
props.each do |name, rules|
|
629
646
|
copied_rules = rules.dup
|
630
|
-
# NB: Calling `child.decorator` here is a
|
647
|
+
# NB: Calling `child.decorator` here is a time bomb that's going to give someone a really bad
|
631
648
|
# time. Any class that defines props and also overrides the `decorator_class` method is going
|
632
649
|
# to reach this line before its override take effect, turning it into a no-op.
|
633
650
|
child.decorator.add_prop_definition(name, copied_rules)
|
@@ -656,19 +673,65 @@ class T::Props::Decorator
|
|
656
673
|
end
|
657
674
|
end
|
658
675
|
|
659
|
-
sig
|
676
|
+
sig(:final) do
|
677
|
+
params(key: Symbol, d: T.untyped, out: T::Hash[Symbol, {allow_incompatible: T::Boolean}])
|
678
|
+
.void
|
679
|
+
.checked(:never)
|
680
|
+
end
|
681
|
+
private def elaborate_override_entry(key, d, out)
|
682
|
+
# It's written this way so that `{reader: false}` will omit the entry for `reader` in the
|
683
|
+
# result entirely
|
684
|
+
case d[key]
|
685
|
+
when TrueClass
|
686
|
+
out[key] = {allow_incompatible: false}
|
687
|
+
when Hash
|
688
|
+
out[key] = {allow_incompatible: !!d[key][:allow_incompatible]}
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
sig(:final) do
|
693
|
+
params(name: Symbol, d: T.untyped)
|
694
|
+
.returns(T::Hash[Symbol, {allow_incompatible: T::Boolean}])
|
695
|
+
.checked(:never)
|
696
|
+
end
|
697
|
+
private def elaborate_override(name, d)
|
698
|
+
return OVERRIDE_TRUE if d == true
|
699
|
+
return OVERRIDE_READER if d == :reader
|
700
|
+
return OVERRIDE_WRITER if d == :writer
|
701
|
+
return OVERRIDE_EMPTY if d == false || d.nil?
|
702
|
+
unless d.is_a?(Hash)
|
703
|
+
raise ArgumentError.new("`override` only accepts `true`, `:reader`, `:writer`, or a Hash in prop #{@class.name}.#{name} (got #{d.class})")
|
704
|
+
end
|
705
|
+
|
706
|
+
# cwong: should we check for bad keys? `sig { override(not_real: true) }` on a normal function
|
707
|
+
# errors statically but not at runtime.
|
708
|
+
|
709
|
+
# XX cwong: this means {reader: false, allow_incompatible: true} will become {allow_incompatible: true},
|
710
|
+
# is that fine?
|
711
|
+
unless (allow_incompatible = d[:allow_incompatible]).nil?
|
712
|
+
return {reader: {allow_incompatible: !!allow_incompatible},
|
713
|
+
writer: {allow_incompatible: !!allow_incompatible}}.to_h
|
714
|
+
end
|
715
|
+
|
716
|
+
result = {}
|
717
|
+
elaborate_override_entry(:reader, d, result)
|
718
|
+
elaborate_override_entry(:writer, d, result)
|
719
|
+
result
|
720
|
+
end
|
721
|
+
|
722
|
+
sig { params(child: T.all(Module, T::Props::ClassMethods), prop: Symbol).returns(T::Boolean).checked(:never) }
|
660
723
|
private def clobber_getter?(child, prop)
|
661
724
|
!!(child.decorator.method(:prop_get).owner != method(:prop_get).owner &&
|
662
725
|
child.instance_method(prop).source_location&.first == __FILE__)
|
663
726
|
end
|
664
727
|
|
665
|
-
sig {params(child: T.all(Module, T::Props::ClassMethods), prop: Symbol).returns(T::Boolean).checked(:never)}
|
728
|
+
sig { params(child: T.all(Module, T::Props::ClassMethods), prop: Symbol).returns(T::Boolean).checked(:never) }
|
666
729
|
private def clobber_setter?(child, prop)
|
667
730
|
!!(child.decorator.method(:prop_set).owner != method(:prop_set).owner &&
|
668
731
|
child.instance_method("#{prop}=").source_location&.first == __FILE__)
|
669
732
|
end
|
670
733
|
|
671
|
-
sig {params(mod: Module).void.checked(:never)}
|
734
|
+
sig { params(mod: Module).void.checked(:never) }
|
672
735
|
def plugin(mod)
|
673
736
|
decorated_class.plugins << mod
|
674
737
|
T::Props::Plugin::Private.apply_class_methods(mod, decorated_class)
|
@@ -159,7 +159,7 @@ module T::Props
|
|
159
159
|
assert_equal(:resbody, rescue_body.type)
|
160
160
|
exceptions, assignment, handler = rescue_body.children
|
161
161
|
assert_equal(:array, exceptions.type)
|
162
|
-
exceptions.children.each {|c| assert_equal(:const, c.type)}
|
162
|
+
exceptions.children.each { |c| assert_equal(:const, c.type) }
|
163
163
|
assert_equal(:lvasgn, assignment.type)
|
164
164
|
assert_equal([:e], assignment.children)
|
165
165
|
|
@@ -169,7 +169,7 @@ module T::Props
|
|
169
169
|
receiver, method, *args = deserialization_error.children
|
170
170
|
assert_equal(nil, receiver)
|
171
171
|
assert_equal(:raise_deserialization_error, method)
|
172
|
-
args.each {|a| validate_lack_of_side_effects(a, whitelisted_methods_for_deserialize)}
|
172
|
+
args.each { |a| validate_lack_of_side_effects(a, whitelisted_methods_for_deserialize) }
|
173
173
|
|
174
174
|
validate_lack_of_side_effects(val_return, whitelisted_methods_for_deserialize)
|
175
175
|
else
|
@@ -222,12 +222,12 @@ module T::Props
|
|
222
222
|
# Primitives & self are ok
|
223
223
|
when :lvar, :arg, :ivar
|
224
224
|
# Reading local & instance variables & arguments is ok
|
225
|
-
unless node.children.all? {|c| c.is_a?(Symbol)}
|
225
|
+
unless node.children.all? { |c| c.is_a?(Symbol) }
|
226
226
|
raise ValidationError.new("Unexpected child for #{node.type}: #{node.inspect}")
|
227
227
|
end
|
228
228
|
when :args, :mlhs, :block, :begin, :if
|
229
229
|
# Blocks etc are read-only if their contents are read-only
|
230
|
-
node.children.each {|c| validate_lack_of_side_effects(c, whitelisted_methods_by_receiver_type) if c}
|
230
|
+
node.children.each { |c| validate_lack_of_side_effects(c, whitelisted_methods_by_receiver_type) if c }
|
231
231
|
when :send
|
232
232
|
# Sends are riskier so check a whitelist
|
233
233
|
receiver, method, *args = node.children
|
@@ -30,30 +30,30 @@ module T::Props
|
|
30
30
|
#
|
31
31
|
# Note it does _not_ prevent explicit calls to `eagerly_define_lazy_methods!`
|
32
32
|
# from working.
|
33
|
-
sig {void}
|
33
|
+
sig { void }
|
34
34
|
def self.disable_lazy_evaluation!
|
35
35
|
@lazy_evaluation_disabled ||= true
|
36
36
|
end
|
37
37
|
|
38
|
-
sig {returns(T::Boolean)}
|
38
|
+
sig { returns(T::Boolean) }
|
39
39
|
def self.lazy_evaluation_enabled?
|
40
|
-
|
40
|
+
!@lazy_evaluation_disabled
|
41
41
|
end
|
42
42
|
|
43
43
|
module DecoratorMethods
|
44
44
|
extend T::Sig
|
45
45
|
|
46
|
-
sig {returns(T::Hash[Symbol, T.proc.returns(String)]).checked(:never)}
|
46
|
+
sig { returns(T::Hash[Symbol, T.proc.returns(String)]).checked(:never) }
|
47
47
|
private def lazily_defined_methods
|
48
48
|
@lazily_defined_methods ||= {}
|
49
49
|
end
|
50
50
|
|
51
|
-
sig {returns(T::Hash[Symbol, T.untyped]).checked(:never)}
|
51
|
+
sig { returns(T::Hash[Symbol, T.untyped]).checked(:never) }
|
52
52
|
private def lazily_defined_vm_methods
|
53
53
|
@lazily_defined_vm_methods ||= {}
|
54
54
|
end
|
55
55
|
|
56
|
-
sig {params(name: Symbol).void}
|
56
|
+
sig { params(name: Symbol).void }
|
57
57
|
private def eval_lazily_defined_method!(name)
|
58
58
|
if !HasLazilySpecializedMethods.lazy_evaluation_enabled?
|
59
59
|
raise SourceEvaluationDisabled.new
|
@@ -68,7 +68,7 @@ module T::Props
|
|
68
68
|
cls.send(:private, name)
|
69
69
|
end
|
70
70
|
|
71
|
-
sig {params(name: Symbol).void}
|
71
|
+
sig { params(name: Symbol).void }
|
72
72
|
private def eval_lazily_defined_vm_method!(name)
|
73
73
|
if !HasLazilySpecializedMethods.lazy_evaluation_enabled?
|
74
74
|
raise SourceEvaluationDisabled.new
|
@@ -80,7 +80,7 @@ module T::Props
|
|
80
80
|
cls.send(:private, name)
|
81
81
|
end
|
82
82
|
|
83
|
-
sig {params(name: Symbol, blk: T.proc.returns(String)).void}
|
83
|
+
sig { params(name: Symbol, blk: T.proc.returns(String)).void }
|
84
84
|
private def enqueue_lazy_method_definition!(name, &blk)
|
85
85
|
lazily_defined_methods[name] = blk
|
86
86
|
|
@@ -100,7 +100,7 @@ module T::Props
|
|
100
100
|
cls.send(:private, name)
|
101
101
|
end
|
102
102
|
|
103
|
-
sig {params(name: Symbol, blk: T.untyped).void}
|
103
|
+
sig { params(name: Symbol, blk: T.untyped).void }
|
104
104
|
private def enqueue_lazy_vm_method_definition!(name, &blk)
|
105
105
|
lazily_defined_vm_methods[name] = blk
|
106
106
|
|
@@ -115,7 +115,7 @@ module T::Props
|
|
115
115
|
cls.send(:private, name)
|
116
116
|
end
|
117
117
|
|
118
|
-
sig {void}
|
118
|
+
sig { void }
|
119
119
|
def eagerly_define_lazy_methods!
|
120
120
|
return if lazily_defined_methods.empty?
|
121
121
|
|
@@ -125,18 +125,18 @@ module T::Props
|
|
125
125
|
|
126
126
|
cls = decorated_class
|
127
127
|
cls.class_eval(source)
|
128
|
-
lazily_defined_methods.each_key {|name| cls.send(:private, name)}
|
128
|
+
lazily_defined_methods.each_key { |name| cls.send(:private, name) }
|
129
129
|
lazily_defined_methods.clear
|
130
130
|
end
|
131
131
|
|
132
|
-
sig {void}
|
132
|
+
sig { void }
|
133
133
|
def eagerly_define_lazy_vm_methods!
|
134
134
|
return if lazily_defined_vm_methods.empty?
|
135
135
|
|
136
136
|
lazily_defined_vm_methods.values.map(&:call)
|
137
137
|
|
138
138
|
cls = decorated_class
|
139
|
-
lazily_defined_vm_methods.each_key {|name| cls.send(:private, name)}
|
139
|
+
lazily_defined_vm_methods.each_key { |name| cls.send(:private, name) }
|
140
140
|
lazily_defined_vm_methods.clear
|
141
141
|
end
|
142
142
|
end
|
data/lib/types/props/optional.rb
CHANGED
@@ -43,11 +43,11 @@ module T::Props::Optional::DecoratorMethods
|
|
43
43
|
end
|
44
44
|
|
45
45
|
# checked(:never) - O(runtime object construction)
|
46
|
-
sig {returns(T::Hash[Symbol, T::Props::Private::ApplyDefault]).checked(:never)}
|
46
|
+
sig { returns(T::Hash[Symbol, T::Props::Private::ApplyDefault]).checked(:never) }
|
47
47
|
attr_reader :props_with_defaults
|
48
48
|
|
49
49
|
# checked(:never) - O(runtime object construction)
|
50
|
-
sig {returns(T::Hash[Symbol, T::Props::Private::SetterFactory::SetterProc]).checked(:never)}
|
50
|
+
sig { returns(T::Hash[Symbol, T::Props::Private::SetterFactory::SetterProc]).checked(:never) }
|
51
51
|
attr_reader :props_without_defaults
|
52
52
|
|
53
53
|
def add_prop_definition(prop, rules)
|