sorbet-runtime 0.0.1.pre.prealpha → 0.4.4253
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/sorbet-runtime.rb +100 -0
- data/lib/types/_types.rb +245 -0
- data/lib/types/abstract_utils.rb +50 -0
- data/lib/types/boolean.rb +8 -0
- data/lib/types/compatibility_patches.rb +37 -0
- data/lib/types/configuration.rb +368 -0
- data/lib/types/generic.rb +23 -0
- data/lib/types/helpers.rb +31 -0
- data/lib/types/interface_wrapper.rb +158 -0
- data/lib/types/private/abstract/data.rb +36 -0
- data/lib/types/private/abstract/declare.rb +39 -0
- data/lib/types/private/abstract/hooks.rb +43 -0
- data/lib/types/private/abstract/validate.rb +128 -0
- data/lib/types/private/casts.rb +22 -0
- data/lib/types/private/class_utils.rb +102 -0
- data/lib/types/private/decl_state.rb +18 -0
- data/lib/types/private/error_handler.rb +37 -0
- data/lib/types/private/methods/_methods.rb +344 -0
- data/lib/types/private/methods/call_validation.rb +1177 -0
- data/lib/types/private/methods/decl_builder.rb +275 -0
- data/lib/types/private/methods/modes.rb +18 -0
- data/lib/types/private/methods/signature.rb +196 -0
- data/lib/types/private/methods/signature_validation.rb +232 -0
- data/lib/types/private/mixins/mixins.rb +27 -0
- data/lib/types/private/runtime_levels.rb +41 -0
- data/lib/types/private/types/not_typed.rb +23 -0
- data/lib/types/private/types/string_holder.rb +26 -0
- data/lib/types/private/types/void.rb +33 -0
- data/lib/types/profile.rb +27 -0
- data/lib/types/props/_props.rb +165 -0
- data/lib/types/props/constructor.rb +20 -0
- data/lib/types/props/custom_type.rb +84 -0
- data/lib/types/props/decorator.rb +826 -0
- data/lib/types/props/errors.rb +8 -0
- data/lib/types/props/optional.rb +73 -0
- data/lib/types/props/plugin.rb +15 -0
- data/lib/types/props/pretty_printable.rb +106 -0
- data/lib/types/props/serializable.rb +376 -0
- data/lib/types/props/type_validation.rb +98 -0
- data/lib/types/props/utils.rb +49 -0
- data/lib/types/props/weak_constructor.rb +30 -0
- data/lib/types/runtime_profiled.rb +36 -0
- data/lib/types/sig.rb +28 -0
- data/lib/types/struct.rb +8 -0
- data/lib/types/types/base.rb +141 -0
- data/lib/types/types/class_of.rb +38 -0
- data/lib/types/types/enum.rb +42 -0
- data/lib/types/types/fixed_array.rb +60 -0
- data/lib/types/types/fixed_hash.rb +59 -0
- data/lib/types/types/intersection.rb +36 -0
- data/lib/types/types/noreturn.rb +25 -0
- data/lib/types/types/proc.rb +51 -0
- data/lib/types/types/self_type.rb +31 -0
- data/lib/types/types/simple.rb +33 -0
- data/lib/types/types/type_member.rb +7 -0
- data/lib/types/types/type_parameter.rb +23 -0
- data/lib/types/types/type_template.rb +7 -0
- data/lib/types/types/type_variable.rb +31 -0
- data/lib/types/types/typed_array.rb +20 -0
- data/lib/types/types/typed_enumerable.rb +141 -0
- data/lib/types/types/typed_enumerator.rb +22 -0
- data/lib/types/types/typed_hash.rb +29 -0
- data/lib/types/types/typed_range.rb +22 -0
- data/lib/types/types/typed_set.rb +22 -0
- data/lib/types/types/union.rb +59 -0
- data/lib/types/types/untyped.rb +25 -0
- data/lib/types/utils.rb +223 -0
- metadata +122 -15
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
module T::Props::TypeValidation
|
5
|
+
include T::Props::Plugin
|
6
|
+
|
7
|
+
BANNED_TYPES = [Object, BasicObject, Kernel]
|
8
|
+
|
9
|
+
class UnderspecifiedType < ArgumentError; end
|
10
|
+
|
11
|
+
module DecoratorMethods
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
sig {returns(T::Array[Symbol])}
|
15
|
+
def valid_props
|
16
|
+
super + [:DEPRECATED_underspecified_type]
|
17
|
+
end
|
18
|
+
|
19
|
+
sig do
|
20
|
+
params(
|
21
|
+
name: T.any(Symbol, String),
|
22
|
+
_cls: Module,
|
23
|
+
rules: T::Hash[Symbol, T.untyped],
|
24
|
+
type: T.any(T::Types::Base, Module)
|
25
|
+
)
|
26
|
+
.void
|
27
|
+
end
|
28
|
+
def prop_validate_definition!(name, _cls, rules, type)
|
29
|
+
super
|
30
|
+
|
31
|
+
if !rules[:DEPRECATED_underspecified_type] && !(type.singleton_class <= T::Props::CustomType)
|
32
|
+
validate_type(type, field_name: name)
|
33
|
+
elsif rules[:DEPRECATED_underspecified_type] && find_invalid_subtype(type).nil?
|
34
|
+
raise ArgumentError.new("DEPRECATED_underspecified_type set unnecessarily for #{@class.name}.#{name} - #{type} is a valid type")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
sig do
|
39
|
+
params(
|
40
|
+
type: T::Types::Base,
|
41
|
+
field_name: T.any(Symbol, String),
|
42
|
+
)
|
43
|
+
.void
|
44
|
+
end
|
45
|
+
private def validate_type(type, field_name:)
|
46
|
+
if (invalid_subtype = find_invalid_subtype(type))
|
47
|
+
raise UnderspecifiedType.new(type_error_message(invalid_subtype, field_name, type))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns an invalid type, if any, found in the given top-level type.
|
52
|
+
# This might be the type itself, if it is e.g. "Object", or might be
|
53
|
+
# a subtype like the type of the values of a typed hash.
|
54
|
+
#
|
55
|
+
# If the type is fully valid, returns nil.
|
56
|
+
sig {params(type: T::Types::Base).returns(T.nilable(T::Types::Base))}
|
57
|
+
private def find_invalid_subtype(type)
|
58
|
+
case type
|
59
|
+
when T::Types::TypedEnumerable
|
60
|
+
find_invalid_subtype(type.type)
|
61
|
+
when T::Types::FixedHash
|
62
|
+
type.types.values.map {|subtype| find_invalid_subtype(subtype)}.compact.first
|
63
|
+
when T::Types::Union, T::Types::FixedArray
|
64
|
+
type.types.map {|subtype| find_invalid_subtype(subtype)}.compact.first
|
65
|
+
when T::Types::Enum, T::Types::ClassOf
|
66
|
+
nil
|
67
|
+
when T::Types::Simple
|
68
|
+
# TODO Could we manage to define a whitelist, consisting of something
|
69
|
+
# like primitives, subdocs, DataInterfaces, and collections/enums/unions
|
70
|
+
# thereof?
|
71
|
+
if BANNED_TYPES.include?(type.raw_type)
|
72
|
+
type
|
73
|
+
else
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
else
|
77
|
+
type
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
sig do
|
82
|
+
params(
|
83
|
+
type: T::Types::Base,
|
84
|
+
field_name: T.any(Symbol, String),
|
85
|
+
orig_type: T::Types::Base,
|
86
|
+
)
|
87
|
+
.returns(String)
|
88
|
+
end
|
89
|
+
private def type_error_message(type, field_name, orig_type)
|
90
|
+
msg_prefix = "#{@class.name}.#{field_name}: #{orig_type} is invalid in prop definition"
|
91
|
+
if type == orig_type
|
92
|
+
"#{msg_prefix}. Please choose a more specific type (T.untyped and ~equivalents like Object are banned)."
|
93
|
+
else
|
94
|
+
"#{msg_prefix}. Please choose a subtype more specific than #{type} (T.untyped and ~equivalents like Object are banned)."
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Props::Utils
|
5
|
+
# Deep copy an object. The object must consist of Ruby primitive
|
6
|
+
# types and Hashes and Arrays.
|
7
|
+
def self.deep_clone_object(what, freeze: false)
|
8
|
+
result = case what
|
9
|
+
when true
|
10
|
+
true
|
11
|
+
when false
|
12
|
+
false
|
13
|
+
when Symbol, NilClass, Numeric
|
14
|
+
what
|
15
|
+
when Array
|
16
|
+
what.map {|v| deep_clone_object(v, freeze: freeze)}
|
17
|
+
when Hash
|
18
|
+
h = what.class.new
|
19
|
+
what.each do |k, v|
|
20
|
+
k.freeze if freeze
|
21
|
+
h[k] = deep_clone_object(v, freeze: freeze)
|
22
|
+
end
|
23
|
+
h
|
24
|
+
when Regexp
|
25
|
+
what.dup
|
26
|
+
else
|
27
|
+
# Some unfortunate nastiness to get around Opus::Enum potentially not
|
28
|
+
# being defined.
|
29
|
+
if defined?(Opus) && defined?(Opus::Enum) && what.class == Opus::Enum
|
30
|
+
what
|
31
|
+
else
|
32
|
+
what.clone
|
33
|
+
end
|
34
|
+
end
|
35
|
+
freeze ? result.freeze : result
|
36
|
+
end
|
37
|
+
|
38
|
+
# The prop_rules indicate whether we should check for reading a nil value for the prop/field.
|
39
|
+
# This is mostly for the compatibility check that we allow existing documents carry some nil prop/field.
|
40
|
+
def self.need_nil_read_check?(prop_rules)
|
41
|
+
# . :on_load allows nil read, but we need to check for the read for future writes
|
42
|
+
prop_rules[:optional] == :on_load || prop_rules[:raise_on_nil_write]
|
43
|
+
end
|
44
|
+
|
45
|
+
# The prop_rules indicate whether we should check for writing a nil value for the prop/field.
|
46
|
+
def self.need_nil_write_check?(prop_rules)
|
47
|
+
need_nil_read_check?(prop_rules) || T::Utils::Props.required_prop?(prop_rules)
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
module T::Props::WeakConstructor
|
5
|
+
include T::Props::Optional
|
6
|
+
|
7
|
+
def initialize(hash={})
|
8
|
+
expected_keys = {}
|
9
|
+
hash.each_key {|key| expected_keys[key] = true}
|
10
|
+
|
11
|
+
decorator = self.class.decorator
|
12
|
+
|
13
|
+
decorator.props.each do |p, rules|
|
14
|
+
if hash.key?(p)
|
15
|
+
expected_keys.delete(p)
|
16
|
+
val = hash[p]
|
17
|
+
elsif decorator.has_default?(rules)
|
18
|
+
val = decorator.get_default(rules, self.class)
|
19
|
+
else
|
20
|
+
next
|
21
|
+
end
|
22
|
+
|
23
|
+
decorator.prop_set(self, p, val, rules)
|
24
|
+
end
|
25
|
+
|
26
|
+
unless expected_keys.empty?
|
27
|
+
raise ArgumentError.new("#{@class}: Unrecognized properties in #with_props: #{expected_keys.keys.join(', ')}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# Hey there!
|
6
|
+
#
|
7
|
+
# T::Utils::Runtime is just a random class. Chances are[^1] it's not a class
|
8
|
+
# that your variables are instances of, which means that using it in a type
|
9
|
+
# signature will always create a type error.
|
10
|
+
#
|
11
|
+
# At first, it doesn't seem like a good thing to have a type that's sole
|
12
|
+
# purpose is to cause type errors. The trick is that within the ruby-types
|
13
|
+
# team, we only use T::Utils::RuntimeProfiled within 'generated' sigs.
|
14
|
+
#
|
15
|
+
# Unlike normal sigs, generated sigs never raise at runtime. They also log the
|
16
|
+
# actual, observed type on type error to a central location. We're using these
|
17
|
+
# observed types to refine and expand our type coverage in pay-server.
|
18
|
+
#
|
19
|
+
# What does this all mean for you?
|
20
|
+
#
|
21
|
+
# - If you were just curious, that's it! Leave the sig as is, and carry on.
|
22
|
+
# - If you wanted to replace this sig with a better, hand-authored one:
|
23
|
+
#
|
24
|
+
# 1. Remove 'generated' from the sig.
|
25
|
+
# 2. Update the sig to your liking
|
26
|
+
#
|
27
|
+
# Questions? :portal-to: => #ruby-types
|
28
|
+
#
|
29
|
+
# [^1]: Unless you happen to be calling T::Utils::RuntimeProfiled.new directly...
|
30
|
+
#
|
31
|
+
|
32
|
+
module T; end
|
33
|
+
module T::Utils; end
|
34
|
+
# Sorbet guesses this type instead of T.untyped when passed --suggest-runtime-profiled
|
35
|
+
|
36
|
+
class T::Utils::RuntimeProfiled; end
|
data/lib/types/sig.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
# rubocop:disable PrisonGuard/NoTopLevelDeclarations, PrisonGuard/PackageMatchesDirectory
|
4
|
+
|
5
|
+
# Used as a mixin to any class so that you can call `sig`.
|
6
|
+
# Docs at http://go/types
|
7
|
+
module T::Sig
|
8
|
+
module WithoutRuntime
|
9
|
+
# At runtime, does nothing, but statically it is treated exactly the same
|
10
|
+
# as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
|
11
|
+
def self.sig(&blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
|
12
|
+
|
13
|
+
# At runtime, does nothing, but statically it is treated exactly the same
|
14
|
+
# as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
|
15
|
+
T::Sig::WithoutRuntime.sig {params(blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void} # rubocop:disable PrisonGuard/PrivateModule
|
16
|
+
def self.sig(&blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride, Lint/DuplicateMethods
|
17
|
+
end
|
18
|
+
|
19
|
+
# Declares a method with type signatures and/or
|
20
|
+
# abstract/override/... helpers. See the documentation URL on
|
21
|
+
# {T::Helpers}
|
22
|
+
T::Sig::WithoutRuntime.sig {params(blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
|
23
|
+
def sig(&blk) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
|
24
|
+
T::Private::Methods.declare_sig(self, &blk)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# rubocop:enable PrisonGuard/NoTopLevelDeclarations, PrisonGuard/PackageMatchesDirectory
|
data/lib/types/struct.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
class Base
|
6
|
+
def self.method_added(method_name)
|
7
|
+
# What is now `subtype_of_single?` used to be named `subtype_of?`. Make sure people don't
|
8
|
+
# override the wrong thing.
|
9
|
+
#
|
10
|
+
# NB: Outside of T::Types, we would enforce this by using `sig` and not declaring the method
|
11
|
+
# as overridable, but doing so here would result in a dependency cycle.
|
12
|
+
if method_name == :subtype_of? && self != T::Types::Base
|
13
|
+
raise "`subtype_of?` should not be overridden. You probably want to override " \
|
14
|
+
"`subtype_of_single?` instead."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid?(obj)
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [T::Boolean] This method must be implemented to return whether the subclass is a subtype
|
23
|
+
# of `type`. This should only be called by `subtype_of?`, which guarantees that `type` will be
|
24
|
+
# a "single" type, by which we mean it won't be a Union or an Intersection (c.f.
|
25
|
+
# `isSubTypeSingle` in sorbet).
|
26
|
+
private def subtype_of_single?(type)
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
|
30
|
+
# Equality is based on name, so be sure the name reflects all relevant state when implementing.
|
31
|
+
def name
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
35
|
+
# Mirrors ruby_typer::core::Types::isSubType
|
36
|
+
# See https://git.corp.stripe.com/stripe-internal/ruby-typer/blob/9fc8ed998c04ac0b96592ae6bb3493b8a925c5c1/core/types/subtyping.cc#L912-L950
|
37
|
+
#
|
38
|
+
# This method cannot be overridden (see `method_added` above).
|
39
|
+
# Subclasses only need to implement `subtype_of_single?`).
|
40
|
+
def subtype_of?(t2)
|
41
|
+
t1 = self
|
42
|
+
|
43
|
+
# pairs to cover: 1 (_, _)
|
44
|
+
# 2 (_, And)
|
45
|
+
# 3 (_, Or)
|
46
|
+
# 4 (And, _)
|
47
|
+
# 5 (And, And)
|
48
|
+
# 6 (And, Or)
|
49
|
+
# 7 (Or, _)
|
50
|
+
# 8 (Or, And)
|
51
|
+
# 9 (Or, Or)
|
52
|
+
|
53
|
+
# Note: order of cases here matters!
|
54
|
+
if t1.is_a?(T::Types::Union) # 7, 8, 9
|
55
|
+
# this will be incorrect if/when we have Type members
|
56
|
+
return t1.types.all? {|t1_member| t1_member.subtype_of?(t2)}
|
57
|
+
end
|
58
|
+
|
59
|
+
if t2.is_a?(T::Types::Intersection) # 2, 5
|
60
|
+
# this will be incorrect if/when we have Type members
|
61
|
+
return t2.types.all? {|t2_member| t1.subtype_of?(t2_member)}
|
62
|
+
end
|
63
|
+
|
64
|
+
if t2.is_a?(T::Types::Union)
|
65
|
+
if t1.is_a?(T::Types::Intersection) # 6
|
66
|
+
# dropping either of parts eagerly make subtype test be too strict.
|
67
|
+
# we have to try both cases, when we normally try only one
|
68
|
+
return t2.types.any? {|t2_member| t1.subtype_of?(t2_member)} ||
|
69
|
+
t1.types.any? {|t1_member| t1_member.subtype_of?(t2)}
|
70
|
+
end
|
71
|
+
return t2.types.any? {|t2_member| t1.subtype_of?(t2_member)} # 3
|
72
|
+
end
|
73
|
+
|
74
|
+
if t1.is_a?(T::Types::Intersection) # 4
|
75
|
+
# this will be incorrect if/when we have Type members
|
76
|
+
return t1.types.any? {|t1_member| t1_member.subtype_of?(t2)}
|
77
|
+
end
|
78
|
+
|
79
|
+
# 1; Start with some special cases
|
80
|
+
if t1.is_a?(T::Private::Types::Void)
|
81
|
+
return t2.is_a?(T::Private::Types::Void)
|
82
|
+
end
|
83
|
+
|
84
|
+
if t1.is_a?(T::Types::Untyped) || t2.is_a?(T::Types::Untyped)
|
85
|
+
return true
|
86
|
+
end
|
87
|
+
|
88
|
+
# Rest of (1)
|
89
|
+
subtype_of_single?(t2)
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_s
|
93
|
+
name
|
94
|
+
end
|
95
|
+
|
96
|
+
def describe_obj(obj)
|
97
|
+
# Would be redundant to print class and value in these common cases.
|
98
|
+
case obj
|
99
|
+
when nil, true, false
|
100
|
+
return "type #{obj.class}"
|
101
|
+
end
|
102
|
+
|
103
|
+
# In rare cases, obj.inspect may fail, or be undefined, so rescue.
|
104
|
+
begin
|
105
|
+
# Default inspect behavior of, eg; `#<Object:0x0...>` is ugly; just print the hash instead, which is more concise/readable.
|
106
|
+
if obj.method(:inspect).owner == Kernel
|
107
|
+
"type #{obj.class} with hash #{obj.hash}"
|
108
|
+
else
|
109
|
+
"type #{obj.class} with value #{T::Utils.string_truncate_middle(obj.inspect, 30, 30)}"
|
110
|
+
end
|
111
|
+
rescue StandardError, SystemStackError
|
112
|
+
"type #{obj.class} with unprintable value"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def error_message_for_obj(obj)
|
117
|
+
if valid?(obj)
|
118
|
+
nil
|
119
|
+
else
|
120
|
+
"Expected type #{self.name}, got #{describe_obj(obj)}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def validate!(obj)
|
125
|
+
err = error_message_for_obj(obj)
|
126
|
+
raise TypeError.new(err) if err
|
127
|
+
end
|
128
|
+
|
129
|
+
### Equality methods (necessary for deduping types with `uniq`)
|
130
|
+
|
131
|
+
def hash
|
132
|
+
name.hash
|
133
|
+
end
|
134
|
+
|
135
|
+
def ==(other)
|
136
|
+
other.class == self.class && other.name == self.name
|
137
|
+
end
|
138
|
+
|
139
|
+
alias_method :eql?, :==
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
# Validates that an object belongs to the specified class.
|
6
|
+
class ClassOf < Base
|
7
|
+
attr_reader :type
|
8
|
+
|
9
|
+
def initialize(type)
|
10
|
+
@type = type
|
11
|
+
end
|
12
|
+
|
13
|
+
# @override Base
|
14
|
+
def name
|
15
|
+
"T.class_of(#{@type})"
|
16
|
+
end
|
17
|
+
|
18
|
+
# @override Base
|
19
|
+
def valid?(obj)
|
20
|
+
obj.is_a?(Module) && obj <= @type
|
21
|
+
end
|
22
|
+
|
23
|
+
# @override Base
|
24
|
+
def subtype_of_single?(other)
|
25
|
+
case other
|
26
|
+
when ClassOf
|
27
|
+
@type <= other.type
|
28
|
+
else
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @override Base
|
34
|
+
def describe_obj(obj)
|
35
|
+
obj.inspect
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
# validates that the provided value is within a given set/enum
|
6
|
+
class Enum < Base
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
attr_reader :values
|
10
|
+
|
11
|
+
# TODO Ideally Hash would not be accepted but there are a lot of uses with prop enum.
|
12
|
+
sig {params(values: T.any(Array, Set, Hash, T::Range[T.untyped])).void}
|
13
|
+
def initialize(values)
|
14
|
+
@values = values
|
15
|
+
end
|
16
|
+
|
17
|
+
# @override Base
|
18
|
+
def valid?(obj)
|
19
|
+
@values.member?(obj)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @override Base
|
23
|
+
private def subtype_of_single?(other)
|
24
|
+
case other
|
25
|
+
when Enum
|
26
|
+
(other.values - @values).empty?
|
27
|
+
else
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# @override Base
|
33
|
+
def name
|
34
|
+
"T.enum([#{@values.map(&:inspect).join(', ')}])"
|
35
|
+
end
|
36
|
+
|
37
|
+
# @override Base
|
38
|
+
def describe_obj(obj)
|
39
|
+
obj.inspect
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|