sorbet-runtime 0.5.11144 → 0.5.11663

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sorbet-runtime.rb +1 -4
  3. data/lib/types/compatibility_patches.rb +1 -1
  4. data/lib/types/configuration.rb +3 -2
  5. data/lib/types/enum.rb +63 -39
  6. data/lib/types/non_forcing_constants.rb +4 -14
  7. data/lib/types/private/abstract/declare.rb +4 -5
  8. data/lib/types/private/caller_utils.rb +27 -0
  9. data/lib/types/private/class_utils.rb +1 -1
  10. data/lib/types/private/methods/_methods.rb +35 -30
  11. data/lib/types/private/methods/call_validation.rb +48 -16
  12. data/lib/types/private/methods/call_validation_2_6.rb +518 -0
  13. data/lib/types/private/methods/call_validation_2_7.rb +518 -0
  14. data/lib/types/private/methods/decl_builder.rb +4 -4
  15. data/lib/types/private/methods/signature.rb +10 -2
  16. data/lib/types/private/methods/signature_validation.rb +13 -7
  17. data/lib/types/private/runtime_levels.rb +0 -3
  18. data/lib/types/private/sealed.rb +8 -8
  19. data/lib/types/private/types/not_typed.rb +4 -0
  20. data/lib/types/private/types/string_holder.rb +4 -0
  21. data/lib/types/private/types/type_alias.rb +4 -0
  22. data/lib/types/private/types/void.rb +4 -0
  23. data/lib/types/props/_props.rb +3 -3
  24. data/lib/types/props/decorator.rb +9 -8
  25. data/lib/types/props/has_lazily_specialized_methods.rb +5 -1
  26. data/lib/types/props/pretty_printable.rb +7 -7
  27. data/lib/types/props/private/deserializer_generator.rb +4 -1
  28. data/lib/types/props/private/setter_factory.rb +129 -69
  29. data/lib/types/props/serializable.rb +24 -3
  30. data/lib/types/struct.rb +1 -1
  31. data/lib/types/types/anything.rb +4 -0
  32. data/lib/types/types/attached_class.rb +4 -0
  33. data/lib/types/types/base.rb +8 -2
  34. data/lib/types/types/class_of.rb +6 -2
  35. data/lib/types/types/enum.rb +5 -1
  36. data/lib/types/types/fixed_array.rb +19 -12
  37. data/lib/types/types/fixed_hash.rb +16 -9
  38. data/lib/types/types/intersection.rb +13 -6
  39. data/lib/types/types/noreturn.rb +4 -0
  40. data/lib/types/types/proc.rb +19 -9
  41. data/lib/types/types/self_type.rb +4 -0
  42. data/lib/types/types/simple.rb +9 -0
  43. data/lib/types/types/t_enum.rb +4 -0
  44. data/lib/types/types/type_parameter.rb +4 -0
  45. data/lib/types/types/type_variable.rb +4 -0
  46. data/lib/types/types/typed_array.rb +7 -2
  47. data/lib/types/types/typed_class.rb +22 -5
  48. data/lib/types/types/typed_enumerable.rb +22 -16
  49. data/lib/types/types/typed_enumerator.rb +2 -4
  50. data/lib/types/types/typed_enumerator_chain.rb +2 -4
  51. data/lib/types/types/typed_enumerator_lazy.rb +2 -4
  52. data/lib/types/types/typed_hash.rb +17 -7
  53. data/lib/types/types/typed_range.rb +2 -4
  54. data/lib/types/types/typed_set.rb +3 -5
  55. data/lib/types/types/union.rb +12 -5
  56. data/lib/types/types/untyped.rb +4 -0
  57. data/lib/types/utils.rb +7 -5
  58. metadata +23 -24
  59. data/lib/types/interface_wrapper.rb +0 -162
  60. data/lib/types/private/compiler.rb +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5b21bcb5dabd49853b11434c9845cf1530e7c5b1fa475b93fda398e5b8121bb
4
- data.tar.gz: 34af59fbefda1547828f8aa5fc668cf588754d9b8b1ac1221a804ba2840ad99b
3
+ metadata.gz: 15e1e8f235a0bf876cc4971b27e37342d27be61b6beaa739e85693a10f55e608
4
+ data.tar.gz: 1f641e418f5c7ee1475eb6c2bc919cca0326f5da6f8034510043a0ff5c3ec412
5
5
  SHA512:
6
- metadata.gz: a093baa9c20a21486f6af5bd315bae13c37e59e2581dfff5a8dd6f3e95be0a50222a988358fbc2b7e14f2642d6cc14d6b3f33227d7bf73e03ecaebde1b1cb08d
7
- data.tar.gz: d814fc35c1b03fdd2d73639515eb52cd438caa7c6719d647917add140bf9a8432757ee037836eab5011b8e8c677378092225d956452e9f6e3ab063609856d805
6
+ metadata.gz: 5c4848e47d7f3a23b24e474386e94b1b6c6d7ac15fa831bee20591abdce7bcc52372cd489ab1333f86dfb3d2f7113e9f3305b762a1b1f4e0b5ecabc682887f22
7
+ data.tar.gz: 1083001bef742120da035d1930ed6fc466c1afe67cc1ecaebb545fcd25fa8cb15e02ddb4fd6e91738582b974ad49c1948775d734fb1e2702581730f5986b4470
@@ -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'
@@ -73,7 +74,6 @@ require_relative 'types/private/abstract/validate'
73
74
 
74
75
  # Catch all. Sort of built by `cd extn; find types -type f | grep -v test | sort`
75
76
  require_relative 'types/generic'
76
- require_relative 'types/interface_wrapper'
77
77
  require_relative 'types/private/abstract/declare'
78
78
  require_relative 'types/private/abstract/hooks'
79
79
  require_relative 'types/private/casts'
@@ -117,6 +117,3 @@ require_relative 'types/struct'
117
117
  require_relative 'types/non_forcing_constants'
118
118
 
119
119
  require_relative 'types/compatibility_patches'
120
-
121
- # Sorbet Compiler support module
122
- require_relative 'types/private/compiler'
@@ -38,7 +38,7 @@ if defined? ::RSpec::Mocks
38
38
 
39
39
  module MethodDoubleExtensions
40
40
  def initialize(object, method_name, proxy)
41
- 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
42
42
  method = ::RSpec::Support.method_handle_for(object, method_name)
43
43
  T::Private::Methods.maybe_run_sig_block_for_method(method)
44
44
  end
@@ -113,7 +113,7 @@ module T::Configuration
113
113
  # statically, so that methods don't have to guard themselves from being
114
114
  # called incorrectly by untyped code.
115
115
  #
116
- # @param [:never, :compiled, :tests, :always] default_checked_level
116
+ # @param [:never, :tests, :always] default_checked_level
117
117
  def self.default_checked_level=(default_checked_level)
118
118
  T::Private::RuntimeLevels.default_checked_level = default_checked_level
119
119
  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
@@ -536,6 +536,7 @@ module T::Configuration
536
536
 
537
537
  @legacy_t_enum_migration_mode = false
538
538
  def self.enable_legacy_t_enum_migration_mode
539
+ T::Enum.include(T::Enum::LegacyMigrationMode)
539
540
  @legacy_t_enum_migration_mode = true
540
541
  end
541
542
  def self.disable_legacy_t_enum_migration_mode
data/lib/types/enum.rb CHANGED
@@ -192,68 +192,92 @@ class T::Enum
192
192
  # responds to the `to_str` method. It does not actually call `to_str` however.
193
193
  #
194
194
  # See https://ruby-doc.org/core-2.4.0/String.html#method-i-3D-3D
195
- sig {returns(String)}
195
+ T::Sig::WithoutRuntime.sig {returns(String)}
196
196
  def to_str
197
197
  msg = 'Implicit conversion of Enum instances to strings is not allowed. Call #serialize instead.'
198
198
  if T::Configuration.legacy_t_enum_migration_mode?
199
199
  T::Configuration.soft_assert_handler(
200
200
  msg,
201
- storytime: {class: self.class.name},
201
+ storytime: {
202
+ class: self.class.name,
203
+ caller_location: Kernel.caller_locations(1..1)&.[](0)&.then {"#{_1.path}:#{_1.lineno}"},
204
+ },
202
205
  )
203
206
  serialize.to_s
204
207
  else
205
- raise NoMethodError.new(msg)
208
+ Kernel.raise NoMethodError.new(msg)
206
209
  end
207
210
  end
208
211
 
209
- sig {params(other: BasicObject).returns(T::Boolean).checked(:never)}
210
- def ==(other)
211
- case other
212
- when String
213
- if T::Configuration.legacy_t_enum_migration_mode?
214
- comparison_assertion_failed(:==, other)
215
- self.serialize == other
212
+ module LegacyMigrationMode
213
+ include Kernel
214
+ extend T::Helpers
215
+ abstract!
216
+
217
+ if T.unsafe(false)
218
+ # Declare to the type system that the `serialize` method for sure exists
219
+ # on whatever we mix this into.
220
+ T::Sig::WithoutRuntime.sig {abstract.returns(T.untyped)}
221
+ def serialize; end
222
+ end
223
+
224
+ # WithoutRuntime so that comparison_assertion_failed can assume a constant stack depth
225
+ T::Sig::WithoutRuntime.sig {params(other: BasicObject).returns(T::Boolean)}
226
+ def ==(other)
227
+ case other
228
+ when String
229
+ if T::Configuration.legacy_t_enum_migration_mode?
230
+ comparison_assertion_failed(:==, other)
231
+ self.serialize == other
232
+ else
233
+ false
234
+ end
216
235
  else
217
- false
236
+ super(other)
218
237
  end
219
- else
220
- super(other)
221
238
  end
222
- end
223
239
 
224
- sig {params(other: BasicObject).returns(T::Boolean).checked(:never)}
225
- def ===(other)
226
- case other
227
- when String
228
- if T::Configuration.legacy_t_enum_migration_mode?
229
- comparison_assertion_failed(:===, other)
230
- self.serialize == other
240
+ # WithoutRuntime so that comparison_assertion_failed can assume a constant stack depth
241
+ T::Sig::WithoutRuntime.sig {params(other: BasicObject).returns(T::Boolean)}
242
+ def ===(other)
243
+ case other
244
+ when String
245
+ if T::Configuration.legacy_t_enum_migration_mode?
246
+ comparison_assertion_failed(:===, other)
247
+ self.serialize == other
248
+ else
249
+ false
250
+ end
231
251
  else
232
- false
252
+ super(other)
233
253
  end
234
- else
235
- super(other)
236
254
  end
237
- end
238
255
 
239
- sig {params(method: Symbol, other: T.untyped).void}
240
- private def comparison_assertion_failed(method, other)
241
- T::Configuration.soft_assert_handler(
242
- 'Enum to string comparison not allowed. Compare to the Enum instance directly instead. See go/enum-migration',
243
- storytime: {
244
- class: self.class.name,
245
- self: self.inspect,
246
- other: other,
247
- other_class: other.class.name,
248
- method: method,
249
- }
250
- )
256
+ # WithoutRuntime so that caller_locations can assume a constant stack depth
257
+ # (Otherwise, the first call would be the method with the wrapping, which would have a different stack depth.)
258
+ T::Sig::WithoutRuntime.sig {params(method: Symbol, other: T.untyped).void}
259
+ private def comparison_assertion_failed(method, other)
260
+ T::Configuration.soft_assert_handler(
261
+ 'Enum to string comparison not allowed. Compare to the Enum instance directly instead. See go/enum-migration',
262
+ storytime: {
263
+ class: self.class.name,
264
+ self: self.inspect,
265
+ other: other,
266
+ other_class: other.class.name,
267
+ method: method,
268
+ caller_location: Kernel.caller_locations(2..2)&.[](0)&.then {"#{_1.path}:#{_1.lineno}"},
269
+ }
270
+ )
271
+ end
251
272
  end
252
273
 
253
274
  ### Private implementation ###
254
275
 
276
+ UNSET = T.let(Module.new.freeze, Module)
277
+ private_constant :UNSET
278
+
255
279
  sig {params(serialized_val: SerializedVal).void}
256
- def initialize(serialized_val=nil)
280
+ def initialize(serialized_val=UNSET)
257
281
  raise 'T::Enum is abstract' if self.class == T::Enum
258
282
  if !self.class.started_initializing?
259
283
  raise "Must instantiate all enum values of #{self.class} inside 'enums do'."
@@ -279,7 +303,7 @@ class T::Enum
279
303
  sig {params(const_name: Symbol).void}
280
304
  def _bind_name(const_name)
281
305
  @const_name = const_name
282
- @serialized_val = const_to_serialized_val(const_name) if @serialized_val.nil?
306
+ @serialized_val = const_to_serialized_val(const_name) if @serialized_val.equal?(UNSET)
283
307
  freeze
284
308
  end
285
309
 
@@ -4,20 +4,13 @@
4
4
  module T::NonForcingConstants
5
5
  # NOTE: This method is documented on the RBI in Sorbet's payload, so that it
6
6
  # shows up in the hover/completion documentation via LSP.
7
- T::Sig::WithoutRuntime.sig {params(val: BasicObject, klass: String, package: T.nilable(String)).returns(T::Boolean)}
8
- def self.non_forcing_is_a?(val, klass, package: nil)
7
+ T::Sig::WithoutRuntime.sig {params(val: BasicObject, klass: String).returns(T::Boolean)}
8
+ def self.non_forcing_is_a?(val, klass)
9
9
  method_name = "T::NonForcingConstants.non_forcing_is_a?"
10
10
  if klass.empty?
11
11
  raise ArgumentError.new("The string given to `#{method_name}` must not be empty")
12
12
  end
13
13
 
14
- # We don't treat packages differently at runtime, but the static
15
- # type-checker still needs to have the package and constant
16
- # separated out. This just re-assembles the string as needed
17
- if !package.nil?
18
- klass = "::#{package}::#{klass}"
19
- end
20
-
21
14
  current_klass = T.let(nil, T.nilable(Module))
22
15
  current_prefix = T.let(nil, T.nilable(String))
23
16
 
@@ -25,10 +18,7 @@ module T::NonForcingConstants
25
18
  parts.each do |part|
26
19
  if current_klass.nil?
27
20
  # First iteration
28
- if part != "" && package.nil?
29
- # if we've supplied a package, we're probably running in
30
- # package mode, which means absolute references are
31
- # meaningless
21
+ if part != ""
32
22
  raise ArgumentError.new("The string given to `#{method_name}` must be an absolute constant reference that starts with `::`")
33
23
  end
34
24
 
@@ -37,7 +27,7 @@ module T::NonForcingConstants
37
27
 
38
28
  # if this had a :: prefix, then there's no more loading to
39
29
  # do---skip to the next one
40
- next if part == ""
30
+ next
41
31
  end
42
32
 
43
33
  if current_klass.autoload?(part)
@@ -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
@@ -35,11 +34,11 @@ module T::Private::Abstract::Declare
35
34
  # define_method because of the guard above
36
35
 
37
36
  mod.send(:define_singleton_method, :new) do |*args, &blk|
38
- super(*args, &blk).tap do |result|
39
- if result.instance_of?(mod)
40
- raise "#{mod} is declared as abstract; it cannot be instantiated"
41
- end
37
+ result = super(*args, &blk)
38
+ if result.instance_of?(mod)
39
+ raise "#{mod} is declared as abstract; it cannot be instantiated"
42
40
  end
41
+ result
43
42
  end
44
43
 
45
44
  # Ruby doesn not emit "method redefined" warnings for aliased methods
@@ -0,0 +1,27 @@
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
+ skipped_first = false
8
+ Thread.each_caller_location do |loc|
9
+ unless skipped_first
10
+ skipped_first = true
11
+ next
12
+ end
13
+
14
+ next if loc.path&.start_with?("<internal:")
15
+
16
+ return loc if yield(loc)
17
+ end
18
+ nil
19
+ end
20
+ else
21
+ def self.find_caller
22
+ caller_locations(2).find do |loc|
23
+ !loc.path&.start_with?("<internal:") && yield(loc)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -65,7 +65,7 @@ module T::Private::ClassUtils
65
65
  elsif mod.private_method_defined?(name)
66
66
  :private
67
67
  else
68
- raise NameError.new("undefined method `#{name}` for `#{mod}`")
68
+ mod.method(name) # Raises
69
69
  end
70
70
  end
71
71
 
@@ -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.
@@ -132,7 +137,7 @@ module T::Private::Methods
132
137
  source_ancestors = nil
133
138
  # use reverse_each to check farther-up ancestors first, for better error messages.
134
139
  target_ancestors.reverse_each do |ancestor|
135
- final_methods = @modules_with_final.fetch(ancestor.object_id, nil)
140
+ final_methods = @modules_with_final.fetch(ancestor, nil)
136
141
  # In this case, either ancestor didn't have any final methods anywhere in its
137
142
  # ancestor chain, or ancestor did have final methods somewhere in its ancestor
138
143
  # chain, but no final methods defined in ancestor itself. Either way, there
@@ -150,20 +155,20 @@ module T::Private::Methods
150
155
  # filter out things without actual final methods just to make sure that
151
156
  # the below checks (which should be uncommon) go as quickly as possible.
152
157
  source_ancestors.select! do |a|
153
- @modules_with_final.fetch(a.object_id, nil)
158
+ @modules_with_final.fetch(a, nil)
154
159
  end
155
160
  end
156
161
  # final-ness means that there should be no more than one index for which
157
162
  # the below block returns true.
158
163
  defining_ancestor_idx = source_ancestors.index do |a|
159
- @modules_with_final.fetch(a.object_id).include?(method_name)
164
+ @modules_with_final.fetch(a).include?(method_name)
160
165
  end
161
166
  next if defining_ancestor_idx && source_ancestors[defining_ancestor_idx] == ancestor
162
167
  end
163
168
 
164
169
  definition_file, definition_line = T::Private::Methods.signature_for_method(ancestor.instance_method(method_name)).method.source_location
165
170
  is_redefined = target == ancestor
166
- caller_loc = caller_locations.find {|l| !l.to_s.start_with?(SORBET_RUNTIME_LIB_PATH)}
171
+ caller_loc = T::Private::CallerUtils.find_caller {|loc| !loc.path.to_s.start_with?(SORBET_RUNTIME_LIB_PATH)}
167
172
  extra_info = "\n"
168
173
  if caller_loc
169
174
  extra_info = (is_redefined ? "Redefined" : "Overridden") + " here: #{caller_loc.path}:#{caller_loc.lineno}\n"
@@ -191,13 +196,11 @@ module T::Private::Methods
191
196
  end
192
197
  end
193
198
 
194
- def self.add_module_with_final_method(mod, method_name, is_singleton_method)
195
- m = is_singleton_method ? mod.singleton_class : mod
196
- mid = m.object_id
197
- methods = @modules_with_final[mid]
199
+ def self.add_module_with_final_method(mod, method_name)
200
+ methods = @modules_with_final[mod]
198
201
  if methods.nil?
199
202
  methods = {}
200
- @modules_with_final[mid] = methods
203
+ @modules_with_final[mod] = methods
201
204
  end
202
205
  methods[method_name] = true
203
206
  nil
@@ -205,24 +208,23 @@ module T::Private::Methods
205
208
 
206
209
  def self.note_module_deals_with_final(mod)
207
210
  # Side-effectfully initialize the value if it's not already there
208
- @modules_with_final[mod.object_id]
209
- @modules_with_final[mod.singleton_class.object_id]
211
+ @modules_with_final[mod]
212
+ @modules_with_final[mod.singleton_class]
210
213
  end
211
214
 
212
215
  # Only public because it needs to get called below inside the replace_method blocks below.
213
- def self._on_method_added(hook_mod, method_name, is_singleton_method: false)
216
+ def self._on_method_added(hook_mod, mod, method_name)
214
217
  if T::Private::DeclState.current.skip_on_method_added
215
218
  return
216
219
  end
217
220
 
218
221
  current_declaration = T::Private::DeclState.current.active_declaration
219
- mod = is_singleton_method ? hook_mod.singleton_class : hook_mod
220
222
 
221
223
  if T::Private::Final.final_module?(mod) && (current_declaration.nil? || !current_declaration.final)
222
224
  raise "#{mod} was declared as final but its method `#{method_name}` was not declared as final"
223
225
  end
224
226
  # Don't compute mod.ancestors if we don't need to bother checking final-ness.
225
- 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)
226
228
  _check_final_ancestors(mod, mod.ancestors, [method_name], nil)
227
229
  # We need to fetch the active declaration again, as _check_final_ancestors
228
230
  # may have reset it (see the comment in that method for details).
@@ -276,7 +278,7 @@ module T::Private::Methods
276
278
  elsif T::Configuration::AT_LEAST_RUBY_2_7
277
279
  original_method.bind_call(self, *args, &blk)
278
280
  else
279
- original_method.bind(self).call(*args, &blk)
281
+ original_method.bind(self).call(*args, &blk) # rubocop:disable Performance/BindCall
280
282
  end
281
283
  end
282
284
  end
@@ -287,7 +289,7 @@ module T::Private::Methods
287
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
288
290
  # final. change this to mod to see some final_method tests fail.
289
291
  note_module_deals_with_final(hook_mod)
290
- add_module_with_final_method(hook_mod, method_name, is_singleton_method)
292
+ add_module_with_final_method(mod, method_name)
291
293
  end
292
294
  end
293
295
 
@@ -431,7 +433,7 @@ module T::Private::Methods
431
433
  run_sig_block_for_key(method_to_key(method))
432
434
  end
433
435
 
434
- 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)
435
437
  blk = @sig_wrappers[key]
436
438
  if !blk
437
439
  sig = @signatures_by_method[key]
@@ -454,14 +456,17 @@ module T::Private::Methods
454
456
  end
455
457
 
456
458
  @sig_wrappers.delete(key)
459
+
460
+ sig.force_type_init if force_type_init
461
+
457
462
  sig
458
463
  end
459
464
 
460
- def self.run_all_sig_blocks
465
+ def self.run_all_sig_blocks(force_type_init: true)
461
466
  loop do
462
467
  break if @sig_wrappers.empty?
463
468
  key, = @sig_wrappers.first
464
- run_sig_block_for_key(key)
469
+ run_sig_block_for_key(key, force_type_init: force_type_init)
465
470
  end
466
471
  end
467
472
 
@@ -474,8 +479,8 @@ module T::Private::Methods
474
479
  def self._hook_impl(target, singleton_class, source)
475
480
  # we do not need to call add_was_ever_final here, because we have already marked
476
481
  # any such methods when source was originally defined.
477
- if !@modules_with_final.include?(target.object_id)
478
- if !@modules_with_final.include?(source.object_id)
482
+ if !@modules_with_final.include?(target)
483
+ if !@modules_with_final.include?(source)
479
484
  return
480
485
  end
481
486
  note_module_deals_with_final(target)
@@ -514,7 +519,7 @@ module T::Private::Methods
514
519
  if T::Configuration::AT_LEAST_RUBY_2_7
515
520
  old_included.bind_call(self, arg)
516
521
  else
517
- old_included.bind(self).call(arg)
522
+ old_included.bind(self).call(arg) # rubocop:disable Performance/BindCall
518
523
  end
519
524
  ::T::Private::Methods._hook_impl(arg, false, self)
520
525
  end
@@ -522,7 +527,7 @@ module T::Private::Methods
522
527
  if T::Configuration::AT_LEAST_RUBY_2_7
523
528
  old_extended.bind_call(self, arg)
524
529
  else
525
- old_extended.bind(self).call(arg)
530
+ old_extended.bind(self).call(arg) # rubocop:disable Performance/BindCall
526
531
  end
527
532
  ::T::Private::Methods._hook_impl(arg, true, self)
528
533
  end
@@ -530,7 +535,7 @@ module T::Private::Methods
530
535
  if T::Configuration::AT_LEAST_RUBY_2_7
531
536
  old_inherited.bind_call(self, arg)
532
537
  else
533
- old_inherited.bind(self).call(arg)
538
+ old_inherited.bind(self).call(arg) # rubocop:disable Performance/BindCall
534
539
  end
535
540
  ::T::Private::Methods._hook_impl(arg, false, self)
536
541
  end
@@ -541,14 +546,14 @@ module T::Private::Methods
541
546
  module MethodHooks
542
547
  def method_added(name)
543
548
  super(name)
544
- ::T::Private::Methods._on_method_added(self, name, is_singleton_method: false)
549
+ ::T::Private::Methods._on_method_added(self, self, name)
545
550
  end
546
551
  end
547
552
 
548
553
  module SingletonMethodHooks
549
554
  def singleton_method_added(name)
550
555
  super(name)
551
- ::T::Private::Methods._on_method_added(self, name, is_singleton_method: true)
556
+ ::T::Private::Methods._on_method_added(self, singleton_class, name)
552
557
  end
553
558
  end
554
559
 
@@ -14,19 +14,7 @@ module T::Private::Methods::CallValidation
14
14
  def self.wrap_method_if_needed(mod, method_sig, original_method)
15
15
  original_visibility = visibility_method_name(mod, method_sig.method_name)
16
16
  if method_sig.mode == T::Private::Methods::Modes.abstract
17
- T::Private::ClassUtils.replace_method(mod, method_sig.method_name, true) do |*args, &blk|
18
- # TODO: write a cop to ensure that abstract methods have an empty body
19
- #
20
- # We allow abstract methods to be implemented by things further down the ancestor chain.
21
- # So, if a super method exists, call it.
22
- if defined?(super)
23
- super(*args, &blk)
24
- else
25
- raise NotImplementedError.new(
26
- "The method `#{method_sig.method_name}` on #{mod} is declared as `abstract`. It does not have an implementation."
27
- )
28
- end
29
- end
17
+ create_abstract_wrapper(mod, method_sig, original_method, original_visibility)
30
18
  # Do nothing in this case; this method was not wrapped in _on_method_added.
31
19
  elsif method_sig.defined_raw
32
20
  # Note, this logic is duplicated (intentionally, for micro-perf) at `Methods._on_method_added`,
@@ -57,6 +45,24 @@ module T::Private::Methods::CallValidation
57
45
  @is_allowed_to_have_fast_path = false
58
46
  end
59
47
 
48
+ def self.create_abstract_wrapper(mod, method_sig, original_method, original_visibility)
49
+ mod.module_eval(<<~METHOD, __FILE__, __LINE__ + 1)
50
+ #{original_visibility}
51
+
52
+ def #{method_sig.method_name}(...)
53
+ # We allow abstract methods to be implemented by things further down the ancestor chain.
54
+ # So, if a super method exists, call it.
55
+ if defined?(super)
56
+ super
57
+ else
58
+ raise NotImplementedError.new(
59
+ "The method `#{method_sig.method_name}` on #{mod} is declared as `abstract`. It does not have an implementation."
60
+ )
61
+ end
62
+ end
63
+ METHOD
64
+ end
65
+
60
66
  def self.create_validator_method(mod, original_method, method_sig, original_visibility)
61
67
  has_fixed_arity = method_sig.kwarg_types.empty? && !method_sig.has_rest && !method_sig.has_keyrest &&
62
68
  original_method.parameters.all? {|(kind, _name)| kind == :req || kind == :block}
@@ -67,14 +73,30 @@ module T::Private::Methods::CallValidation
67
73
  simple_method = all_args_are_simple && method_sig.return_type.is_a?(T::Types::Simple)
68
74
  simple_procedure = all_args_are_simple && method_sig.return_type.is_a?(T::Private::Types::Void)
69
75
 
76
+ # All the types for which valid? unconditionally returns `true`
77
+ return_is_ignorable =
78
+ (method_sig.return_type.equal?(T::Types::Untyped::Private::INSTANCE) ||
79
+ method_sig.return_type.equal?(T::Types::Anything::Private::INSTANCE) ||
80
+ method_sig.return_type.equal?(T::Types::AttachedClassType::Private::INSTANCE) ||
81
+ method_sig.return_type.equal?(T::Types::SelfType::Private::INSTANCE) ||
82
+ method_sig.return_type.is_a?(T::Types::TypeParameter) ||
83
+ method_sig.return_type.is_a?(T::Types::TypeVariable) ||
84
+ (method_sig.return_type.is_a?(T::Types::Simple) && method_sig.return_type.raw_type.equal?(BasicObject)))
85
+
86
+ returns_anything_method = all_args_are_simple && return_is_ignorable
87
+
70
88
  T::Configuration.without_ruby_warnings do
71
89
  T::Private::DeclState.current.without_on_method_added do
72
90
  if simple_method
73
91
  create_validator_method_fast(mod, original_method, method_sig, original_visibility)
92
+ elsif returns_anything_method
93
+ create_validator_method_skip_return_fast(mod, original_method, method_sig, original_visibility)
74
94
  elsif simple_procedure
75
95
  create_validator_procedure_fast(mod, original_method, method_sig, original_visibility)
76
96
  elsif ok_for_fast_path && method_sig.return_type.is_a?(T::Private::Types::Void)
77
97
  create_validator_procedure_medium(mod, original_method, method_sig, original_visibility)
98
+ elsif ok_for_fast_path && return_is_ignorable
99
+ create_validator_method_skip_return_medium(mod, original_method, method_sig, original_visibility)
78
100
  elsif ok_for_fast_path
79
101
  create_validator_method_medium(mod, original_method, method_sig, original_visibility)
80
102
  elsif can_skip_block_type
@@ -277,11 +299,21 @@ module T::Private::Methods::CallValidation
277
299
 
278
300
  def self.report_error(method_sig, error_message, kind, name, type, value, caller_offset: 0)
279
301
  caller_loc = T.must(caller_locations(3 + caller_offset, 1))[0]
280
- definition_file, definition_line = method_sig.method.source_location
302
+ method = method_sig.method
303
+ definition_file, definition_line = method.source_location
304
+
305
+ owner = method.owner
306
+ pretty_method_name =
307
+ if owner.singleton_class? && owner.respond_to?(:attached_object)
308
+ # attached_object is new in Ruby 3.2
309
+ "#{owner.attached_object}.#{method.name}"
310
+ else
311
+ "#{owner}##{method.name}"
312
+ end
281
313
 
282
314
  pretty_message = "#{kind}#{name ? " '#{name}'" : ''}: #{error_message}\n" \
283
315
  "Caller: #{caller_loc.path}:#{caller_loc.lineno}\n" \
284
- "Definition: #{definition_file}:#{definition_line}"
316
+ "Definition: #{definition_file}:#{definition_line} (#{pretty_method_name})"
285
317
 
286
318
  T::Configuration.call_validation_error_handler(
287
319
  method_sig,
@@ -304,7 +336,7 @@ module T::Private::Methods::CallValidation
304
336
  elsif mod.private_method_defined?(name)
305
337
  :private
306
338
  else
307
- raise NameError.new("undefined method `#{name}` for `#{mod}`")
339
+ mod.method(name) # Raises
308
340
  end
309
341
  end
310
342
  end