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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sorbet-runtime.rb +8 -2
  3. data/lib/types/_types.rb +61 -7
  4. data/lib/types/compatibility_patches.rb +5 -3
  5. data/lib/types/configuration.rb +1 -1
  6. data/lib/types/enum.rb +6 -1
  7. data/lib/types/generic.rb +2 -0
  8. data/lib/types/helpers.rb +7 -0
  9. data/lib/types/private/abstract/declare.rb +9 -9
  10. data/lib/types/private/caller_utils.rb +21 -0
  11. data/lib/types/private/casts.rb +6 -3
  12. data/lib/types/private/class_utils.rb +13 -6
  13. data/lib/types/private/methods/_methods.rb +68 -38
  14. data/lib/types/private/methods/call_validation.rb +120 -7
  15. data/lib/types/private/methods/call_validation_2_6.rb +586 -60
  16. data/lib/types/private/methods/call_validation_2_7.rb +586 -60
  17. data/lib/types/private/methods/decl_builder.rb +22 -7
  18. data/lib/types/private/methods/signature.rb +71 -38
  19. data/lib/types/private/methods/signature_validation.rb +77 -9
  20. data/lib/types/private/runtime_levels.rb +19 -0
  21. data/lib/types/private/sealed.rb +6 -6
  22. data/lib/types/private/types/not_typed.rb +6 -0
  23. data/lib/types/private/types/simple_pair_union.rb +55 -0
  24. data/lib/types/private/types/string_holder.rb +4 -0
  25. data/lib/types/private/types/type_alias.rb +4 -0
  26. data/lib/types/private/types/void.rb +33 -23
  27. data/lib/types/props/custom_type.rb +2 -2
  28. data/lib/types/props/decorator.rb +49 -43
  29. data/lib/types/props/has_lazily_specialized_methods.rb +4 -2
  30. data/lib/types/props/pretty_printable.rb +45 -83
  31. data/lib/types/props/private/deserializer_generator.rb +4 -1
  32. data/lib/types/props/private/setter_factory.rb +130 -70
  33. data/lib/types/props/serializable.rb +24 -10
  34. data/lib/types/props/type_validation.rb +5 -2
  35. data/lib/types/struct.rb +2 -2
  36. data/lib/types/types/anything.rb +35 -0
  37. data/lib/types/types/attached_class.rb +4 -0
  38. data/lib/types/types/base.rb +21 -1
  39. data/lib/types/types/class_of.rb +15 -0
  40. data/lib/types/types/enum.rb +5 -1
  41. data/lib/types/types/fixed_array.rb +32 -12
  42. data/lib/types/types/fixed_hash.rb +38 -9
  43. data/lib/types/types/intersection.rb +13 -6
  44. data/lib/types/types/noreturn.rb +4 -1
  45. data/lib/types/types/proc.rb +18 -8
  46. data/lib/types/types/self_type.rb +4 -0
  47. data/lib/types/types/simple.rb +36 -4
  48. data/lib/types/types/t_enum.rb +4 -0
  49. data/lib/types/types/type_parameter.rb +23 -0
  50. data/lib/types/types/type_variable.rb +4 -0
  51. data/lib/types/types/typed_array.rb +35 -1
  52. data/lib/types/types/typed_class.rb +102 -0
  53. data/lib/types/types/typed_enumerable.rb +26 -12
  54. data/lib/types/types/typed_enumerator.rb +1 -3
  55. data/lib/types/types/typed_enumerator_chain.rb +39 -0
  56. data/lib/types/types/typed_enumerator_lazy.rb +1 -3
  57. data/lib/types/types/typed_hash.rb +17 -7
  58. data/lib/types/types/typed_range.rb +1 -3
  59. data/lib/types/types/typed_set.rb +1 -3
  60. data/lib/types/types/union.rb +62 -19
  61. data/lib/types/types/untyped.rb +4 -0
  62. data/lib/types/utils.rb +47 -37
  63. metadata +19 -29
  64. data/lib/types/interface_wrapper.rb +0 -158
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1adb9a8fbde97258c22cfe8b67725eccbf5227dc90d6f8907c76a3a2547f19d2
4
- data.tar.gz: 5f98dea61861b348ecd7f7d6553cc0220566938280ef5e0c3bd0e91861146be6
3
+ metadata.gz: 62eef630524c0b1ad1f2a3d8749bd5f821a28ebfabbe7e4909010d530ffa698b
4
+ data.tar.gz: eb3fb942cb3ead21ec80e425726f823a6eb2cef834354ae2279a00c7fc5bf599
5
5
  SHA512:
6
- metadata.gz: 4ae628db18157eaa445b200eae89737d317251378b14128ff86c3f9ea65ea3f72387734c4ce52e469a41ceee0507642eee2706c74c2b2082f245022c2ed04b3c
7
- data.tar.gz: 63152be7a538c504777b332d8fc19408f63566a55c76f54875222732fc86e0e19efb56223b79ac60f64ba399dcccdbc3bb78f34a14ffef3e3347d20126c63c67
6
+ metadata.gz: 62855bc96c970aab1e89be225e336d6e7adc53b486137bb2f09457abdd9f932d23d4e6a37d05d6c8dad5d1fe098fad3b8cd4c529fcb7926e0bfefdc7b63249f3
7
+ data.tar.gz: d14d50ff7afbcad3fb245d2e9f815857c389b90a619fce352eca24da6be967728b748c89bac041ab71beed0f9bf4bf690c19a08b193d9ec701f0364026b7bc2b
@@ -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.new(name)
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, cast_method: "T.cast")
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, cast_method: "T.let")
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, cast_method: "T.bind")
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, cast_method: "T.assert_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.new
286
+ T::Types::TypedArray::Untyped::Private::INSTANCE
255
287
  else
256
- T::Types::TypedArray.new(type)
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
- method = @klass.instance_method(method_name.to_sym)
31
- T::Private::Methods.maybe_run_sig_block_for_method(method)
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
@@ -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: Module).void}
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
@@ -19,4 +19,6 @@ module T::Generic
19
19
  def type_template(variance=:invariant, &blk)
20
20
  T::Types::TypeTemplate.new(variance)
21
21
  end
22
+
23
+ def has_attached_class!(variance=:invariant, &blk); end
22
24
  end
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 mod.instance_method(:initialize).owner == mod
31
- raise "You must call `abstract!` *before* defining an initialize method"
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(:define_method, :initialize) do |*args, &blk|
38
- if self.class == mod
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
- super(*args, &blk)
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, :initialize, :initialize)
46
+ mod.singleton_class.send(:alias_method, :new, :new)
47
47
 
48
- if mod.respond_to?(:ruby2_keywords, true)
49
- mod.send(:ruby2_keywords, :initialize)
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
@@ -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
- error = T::Utils.coerce(type).error_message_for_obj(value)
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). Returns a ReplacedMethod instance
95
- # on which you can call `bind(...).call(...)` to call the original method, or `restore` to
96
- # restore the original method (by overwriting or removing the override).
97
- def self.replace_method(mod, name, &blk)
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
- ReplacedMethod.new(mod, original_method, new_method, overwritten, original_visibility)
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
- @signatures_by_method = {}
7
- @sig_wrappers = {}
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.object_id, nil)
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.object_id, nil)
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.object_id).include?(method_name)
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 = caller_locations&.find {|l| !l.to_s.match?(%r{sorbet-runtime[^/]*/lib/})}
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, is_singleton_method)
190
- m = is_singleton_method ? mod.singleton_class : mod
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[mid] = methods
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.object_id]
204
- @modules_with_final[mod.singleton_class.object_id]
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, method_name, is_singleton_method: false)
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.object_id)
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(hook_mod, method_name, is_singleton_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, declaration_block.loc)
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, loc)
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.object_id)
471
- if !@modules_with_final.include?(source.object_id)
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
- @old_hooks.each(&:restore)
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
- old_included.bind(self).call(arg)
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
- old_extended.bind(self).call(arg)
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
- old_inherited.bind(self).call(arg)
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, name, is_singleton_method: false)
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, name, is_singleton_method: true)
556
+ ::T::Private::Methods._on_method_added(self, singleton_class, name)
527
557
  end
528
558
  end
529
559