sorbet-runtime 0.5.5794 → 0.5.6539
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/sorbet-runtime.rb +0 -2
- data/lib/types/_types.rb +28 -8
- data/lib/types/compatibility_patches.rb +0 -2
- data/lib/types/configuration.rb +136 -6
- data/lib/types/enum.rb +24 -2
- data/lib/types/generic.rb +2 -3
- data/lib/types/helpers.rb +19 -2
- data/lib/types/interface_wrapper.rb +4 -4
- data/lib/types/non_forcing_constants.rb +38 -20
- data/lib/types/private/abstract/data.rb +2 -2
- data/lib/types/private/abstract/validate.rb +6 -6
- data/lib/types/private/casts.rb +27 -0
- data/lib/types/private/class_utils.rb +5 -5
- data/lib/types/private/final.rb +0 -1
- data/lib/types/private/methods/_methods.rb +255 -140
- data/lib/types/private/methods/call_validation.rb +54 -982
- data/lib/types/private/methods/call_validation_2_6.rb +1203 -0
- data/lib/types/private/methods/call_validation_2_7.rb +1203 -0
- data/lib/types/private/methods/decl_builder.rb +7 -3
- data/lib/types/private/methods/modes.rb +3 -3
- data/lib/types/private/methods/signature.rb +31 -11
- data/lib/types/private/methods/signature_validation.rb +14 -17
- data/lib/types/private/mixins/mixins.rb +5 -5
- data/lib/types/private/sealed.rb +17 -2
- data/lib/types/private/types/type_alias.rb +5 -0
- data/lib/types/props/_props.rb +2 -1
- data/lib/types/props/constructor.rb +9 -3
- data/lib/types/props/decorator.rb +48 -41
- data/lib/types/props/generated_code_validation.rb +18 -9
- data/lib/types/props/has_lazily_specialized_methods.rb +44 -1
- data/lib/types/props/plugin.rb +2 -2
- data/lib/types/props/private/deserializer_generator.rb +7 -12
- data/lib/types/props/private/serde_transform.rb +12 -18
- data/lib/types/props/private/setter_factory.rb +74 -11
- data/lib/types/props/serializable.rb +41 -11
- data/lib/types/props/type_validation.rb +2 -2
- data/lib/types/props/weak_constructor.rb +14 -9
- data/lib/types/sig.rb +4 -4
- data/lib/types/types/attached_class.rb +5 -1
- data/lib/types/types/base.rb +16 -1
- data/lib/types/types/fixed_array.rb +28 -2
- data/lib/types/types/fixed_hash.rb +21 -12
- 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 +37 -1
- data/lib/types/types/type_variable.rb +1 -1
- data/lib/types/types/typed_array.rb +7 -2
- data/lib/types/types/typed_enumerable.rb +20 -10
- 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 +38 -7
- data/lib/types/types/untyped.rb +4 -0
- data/lib/types/utils.rb +17 -33
- metadata +49 -7
- data/lib/types/profile.rb +0 -27
- data/lib/types/runtime_profiled.rb +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3594dc0c726d2a7189c7544226a6ced4240f4a6df2c18bdae635cc30bb223e7b
|
|
4
|
+
data.tar.gz: dae60008d7f685ecdf231edefdf0b01b595c597c6ae646e7ba7070eada079fa2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 519bf1b1c4f04792d88c817a9bb3a213b01e783a96597e1e328a8a5c9736e78ff7a0a93ef31d56787fd9468d09980e4670edffcd71f59ff299cc7a09a80a714b
|
|
7
|
+
data.tar.gz: 0150d316fb26d07fd0693d3b948dfaa6fe1979053dc12f9811c5c9c6d29b711affaa3e6be16a34a08fd6abf5a1db816b4872b7b655895353cdf65094f7e0ad5c
|
data/lib/sorbet-runtime.rb
CHANGED
|
@@ -19,10 +19,8 @@ require 'set'
|
|
|
19
19
|
|
|
20
20
|
# These are pre-reqs for almost everything in here.
|
|
21
21
|
require_relative 'types/configuration'
|
|
22
|
-
require_relative 'types/profile'
|
|
23
22
|
require_relative 'types/_types'
|
|
24
23
|
require_relative 'types/private/decl_state'
|
|
25
|
-
require_relative 'types/runtime_profiled'
|
|
26
24
|
require_relative 'types/private/class_utils'
|
|
27
25
|
require_relative 'types/private/runtime_levels'
|
|
28
26
|
require_relative 'types/private/methods/_methods'
|
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
|
|
@@ -145,6 +147,25 @@ module T
|
|
|
145
147
|
Private::Casts.cast(value, type, cast_method: "T.let")
|
|
146
148
|
end
|
|
147
149
|
|
|
150
|
+
# Tells the type checker to treat `self` in the current block as `type`.
|
|
151
|
+
# Useful for blocks that are captured and executed later with instance_exec.
|
|
152
|
+
# Use like:
|
|
153
|
+
#
|
|
154
|
+
# seconds = lambda do
|
|
155
|
+
# T.bind(self, NewBinding)
|
|
156
|
+
# ...
|
|
157
|
+
# end
|
|
158
|
+
#
|
|
159
|
+
# `T.bind` behaves like `T.cast` in that it is assumed to be true statically.
|
|
160
|
+
#
|
|
161
|
+
# If `checked` is true, raises an exception at runtime if the value
|
|
162
|
+
# doesn't match the type (this is the default).
|
|
163
|
+
def self.bind(value, type, checked: true)
|
|
164
|
+
return value unless checked
|
|
165
|
+
|
|
166
|
+
Private::Casts.cast(value, type, cast_method: "T.bind")
|
|
167
|
+
end
|
|
168
|
+
|
|
148
169
|
# Tells the typechecker to ensure that `value` is of type `type` (if not, the typechecker will
|
|
149
170
|
# fail). Use this for debugging typechecking errors, or to ensure that type information is
|
|
150
171
|
# statically known and being checked appropriately. If `checked` is true, raises an exception at
|
|
@@ -282,4 +303,3 @@ module T
|
|
|
282
303
|
end
|
|
283
304
|
end
|
|
284
305
|
end
|
|
285
|
-
# rubocop:enable PrisonGuard/UseOpusTypesShortcut
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
# typed: ignore
|
|
3
3
|
|
|
4
|
-
require_relative 'private/methods/_methods'
|
|
5
|
-
|
|
6
4
|
# Work around an interaction bug with sorbet-runtime and rspec-mocks,
|
|
7
5
|
# which occurs when using message expectations (*_any_instance_of,
|
|
8
6
|
# expect, allow) and and_call_original.
|
data/lib/types/configuration.rb
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module T::Configuration
|
|
5
|
+
# Cache this comparisonn to avoid two allocations all over the place.
|
|
6
|
+
AT_LEAST_RUBY_2_7 = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7')
|
|
7
|
+
|
|
5
8
|
# Announces to Sorbet that we are currently in a test environment, so it
|
|
6
9
|
# should treat any sigs which are marked `.checked(:tests)` as if they were
|
|
7
10
|
# just a normal sig.
|
|
@@ -42,6 +45,65 @@ module T::Configuration
|
|
|
42
45
|
T::Private::Methods.set_final_checks_on_hooks(false)
|
|
43
46
|
end
|
|
44
47
|
|
|
48
|
+
@include_value_in_type_errors = true
|
|
49
|
+
# Whether to include values in TypeError messages.
|
|
50
|
+
#
|
|
51
|
+
# Including values is useful for debugging, but can potentially leak
|
|
52
|
+
# sensitive information to logs.
|
|
53
|
+
#
|
|
54
|
+
# @return [T::Boolean]
|
|
55
|
+
def self.include_value_in_type_errors?
|
|
56
|
+
@include_value_in_type_errors
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Configure if type errors excludes the value of the problematic type.
|
|
60
|
+
#
|
|
61
|
+
# The default is to include values in type errors:
|
|
62
|
+
# TypeError: Expected type Integer, got String with value "foo"
|
|
63
|
+
#
|
|
64
|
+
# When values are excluded from type errors:
|
|
65
|
+
# TypeError: Expected type Integer, got String
|
|
66
|
+
def self.exclude_value_in_type_errors
|
|
67
|
+
@include_value_in_type_errors = false
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Opposite of exclude_value_in_type_errors.
|
|
71
|
+
# (Including values in type errors is the default)
|
|
72
|
+
def self.include_value_in_type_errors
|
|
73
|
+
@include_value_in_type_errors = true
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Whether VM-defined prop serialization/deserialization routines can be enabled.
|
|
77
|
+
#
|
|
78
|
+
# @return [T::Boolean]
|
|
79
|
+
def self.can_enable_vm_prop_serde?
|
|
80
|
+
T::Props::Private::DeserializerGenerator.respond_to?(:generate2)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Whether to use VM-defined prop serialization/deserialization routines.
|
|
84
|
+
#
|
|
85
|
+
# The default is to use runtime codegen inside sorbet-runtime itself.
|
|
86
|
+
#
|
|
87
|
+
# @return [T::Boolean]
|
|
88
|
+
def self.use_vm_prop_serde?
|
|
89
|
+
@use_vm_prop_serde || false
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Enable using VM-defined prop serialization/deserialization routines.
|
|
93
|
+
#
|
|
94
|
+
# This method is likely to break things outside of Stripe's systems.
|
|
95
|
+
def self.enable_vm_prop_serde
|
|
96
|
+
if !can_enable_vm_prop_serde?
|
|
97
|
+
hard_assert_handler('Ruby VM is not setup to use VM-defined prop serde')
|
|
98
|
+
end
|
|
99
|
+
@use_vm_prop_serde = true
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Disable using VM-defined prop serialization/deserialization routines.
|
|
103
|
+
def self.disable_vm_prop_serde
|
|
104
|
+
@use_vm_prop_serde = false
|
|
105
|
+
end
|
|
106
|
+
|
|
45
107
|
# Configure the default checked level for a sig with no explicit `.checked`
|
|
46
108
|
# builder. When unset, the default checked level is `:always`.
|
|
47
109
|
#
|
|
@@ -248,7 +310,7 @@ module T::Configuration
|
|
|
248
310
|
end
|
|
249
311
|
|
|
250
312
|
private_class_method def self.log_info_handler_default(str, extra)
|
|
251
|
-
puts "#{str}, extra: #{extra}"
|
|
313
|
+
puts "#{str}, extra: #{extra}"
|
|
252
314
|
end
|
|
253
315
|
|
|
254
316
|
def self.log_info_handler(str, extra)
|
|
@@ -282,7 +344,7 @@ module T::Configuration
|
|
|
282
344
|
end
|
|
283
345
|
|
|
284
346
|
private_class_method def self.soft_assert_handler_default(str, extra)
|
|
285
|
-
puts "#{str}, extra: #{extra}"
|
|
347
|
+
puts "#{str}, extra: #{extra}"
|
|
286
348
|
end
|
|
287
349
|
|
|
288
350
|
def self.soft_assert_handler(str, extra)
|
|
@@ -338,7 +400,7 @@ module T::Configuration
|
|
|
338
400
|
if values.nil?
|
|
339
401
|
@scalar_types = values
|
|
340
402
|
else
|
|
341
|
-
bad_values = values.
|
|
403
|
+
bad_values = values.reject {|v| v.class == String}
|
|
342
404
|
unless bad_values.empty?
|
|
343
405
|
raise ArgumentError.new("Provided values must all be class name strings.")
|
|
344
406
|
end
|
|
@@ -347,7 +409,7 @@ module T::Configuration
|
|
|
347
409
|
end
|
|
348
410
|
end
|
|
349
411
|
|
|
350
|
-
@default_scalar_types = Set.new(%w
|
|
412
|
+
@default_scalar_types = Set.new(%w[
|
|
351
413
|
NilClass
|
|
352
414
|
TrueClass
|
|
353
415
|
FalseClass
|
|
@@ -357,12 +419,80 @@ module T::Configuration
|
|
|
357
419
|
Symbol
|
|
358
420
|
Time
|
|
359
421
|
T::Enum
|
|
360
|
-
|
|
422
|
+
]).freeze
|
|
361
423
|
|
|
362
424
|
def self.scalar_types
|
|
363
425
|
@scalar_types || @default_scalar_types
|
|
364
426
|
end
|
|
365
427
|
|
|
428
|
+
# Guard against overrides of `name` or `to_s`
|
|
429
|
+
MODULE_NAME = Module.instance_method(:name)
|
|
430
|
+
private_constant :MODULE_NAME
|
|
431
|
+
|
|
432
|
+
if T::Configuration::AT_LEAST_RUBY_2_7
|
|
433
|
+
@default_module_name_mangler = ->(type) {MODULE_NAME.bind_call(type)}
|
|
434
|
+
else
|
|
435
|
+
@default_module_name_mangler = ->(type) {MODULE_NAME.bind(type).call}
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
@module_name_mangler = nil
|
|
439
|
+
|
|
440
|
+
def self.module_name_mangler
|
|
441
|
+
@module_name_mangler || @default_module_name_mangler
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# Set to override the default behavior for converting types
|
|
445
|
+
# to names in generated code. Used by the runtime implementation
|
|
446
|
+
# associated with `--stripe-packages` mode.
|
|
447
|
+
#
|
|
448
|
+
# @param [Lambda, Proc, nil] value Proc that converts a type (Class/Module)
|
|
449
|
+
# to a String (pass nil to reset to default behavior)
|
|
450
|
+
def self.module_name_mangler=(handler)
|
|
451
|
+
@module_name_mangler = handler
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
# Set to a PII handler function. This will be called with the `sensitivity:`
|
|
455
|
+
# annotations on things that use `T::Props` and can modify them ahead-of-time.
|
|
456
|
+
#
|
|
457
|
+
# @param [Lambda, Proc, nil] value Proc that takes a hash mapping symbols to the
|
|
458
|
+
# prop values. Pass nil to avoid changing `sensitivity:` annotations.
|
|
459
|
+
def self.normalize_sensitivity_and_pii_handler=(handler)
|
|
460
|
+
@sensitivity_and_pii_handler = handler
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def self.normalize_sensitivity_and_pii_handler
|
|
464
|
+
@sensitivity_and_pii_handler
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
@redaction_handler = nil
|
|
468
|
+
# Set to a redaction handling function. This will be called when the
|
|
469
|
+
# `_redacted` version of a prop reader is used. By default this is set to
|
|
470
|
+
# `nil` and will raise an exception when the redacted version of a prop is
|
|
471
|
+
# accessed.
|
|
472
|
+
#
|
|
473
|
+
# @param [Lambda, Proc, nil] value Proc that converts a value into its
|
|
474
|
+
# redacted version according to the spec passed as the second argument.
|
|
475
|
+
def self.redaction_handler=(handler)
|
|
476
|
+
@redaction_handler = handler
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def self.redaction_handler
|
|
480
|
+
@redaction_handler
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
# Set to a function which can get the 'owner' of a class. This is
|
|
484
|
+
# used in reporting deserialization errors
|
|
485
|
+
#
|
|
486
|
+
# @param [Lambda, Proc, nil] value Proc that takes a class and
|
|
487
|
+
# produces its owner, or `nil` if it does not have one.
|
|
488
|
+
def self.class_owner_finder=(handler)
|
|
489
|
+
@class_owner_finder = handler
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
def self.class_owner_finder
|
|
493
|
+
@class_owner_finder
|
|
494
|
+
end
|
|
495
|
+
|
|
366
496
|
# Temporarily disable ruby warnings while executing the given block. This is
|
|
367
497
|
# useful when doing something that would normally cause a warning to be
|
|
368
498
|
# emitted in Ruby verbose mode ($VERBOSE = true).
|
|
@@ -398,7 +528,7 @@ module T::Configuration
|
|
|
398
528
|
# should be allowed. Useful to whitelist benign violations, like shim files
|
|
399
529
|
# generated for an autoloader.
|
|
400
530
|
def self.sealed_violation_whitelist=(sealed_violation_whitelist)
|
|
401
|
-
if
|
|
531
|
+
if !@sealed_violation_whitelist.nil?
|
|
402
532
|
raise ArgumentError.new("Cannot overwrite sealed_violation_whitelist after setting it")
|
|
403
533
|
end
|
|
404
534
|
|
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." \
|
|
@@ -240,7 +251,7 @@ class T::Enum
|
|
|
240
251
|
|
|
241
252
|
|
|
242
253
|
sig {params(serialized_val: SerializedVal).void}
|
|
243
|
-
|
|
254
|
+
def initialize(serialized_val=nil)
|
|
244
255
|
raise 'T::Enum is abstract' if self.class == T::Enum
|
|
245
256
|
if !self.class.started_initializing?
|
|
246
257
|
raise "Must instantiate all enum values of #{self.class} inside 'enums do'."
|
|
@@ -346,4 +357,15 @@ class T::Enum
|
|
|
346
357
|
|
|
347
358
|
raise "Inheriting from children of T::Enum is prohibited" if self != T::Enum
|
|
348
359
|
end
|
|
360
|
+
|
|
361
|
+
# Marshal support
|
|
362
|
+
sig {params(_level: Integer).returns(String)}
|
|
363
|
+
def _dump(_level)
|
|
364
|
+
Marshal.dump(serialize)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
sig {params(args: String).returns(T.attached_class)}
|
|
368
|
+
def self._load(args)
|
|
369
|
+
deserialize(Marshal.load(args))
|
|
370
|
+
end
|
|
349
371
|
end
|
data/lib/types/generic.rb
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
# typed: true
|
|
3
3
|
|
|
4
4
|
# Use as a mixin with extend (`extend T::Generic`).
|
|
5
|
-
# Docs at https://hackpad.corp.stripe.com/Type-Validation-in-pay-server-1JaoTHir5Mo.
|
|
6
5
|
module T::Generic
|
|
7
6
|
include T::Helpers
|
|
8
7
|
include Kernel
|
|
@@ -14,10 +13,10 @@ module T::Generic
|
|
|
14
13
|
end
|
|
15
14
|
|
|
16
15
|
def type_member(variance=:invariant, fixed: nil, lower: T.untyped, upper: BasicObject)
|
|
17
|
-
T::Types::TypeMember.new(variance)
|
|
16
|
+
T::Types::TypeMember.new(variance)
|
|
18
17
|
end
|
|
19
18
|
|
|
20
19
|
def type_template(variance=:invariant, fixed: nil, lower: T.untyped, upper: BasicObject)
|
|
21
|
-
T::Types::TypeTemplate.new(variance)
|
|
20
|
+
T::Types::TypeTemplate.new(variance)
|
|
22
21
|
end
|
|
23
22
|
end
|
data/lib/types/helpers.rb
CHANGED
|
@@ -33,7 +33,24 @@ module T::Helpers
|
|
|
33
33
|
# end
|
|
34
34
|
#
|
|
35
35
|
# Except that it is statically analyzed by sorbet.
|
|
36
|
-
def mixes_in_class_methods(mod)
|
|
37
|
-
Private::Mixins.declare_mixes_in_class_methods(self, mod)
|
|
36
|
+
def mixes_in_class_methods(mod, *mods)
|
|
37
|
+
Private::Mixins.declare_mixes_in_class_methods(self, [mod].concat(mods))
|
|
38
38
|
end
|
|
39
|
+
|
|
40
|
+
# Specify an inclusion or inheritance requirement for `self`.
|
|
41
|
+
#
|
|
42
|
+
# Example:
|
|
43
|
+
#
|
|
44
|
+
# module MyHelper
|
|
45
|
+
# extend T::Helpers
|
|
46
|
+
#
|
|
47
|
+
# requires_ancestor Kernel
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
# class MyClass < BasicObject # error: `MyClass` must include `Kernel` (required by `MyHelper`)
|
|
51
|
+
# include MyHelper
|
|
52
|
+
# end
|
|
53
|
+
#
|
|
54
|
+
# TODO: implement the checks in sorbet-runtime.
|
|
55
|
+
def requires_ancestor(mod, *mods); end
|
|
39
56
|
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
|
|
|
@@ -2,15 +2,26 @@
|
|
|
2
2
|
# typed: strict
|
|
3
3
|
|
|
4
4
|
module T::NonForcingConstants
|
|
5
|
+
T::Sig::WithoutRuntime.sig {returns(T::Boolean)}
|
|
6
|
+
private_class_method def self.stripe_packages_enabled?
|
|
7
|
+
const_defined?('StripePackages') && const_get('StripePackages').enabled?
|
|
8
|
+
end
|
|
9
|
+
|
|
5
10
|
# NOTE: This method is documented on the RBI in Sorbet's payload, so that it
|
|
6
11
|
# 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)
|
|
12
|
+
T::Sig::WithoutRuntime.sig {params(val: BasicObject, klass: String, package: T.nilable(String)).returns(T::Boolean)}
|
|
13
|
+
def self.non_forcing_is_a?(val, klass, package: nil)
|
|
9
14
|
method_name = "T::NonForcingConstants.non_forcing_is_a?"
|
|
10
15
|
if klass.empty?
|
|
11
16
|
raise ArgumentError.new("The string given to `#{method_name}` must not be empty")
|
|
12
17
|
end
|
|
13
18
|
|
|
19
|
+
# TODO(gdritter): this might not be want we want in a multipackage
|
|
20
|
+
# world; revisit this implementation we move on from the monopackage
|
|
21
|
+
if stripe_packages_enabled?
|
|
22
|
+
klass = "PkgRegistry::#{package}::#{klass}"
|
|
23
|
+
end
|
|
24
|
+
|
|
14
25
|
current_klass = T.let(nil, T.nilable(Module))
|
|
15
26
|
current_prefix = T.let(nil, T.nilable(String))
|
|
16
27
|
|
|
@@ -18,34 +29,41 @@ module T::NonForcingConstants
|
|
|
18
29
|
parts.each do |part|
|
|
19
30
|
if current_klass.nil?
|
|
20
31
|
# First iteration
|
|
21
|
-
if part != ""
|
|
32
|
+
if part != "" && package.nil?
|
|
33
|
+
# if we've supplied a package, we're probably running in
|
|
34
|
+
# package mode, which means absolute references are
|
|
35
|
+
# meaningless
|
|
22
36
|
raise ArgumentError.new("The string given to `#{method_name}` must be an absolute constant reference that starts with `::`")
|
|
23
37
|
end
|
|
24
38
|
|
|
25
39
|
current_klass = Object
|
|
26
40
|
current_prefix = ''
|
|
27
|
-
else
|
|
28
|
-
if current_klass.autoload?(part)
|
|
29
|
-
# There's an autoload registered for that constant, which means it's not
|
|
30
|
-
# yet loaded. `value` can't be an instance of something not yet loaded.
|
|
31
|
-
return false
|
|
32
|
-
end
|
|
33
41
|
|
|
34
|
-
#
|
|
35
|
-
|
|
36
|
-
if
|
|
37
|
-
|
|
38
|
-
end
|
|
42
|
+
# if this had a :: prefix, then there's no more loading to
|
|
43
|
+
# do---skip to the next one
|
|
44
|
+
next if part == ""
|
|
45
|
+
end
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
if current_klass.autoload?(part)
|
|
48
|
+
# There's an autoload registered for that constant, which means it's not
|
|
49
|
+
# yet loaded. `value` can't be an instance of something not yet loaded.
|
|
50
|
+
return false
|
|
51
|
+
end
|
|
42
52
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
53
|
+
# Sorbet guarantees that the string is an absolutely resolved name.
|
|
54
|
+
search_inheritance_chain = false
|
|
55
|
+
if !current_klass.const_defined?(part, search_inheritance_chain)
|
|
56
|
+
return false
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
current_klass = current_klass.const_get(part)
|
|
60
|
+
current_prefix = "#{current_prefix}::#{part}"
|
|
61
|
+
|
|
62
|
+
if !Module.===(current_klass)
|
|
63
|
+
raise ArgumentError.new("#{current_prefix} is not a class or module")
|
|
46
64
|
end
|
|
47
65
|
end
|
|
48
66
|
|
|
49
|
-
|
|
67
|
+
current_klass.===(val)
|
|
50
68
|
end
|
|
51
69
|
end
|
|
@@ -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)
|
|
@@ -111,16 +111,16 @@ module T::Private::Abstract::Validate
|
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
private_class_method def self.describe_method(method, show_owner: true)
|
|
114
|
-
if method.source_location
|
|
115
|
-
|
|
114
|
+
loc = if method.source_location
|
|
115
|
+
method.source_location.join(':')
|
|
116
116
|
else
|
|
117
|
-
|
|
117
|
+
"<unknown location>"
|
|
118
118
|
end
|
|
119
119
|
|
|
120
|
-
if show_owner
|
|
121
|
-
|
|
120
|
+
owner = if show_owner
|
|
121
|
+
" declared in #{method.owner}"
|
|
122
122
|
else
|
|
123
|
-
|
|
123
|
+
""
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
" * `#{method.name}`#{owner} at #{loc}"
|
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)
|
data/lib/types/private/final.rb
CHANGED
|
@@ -37,7 +37,6 @@ module T::Private::Final
|
|
|
37
37
|
mod.extend(mod.is_a?(Class) ? NoInherit : NoIncludeExtend)
|
|
38
38
|
mark_as_final_module(mod)
|
|
39
39
|
mark_as_final_module(mod.singleton_class)
|
|
40
|
-
T::Private::Methods.add_module_with_final(mod)
|
|
41
40
|
T::Private::Methods.install_hooks(mod)
|
|
42
41
|
end
|
|
43
42
|
|