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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: de11dcda7e38c4977b8e8adb679d16e2e34b94df
|
4
|
+
data.tar.gz: 930cdf2804d7b8919dea38ccd474e261a654e03d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77caf2c55b6c999939a4860e8be38088854a4530cac75bee1c9cca68ed2d8b8c9ef6182061a15c73d0e5cf343ef6e2caaa7f3e5c45690b79ed41af18ebe1d2b4
|
7
|
+
data.tar.gz: 76a0990ca32cd691b58ae71ff7a34e6e63f0c674da3945718d6a5060f91bc08e50ec55972aafb8cf87930fba2220da3a336b557c8ad9121475e0a97445539a78
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
# This file is hand-crafted to encode the dependencies. They load the whole type
|
5
|
+
# system since there is such a high chance of it being used, using an autoloader
|
6
|
+
# wouldn't buy us any startup time saving.
|
7
|
+
|
8
|
+
# Namespaces without any implementation
|
9
|
+
module T; end
|
10
|
+
module T::Helpers; end
|
11
|
+
module T::Private; end
|
12
|
+
module T::Private::Abstract; end
|
13
|
+
module T::Private::Types; end
|
14
|
+
|
15
|
+
require 'set'
|
16
|
+
|
17
|
+
# Each section is a group that I believe need a fixed ordering. There is also
|
18
|
+
# an ordering between groups.
|
19
|
+
|
20
|
+
# These are pre-reqs for almost everything in here.
|
21
|
+
require_relative 'types/configuration'
|
22
|
+
require_relative 'types/profile'
|
23
|
+
require_relative 'types/_types'
|
24
|
+
require_relative 'types/private/decl_state'
|
25
|
+
require_relative 'types/runtime_profiled'
|
26
|
+
require_relative 'types/private/class_utils'
|
27
|
+
require_relative 'types/private/error_handler'
|
28
|
+
require_relative 'types/private/runtime_levels'
|
29
|
+
require_relative 'types/private/methods/_methods'
|
30
|
+
require_relative 'types/sig'
|
31
|
+
require_relative 'types/helpers'
|
32
|
+
|
33
|
+
# The types themselves. First base classes
|
34
|
+
require_relative 'types/types/base'
|
35
|
+
require_relative 'types/types/typed_enumerable'
|
36
|
+
# Everything else
|
37
|
+
require_relative 'types/types/class_of'
|
38
|
+
require_relative 'types/types/enum'
|
39
|
+
require_relative 'types/types/fixed_array'
|
40
|
+
require_relative 'types/types/fixed_hash'
|
41
|
+
require_relative 'types/types/intersection'
|
42
|
+
require_relative 'types/types/noreturn'
|
43
|
+
require_relative 'types/types/proc'
|
44
|
+
require_relative 'types/types/self_type'
|
45
|
+
require_relative 'types/types/simple'
|
46
|
+
require_relative 'types/types/type_parameter'
|
47
|
+
require_relative 'types/types/typed_array'
|
48
|
+
require_relative 'types/types/typed_enumerator'
|
49
|
+
require_relative 'types/types/typed_hash'
|
50
|
+
require_relative 'types/types/typed_range'
|
51
|
+
require_relative 'types/types/typed_set'
|
52
|
+
require_relative 'types/types/union'
|
53
|
+
require_relative 'types/types/untyped'
|
54
|
+
require_relative 'types/private/types/not_typed'
|
55
|
+
require_relative 'types/private/types/void'
|
56
|
+
require_relative 'types/private/types/string_holder'
|
57
|
+
|
58
|
+
require_relative 'types/types/type_variable'
|
59
|
+
require_relative 'types/types/type_member'
|
60
|
+
require_relative 'types/types/type_template'
|
61
|
+
|
62
|
+
# Call validation
|
63
|
+
require_relative 'types/private/methods/modes'
|
64
|
+
require_relative 'types/private/methods/call_validation'
|
65
|
+
|
66
|
+
# Signature validation
|
67
|
+
require_relative 'types/private/methods/signature_validation'
|
68
|
+
require_relative 'types/abstract_utils'
|
69
|
+
require_relative 'types/private/abstract/validate'
|
70
|
+
|
71
|
+
# Catch all. Sort of built by `cd extn; find types -type f | grep -v test | sort`
|
72
|
+
require_relative 'types/generic'
|
73
|
+
require_relative 'types/interface_wrapper'
|
74
|
+
require_relative 'types/private/abstract/declare'
|
75
|
+
require_relative 'types/private/abstract/hooks'
|
76
|
+
require_relative 'types/private/casts'
|
77
|
+
require_relative 'types/private/methods/decl_builder'
|
78
|
+
require_relative 'types/private/methods/signature'
|
79
|
+
require_relative 'types/utils'
|
80
|
+
require_relative 'types/boolean'
|
81
|
+
|
82
|
+
# Props dependencies
|
83
|
+
require_relative 'types/private/abstract/data'
|
84
|
+
require_relative 'types/private/mixins/mixins'
|
85
|
+
require_relative 'types/props/_props'
|
86
|
+
require_relative 'types/props/custom_type'
|
87
|
+
require_relative 'types/props/decorator'
|
88
|
+
require_relative 'types/props/errors'
|
89
|
+
require_relative 'types/props/plugin'
|
90
|
+
require_relative 'types/props/utils'
|
91
|
+
# Props that run sigs statically so have to be after all the others :(
|
92
|
+
require_relative 'types/props/optional'
|
93
|
+
require_relative 'types/props/weak_constructor'
|
94
|
+
require_relative 'types/props/constructor'
|
95
|
+
require_relative 'types/props/pretty_printable'
|
96
|
+
require_relative 'types/props/serializable'
|
97
|
+
require_relative 'types/props/type_validation'
|
98
|
+
require_relative 'types/struct'
|
99
|
+
|
100
|
+
require_relative 'types/compatibility_patches'
|
data/lib/types/_types.rb
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
# This is where we define the shortcuts, so we can't use them here
|
4
|
+
# rubocop:disable PrisonGuard/UseOpusTypesShortcut
|
5
|
+
|
6
|
+
# _____
|
7
|
+
# |_ _| _ _ __ ___ ___
|
8
|
+
# | || | | | '_ \ / _ \/ __|
|
9
|
+
# | || |_| | |_) | __/\__ \
|
10
|
+
# |_| \__, | .__/ \___||___/
|
11
|
+
# |___/|_|
|
12
|
+
#
|
13
|
+
# Docs at http://go/types.
|
14
|
+
#
|
15
|
+
# Types that you can pass to `sig`:
|
16
|
+
#
|
17
|
+
# - a Ruby class
|
18
|
+
#
|
19
|
+
# - [<Type>, <Type>, ...] -- to specify a "tuple"; a fixed-size array with known types for each member
|
20
|
+
#
|
21
|
+
# - {key: <Type>, key2: <Type>, ...} -- to speicfy a "shape"; a fixed-size hash
|
22
|
+
# with known keys and type values
|
23
|
+
#
|
24
|
+
# - Any of the `T.foo` methods below
|
25
|
+
|
26
|
+
module T
|
27
|
+
# T.any(<Type>, <Type>, ...) -- matches any of the types listed
|
28
|
+
def self.any(type_a, type_b, *types)
|
29
|
+
T::Types::Union.new([type_a, type_b] + types)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Shorthand for T.any(type, NilClass)
|
33
|
+
def self.nilable(type)
|
34
|
+
T::Types::Union.new([type, NilClass])
|
35
|
+
end
|
36
|
+
|
37
|
+
# Matches any object. In the static checker, T.untyped allows any
|
38
|
+
# method calls or operations.
|
39
|
+
def self.untyped
|
40
|
+
T::Types::Untyped.new
|
41
|
+
end
|
42
|
+
|
43
|
+
# Indicates a function never returns (e.g. "Kernel#raise")
|
44
|
+
def self.noreturn
|
45
|
+
T::Types::NoReturn.new
|
46
|
+
end
|
47
|
+
|
48
|
+
# T.all(<Type>, <Type>, ...) -- matches an object that has all of the types listed
|
49
|
+
def self.all(type_a, type_b, *types)
|
50
|
+
T::Types::Intersection.new([type_a, type_b] + types)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Matches any of the listed values
|
54
|
+
def self.enum(values)
|
55
|
+
T::Types::Enum.new(values)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Creates a proc type
|
59
|
+
def self.proc
|
60
|
+
T::Private::Methods.start_proc
|
61
|
+
end
|
62
|
+
|
63
|
+
# Matches `self`:
|
64
|
+
def self.self_type
|
65
|
+
T::Types::SelfType.new
|
66
|
+
end
|
67
|
+
|
68
|
+
# Matches any class that subclasses or includes the provided class
|
69
|
+
# or module
|
70
|
+
def self.class_of(klass)
|
71
|
+
T::Types::ClassOf.new(klass)
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
## END OF THE METHODS TO PASS TO `sig`.
|
76
|
+
|
77
|
+
|
78
|
+
# Constructs a type alias. Used to create a short name for a larger
|
79
|
+
# type. In Ruby this is just equivalent to assignment, but this is
|
80
|
+
# needed for support by the static checker. Example usage:
|
81
|
+
#
|
82
|
+
# NilableString = T.type_alias(T.nilable(String))
|
83
|
+
#
|
84
|
+
# sig {params(arg: NilableString, default: String).returns(String)}
|
85
|
+
# def or_else(arg, default)
|
86
|
+
# arg || default
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# The name of the type alias is not preserved; Error messages will
|
90
|
+
# be printed with reference to the underlying type.
|
91
|
+
def self.type_alias(type)
|
92
|
+
T::Utils.coerce(type)
|
93
|
+
end
|
94
|
+
|
95
|
+
# References a type paramater which was previously defined with
|
96
|
+
# `type_parameters`.
|
97
|
+
#
|
98
|
+
# This is used for generic methods. Example usage:
|
99
|
+
#
|
100
|
+
# sig
|
101
|
+
# .type_parameters(:U)
|
102
|
+
# .params(
|
103
|
+
# blk: T.proc.params(arg0: Elem).returns(T.type_parameter(:U)),
|
104
|
+
# )
|
105
|
+
# .returns(T::Array[T.type_parameter(:U)])
|
106
|
+
# def map(&blk); end
|
107
|
+
def self.type_parameter(name)
|
108
|
+
T::Types::TypeParameter.new(name)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Tells the typechecker that `value` is of type `type`. Use this to get additional checking after
|
112
|
+
# an expression that the typechecker is unable to analyze. If `checked` is true, raises an
|
113
|
+
# exception at runtime if the value doesn't match the type.
|
114
|
+
#
|
115
|
+
# Compared to `T.let`, `T.cast` is _trusted_ by static system.
|
116
|
+
def self.cast(value, type, checked: true)
|
117
|
+
return value unless checked
|
118
|
+
|
119
|
+
Private::Casts.cast(value, type, cast_method: "T.cast")
|
120
|
+
end
|
121
|
+
|
122
|
+
# Tells the typechecker to declare a variable of type `type`. Use
|
123
|
+
# like:
|
124
|
+
#
|
125
|
+
# seconds = T.let(0.0, Float)
|
126
|
+
#
|
127
|
+
# Compared to `T.cast`, `T.let` is _checked_ by static system.
|
128
|
+
#
|
129
|
+
# If `checked` is true, raises an exception at runtime if the value
|
130
|
+
# doesn't match the type.
|
131
|
+
def self.let(value, type, checked: true)
|
132
|
+
return value unless checked
|
133
|
+
|
134
|
+
Private::Casts.cast(value, type, cast_method: "T.let")
|
135
|
+
end
|
136
|
+
|
137
|
+
# Tells the typechecker to ensure that `value` is of type `type` (if not, the typechecker will
|
138
|
+
# fail). Use this for debugging typechecking errors, or to ensure that type information is
|
139
|
+
# statically known and being checked appropriately. If `checked` is true, raises an exception at
|
140
|
+
# runtime if the value doesn't match the type.
|
141
|
+
def self.assert_type!(value, type, checked: true)
|
142
|
+
return value unless checked
|
143
|
+
|
144
|
+
Private::Casts.cast(value, type, cast_method: "T.assert_type!")
|
145
|
+
end
|
146
|
+
|
147
|
+
# For the static type checker, strips all type information from a value
|
148
|
+
# and returns the same value, but statically-typed as `T.untyped`.
|
149
|
+
# Can be used to tell the static checker to "trust you" by discarding type information
|
150
|
+
# you know to be incorrect. Use with care!
|
151
|
+
# (This has no effect at runtime.)
|
152
|
+
#
|
153
|
+
# We can't actually write this sig because we ourselves are inside
|
154
|
+
# the `T::` module and doing this would create a bootstrapping
|
155
|
+
# cycle. However, we also don't actually need to do so; An untyped
|
156
|
+
# identity method works just as well here.
|
157
|
+
#
|
158
|
+
# sig {params(value: T.untyped).returns(T.untyped)}
|
159
|
+
def self.unsafe(value)
|
160
|
+
value
|
161
|
+
end
|
162
|
+
|
163
|
+
# A convenience method to `raise` when the argument is `nil` and return it
|
164
|
+
# otherwise.
|
165
|
+
#
|
166
|
+
# Intended to be used as:
|
167
|
+
#
|
168
|
+
# needs_foo(T.must(maybe_gives_foo))
|
169
|
+
#
|
170
|
+
# Equivalent to:
|
171
|
+
#
|
172
|
+
# foo = maybe_gives_foo
|
173
|
+
# raise "nil" if foo.nil?
|
174
|
+
# needs_foo(foo)
|
175
|
+
#
|
176
|
+
# Intended to be used to promise sorbet that a given nilable value happens
|
177
|
+
# to contain a non-nil value at this point.
|
178
|
+
#
|
179
|
+
# sig {params(arg: T.nilable(A), msg: T.nilable(String)).returns(A)}
|
180
|
+
def self.must(arg, msg=nil)
|
181
|
+
begin
|
182
|
+
if msg
|
183
|
+
if !T.unsafe(msg).is_a?(String)
|
184
|
+
raise TypeError.new("T.must expects a string as second argument")
|
185
|
+
end
|
186
|
+
else
|
187
|
+
msg = "Passed `nil` into T.must"
|
188
|
+
end
|
189
|
+
raise TypeError.new(msg) if arg.nil?
|
190
|
+
arg
|
191
|
+
rescue TypeError => e # raise into rescue to ensure e.backtrace is populated
|
192
|
+
T::Private::ErrorHandler.handle_inline_type_error(e)
|
193
|
+
arg
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# A way to ask Sorbet to show what type it thinks an expression has.
|
198
|
+
# This can be useful for debugging and checking assumptions.
|
199
|
+
# In the runtime, merely returns the value passed in.
|
200
|
+
def self.reveal_type(value)
|
201
|
+
value
|
202
|
+
end
|
203
|
+
|
204
|
+
### Generic classes ###
|
205
|
+
|
206
|
+
module Array
|
207
|
+
def self.[](type)
|
208
|
+
T::Types::TypedArray.new(type)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
module Hash
|
213
|
+
def self.[](keys, values)
|
214
|
+
T::Types::TypedHash.new(keys: keys, values: values)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
module Enumerable
|
219
|
+
def self.[](type)
|
220
|
+
T::Types::TypedEnumerable.new(type)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
module Enumerator
|
225
|
+
def self.[](type)
|
226
|
+
T::Types::TypedEnumerator.new(type)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
module Range
|
231
|
+
def self.[](type)
|
232
|
+
T::Types::TypedRange.new(type)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
module Set
|
237
|
+
def self.[](type)
|
238
|
+
T::Types::TypedSet.new(type)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# When mixed into a module, indicates that Sorbet may export the CFG for methods in that module
|
243
|
+
module CFGExport; end
|
244
|
+
end
|
245
|
+
# rubocop:enable PrisonGuard/UseOpusTypesShortcut
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::AbstractUtils
|
5
|
+
Methods = T::Private::Methods
|
6
|
+
|
7
|
+
# Returns whether a module is declared as abstract. After the module is finished being declared,
|
8
|
+
# this is equivalent to whether it has any abstract methods that haven't been implemented
|
9
|
+
# (because we validate that and raise an error otherwise).
|
10
|
+
#
|
11
|
+
# Note that checking `mod.is_a?(Abstract::Hooks)` is not a safe substitute for this method; when
|
12
|
+
# a class extends `Abstract::Hooks`, all of its subclasses, including the eventual concrete
|
13
|
+
# ones, will still have `Abstract::Hooks` as an ancestor.
|
14
|
+
def self.abstract_module?(mod)
|
15
|
+
!T::Private::Abstract::Data.get(mod, :abstract_type).nil?
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.abstract_method?(method)
|
19
|
+
signature = Methods.signature_for_method(method)
|
20
|
+
signature&.mode == Methods::Modes.abstract
|
21
|
+
end
|
22
|
+
|
23
|
+
# Given a module, returns the set of methods declared as abstract (in itself or ancestors)
|
24
|
+
# that have not been implemented.
|
25
|
+
def self.abstract_methods_for(mod)
|
26
|
+
declared_methods = declared_abstract_methods_for(mod)
|
27
|
+
declared_methods.select do |declared_method|
|
28
|
+
actual_method = mod.instance_method(declared_method.name)
|
29
|
+
# Note that in the case where an abstract method is overridden by another abstract method,
|
30
|
+
# this method will return them both. This is intentional to ensure we validate the final
|
31
|
+
# implementation against all declarations of an abstract method (they might not all have the
|
32
|
+
# same signature).
|
33
|
+
abstract_method?(actual_method)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Given a module, returns the set of methods declared as abstract (in itself or ancestors)
|
38
|
+
# regardless of whether they have been implemented.
|
39
|
+
def self.declared_abstract_methods_for(mod)
|
40
|
+
methods = []
|
41
|
+
mod.ancestors.each do |ancestor|
|
42
|
+
ancestor_methods = ancestor.private_instance_methods(false) + ancestor.instance_methods(false)
|
43
|
+
ancestor_methods.each do |method_name|
|
44
|
+
method = ancestor.instance_method(method_name)
|
45
|
+
methods << method if abstract_method?(method)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
methods
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module T
|
5
|
+
# T::Boolean is a type alias helper for the common `T.any(TrueClass, FalseClass)`.
|
6
|
+
# Defined separately from _types.rb because it has a dependency on T::Types::Union.
|
7
|
+
Boolean = T.type_alias(T.any(TrueClass, FalseClass))
|
8
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# typed: ignore
|
2
|
+
|
3
|
+
require_relative 'private/methods/_methods'
|
4
|
+
|
5
|
+
# Work around an interaction bug with sorbet-runtime and rspec-mocks,
|
6
|
+
# which occurs when using *_any_instance_of and and_call_original.
|
7
|
+
#
|
8
|
+
# When a sig is defined, sorbet-runtime will replace the sigged method
|
9
|
+
# with a wrapper that, upon first invocation, re-wraps the method with a faster
|
10
|
+
# implementation.
|
11
|
+
#
|
12
|
+
# When expect_any_instance_of is used, rspec stores a reference to the first wrapper,
|
13
|
+
# to be restored later.
|
14
|
+
#
|
15
|
+
# The first wrapper is invoked as part of the test and sorbet-runtime replaces
|
16
|
+
# the method definition with the second wrapper.
|
17
|
+
#
|
18
|
+
# But when mocks are cleaned up, rspec restores back to the first wrapper.
|
19
|
+
# Upon subsequent invocations, the first wrapper is called, and sorbet-runtime
|
20
|
+
# throws a runtime error, since this is an unexpected state.
|
21
|
+
#
|
22
|
+
# We work around this by forcing re-wrapping before rspec stores a reference
|
23
|
+
# to the method.
|
24
|
+
if defined? ::RSpec::Mocks::AnyInstance
|
25
|
+
module T
|
26
|
+
module CompatibilityPatches
|
27
|
+
module RecorderExtensions
|
28
|
+
def observe!(method_name)
|
29
|
+
method = @klass.instance_method(method_name.to_sym)
|
30
|
+
T::Private::Methods.maybe_run_sig_block_for_method(method)
|
31
|
+
super(method_name)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
::RSpec::Mocks::AnyInstance::Recorder.prepend(RecorderExtensions)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|