sorbet-runtime 0.0.1.pre.prealpha → 0.4.4253
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 +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,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# https://jira.corp.stripe.com/browse/RUBYPLAT-1107
|
3
|
+
# typed: false
|
4
|
+
|
5
|
+
module T::Types
|
6
|
+
# Takes a list of types. Validates each item in an array using the type in the same position
|
7
|
+
# in the list.
|
8
|
+
class FixedArray < Base
|
9
|
+
attr_reader :types
|
10
|
+
|
11
|
+
def initialize(types)
|
12
|
+
@types = types.map {|type| T::Utils.coerce(type)}
|
13
|
+
end
|
14
|
+
|
15
|
+
# @override Base
|
16
|
+
def name
|
17
|
+
"[#{@types.join(', ')}]"
|
18
|
+
end
|
19
|
+
|
20
|
+
# @override Base
|
21
|
+
def valid?(obj)
|
22
|
+
obj.is_a?(Array) && obj.length == @types.length &&
|
23
|
+
obj.zip(@types).all? {|item, type| type.valid?(item)}
|
24
|
+
end
|
25
|
+
|
26
|
+
# @override Base
|
27
|
+
private def subtype_of_single?(other)
|
28
|
+
case other
|
29
|
+
when FixedArray
|
30
|
+
# Properly speaking, covariance here is unsound since arrays
|
31
|
+
# can be mutated, but sorbet implements covariant tuples for
|
32
|
+
# ease of adoption.
|
33
|
+
@types.size == other.types.size && @types.zip(other.types).all? do |t1, t2|
|
34
|
+
t1.subtype_of?(t2)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# This gives us better errors, e.g.:
|
42
|
+
# "Expected [String, Symbol], got [String, String]"
|
43
|
+
# instead of
|
44
|
+
# "Expected [String, Symbol], got Array".
|
45
|
+
#
|
46
|
+
# @override Base
|
47
|
+
def describe_obj(obj)
|
48
|
+
if obj.is_a?(Array)
|
49
|
+
if obj.length == @types.length
|
50
|
+
item_classes = obj.map(&:class).join(', ')
|
51
|
+
"type [#{item_classes}]"
|
52
|
+
else
|
53
|
+
"array of size #{obj.length}"
|
54
|
+
end
|
55
|
+
else
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
# Takes a hash of types. Validates each item in an hash using the type in the same position
|
6
|
+
# in the list.
|
7
|
+
class FixedHash < Base
|
8
|
+
attr_reader :types
|
9
|
+
|
10
|
+
def initialize(types)
|
11
|
+
@types = types.each_with_object({}) {|(k, v), h| h[k] = T::Utils.coerce(v)}
|
12
|
+
end
|
13
|
+
|
14
|
+
# @override Base
|
15
|
+
def name
|
16
|
+
"{#{@types.map {|(k, v)| "#{k}: #{v}"}.join(', ')}}"
|
17
|
+
end
|
18
|
+
|
19
|
+
# @override Base
|
20
|
+
def valid?(obj)
|
21
|
+
return false unless obj.is_a?(Hash)
|
22
|
+
|
23
|
+
@types.each do |key, type|
|
24
|
+
return false unless type.valid?(obj[key])
|
25
|
+
end
|
26
|
+
|
27
|
+
obj.each_key do |key|
|
28
|
+
return false unless @types[key]
|
29
|
+
end
|
30
|
+
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
# @override Base
|
35
|
+
private def subtype_of_single?(other)
|
36
|
+
case other
|
37
|
+
when FixedHash
|
38
|
+
# Using `subtype_of?` here instead of == would be unsound
|
39
|
+
@types == other.types
|
40
|
+
else
|
41
|
+
false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# This gives us better errors, e.g.:
|
46
|
+
# "Expected {a: String}, got {a: TrueClass}"
|
47
|
+
# instead of
|
48
|
+
# "Expected {a: String}, got Hash".
|
49
|
+
#
|
50
|
+
# @override Base
|
51
|
+
def describe_obj(obj)
|
52
|
+
if obj.is_a?(Hash)
|
53
|
+
"type {#{obj.map {|(k, v)| "#{k}: #{v.class}"}.join(', ')}}"
|
54
|
+
else
|
55
|
+
super
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
# Takes a list of types. Validates that an object matches all of the types.
|
6
|
+
class Intersection < Base
|
7
|
+
attr_reader :types
|
8
|
+
|
9
|
+
def initialize(types)
|
10
|
+
@types = types.flat_map do |type|
|
11
|
+
if type.is_a?(Intersection)
|
12
|
+
# Simplify nested intersections (mostly so `name` returns a nicer value)
|
13
|
+
type.types
|
14
|
+
else
|
15
|
+
T::Utils.coerce(type)
|
16
|
+
end
|
17
|
+
end.uniq
|
18
|
+
end
|
19
|
+
|
20
|
+
# @override Base
|
21
|
+
def name
|
22
|
+
"T.all(#{@types.map(&:name).sort.join(', ')})"
|
23
|
+
end
|
24
|
+
|
25
|
+
# @override Base
|
26
|
+
def valid?(obj)
|
27
|
+
@types.all? {|type| type.valid?(obj)}
|
28
|
+
end
|
29
|
+
|
30
|
+
# @override Base
|
31
|
+
private def subtype_of_single?(other)
|
32
|
+
raise "This should never be reached if you're going through `subtype_of?` (and you should be)"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
# The bottom type
|
6
|
+
class NoReturn < Base
|
7
|
+
|
8
|
+
def initialize; end
|
9
|
+
|
10
|
+
# @override Base
|
11
|
+
def name
|
12
|
+
"T.noreturn"
|
13
|
+
end
|
14
|
+
|
15
|
+
# @override Base
|
16
|
+
def valid?(obj)
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
# @override Base
|
21
|
+
private def subtype_of_single?(other)
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
# Defines the type of a proc (a ruby callable). At runtime, only
|
6
|
+
# validates that the value is a `::Proc`.
|
7
|
+
#
|
8
|
+
# At present, we only support fixed-arity procs with no optional or
|
9
|
+
# keyword arguments.
|
10
|
+
class Proc < Base
|
11
|
+
attr_reader :arg_types
|
12
|
+
attr_reader :returns
|
13
|
+
|
14
|
+
def initialize(arg_types, returns)
|
15
|
+
@arg_types = {}
|
16
|
+
arg_types.each do |key, raw_type|
|
17
|
+
@arg_types[key] = T::Utils.coerce(raw_type)
|
18
|
+
end
|
19
|
+
@returns = T::Utils.coerce(returns)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @override Base
|
23
|
+
def name
|
24
|
+
args = []
|
25
|
+
@arg_types.each do |k, v|
|
26
|
+
args << "#{k}: #{v.name}"
|
27
|
+
end
|
28
|
+
"T.proc.params(#{args.join(', ')}).returns(#{returns})"
|
29
|
+
end
|
30
|
+
|
31
|
+
# @override Base
|
32
|
+
def valid?(obj)
|
33
|
+
obj.is_a?(::Proc)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @override Base
|
37
|
+
private def subtype_of_single?(other)
|
38
|
+
case other
|
39
|
+
when self.class
|
40
|
+
if arg_types.size != other.arg_types.size
|
41
|
+
return false
|
42
|
+
end
|
43
|
+
arg_types.values.zip(other.arg_types.values).all? do |a, b|
|
44
|
+
b.subtype_of?(a)
|
45
|
+
end && returns.subtype_of?(other.returns)
|
46
|
+
else
|
47
|
+
false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
# Modeling self-types properly at runtime would require additional tracking,
|
6
|
+
# so at runtime we permit all values and rely on the static checker.
|
7
|
+
class SelfType < Base
|
8
|
+
|
9
|
+
def initialize(); end
|
10
|
+
|
11
|
+
# @override Base
|
12
|
+
def name
|
13
|
+
"T.self_type"
|
14
|
+
end
|
15
|
+
|
16
|
+
# @override Base
|
17
|
+
def valid?(obj)
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
# @override Base
|
22
|
+
private def subtype_of_single?(other)
|
23
|
+
case other
|
24
|
+
when SelfType
|
25
|
+
true
|
26
|
+
else
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
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 Simple < Base
|
7
|
+
attr_reader :raw_type
|
8
|
+
|
9
|
+
def initialize(raw_type)
|
10
|
+
@raw_type = raw_type
|
11
|
+
end
|
12
|
+
|
13
|
+
# @override Base
|
14
|
+
def name
|
15
|
+
@raw_type.name
|
16
|
+
end
|
17
|
+
|
18
|
+
# @override Base
|
19
|
+
def valid?(obj)
|
20
|
+
obj.is_a?(@raw_type)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @override Base
|
24
|
+
private def subtype_of_single?(other)
|
25
|
+
case other
|
26
|
+
when Simple
|
27
|
+
@raw_type <= other.raw_type
|
28
|
+
else
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
class TypeParameter < Base
|
6
|
+
def initialize(name)
|
7
|
+
raise ArgumentError.new("not a symbol: #{name}") unless name.is_a?(Symbol)
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid?(obj)
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def subtype_of_single?(type)
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
def name
|
20
|
+
"T.type_parameter(#{@name})"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
# Since we do type erasure at runtime, this just validates the variance and
|
6
|
+
# provides some syntax for the static type checker
|
7
|
+
class TypeVariable < Base
|
8
|
+
attr_reader :variance
|
9
|
+
|
10
|
+
VALID_VARIANCES = [:in, :out, :invariant]
|
11
|
+
|
12
|
+
def initialize(variance)
|
13
|
+
if !VALID_VARIANCES.include?(variance)
|
14
|
+
raise TypeError.new("invalid variance #{variance}")
|
15
|
+
end
|
16
|
+
@variance = variance
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid?(obj)
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def subtype_of_single?(type)
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def name
|
28
|
+
Untyped.new.name
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
class TypedArray < TypedEnumerable
|
6
|
+
# @override Base
|
7
|
+
def name
|
8
|
+
"T::Array[#{@type.name}]"
|
9
|
+
end
|
10
|
+
|
11
|
+
# @override Base
|
12
|
+
def valid?(obj)
|
13
|
+
obj.is_a?(Array) && super
|
14
|
+
end
|
15
|
+
|
16
|
+
def new(*args) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
|
17
|
+
Array.new(*T.unsafe(args))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
# Note: All subclasses of Enumerable should add themselves to the
|
6
|
+
# `case` statement below in `describe_obj` in order to get better
|
7
|
+
# error messages.
|
8
|
+
class TypedEnumerable < Base
|
9
|
+
attr_reader :type
|
10
|
+
|
11
|
+
def initialize(type)
|
12
|
+
@type = T::Utils.coerce(type)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @override Base
|
16
|
+
def name
|
17
|
+
"T::Enumerable[#{@type.name}]"
|
18
|
+
end
|
19
|
+
|
20
|
+
# @override Base
|
21
|
+
def valid?(obj)
|
22
|
+
return false unless obj.is_a?(Enumerable)
|
23
|
+
case obj
|
24
|
+
when Array
|
25
|
+
begin
|
26
|
+
it = 0
|
27
|
+
while it < obj.count
|
28
|
+
return false unless @type.valid?(obj[it])
|
29
|
+
it += 1
|
30
|
+
end
|
31
|
+
return true
|
32
|
+
end
|
33
|
+
when Hash
|
34
|
+
return false unless @type.is_a?(FixedArray)
|
35
|
+
types = @type.types
|
36
|
+
return false if types.count != 2
|
37
|
+
key_type = types[0]
|
38
|
+
value_type = types[1]
|
39
|
+
obj.each_pair do |key, val|
|
40
|
+
# Some objects (I'm looking at you Rack::Utils::HeaderHash) don't
|
41
|
+
# iterate over a [key, value] array, so we can't juse use the @type.valid?(v)
|
42
|
+
return false if !key_type.valid?(key) || !value_type.valid?(val)
|
43
|
+
end
|
44
|
+
return true
|
45
|
+
when Enumerator::Lazy
|
46
|
+
# Users don't want these walked
|
47
|
+
return true
|
48
|
+
when Enumerator
|
49
|
+
obj.each do |elem|
|
50
|
+
return false unless @type.valid?(elem)
|
51
|
+
end
|
52
|
+
return true
|
53
|
+
when Range
|
54
|
+
@type.valid?(obj.first) && @type.valid?(obj.last)
|
55
|
+
when Set
|
56
|
+
obj.each do |item|
|
57
|
+
return false unless @type.valid?(item)
|
58
|
+
end
|
59
|
+
|
60
|
+
return true
|
61
|
+
else
|
62
|
+
# We don't check the enumerable since it isn't guaranteed to be
|
63
|
+
# rewindable (e.g. STDIN) and it may be expensive to enumerate
|
64
|
+
# (e.g. an enumerator that makes an HTTP request)"
|
65
|
+
true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# @override Base
|
70
|
+
private def subtype_of_single?(other)
|
71
|
+
if self.class <= other.class
|
72
|
+
# Enumerables are covariant because they are read only
|
73
|
+
#
|
74
|
+
# Properly speaking, many Enumerable subtypes (e.g. Set)
|
75
|
+
# should be invariant because they are mutable and support
|
76
|
+
# both reading and writing. However, Sorbet treats *all*
|
77
|
+
# Enumerable subclasses as covariant for ease of adoption.
|
78
|
+
@type.subtype_of?(other.type)
|
79
|
+
else
|
80
|
+
false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# @override Base
|
85
|
+
def describe_obj(obj)
|
86
|
+
return super unless obj.is_a?(Enumerable)
|
87
|
+
type_from_instance(obj).name
|
88
|
+
end
|
89
|
+
|
90
|
+
private def type_from_instances(objs)
|
91
|
+
return objs.class unless objs.is_a?(Enumerable)
|
92
|
+
obtained_types = []
|
93
|
+
begin
|
94
|
+
objs.each do |x|
|
95
|
+
obtained_types << type_from_instance(x)
|
96
|
+
end
|
97
|
+
rescue
|
98
|
+
return T.untyped # all we can do is go with the types we have so far
|
99
|
+
end
|
100
|
+
if obtained_types.count > 1
|
101
|
+
# Multiple kinds of bad types showed up, we'll suggest a union
|
102
|
+
# type you might want.
|
103
|
+
Union.new(obtained_types)
|
104
|
+
elsif obtained_types.empty?
|
105
|
+
T.noreturn
|
106
|
+
else
|
107
|
+
# Everything was the same bad type, lets just show that
|
108
|
+
obtained_types.first
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
private def type_from_instance(obj)
|
113
|
+
if [true, false].include?(obj)
|
114
|
+
return T::Boolean
|
115
|
+
elsif !obj.is_a?(Enumerable)
|
116
|
+
return obj.class
|
117
|
+
end
|
118
|
+
|
119
|
+
case obj
|
120
|
+
when Array
|
121
|
+
T::Array[type_from_instances(obj)]
|
122
|
+
when Hash
|
123
|
+
inferred_key = type_from_instances(obj.keys)
|
124
|
+
inferred_val = type_from_instances(obj.values)
|
125
|
+
T::Hash[inferred_key, inferred_val]
|
126
|
+
when Range
|
127
|
+
T::Range[type_from_instances([obj.first, obj.last])]
|
128
|
+
when Enumerator
|
129
|
+
T::Enumerator[type_from_instances(obj)]
|
130
|
+
when Set
|
131
|
+
T::Set[type_from_instances(obj)]
|
132
|
+
when IO
|
133
|
+
# Short circuit for anything IO-like (File, etc.). In these cases,
|
134
|
+
# enumerating the object is a destructive operation and might hang.
|
135
|
+
obj.class
|
136
|
+
else
|
137
|
+
self.class.new(type_from_instances(obj))
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|