sorbet-runtime 0.5.10324 → 0.5.11267
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 +8 -2
- data/lib/types/_types.rb +61 -7
- data/lib/types/compatibility_patches.rb +5 -3
- data/lib/types/configuration.rb +1 -1
- data/lib/types/enum.rb +6 -1
- data/lib/types/generic.rb +2 -0
- data/lib/types/helpers.rb +7 -0
- data/lib/types/private/abstract/declare.rb +9 -9
- data/lib/types/private/caller_utils.rb +21 -0
- data/lib/types/private/casts.rb +6 -3
- data/lib/types/private/class_utils.rb +13 -6
- data/lib/types/private/methods/_methods.rb +68 -38
- data/lib/types/private/methods/call_validation.rb +120 -7
- data/lib/types/private/methods/call_validation_2_6.rb +586 -60
- data/lib/types/private/methods/call_validation_2_7.rb +586 -60
- data/lib/types/private/methods/decl_builder.rb +22 -7
- data/lib/types/private/methods/signature.rb +71 -38
- data/lib/types/private/methods/signature_validation.rb +77 -9
- data/lib/types/private/runtime_levels.rb +19 -0
- data/lib/types/private/sealed.rb +6 -6
- data/lib/types/private/types/not_typed.rb +6 -0
- data/lib/types/private/types/simple_pair_union.rb +55 -0
- data/lib/types/private/types/string_holder.rb +4 -0
- data/lib/types/private/types/type_alias.rb +4 -0
- data/lib/types/private/types/void.rb +33 -23
- data/lib/types/props/custom_type.rb +2 -2
- data/lib/types/props/decorator.rb +49 -43
- data/lib/types/props/has_lazily_specialized_methods.rb +4 -2
- data/lib/types/props/pretty_printable.rb +45 -83
- data/lib/types/props/private/deserializer_generator.rb +4 -1
- data/lib/types/props/private/setter_factory.rb +130 -70
- data/lib/types/props/serializable.rb +24 -10
- data/lib/types/props/type_validation.rb +5 -2
- data/lib/types/struct.rb +2 -2
- data/lib/types/types/anything.rb +35 -0
- data/lib/types/types/attached_class.rb +4 -0
- data/lib/types/types/base.rb +21 -1
- data/lib/types/types/class_of.rb +15 -0
- data/lib/types/types/enum.rb +5 -1
- data/lib/types/types/fixed_array.rb +32 -12
- data/lib/types/types/fixed_hash.rb +38 -9
- data/lib/types/types/intersection.rb +13 -6
- data/lib/types/types/noreturn.rb +4 -1
- data/lib/types/types/proc.rb +18 -8
- data/lib/types/types/self_type.rb +4 -0
- data/lib/types/types/simple.rb +36 -4
- data/lib/types/types/t_enum.rb +4 -0
- data/lib/types/types/type_parameter.rb +23 -0
- data/lib/types/types/type_variable.rb +4 -0
- data/lib/types/types/typed_array.rb +35 -1
- data/lib/types/types/typed_class.rb +102 -0
- data/lib/types/types/typed_enumerable.rb +26 -12
- data/lib/types/types/typed_enumerator.rb +1 -3
- data/lib/types/types/typed_enumerator_chain.rb +39 -0
- data/lib/types/types/typed_enumerator_lazy.rb +1 -3
- data/lib/types/types/typed_hash.rb +17 -7
- data/lib/types/types/typed_range.rb +1 -3
- data/lib/types/types/typed_set.rb +1 -3
- data/lib/types/types/union.rb +62 -19
- data/lib/types/types/untyped.rb +4 -0
- data/lib/types/utils.rb +47 -37
- metadata +19 -29
- data/lib/types/interface_wrapper.rb +0 -158
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 62eef630524c0b1ad1f2a3d8749bd5f821a28ebfabbe7e4909010d530ffa698b
|
|
4
|
+
data.tar.gz: eb3fb942cb3ead21ec80e425726f823a6eb2cef834354ae2279a00c7fc5bf599
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 62855bc96c970aab1e89be225e336d6e7adc53b486137bb2f09457abdd9f932d23d4e6a37d05d6c8dad5d1fe098fad3b8cd4c529fcb7926e0bfefdc7b63249f3
|
|
7
|
+
data.tar.gz: d14d50ff7afbcad3fb245d2e9f815857c389b90a619fce352eca24da6be967728b748c89bac041ab71beed0f9bf4bf690c19a08b193d9ec701f0364026b7bc2b
|
data/lib/sorbet-runtime.rb
CHANGED
|
@@ -19,6 +19,7 @@ module T::Private::Types; end
|
|
|
19
19
|
require_relative 'types/configuration'
|
|
20
20
|
require_relative 'types/_types'
|
|
21
21
|
require_relative 'types/private/decl_state'
|
|
22
|
+
require_relative 'types/private/caller_utils'
|
|
22
23
|
require_relative 'types/private/class_utils'
|
|
23
24
|
require_relative 'types/private/runtime_levels'
|
|
24
25
|
require_relative 'types/private/methods/_methods'
|
|
@@ -37,14 +38,15 @@ require_relative 'types/types/fixed_array'
|
|
|
37
38
|
require_relative 'types/types/fixed_hash'
|
|
38
39
|
require_relative 'types/types/intersection'
|
|
39
40
|
require_relative 'types/types/noreturn'
|
|
41
|
+
require_relative 'types/types/anything'
|
|
40
42
|
require_relative 'types/types/proc'
|
|
41
43
|
require_relative 'types/types/attached_class'
|
|
42
44
|
require_relative 'types/types/self_type'
|
|
43
45
|
require_relative 'types/types/simple'
|
|
44
46
|
require_relative 'types/types/t_enum'
|
|
45
47
|
require_relative 'types/types/type_parameter'
|
|
46
|
-
require_relative 'types/types/typed_array'
|
|
47
48
|
require_relative 'types/types/typed_enumerator'
|
|
49
|
+
require_relative 'types/types/typed_enumerator_chain'
|
|
48
50
|
require_relative 'types/types/typed_enumerator_lazy'
|
|
49
51
|
require_relative 'types/types/typed_hash'
|
|
50
52
|
require_relative 'types/types/typed_range'
|
|
@@ -55,6 +57,7 @@ require_relative 'types/private/types/not_typed'
|
|
|
55
57
|
require_relative 'types/private/types/void'
|
|
56
58
|
require_relative 'types/private/types/string_holder'
|
|
57
59
|
require_relative 'types/private/types/type_alias'
|
|
60
|
+
require_relative 'types/private/types/simple_pair_union'
|
|
58
61
|
|
|
59
62
|
require_relative 'types/types/type_variable'
|
|
60
63
|
require_relative 'types/types/type_member'
|
|
@@ -71,7 +74,6 @@ require_relative 'types/private/abstract/validate'
|
|
|
71
74
|
|
|
72
75
|
# Catch all. Sort of built by `cd extn; find types -type f | grep -v test | sort`
|
|
73
76
|
require_relative 'types/generic'
|
|
74
|
-
require_relative 'types/interface_wrapper'
|
|
75
77
|
require_relative 'types/private/abstract/declare'
|
|
76
78
|
require_relative 'types/private/abstract/hooks'
|
|
77
79
|
require_relative 'types/private/casts'
|
|
@@ -81,6 +83,10 @@ require_relative 'types/private/retry'
|
|
|
81
83
|
require_relative 'types/utils'
|
|
82
84
|
require_relative 'types/boolean'
|
|
83
85
|
|
|
86
|
+
# Depends on types/utils
|
|
87
|
+
require_relative 'types/types/typed_array'
|
|
88
|
+
require_relative 'types/types/typed_class'
|
|
89
|
+
|
|
84
90
|
# Props dependencies
|
|
85
91
|
require_relative 'types/private/abstract/data'
|
|
86
92
|
require_relative 'types/private/mixins/mixins'
|
data/lib/types/_types.rb
CHANGED
|
@@ -47,6 +47,10 @@ module T
|
|
|
47
47
|
T::Types::NoReturn::Private::INSTANCE
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
def self.anything
|
|
51
|
+
T::Types::Anything::Private::INSTANCE
|
|
52
|
+
end
|
|
53
|
+
|
|
50
54
|
# T.all(<Type>, <Type>, ...) -- matches an object that has all of the types listed
|
|
51
55
|
def self.all(type_a, type_b, *types)
|
|
52
56
|
T::Types::Intersection.new([type_a, type_b] + types)
|
|
@@ -119,7 +123,7 @@ module T
|
|
|
119
123
|
# .returns(T::Array[T.type_parameter(:U)])
|
|
120
124
|
# def map(&blk); end
|
|
121
125
|
def self.type_parameter(name)
|
|
122
|
-
T::Types::TypeParameter.
|
|
126
|
+
T::Types::TypeParameter.make(name)
|
|
123
127
|
end
|
|
124
128
|
|
|
125
129
|
# Tells the typechecker that `value` is of type `type`. Use this to get additional checking after
|
|
@@ -130,7 +134,7 @@ module T
|
|
|
130
134
|
def self.cast(value, type, checked: true)
|
|
131
135
|
return value unless checked
|
|
132
136
|
|
|
133
|
-
Private::Casts.cast(value, type,
|
|
137
|
+
Private::Casts.cast(value, type, "T.cast")
|
|
134
138
|
end
|
|
135
139
|
|
|
136
140
|
# Tells the typechecker to declare a variable of type `type`. Use
|
|
@@ -145,7 +149,7 @@ module T
|
|
|
145
149
|
def self.let(value, type, checked: true)
|
|
146
150
|
return value unless checked
|
|
147
151
|
|
|
148
|
-
Private::Casts.cast(value, type,
|
|
152
|
+
Private::Casts.cast(value, type, "T.let")
|
|
149
153
|
end
|
|
150
154
|
|
|
151
155
|
# Tells the type checker to treat `self` in the current block as `type`.
|
|
@@ -164,7 +168,7 @@ module T
|
|
|
164
168
|
def self.bind(value, type, checked: true)
|
|
165
169
|
return value unless checked
|
|
166
170
|
|
|
167
|
-
Private::Casts.cast(value, type,
|
|
171
|
+
Private::Casts.cast(value, type, "T.bind")
|
|
168
172
|
end
|
|
169
173
|
|
|
170
174
|
# Tells the typechecker to ensure that `value` is of type `type` (if not, the typechecker will
|
|
@@ -174,7 +178,7 @@ module T
|
|
|
174
178
|
def self.assert_type!(value, type, checked: true)
|
|
175
179
|
return value unless checked
|
|
176
180
|
|
|
177
|
-
Private::Casts.cast(value, type,
|
|
181
|
+
Private::Casts.cast(value, type, "T.assert_type!")
|
|
178
182
|
end
|
|
179
183
|
|
|
180
184
|
# For the static type checker, strips all type information from a value
|
|
@@ -221,6 +225,34 @@ module T
|
|
|
221
225
|
end
|
|
222
226
|
end
|
|
223
227
|
|
|
228
|
+
# A convenience method to `raise` with a provided error reason when the argument
|
|
229
|
+
# is `nil` and return it otherwise.
|
|
230
|
+
#
|
|
231
|
+
# Intended to be used as:
|
|
232
|
+
#
|
|
233
|
+
# needs_foo(T.must_because(maybe_gives_foo) {"reason_foo_should_not_be_nil"})
|
|
234
|
+
#
|
|
235
|
+
# Equivalent to:
|
|
236
|
+
#
|
|
237
|
+
# foo = maybe_gives_foo
|
|
238
|
+
# raise "reason_foo_should_not_be_nil" if foo.nil?
|
|
239
|
+
# needs_foo(foo)
|
|
240
|
+
#
|
|
241
|
+
# Intended to be used to promise sorbet that a given nilable value happens
|
|
242
|
+
# to contain a non-nil value at this point.
|
|
243
|
+
#
|
|
244
|
+
# `sig {params(arg: T.nilable(A), reason_blk: T.proc.returns(String)).returns(A)}`
|
|
245
|
+
def self.must_because(arg)
|
|
246
|
+
return arg if arg
|
|
247
|
+
return arg if arg == false
|
|
248
|
+
|
|
249
|
+
begin
|
|
250
|
+
raise TypeError.new("Unexpected `nil` because #{yield}")
|
|
251
|
+
rescue TypeError => e # raise into rescue to ensure e.backtrace is populated
|
|
252
|
+
T::Configuration.inline_type_error_handler(e, {kind: 'T.must_because', value: arg, type: nil})
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
224
256
|
# A way to ask Sorbet to show what type it thinks an expression has.
|
|
225
257
|
# This can be useful for debugging and checking assumptions.
|
|
226
258
|
# In the runtime, merely returns the value passed in.
|
|
@@ -251,9 +283,9 @@ module T
|
|
|
251
283
|
module Array
|
|
252
284
|
def self.[](type)
|
|
253
285
|
if type.is_a?(T::Types::Untyped)
|
|
254
|
-
T::Types::TypedArray::Untyped
|
|
286
|
+
T::Types::TypedArray::Untyped::Private::INSTANCE
|
|
255
287
|
else
|
|
256
|
-
T::Types::TypedArray.
|
|
288
|
+
T::Types::TypedArray::Private::Pool.type_for_module(type)
|
|
257
289
|
end
|
|
258
290
|
end
|
|
259
291
|
end
|
|
@@ -296,6 +328,16 @@ module T
|
|
|
296
328
|
end
|
|
297
329
|
end
|
|
298
330
|
end
|
|
331
|
+
|
|
332
|
+
module Chain
|
|
333
|
+
def self.[](type)
|
|
334
|
+
if type.is_a?(T::Types::Untyped)
|
|
335
|
+
T::Types::TypedEnumeratorChain::Untyped.new
|
|
336
|
+
else
|
|
337
|
+
T::Types::TypedEnumeratorChain.new(type)
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
299
341
|
end
|
|
300
342
|
|
|
301
343
|
module Range
|
|
@@ -313,4 +355,16 @@ module T
|
|
|
313
355
|
end
|
|
314
356
|
end
|
|
315
357
|
end
|
|
358
|
+
|
|
359
|
+
module Class
|
|
360
|
+
def self.[](type)
|
|
361
|
+
if type.is_a?(T::Types::Untyped)
|
|
362
|
+
T::Types::TypedClass::Untyped::Private::INSTANCE
|
|
363
|
+
elsif type.is_a?(T::Types::Anything)
|
|
364
|
+
T::Types::TypedClass::Anything::Private::INSTANCE
|
|
365
|
+
else
|
|
366
|
+
T::Types::TypedClass::Private::Pool.type_for_module(type)
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
end
|
|
316
370
|
end
|
|
@@ -27,8 +27,10 @@ if defined? ::RSpec::Mocks
|
|
|
27
27
|
module RSpecCompatibility
|
|
28
28
|
module RecorderExtensions
|
|
29
29
|
def observe!(method_name)
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
if @klass.method_defined?(method_name.to_sym)
|
|
31
|
+
method = @klass.instance_method(method_name.to_sym)
|
|
32
|
+
T::Private::Methods.maybe_run_sig_block_for_method(method)
|
|
33
|
+
end
|
|
32
34
|
super(method_name)
|
|
33
35
|
end
|
|
34
36
|
end
|
|
@@ -36,7 +38,7 @@ if defined? ::RSpec::Mocks
|
|
|
36
38
|
|
|
37
39
|
module MethodDoubleExtensions
|
|
38
40
|
def initialize(object, method_name, proxy)
|
|
39
|
-
if ::Kernel.instance_method(:respond_to?).bind(object).call(method_name, true)
|
|
41
|
+
if ::Kernel.instance_method(:respond_to?).bind(object).call(method_name, true) # rubocop:disable Performance/BindCall
|
|
40
42
|
method = ::RSpec::Support.method_handle_for(object, method_name)
|
|
41
43
|
T::Private::Methods.maybe_run_sig_block_for_method(method)
|
|
42
44
|
end
|
data/lib/types/configuration.rb
CHANGED
|
@@ -451,7 +451,7 @@ module T::Configuration
|
|
|
451
451
|
@default_module_name_mangler = if T::Configuration::AT_LEAST_RUBY_2_7
|
|
452
452
|
->(type) {MODULE_NAME.bind_call(type)}
|
|
453
453
|
else
|
|
454
|
-
->(type) {MODULE_NAME.bind(type).call}
|
|
454
|
+
->(type) {MODULE_NAME.bind(type).call} # rubocop:disable Performance/BindCall
|
|
455
455
|
end
|
|
456
456
|
|
|
457
457
|
@module_name_mangler = nil
|
data/lib/types/enum.rb
CHANGED
|
@@ -357,11 +357,16 @@ class T::Enum
|
|
|
357
357
|
@fully_initialized = true
|
|
358
358
|
end
|
|
359
359
|
|
|
360
|
-
sig {params(child_class:
|
|
360
|
+
sig {params(child_class: T::Class[T.anything]).void}
|
|
361
361
|
def self.inherited(child_class)
|
|
362
362
|
super
|
|
363
363
|
|
|
364
364
|
raise "Inheriting from children of T::Enum is prohibited" if self != T::Enum
|
|
365
|
+
|
|
366
|
+
# "oj" gem JSON support
|
|
367
|
+
if Object.const_defined?(:Oj)
|
|
368
|
+
Object.const_get(:Oj).register_odd(child_class, child_class, :try_deserialize, :serialize)
|
|
369
|
+
end
|
|
365
370
|
end
|
|
366
371
|
|
|
367
372
|
# Marshal support
|
data/lib/types/generic.rb
CHANGED
data/lib/types/helpers.rb
CHANGED
|
@@ -11,6 +11,13 @@ module T::Helpers
|
|
|
11
11
|
### Class/Module Helpers ###
|
|
12
12
|
|
|
13
13
|
def abstract!
|
|
14
|
+
if defined?(super)
|
|
15
|
+
# This is to play nicely with Rails' AbstractController::Base,
|
|
16
|
+
# which also defines an `abstract!` method.
|
|
17
|
+
# https://api.rubyonrails.org/classes/AbstractController/Base.html#method-c-abstract-21
|
|
18
|
+
super
|
|
19
|
+
end
|
|
20
|
+
|
|
14
21
|
Private::Abstract::Declare.declare_abstract(self, type: :abstract)
|
|
15
22
|
end
|
|
16
23
|
|
|
@@ -18,7 +18,6 @@ module T::Private::Abstract::Declare
|
|
|
18
18
|
Abstract::Data.set(mod, :abstract_type, type)
|
|
19
19
|
|
|
20
20
|
mod.extend(Abstract::Hooks)
|
|
21
|
-
mod.extend(T::InterfaceWrapper::Helpers)
|
|
22
21
|
|
|
23
22
|
if mod.is_a?(Class)
|
|
24
23
|
if type == :interface
|
|
@@ -27,26 +26,27 @@ module T::Private::Abstract::Declare
|
|
|
27
26
|
raise "Classes can't be interfaces. Use `abstract!` instead of `interface!`."
|
|
28
27
|
end
|
|
29
28
|
|
|
30
|
-
if
|
|
31
|
-
raise "You must call `abstract!` *before* defining
|
|
29
|
+
if Object.instance_method(:method).bind_call(mod, :new).owner == mod
|
|
30
|
+
raise "You must call `abstract!` *before* defining a `new` method"
|
|
32
31
|
end
|
|
33
32
|
|
|
34
33
|
# Don't need to silence warnings via without_ruby_warnings when calling
|
|
35
34
|
# define_method because of the guard above
|
|
36
35
|
|
|
37
|
-
mod.send(:
|
|
38
|
-
|
|
36
|
+
mod.send(:define_singleton_method, :new) do |*args, &blk|
|
|
37
|
+
result = super(*args, &blk)
|
|
38
|
+
if result.instance_of?(mod)
|
|
39
39
|
raise "#{mod} is declared as abstract; it cannot be instantiated"
|
|
40
40
|
end
|
|
41
|
-
|
|
41
|
+
result
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
# Ruby doesn not emit "method redefined" warnings for aliased methods
|
|
45
45
|
# (more robust than undef_method that would create a small window in which the method doesn't exist)
|
|
46
|
-
mod.send(:alias_method, :
|
|
46
|
+
mod.singleton_class.send(:alias_method, :new, :new)
|
|
47
47
|
|
|
48
|
-
if mod.respond_to?(:ruby2_keywords, true)
|
|
49
|
-
mod.send(:ruby2_keywords, :
|
|
48
|
+
if mod.singleton_class.respond_to?(:ruby2_keywords, true)
|
|
49
|
+
mod.singleton_class.send(:ruby2_keywords, :new)
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: false
|
|
3
|
+
|
|
4
|
+
module T::Private::CallerUtils
|
|
5
|
+
if Thread.respond_to?(:each_caller_location) # RUBY_VERSION >= "3.2"
|
|
6
|
+
def self.find_caller
|
|
7
|
+
Thread.each_caller_location do |loc|
|
|
8
|
+
next if loc.path&.start_with?("<internal:")
|
|
9
|
+
|
|
10
|
+
return loc if yield(loc)
|
|
11
|
+
end
|
|
12
|
+
nil
|
|
13
|
+
end
|
|
14
|
+
else
|
|
15
|
+
def self.find_caller
|
|
16
|
+
caller_locations(2).find do |loc|
|
|
17
|
+
!loc.path&.start_with?("<internal:") && yield(loc)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/types/private/casts.rb
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
|
|
4
4
|
module T::Private
|
|
5
5
|
module Casts
|
|
6
|
-
def self.cast(value, type, cast_method
|
|
6
|
+
def self.cast(value, type, cast_method)
|
|
7
7
|
begin
|
|
8
|
-
|
|
8
|
+
coerced_type = T::Utils::Private.coerce_and_check_module_types(type, value, true)
|
|
9
|
+
return value unless coerced_type
|
|
10
|
+
|
|
11
|
+
error = coerced_type.error_message_for_obj(value)
|
|
9
12
|
return value unless error
|
|
10
13
|
|
|
11
14
|
caller_loc = T.must(caller_locations(2..2)).first
|
|
@@ -22,7 +25,7 @@ module T::Private
|
|
|
22
25
|
# there's a lot of shared logic with the above one, but factoring
|
|
23
26
|
# it out like this makes it easier to hopefully one day delete
|
|
24
27
|
# this one
|
|
25
|
-
def self.cast_recursive(value, type, cast_method
|
|
28
|
+
def self.cast_recursive(value, type, cast_method)
|
|
26
29
|
begin
|
|
27
30
|
error = T::Utils.coerce(type).error_message_for_obj_recursive(value)
|
|
28
31
|
return value unless error
|
|
@@ -91,10 +91,13 @@ module T::Private::ClassUtils
|
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
# Replaces a method, either by overwriting it (if it is defined directly on `mod`) or by
|
|
94
|
-
# overriding it (if it is defined by one of mod's ancestors).
|
|
95
|
-
# on which you can call `bind(...).call(...)`
|
|
96
|
-
#
|
|
97
|
-
|
|
94
|
+
# overriding it (if it is defined by one of mod's ancestors). If `original_only` is
|
|
95
|
+
# false, returns a ReplacedMethod instance on which you can call `bind(...).call(...)`
|
|
96
|
+
# to call the original method, or `restore` to restore the original method (by
|
|
97
|
+
# overwriting or removing the override).
|
|
98
|
+
#
|
|
99
|
+
# If `original_only` is true, return the `UnboundMethod` representing the original method.
|
|
100
|
+
def self.replace_method(mod, name, original_only=false, &blk)
|
|
98
101
|
original_method = mod.instance_method(name)
|
|
99
102
|
original_visibility = visibility_method_name(mod, name)
|
|
100
103
|
original_owner = original_method.owner
|
|
@@ -120,8 +123,12 @@ module T::Private::ClassUtils
|
|
|
120
123
|
def_with_visibility(mod, name, original_visibility, &blk)
|
|
121
124
|
end
|
|
122
125
|
end
|
|
123
|
-
new_method = mod.instance_method(name)
|
|
124
126
|
|
|
125
|
-
|
|
127
|
+
if original_only
|
|
128
|
+
original_method
|
|
129
|
+
else
|
|
130
|
+
new_method = mod.instance_method(name)
|
|
131
|
+
ReplacedMethod.new(mod, original_method, new_method, overwritten, original_visibility)
|
|
132
|
+
end
|
|
126
133
|
end
|
|
127
134
|
end
|
|
@@ -3,12 +3,17 @@
|
|
|
3
3
|
|
|
4
4
|
module T::Private::Methods
|
|
5
5
|
@installed_hooks = {}
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
if defined?(Concurrent::Hash)
|
|
7
|
+
@signatures_by_method = Concurrent::Hash.new
|
|
8
|
+
@sig_wrappers = Concurrent::Hash.new
|
|
9
|
+
else
|
|
10
|
+
@signatures_by_method = {}
|
|
11
|
+
@sig_wrappers = {}
|
|
12
|
+
end
|
|
8
13
|
@sigs_that_raised = {}
|
|
9
14
|
# stores method names that were declared final without regard for where.
|
|
10
15
|
# enables early rejection of names that we know can't induce final method violations.
|
|
11
|
-
@was_ever_final_names = {}
|
|
16
|
+
@was_ever_final_names = {}.compare_by_identity
|
|
12
17
|
# maps from a module's object_id to the set of final methods declared in that module.
|
|
13
18
|
# we also overload entries slightly: if the value is nil, that means that the
|
|
14
19
|
# module has final methods somewhere along its ancestor chain, but does not itself
|
|
@@ -20,7 +25,7 @@ module T::Private::Methods
|
|
|
20
25
|
# twice is permitted). we could do this with two tables, but it seems slightly
|
|
21
26
|
# cleaner with a single table.
|
|
22
27
|
# Effectively T::Hash[Module, T.nilable(Set))]
|
|
23
|
-
@modules_with_final = Hash.new {|hash, key| hash[key] = nil}
|
|
28
|
+
@modules_with_final = Hash.new {|hash, key| hash[key] = nil}.compare_by_identity
|
|
24
29
|
# this stores the old [included, extended] hooks for Module and inherited hook for Class that we override when
|
|
25
30
|
# enabling final checks for when those hooks are called. the 'hooks' here don't have anything to do with the 'hooks'
|
|
26
31
|
# in installed_hooks.
|
|
@@ -83,7 +88,7 @@ module T::Private::Methods
|
|
|
83
88
|
raise "Procs cannot have override/abstract modifiers"
|
|
84
89
|
end
|
|
85
90
|
if decl.mod != PROC_TYPE
|
|
86
|
-
raise "You are passing a DeclBuilder as a type. Did you accidentally use `self` inside a `sig` block?"
|
|
91
|
+
raise "You are passing a DeclBuilder as a type. Did you accidentally use `self` inside a `sig` block? Perhaps you wanted the `T.self_type` instead: https://sorbet.org/docs/self-type"
|
|
87
92
|
end
|
|
88
93
|
if decl.returns == ARG_NOT_PROVIDED
|
|
89
94
|
raise "Procs must specify a return type"
|
|
@@ -117,6 +122,11 @@ module T::Private::Methods
|
|
|
117
122
|
@signatures_by_method[key]
|
|
118
123
|
end
|
|
119
124
|
|
|
125
|
+
# Fetch the directory name of the file that defines the `T::Private` constant and
|
|
126
|
+
# add a trailing slash to allow us to match it as a directory prefix.
|
|
127
|
+
SORBET_RUNTIME_LIB_PATH = File.dirname(T.const_source_location(:Private).first) + File::SEPARATOR
|
|
128
|
+
private_constant :SORBET_RUNTIME_LIB_PATH
|
|
129
|
+
|
|
120
130
|
# when target includes a module with instance methods source_method_names, ensure there is zero intersection between
|
|
121
131
|
# the final instance methods of target and source_method_names. so, for every m in source_method_names, check if there
|
|
122
132
|
# is already a method defined on one of target_ancestors with the same name that is final.
|
|
@@ -127,7 +137,7 @@ module T::Private::Methods
|
|
|
127
137
|
source_ancestors = nil
|
|
128
138
|
# use reverse_each to check farther-up ancestors first, for better error messages.
|
|
129
139
|
target_ancestors.reverse_each do |ancestor|
|
|
130
|
-
final_methods = @modules_with_final.fetch(ancestor
|
|
140
|
+
final_methods = @modules_with_final.fetch(ancestor, nil)
|
|
131
141
|
# In this case, either ancestor didn't have any final methods anywhere in its
|
|
132
142
|
# ancestor chain, or ancestor did have final methods somewhere in its ancestor
|
|
133
143
|
# chain, but no final methods defined in ancestor itself. Either way, there
|
|
@@ -145,20 +155,20 @@ module T::Private::Methods
|
|
|
145
155
|
# filter out things without actual final methods just to make sure that
|
|
146
156
|
# the below checks (which should be uncommon) go as quickly as possible.
|
|
147
157
|
source_ancestors.select! do |a|
|
|
148
|
-
@modules_with_final.fetch(a
|
|
158
|
+
@modules_with_final.fetch(a, nil)
|
|
149
159
|
end
|
|
150
160
|
end
|
|
151
161
|
# final-ness means that there should be no more than one index for which
|
|
152
162
|
# the below block returns true.
|
|
153
163
|
defining_ancestor_idx = source_ancestors.index do |a|
|
|
154
|
-
@modules_with_final.fetch(a
|
|
164
|
+
@modules_with_final.fetch(a).include?(method_name)
|
|
155
165
|
end
|
|
156
166
|
next if defining_ancestor_idx && source_ancestors[defining_ancestor_idx] == ancestor
|
|
157
167
|
end
|
|
158
168
|
|
|
159
169
|
definition_file, definition_line = T::Private::Methods.signature_for_method(ancestor.instance_method(method_name)).method.source_location
|
|
160
170
|
is_redefined = target == ancestor
|
|
161
|
-
caller_loc =
|
|
171
|
+
caller_loc = T::Private::CallerUtils.find_caller {|loc| !loc.path.to_s.start_with?(SORBET_RUNTIME_LIB_PATH)}
|
|
162
172
|
extra_info = "\n"
|
|
163
173
|
if caller_loc
|
|
164
174
|
extra_info = (is_redefined ? "Redefined" : "Overridden") + " here: #{caller_loc.path}:#{caller_loc.lineno}\n"
|
|
@@ -186,13 +196,11 @@ module T::Private::Methods
|
|
|
186
196
|
end
|
|
187
197
|
end
|
|
188
198
|
|
|
189
|
-
def self.add_module_with_final_method(mod, method_name
|
|
190
|
-
|
|
191
|
-
mid = m.object_id
|
|
192
|
-
methods = @modules_with_final[mid]
|
|
199
|
+
def self.add_module_with_final_method(mod, method_name)
|
|
200
|
+
methods = @modules_with_final[mod]
|
|
193
201
|
if methods.nil?
|
|
194
202
|
methods = {}
|
|
195
|
-
@modules_with_final[
|
|
203
|
+
@modules_with_final[mod] = methods
|
|
196
204
|
end
|
|
197
205
|
methods[method_name] = true
|
|
198
206
|
nil
|
|
@@ -200,24 +208,23 @@ module T::Private::Methods
|
|
|
200
208
|
|
|
201
209
|
def self.note_module_deals_with_final(mod)
|
|
202
210
|
# Side-effectfully initialize the value if it's not already there
|
|
203
|
-
@modules_with_final[mod
|
|
204
|
-
@modules_with_final[mod.singleton_class
|
|
211
|
+
@modules_with_final[mod]
|
|
212
|
+
@modules_with_final[mod.singleton_class]
|
|
205
213
|
end
|
|
206
214
|
|
|
207
215
|
# Only public because it needs to get called below inside the replace_method blocks below.
|
|
208
|
-
def self._on_method_added(hook_mod,
|
|
216
|
+
def self._on_method_added(hook_mod, mod, method_name)
|
|
209
217
|
if T::Private::DeclState.current.skip_on_method_added
|
|
210
218
|
return
|
|
211
219
|
end
|
|
212
220
|
|
|
213
221
|
current_declaration = T::Private::DeclState.current.active_declaration
|
|
214
|
-
mod = is_singleton_method ? hook_mod.singleton_class : hook_mod
|
|
215
222
|
|
|
216
223
|
if T::Private::Final.final_module?(mod) && (current_declaration.nil? || !current_declaration.final)
|
|
217
224
|
raise "#{mod} was declared as final but its method `#{method_name}` was not declared as final"
|
|
218
225
|
end
|
|
219
226
|
# Don't compute mod.ancestors if we don't need to bother checking final-ness.
|
|
220
|
-
if @was_ever_final_names.include?(method_name) && @modules_with_final.include?(mod
|
|
227
|
+
if @was_ever_final_names.include?(method_name) && @modules_with_final.include?(mod)
|
|
221
228
|
_check_final_ancestors(mod, mod.ancestors, [method_name], nil)
|
|
222
229
|
# We need to fetch the active declaration again, as _check_final_ancestors
|
|
223
230
|
# may have reset it (see the comment in that method for details).
|
|
@@ -247,7 +254,7 @@ module T::Private::Methods
|
|
|
247
254
|
# (or unwrap back to the original method).
|
|
248
255
|
key = method_owner_and_name_to_key(mod, method_name)
|
|
249
256
|
unless current_declaration.raw
|
|
250
|
-
T::Private::ClassUtils.replace_method(mod, method_name) do |*args, &blk|
|
|
257
|
+
T::Private::ClassUtils.replace_method(mod, method_name, true) do |*args, &blk|
|
|
251
258
|
method_sig = T::Private::Methods.maybe_run_sig_block_for_key(key)
|
|
252
259
|
method_sig ||= T::Private::Methods._handle_missing_method_signature(
|
|
253
260
|
self,
|
|
@@ -271,7 +278,7 @@ module T::Private::Methods
|
|
|
271
278
|
elsif T::Configuration::AT_LEAST_RUBY_2_7
|
|
272
279
|
original_method.bind_call(self, *args, &blk)
|
|
273
280
|
else
|
|
274
|
-
original_method.bind(self).call(*args, &blk)
|
|
281
|
+
original_method.bind(self).call(*args, &blk) # rubocop:disable Performance/BindCall
|
|
275
282
|
end
|
|
276
283
|
end
|
|
277
284
|
end
|
|
@@ -282,7 +289,7 @@ module T::Private::Methods
|
|
|
282
289
|
# use hook_mod, not mod, because for example, we want class C to be marked as having final if we def C.foo as
|
|
283
290
|
# final. change this to mod to see some final_method tests fail.
|
|
284
291
|
note_module_deals_with_final(hook_mod)
|
|
285
|
-
add_module_with_final_method(
|
|
292
|
+
add_module_with_final_method(mod, method_name)
|
|
286
293
|
end
|
|
287
294
|
end
|
|
288
295
|
|
|
@@ -334,9 +341,11 @@ module T::Private::Methods
|
|
|
334
341
|
nil
|
|
335
342
|
end
|
|
336
343
|
|
|
344
|
+
declaration_block.loc = nil
|
|
345
|
+
|
|
337
346
|
signature =
|
|
338
347
|
if current_declaration
|
|
339
|
-
build_sig(hook_mod, method_name, original_method, current_declaration
|
|
348
|
+
build_sig(hook_mod, method_name, original_method, current_declaration)
|
|
340
349
|
else
|
|
341
350
|
Signature.new_untyped(method: original_method)
|
|
342
351
|
end
|
|
@@ -353,7 +362,7 @@ module T::Private::Methods
|
|
|
353
362
|
.decl
|
|
354
363
|
end
|
|
355
364
|
|
|
356
|
-
def self.build_sig(hook_mod, method_name, original_method, current_declaration
|
|
365
|
+
def self.build_sig(hook_mod, method_name, original_method, current_declaration)
|
|
357
366
|
begin
|
|
358
367
|
# We allow `sig` in the current module's context (normal case) and
|
|
359
368
|
if hook_mod != current_declaration.mod &&
|
|
@@ -424,7 +433,7 @@ module T::Private::Methods
|
|
|
424
433
|
run_sig_block_for_key(method_to_key(method))
|
|
425
434
|
end
|
|
426
435
|
|
|
427
|
-
private_class_method def self.run_sig_block_for_key(key)
|
|
436
|
+
private_class_method def self.run_sig_block_for_key(key, force_type_init: false)
|
|
428
437
|
blk = @sig_wrappers[key]
|
|
429
438
|
if !blk
|
|
430
439
|
sig = @signatures_by_method[key]
|
|
@@ -447,14 +456,17 @@ module T::Private::Methods
|
|
|
447
456
|
end
|
|
448
457
|
|
|
449
458
|
@sig_wrappers.delete(key)
|
|
459
|
+
|
|
460
|
+
sig.force_type_init if force_type_init
|
|
461
|
+
|
|
450
462
|
sig
|
|
451
463
|
end
|
|
452
464
|
|
|
453
|
-
def self.run_all_sig_blocks
|
|
465
|
+
def self.run_all_sig_blocks(force_type_init: true)
|
|
454
466
|
loop do
|
|
455
467
|
break if @sig_wrappers.empty?
|
|
456
468
|
key, = @sig_wrappers.first
|
|
457
|
-
run_sig_block_for_key(key)
|
|
469
|
+
run_sig_block_for_key(key, force_type_init: force_type_init)
|
|
458
470
|
end
|
|
459
471
|
end
|
|
460
472
|
|
|
@@ -467,8 +479,8 @@ module T::Private::Methods
|
|
|
467
479
|
def self._hook_impl(target, singleton_class, source)
|
|
468
480
|
# we do not need to call add_was_ever_final here, because we have already marked
|
|
469
481
|
# any such methods when source was originally defined.
|
|
470
|
-
if !@modules_with_final.include?(target
|
|
471
|
-
if !@modules_with_final.include?(source
|
|
482
|
+
if !@modules_with_final.include?(target)
|
|
483
|
+
if !@modules_with_final.include?(source)
|
|
472
484
|
return
|
|
473
485
|
end
|
|
474
486
|
note_module_deals_with_final(target)
|
|
@@ -494,19 +506,37 @@ module T::Private::Methods
|
|
|
494
506
|
return
|
|
495
507
|
end
|
|
496
508
|
if is_enabled
|
|
497
|
-
|
|
509
|
+
# A cut-down version of T::Private::ClassUtils::ReplacedMethod#restore, because we
|
|
510
|
+
# should only be resetting final hooks during tests.
|
|
511
|
+
T::Configuration.without_ruby_warnings do
|
|
512
|
+
Module.define_method(:included, @old_hooks[0])
|
|
513
|
+
Module.define_method(:extended, @old_hooks[1])
|
|
514
|
+
Class.define_method(:inherited, @old_hooks[2])
|
|
515
|
+
end
|
|
498
516
|
@old_hooks = nil
|
|
499
517
|
else
|
|
500
|
-
old_included = T::Private::ClassUtils.replace_method(Module, :included) do |arg|
|
|
501
|
-
|
|
518
|
+
old_included = T::Private::ClassUtils.replace_method(Module, :included, true) do |arg|
|
|
519
|
+
if T::Configuration::AT_LEAST_RUBY_2_7
|
|
520
|
+
old_included.bind_call(self, arg)
|
|
521
|
+
else
|
|
522
|
+
old_included.bind(self).call(arg) # rubocop:disable Performance/BindCall
|
|
523
|
+
end
|
|
502
524
|
::T::Private::Methods._hook_impl(arg, false, self)
|
|
503
525
|
end
|
|
504
|
-
old_extended = T::Private::ClassUtils.replace_method(Module, :extended) do |arg|
|
|
505
|
-
|
|
526
|
+
old_extended = T::Private::ClassUtils.replace_method(Module, :extended, true) do |arg|
|
|
527
|
+
if T::Configuration::AT_LEAST_RUBY_2_7
|
|
528
|
+
old_extended.bind_call(self, arg)
|
|
529
|
+
else
|
|
530
|
+
old_extended.bind(self).call(arg) # rubocop:disable Performance/BindCall
|
|
531
|
+
end
|
|
506
532
|
::T::Private::Methods._hook_impl(arg, true, self)
|
|
507
533
|
end
|
|
508
|
-
old_inherited = T::Private::ClassUtils.replace_method(Class, :inherited) do |arg|
|
|
509
|
-
|
|
534
|
+
old_inherited = T::Private::ClassUtils.replace_method(Class, :inherited, true) do |arg|
|
|
535
|
+
if T::Configuration::AT_LEAST_RUBY_2_7
|
|
536
|
+
old_inherited.bind_call(self, arg)
|
|
537
|
+
else
|
|
538
|
+
old_inherited.bind(self).call(arg) # rubocop:disable Performance/BindCall
|
|
539
|
+
end
|
|
510
540
|
::T::Private::Methods._hook_impl(arg, false, self)
|
|
511
541
|
end
|
|
512
542
|
@old_hooks = [old_included, old_extended, old_inherited]
|
|
@@ -516,14 +546,14 @@ module T::Private::Methods
|
|
|
516
546
|
module MethodHooks
|
|
517
547
|
def method_added(name)
|
|
518
548
|
super(name)
|
|
519
|
-
::T::Private::Methods._on_method_added(self,
|
|
549
|
+
::T::Private::Methods._on_method_added(self, self, name)
|
|
520
550
|
end
|
|
521
551
|
end
|
|
522
552
|
|
|
523
553
|
module SingletonMethodHooks
|
|
524
554
|
def singleton_method_added(name)
|
|
525
555
|
super(name)
|
|
526
|
-
::T::Private::Methods._on_method_added(self,
|
|
556
|
+
::T::Private::Methods._on_method_added(self, singleton_class, name)
|
|
527
557
|
end
|
|
528
558
|
end
|
|
529
559
|
|