sorbet-runtime 0.5.5841
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 +7 -0
- data/lib/sorbet-runtime.rb +116 -0
- data/lib/types/_types.rb +285 -0
- data/lib/types/abstract_utils.rb +50 -0
- data/lib/types/boolean.rb +8 -0
- data/lib/types/compatibility_patches.rb +95 -0
- data/lib/types/configuration.rb +428 -0
- data/lib/types/enum.rb +349 -0
- data/lib/types/generic.rb +23 -0
- data/lib/types/helpers.rb +39 -0
- data/lib/types/interface_wrapper.rb +158 -0
- data/lib/types/non_forcing_constants.rb +51 -0
- data/lib/types/private/abstract/data.rb +36 -0
- data/lib/types/private/abstract/declare.rb +48 -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 +111 -0
- data/lib/types/private/decl_state.rb +30 -0
- data/lib/types/private/final.rb +51 -0
- data/lib/types/private/methods/_methods.rb +460 -0
- data/lib/types/private/methods/call_validation.rb +1149 -0
- data/lib/types/private/methods/decl_builder.rb +228 -0
- data/lib/types/private/methods/modes.rb +16 -0
- data/lib/types/private/methods/signature.rb +196 -0
- data/lib/types/private/methods/signature_validation.rb +229 -0
- data/lib/types/private/mixins/mixins.rb +27 -0
- data/lib/types/private/retry.rb +10 -0
- data/lib/types/private/runtime_levels.rb +56 -0
- data/lib/types/private/sealed.rb +65 -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/type_alias.rb +26 -0
- data/lib/types/private/types/void.rb +34 -0
- data/lib/types/profile.rb +31 -0
- data/lib/types/props/_props.rb +161 -0
- data/lib/types/props/constructor.rb +40 -0
- data/lib/types/props/custom_type.rb +108 -0
- data/lib/types/props/decorator.rb +672 -0
- data/lib/types/props/errors.rb +8 -0
- data/lib/types/props/generated_code_validation.rb +268 -0
- data/lib/types/props/has_lazily_specialized_methods.rb +92 -0
- data/lib/types/props/optional.rb +81 -0
- data/lib/types/props/plugin.rb +37 -0
- data/lib/types/props/pretty_printable.rb +107 -0
- data/lib/types/props/private/apply_default.rb +170 -0
- data/lib/types/props/private/deserializer_generator.rb +165 -0
- data/lib/types/props/private/parser.rb +32 -0
- data/lib/types/props/private/serde_transform.rb +192 -0
- data/lib/types/props/private/serializer_generator.rb +77 -0
- data/lib/types/props/private/setter_factory.rb +134 -0
- data/lib/types/props/serializable.rb +330 -0
- data/lib/types/props/type_validation.rb +111 -0
- data/lib/types/props/utils.rb +59 -0
- data/lib/types/props/weak_constructor.rb +67 -0
- data/lib/types/runtime_profiled.rb +24 -0
- data/lib/types/sig.rb +30 -0
- data/lib/types/struct.rb +18 -0
- data/lib/types/types/attached_class.rb +37 -0
- data/lib/types/types/base.rb +151 -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 +37 -0
- data/lib/types/types/noreturn.rb +29 -0
- data/lib/types/types/proc.rb +51 -0
- data/lib/types/types/self_type.rb +35 -0
- data/lib/types/types/simple.rb +33 -0
- data/lib/types/types/t_enum.rb +38 -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 +34 -0
- data/lib/types/types/typed_enumerable.rb +161 -0
- data/lib/types/types/typed_enumerator.rb +36 -0
- data/lib/types/types/typed_hash.rb +43 -0
- data/lib/types/types/typed_range.rb +26 -0
- data/lib/types/types/typed_set.rb +36 -0
- data/lib/types/types/union.rb +56 -0
- data/lib/types/types/untyped.rb +29 -0
- data/lib/types/utils.rb +217 -0
- metadata +223 -0
@@ -0,0 +1,111 @@
|
|
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 {params(key: Symbol).returns(T::Boolean).checked(:never)}
|
15
|
+
def valid_rule_key?(key)
|
16
|
+
super || :DEPRECATED_underspecified_type == key
|
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]
|
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
|
+
#
|
57
|
+
# checked(:never) - called potentially many times recursively
|
58
|
+
sig {params(type: T::Types::Base).returns(T.nilable(T::Types::Base)).checked(:never)}
|
59
|
+
private def find_invalid_subtype(type)
|
60
|
+
case type
|
61
|
+
when T::Types::TypedEnumerable
|
62
|
+
find_invalid_subtype(type.type)
|
63
|
+
when T::Types::FixedHash
|
64
|
+
type.types.values.map {|subtype| find_invalid_subtype(subtype)}.compact.first
|
65
|
+
when T::Types::Union, T::Types::FixedArray
|
66
|
+
# `T.any` is valid if all of the members are valid
|
67
|
+
type.types.map {|subtype| find_invalid_subtype(subtype)}.compact.first
|
68
|
+
when T::Types::Intersection
|
69
|
+
# `T.all` is valid if at least one of the members is valid
|
70
|
+
invalid = type.types.map {|subtype| find_invalid_subtype(subtype)}.compact
|
71
|
+
if invalid.length == type.types.length
|
72
|
+
invalid.first
|
73
|
+
else
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
when T::Types::Enum, T::Types::ClassOf
|
77
|
+
nil
|
78
|
+
when T::Private::Types::TypeAlias
|
79
|
+
find_invalid_subtype(type.aliased_type)
|
80
|
+
when T::Types::Simple
|
81
|
+
# TODO Could we manage to define a whitelist, consisting of something
|
82
|
+
# like primitives, subdocs, DataInterfaces, and collections/enums/unions
|
83
|
+
# thereof?
|
84
|
+
if BANNED_TYPES.include?(type.raw_type)
|
85
|
+
type
|
86
|
+
else
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
else
|
90
|
+
type
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
sig do
|
95
|
+
params(
|
96
|
+
type: T::Types::Base,
|
97
|
+
field_name: T.any(Symbol, String),
|
98
|
+
orig_type: T::Types::Base,
|
99
|
+
)
|
100
|
+
.returns(String)
|
101
|
+
end
|
102
|
+
private def type_error_message(type, field_name, orig_type)
|
103
|
+
msg_prefix = "#{@class.name}.#{field_name}: #{orig_type} is invalid in prop definition"
|
104
|
+
if type == orig_type
|
105
|
+
"#{msg_prefix}. Please choose a more specific type (T.untyped and ~equivalents like Object are banned)."
|
106
|
+
else
|
107
|
+
"#{msg_prefix}. Please choose a subtype more specific than #{type} (T.untyped and ~equivalents like Object are banned)."
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,59 @@
|
|
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
|
+
when T::Enum
|
27
|
+
what
|
28
|
+
else
|
29
|
+
what.clone
|
30
|
+
end
|
31
|
+
freeze ? result.freeze : result
|
32
|
+
end
|
33
|
+
|
34
|
+
# The prop_rules indicate whether we should check for reading a nil value for the prop/field.
|
35
|
+
# This is mostly for the compatibility check that we allow existing documents carry some nil prop/field.
|
36
|
+
def self.need_nil_read_check?(prop_rules)
|
37
|
+
# . :on_load allows nil read, but we need to check for the read for future writes
|
38
|
+
prop_rules[:optional] == :on_load || prop_rules[:raise_on_nil_write]
|
39
|
+
end
|
40
|
+
|
41
|
+
# The prop_rules indicate whether we should check for writing a nil value for the prop/field.
|
42
|
+
def self.need_nil_write_check?(prop_rules)
|
43
|
+
need_nil_read_check?(prop_rules) || T::Props::Utils.required_prop?(prop_rules)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.required_prop?(prop_rules)
|
47
|
+
# Clients should never reference :_tnilable as the implementation can change.
|
48
|
+
!prop_rules[:_tnilable]
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.optional_prop?(prop_rules)
|
52
|
+
# Clients should never reference :_tnilable as the implementation can change.
|
53
|
+
!!prop_rules[:_tnilable]
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.merge_serialized_optional_rule(prop_rules)
|
57
|
+
{'_tnilable' => true}.merge(prop_rules.merge('_tnilable' => true))
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
module T::Props::WeakConstructor
|
5
|
+
include T::Props::Optional
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
# checked(:never) - O(runtime object construction)
|
9
|
+
sig {params(hash: T::Hash[Symbol, T.untyped]).void.checked(:never)}
|
10
|
+
def initialize(hash={})
|
11
|
+
decorator = self.class.decorator
|
12
|
+
|
13
|
+
hash_keys_matching_props = decorator.construct_props_with_defaults(self, hash) +
|
14
|
+
decorator.construct_props_without_defaults(self, hash)
|
15
|
+
|
16
|
+
if hash_keys_matching_props < hash.size
|
17
|
+
raise ArgumentError.new("#{self.class}: Unrecognized properties: #{(hash.keys - decorator.props.keys).join(', ')}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module T::Props::WeakConstructor::DecoratorMethods
|
23
|
+
extend T::Sig
|
24
|
+
|
25
|
+
# Set values for all props that have no defaults. Ignore any not present.
|
26
|
+
#
|
27
|
+
# @return [Integer] A count of props that we successfully initialized (which
|
28
|
+
# we'll use to check for any unrecognized input.)
|
29
|
+
#
|
30
|
+
# checked(:never) - O(runtime object construction)
|
31
|
+
sig {params(instance: T::Props::WeakConstructor, hash: T::Hash[Symbol, T.untyped]).returns(Integer).checked(:never)}
|
32
|
+
def construct_props_without_defaults(instance, hash)
|
33
|
+
# Use `each_pair` rather than `count` because, as of Ruby 2.6, the latter delegates to Enumerator
|
34
|
+
# and therefore allocates for each entry.
|
35
|
+
result = 0
|
36
|
+
@props_without_defaults&.each_pair do |p, setter_proc|
|
37
|
+
if hash.key?(p)
|
38
|
+
instance.instance_exec(hash[p], &setter_proc)
|
39
|
+
result += 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
# Set values for all props that have defaults. Use the default if and only if
|
46
|
+
# the prop key isn't in the input.
|
47
|
+
#
|
48
|
+
# @return [Integer] A count of props that we successfully initialized (which
|
49
|
+
# we'll use to check for any unrecognized input.)
|
50
|
+
#
|
51
|
+
# checked(:never) - O(runtime object construction)
|
52
|
+
sig {params(instance: T::Props::WeakConstructor, hash: T::Hash[Symbol, T.untyped]).returns(Integer).checked(:never)}
|
53
|
+
def construct_props_with_defaults(instance, hash)
|
54
|
+
# Use `each_pair` rather than `count` because, as of Ruby 2.6, the latter delegates to Enumerator
|
55
|
+
# and therefore allocates for each entry.
|
56
|
+
result = 0
|
57
|
+
@props_with_defaults&.each_pair do |p, default_struct|
|
58
|
+
if hash.key?(p)
|
59
|
+
instance.instance_exec(hash[p], &default_struct.setter_proc)
|
60
|
+
result += 1
|
61
|
+
else
|
62
|
+
default_struct.set_default(instance)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
result
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# From the static system, T::Utils::RuntimeProfiled is T.untyped.
|
6
|
+
#
|
7
|
+
# But from the runtime system, it's a random class (specifically, a class that
|
8
|
+
# normal programs currently don't have any instances of).
|
9
|
+
#
|
10
|
+
# Thus, T::Utils::RuntimeProfiled can be used to introduce runtime-only type
|
11
|
+
# errors. This seems like a bad idea, but it's not. It can be used to gather
|
12
|
+
# runtime type information from running code via a custom T::Configuration
|
13
|
+
# handler.
|
14
|
+
#
|
15
|
+
# This process has only ever been used at Stripe, and is likely to have rough
|
16
|
+
# edges. If you've managed to find your way here and you're curious to try it,
|
17
|
+
# please chat with us on Slack. There are no docs.
|
18
|
+
#
|
19
|
+
# See also: the --suggest-runtime-profiled flag to sorbet.
|
20
|
+
#
|
21
|
+
|
22
|
+
module T; end
|
23
|
+
module T::Utils; end
|
24
|
+
class T::Utils::RuntimeProfiled; end
|
data/lib/types/sig.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: strict
|
3
|
+
|
4
|
+
# Used as a mixin to any class so that you can call `sig`.
|
5
|
+
# Docs at https://sorbet.org/docs/sigs
|
6
|
+
module T::Sig
|
7
|
+
module WithoutRuntime
|
8
|
+
# At runtime, does nothing, but statically it is treated exactly the same
|
9
|
+
# as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
|
10
|
+
def self.sig(arg0=nil, &blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
|
11
|
+
|
12
|
+
original_verbose = $VERBOSE
|
13
|
+
$VERBOSE = false
|
14
|
+
|
15
|
+
# At runtime, does nothing, but statically it is treated exactly the same
|
16
|
+
# as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
|
17
|
+
T::Sig::WithoutRuntime.sig {params(arg0: T.nilable(Symbol), blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
|
18
|
+
def self.sig(arg0=nil, &blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride, Lint/DuplicateMethods
|
19
|
+
|
20
|
+
$VERBOSE = original_verbose
|
21
|
+
end
|
22
|
+
|
23
|
+
# Declares a method with type signatures and/or
|
24
|
+
# abstract/override/... helpers. See the documentation URL on
|
25
|
+
# {T::Helpers}
|
26
|
+
T::Sig::WithoutRuntime.sig {params(arg0: T.nilable(Symbol), blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
|
27
|
+
def sig(arg0=nil, &blk) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
|
28
|
+
T::Private::Methods.declare_sig(self, arg0, &blk)
|
29
|
+
end
|
30
|
+
end
|
data/lib/types/struct.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
class T::InexactStruct
|
5
|
+
include T::Props
|
6
|
+
include T::Props::Serializable
|
7
|
+
include T::Props::Constructor
|
8
|
+
end
|
9
|
+
|
10
|
+
class T::Struct < T::InexactStruct
|
11
|
+
def self.inherited(subclass)
|
12
|
+
super(subclass)
|
13
|
+
T::Private::ClassUtils.replace_method(subclass.singleton_class, :inherited) do |s|
|
14
|
+
super(s)
|
15
|
+
raise "#{self.name} is a subclass of T::Struct and cannot be subclassed"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
# Modeling AttachedClass properly at runtime would require additional
|
6
|
+
# tracking, so at runtime we permit all values and rely on the static checker.
|
7
|
+
# As AttachedClass is modeled statically as a type member on every singleton
|
8
|
+
# class, this is consistent with the runtime behavior for all type members.
|
9
|
+
class AttachedClassType < Base
|
10
|
+
|
11
|
+
def initialize(); end
|
12
|
+
|
13
|
+
# @override Base
|
14
|
+
def name
|
15
|
+
"AttachedClass"
|
16
|
+
end
|
17
|
+
|
18
|
+
# @override Base
|
19
|
+
def valid?(obj)
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
# @override Base
|
24
|
+
private def subtype_of_single?(other)
|
25
|
+
case other
|
26
|
+
when AttachedClassType
|
27
|
+
true
|
28
|
+
else
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module Private
|
34
|
+
INSTANCE = AttachedClassType.new.freeze
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
class Base
|
6
|
+
def self.method_added(method_name)
|
7
|
+
super(method_name)
|
8
|
+
# What is now `subtype_of_single?` used to be named `subtype_of?`. Make sure people don't
|
9
|
+
# override the wrong thing.
|
10
|
+
#
|
11
|
+
# NB: Outside of T::Types, we would enforce this by using `sig` and not declaring the method
|
12
|
+
# as overridable, but doing so here would result in a dependency cycle.
|
13
|
+
if method_name == :subtype_of? && self != T::Types::Base
|
14
|
+
raise "`subtype_of?` should not be overridden. You probably want to override " \
|
15
|
+
"`subtype_of_single?` instead."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid?(obj)
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [T::Boolean] This method must be implemented to return whether the subclass is a subtype
|
24
|
+
# of `type`. This should only be called by `subtype_of?`, which guarantees that `type` will be
|
25
|
+
# a "single" type, by which we mean it won't be a Union or an Intersection (c.f.
|
26
|
+
# `isSubTypeSingle` in sorbet).
|
27
|
+
private def subtype_of_single?(type)
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
# Equality is based on name, so be sure the name reflects all relevant state when implementing.
|
32
|
+
def name
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
# Mirrors ruby_typer::core::Types::isSubType
|
37
|
+
# See https://git.corp.stripe.com/stripe-internal/ruby-typer/blob/9fc8ed998c04ac0b96592ae6bb3493b8a925c5c1/core/types/subtyping.cc#L912-L950
|
38
|
+
#
|
39
|
+
# This method cannot be overridden (see `method_added` above).
|
40
|
+
# Subclasses only need to implement `subtype_of_single?`).
|
41
|
+
def subtype_of?(t2)
|
42
|
+
t1 = self
|
43
|
+
|
44
|
+
if t2.is_a?(T::Private::Types::TypeAlias)
|
45
|
+
t2 = t2.aliased_type
|
46
|
+
end
|
47
|
+
|
48
|
+
if t1.is_a?(T::Private::Types::TypeAlias)
|
49
|
+
return t1.aliased_type.subtype_of?(t2)
|
50
|
+
end
|
51
|
+
|
52
|
+
# pairs to cover: 1 (_, _)
|
53
|
+
# 2 (_, And)
|
54
|
+
# 3 (_, Or)
|
55
|
+
# 4 (And, _)
|
56
|
+
# 5 (And, And)
|
57
|
+
# 6 (And, Or)
|
58
|
+
# 7 (Or, _)
|
59
|
+
# 8 (Or, And)
|
60
|
+
# 9 (Or, Or)
|
61
|
+
|
62
|
+
# Note: order of cases here matters!
|
63
|
+
if t1.is_a?(T::Types::Union) # 7, 8, 9
|
64
|
+
# this will be incorrect if/when we have Type members
|
65
|
+
return t1.types.all? {|t1_member| t1_member.subtype_of?(t2)}
|
66
|
+
end
|
67
|
+
|
68
|
+
if t2.is_a?(T::Types::Intersection) # 2, 5
|
69
|
+
# this will be incorrect if/when we have Type members
|
70
|
+
return t2.types.all? {|t2_member| t1.subtype_of?(t2_member)}
|
71
|
+
end
|
72
|
+
|
73
|
+
if t2.is_a?(T::Types::Union)
|
74
|
+
if t1.is_a?(T::Types::Intersection) # 6
|
75
|
+
# dropping either of parts eagerly make subtype test be too strict.
|
76
|
+
# we have to try both cases, when we normally try only one
|
77
|
+
return t2.types.any? {|t2_member| t1.subtype_of?(t2_member)} ||
|
78
|
+
t1.types.any? {|t1_member| t1_member.subtype_of?(t2)}
|
79
|
+
end
|
80
|
+
return t2.types.any? {|t2_member| t1.subtype_of?(t2_member)} # 3
|
81
|
+
end
|
82
|
+
|
83
|
+
if t1.is_a?(T::Types::Intersection) # 4
|
84
|
+
# this will be incorrect if/when we have Type members
|
85
|
+
return t1.types.any? {|t1_member| t1_member.subtype_of?(t2)}
|
86
|
+
end
|
87
|
+
|
88
|
+
# 1; Start with some special cases
|
89
|
+
if t1.is_a?(T::Private::Types::Void)
|
90
|
+
return t2.is_a?(T::Private::Types::Void)
|
91
|
+
end
|
92
|
+
|
93
|
+
if t1.is_a?(T::Types::Untyped) || t2.is_a?(T::Types::Untyped)
|
94
|
+
return true
|
95
|
+
end
|
96
|
+
|
97
|
+
# Rest of (1)
|
98
|
+
subtype_of_single?(t2)
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_s
|
102
|
+
name
|
103
|
+
end
|
104
|
+
|
105
|
+
def describe_obj(obj)
|
106
|
+
# Would be redundant to print class and value in these common cases.
|
107
|
+
case obj
|
108
|
+
when nil, true, false
|
109
|
+
return "type #{obj.class}"
|
110
|
+
end
|
111
|
+
|
112
|
+
# In rare cases, obj.inspect may fail, or be undefined, so rescue.
|
113
|
+
begin
|
114
|
+
# Default inspect behavior of, eg; `#<Object:0x0...>` is ugly; just print the hash instead, which is more concise/readable.
|
115
|
+
if obj.method(:inspect).owner == Kernel
|
116
|
+
"type #{obj.class} with hash #{obj.hash}"
|
117
|
+
else
|
118
|
+
"type #{obj.class} with value #{T::Utils.string_truncate_middle(obj.inspect, 30, 30)}"
|
119
|
+
end
|
120
|
+
rescue StandardError, SystemStackError
|
121
|
+
"type #{obj.class} with unprintable value"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def error_message_for_obj(obj)
|
126
|
+
if valid?(obj)
|
127
|
+
nil
|
128
|
+
else
|
129
|
+
"Expected type #{self.name}, got #{describe_obj(obj)}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def validate!(obj)
|
134
|
+
err = error_message_for_obj(obj)
|
135
|
+
raise TypeError.new(err) if err
|
136
|
+
end
|
137
|
+
|
138
|
+
### Equality methods (necessary for deduping types with `uniq`)
|
139
|
+
|
140
|
+
def hash
|
141
|
+
name.hash
|
142
|
+
end
|
143
|
+
|
144
|
+
def ==(other)
|
145
|
+
(T::Utils.resolve_alias(other).class == T::Utils.resolve_alias(self).class) &&
|
146
|
+
other.name == self.name
|
147
|
+
end
|
148
|
+
|
149
|
+
alias_method :eql?, :==
|
150
|
+
end
|
151
|
+
end
|