sorbet-runtime 0.5.6295 → 0.5.6483

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1499d7c137f6d9b53c61885843cc7631c7e8984fdcb5c83e3e9c06316706a7bd
4
- data.tar.gz: 63f24495ef2ef2b14e9afa6b30db65109631684c91b5cf400f0896027c7cd91f
3
+ metadata.gz: cd98656f464dabae597c1f8e68368144f0cce86a1ea33e74978355a7079e0394
4
+ data.tar.gz: 33c17eba9561f989af082c9e09a70f58b63a9bda6b5074cc1615f066ecbba644
5
5
  SHA512:
6
- metadata.gz: f1642f8bfb9f6c69a9496837e270d916e1a187044d721484235ed1a0e292a4ed8199b520f766f77e7e6c9a1ab3006bf6f4a6c2158511524fdb3cfaf91b96c093
7
- data.tar.gz: 16157845afa712815f96724daff65a38b3e73f16a46110c76571ff4c75410b1e0d464f246478e7ae5c5a77532ddddb0dccf6d1ccbbe6865bfdc7989e3832e7ca
6
+ metadata.gz: b9fac1efbc3e5ab2d1403a3983907faa40bb0c330cc26c9a1f513984320e3a3ca697718cf1393731b37e42d0368a0b34f5db62c9fea350d68d3052e1dee549bf
7
+ data.tar.gz: 2cb2d5baa9f0176f4787fb3017aefcfbbb4bcfad5a33ecb598c63b01b1618acc45a6b88fe1e2e7564387c51cd16c6f2bdfe69f63d0449cf7c3fe8bbf2623a213
@@ -19,7 +19,6 @@ require 'set'
19
19
 
20
20
  # These are pre-reqs for almost everything in here.
21
21
  require_relative 'types/configuration'
22
- require_relative 'types/profile'
23
22
  require_relative 'types/_types'
24
23
  require_relative 'types/private/decl_state'
25
24
  require_relative 'types/private/class_utils'
data/lib/types/_types.rb CHANGED
@@ -147,6 +147,25 @@ module T
147
147
  Private::Casts.cast(value, type, cast_method: "T.let")
148
148
  end
149
149
 
150
+ # Tells the type checker to treat `self` in the current block as `type`.
151
+ # Useful for blocks that are captured and executed later with instance_exec.
152
+ # Use like:
153
+ #
154
+ # seconds = lambda do
155
+ # T.bind(self, NewBinding)
156
+ # ...
157
+ # end
158
+ #
159
+ # `T.bind` behaves like `T.cast` in that it is assumed to be true statically.
160
+ #
161
+ # If `checked` is true, raises an exception at runtime if the value
162
+ # doesn't match the type (this is the default).
163
+ def self.bind(value, type, checked: true)
164
+ return value unless checked
165
+
166
+ Private::Casts.cast(value, type, cast_method: "T.bind")
167
+ end
168
+
150
169
  # Tells the typechecker to ensure that `value` is of type `type` (if not, the typechecker will
151
170
  # fail). Use this for debugging typechecking errors, or to ensure that type information is
152
171
  # statically known and being checked appropriately. If `checked` is true, raises an exception at
@@ -2,6 +2,9 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module T::Configuration
5
+ # Cache this comparisonn to avoid two allocations all over the place.
6
+ AT_LEAST_RUBY_2_7 = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7')
7
+
5
8
  # Announces to Sorbet that we are currently in a test environment, so it
6
9
  # should treat any sigs which are marked `.checked(:tests)` as if they were
7
10
  # just a normal sig.
@@ -70,6 +73,37 @@ module T::Configuration
70
73
  @include_value_in_type_errors = true
71
74
  end
72
75
 
76
+ # Whether VM-defined prop serialization/deserialization routines can be enabled.
77
+ #
78
+ # @return [T::Boolean]
79
+ def self.can_enable_vm_prop_serde?
80
+ T::Props::Private::DeserializerGenerator.respond_to?(:generate2)
81
+ end
82
+
83
+ # Whether to use VM-defined prop serialization/deserialization routines.
84
+ #
85
+ # The default is to use runtime codegen inside sorbet-runtime itself.
86
+ #
87
+ # @return [T::Boolean]
88
+ def self.use_vm_prop_serde?
89
+ @use_vm_prop_serde || false
90
+ end
91
+
92
+ # Enable using VM-defined prop serialization/deserialization routines.
93
+ #
94
+ # This method is likely to break things outside of Stripe's systems.
95
+ def self.enable_vm_prop_serde
96
+ if !can_enable_vm_prop_serde?
97
+ hard_assert_handler('Ruby VM is not setup to use VM-defined prop serde')
98
+ end
99
+ @use_vm_prop_serde = true
100
+ end
101
+
102
+ # Disable using VM-defined prop serialization/deserialization routines.
103
+ def self.disable_vm_prop_serde
104
+ @use_vm_prop_serde = false
105
+ end
106
+
73
107
  # Configure the default checked level for a sig with no explicit `.checked`
74
108
  # builder. When unset, the default checked level is `:always`.
75
109
  #
@@ -395,7 +429,12 @@ module T::Configuration
395
429
  MODULE_NAME = Module.instance_method(:name)
396
430
  private_constant :MODULE_NAME
397
431
 
398
- @default_module_name_mangler = ->(type) {MODULE_NAME.bind(type).call}
432
+ if T::Configuration::AT_LEAST_RUBY_2_7
433
+ @default_module_name_mangler = ->(type) {MODULE_NAME.bind_call(type)}
434
+ else
435
+ @default_module_name_mangler = ->(type) {MODULE_NAME.bind(type).call}
436
+ end
437
+
399
438
  @module_name_mangler = nil
400
439
 
401
440
  def self.module_name_mangler
@@ -425,6 +464,22 @@ module T::Configuration
425
464
  @sensitivity_and_pii_handler
426
465
  end
427
466
 
467
+ @redaction_handler = nil
468
+ # Set to a redaction handling function. This will be called when the
469
+ # `_redacted` version of a prop reader is used. By default this is set to
470
+ # `nil` and will raise an exception when the redacted version of a prop is
471
+ # accessed.
472
+ #
473
+ # @param [Lambda, Proc, nil] value Proc that converts a value into its
474
+ # redacted version according to the spec passed as the second argument.
475
+ def self.redaction_handler=(handler)
476
+ @redaction_handler = handler
477
+ end
478
+
479
+ def self.redaction_handler
480
+ @redaction_handler
481
+ end
482
+
428
483
  # Set to a function which can get the 'owner' of a class. This is
429
484
  # used in reporting deserialization errors
430
485
  #
@@ -2,18 +2,26 @@
2
2
  # typed: strict
3
3
 
4
4
  module T::NonForcingConstants
5
+ T::Sig::WithoutRuntime.sig {returns(T::Boolean)}
6
+ private_class_method def self.stripe_packages_enabled?
7
+ const_defined?('StripePackages') && const_get('StripePackages').enabled?
8
+ end
9
+
5
10
  # NOTE: This method is documented on the RBI in Sorbet's payload, so that it
6
11
  # shows up in the hover/completion documentation via LSP.
7
12
  T::Sig::WithoutRuntime.sig {params(val: BasicObject, klass: String, package: T.nilable(String)).returns(T::Boolean)}
8
13
  def self.non_forcing_is_a?(val, klass, package: nil)
9
- # TODO(gdritter): once we have a runtime implementation of
10
- # packages, we'll need to actually handle the `package` argument
11
- # here.
12
14
  method_name = "T::NonForcingConstants.non_forcing_is_a?"
13
15
  if klass.empty?
14
16
  raise ArgumentError.new("The string given to `#{method_name}` must not be empty")
15
17
  end
16
18
 
19
+ # TODO(gdritter): this might not be want we want in a multipackage
20
+ # world; revisit this implementation we move on from the monopackage
21
+ if stripe_packages_enabled?
22
+ klass = "PkgRegistry::#{package}::#{klass}"
23
+ end
24
+
17
25
  current_klass = T.let(nil, T.nilable(Module))
18
26
  current_prefix = T.let(nil, T.nilable(String))
19
27
 
@@ -37,7 +37,6 @@ module T::Private::Final
37
37
  mod.extend(mod.is_a?(Class) ? NoInherit : NoIncludeExtend)
38
38
  mark_as_final_module(mod)
39
39
  mark_as_final_module(mod.singleton_class)
40
- T::Private::Methods.add_module_with_final(mod)
41
40
  T::Private::Methods.install_hooks(mod)
42
41
  end
43
42
 
@@ -6,19 +6,21 @@ module T::Private::Methods
6
6
  @signatures_by_method = {}
7
7
  @sig_wrappers = {}
8
8
  @sigs_that_raised = {}
9
- # the info about whether a method is final is not stored in a DeclBuilder nor a Signature, but instead right here.
10
- # this is because final checks are special:
11
- # - they are done possibly before any sig block has run.
12
- # - they are done even if the method being defined doesn't have a sig.
13
- @final_methods = Set.new
14
- # a non-singleton is a module for which at least one of the following is true:
15
- # - is declared final
16
- # - defines a method that is declared final
17
- # - includes an non-singleton
18
- # - extends an non-singleton
19
- # a singleton is the singleton_class of a non-singleton.
20
- # modules_with_final is the set of singletons and non-singletons.
21
- @modules_with_final = Set.new
9
+ # stores method names that were declared final without regard for where.
10
+ # enables early rejection of names that we know can't induce final method violations.
11
+ @was_ever_final_names = Set.new
12
+ # maps from modules to the set of final methods declared in that module.
13
+ # we also overload entries slightly: if the value is nil, that means that the
14
+ # module has final methods somewhere along its ancestor chain, but does not itself
15
+ # have any final methods.
16
+ #
17
+ # we need the latter information to know whether we need to check along the ancestor
18
+ # chain for final method violations. we need the former information because we
19
+ # care about exactly where a final method is defined (e.g. including the same module
20
+ # twice is permitted). we could do this with two tables, but it seems slightly
21
+ # cleaner with a single table.
22
+ # Effectively T::Hash[Module, T.nilable(Set))]
23
+ @modules_with_final = Hash.new { |hash, key| hash[key] = nil }
22
24
  # this stores the old [included, extended] hooks for Module and inherited hook for Class that we override when
23
25
  # enabling final checks for when those hooks are called. the 'hooks' here don't have anything to do with the 'hooks'
24
26
  # in installed_hooks.
@@ -27,9 +29,20 @@ module T::Private::Methods
27
29
  ARG_NOT_PROVIDED = Object.new
28
30
  PROC_TYPE = Object.new
29
31
 
30
- DeclarationBlock = Struct.new(:mod, :loc, :blk, :final)
32
+ DeclarationBlock = Struct.new(:mod, :loc, :blk, :final, :raw)
33
+
34
+ def self.declare_sig(mod, loc, arg, &blk)
35
+ T::Private::DeclState.current.active_declaration = _declare_sig_internal(mod, loc, arg, &blk)
36
+
37
+ nil
38
+ end
39
+
40
+ # See tests for how to use this. But you shouldn't be using this.
41
+ def self._declare_sig(mod, arg=nil, &blk)
42
+ _declare_sig_internal(mod, caller_locations(1, 1).first, arg, raw: true, &blk)
43
+ end
31
44
 
32
- def self.declare_sig(mod, arg, &blk)
45
+ private_class_method def self._declare_sig_internal(mod, loc, arg, raw: false, &blk)
33
46
  install_hooks(mod)
34
47
 
35
48
  if T::Private::DeclState.current.active_declaration
@@ -41,15 +54,26 @@ module T::Private::Methods
41
54
  raise "Invalid argument to `sig`: #{arg}"
42
55
  end
43
56
 
44
- loc = caller_locations(2, 1).first
45
-
46
- T::Private::DeclState.current.active_declaration = DeclarationBlock.new(mod, loc, blk, arg == :final)
57
+ DeclarationBlock.new(mod, loc, blk, arg == :final, raw)
58
+ end
47
59
 
48
- nil
60
+ def self._with_declared_signature(mod, declblock, &blk)
61
+ # If declblock is provided, this code is equivalent to the check in
62
+ # _declare_sig_internal, above.
63
+ # If declblock is not provided and we have an active declaration, we are
64
+ # obviously doing something wrong.
65
+ if T::Private::DeclState.current.active_declaration
66
+ T::Private::DeclState.current.reset!
67
+ raise "You called sig twice without declaring a method in between"
68
+ end
69
+ if declblock
70
+ T::Private::DeclState.current.active_declaration = declblock
71
+ end
72
+ mod.module_exec(&blk)
49
73
  end
50
74
 
51
75
  def self.start_proc
52
- DeclBuilder.new(PROC_TYPE)
76
+ DeclBuilder.new(PROC_TYPE, false)
53
77
  end
54
78
 
55
79
  def self.finalize_proc(decl)
@@ -96,68 +120,86 @@ module T::Private::Methods
96
120
  # when target includes a module with instance methods source_method_names, ensure there is zero intersection between
97
121
  # the final instance methods of target and source_method_names. so, for every m in source_method_names, check if there
98
122
  # is already a method defined on one of target_ancestors with the same name that is final.
99
- def self._check_final_ancestors(target, target_ancestors, source_method_names)
100
- if !module_with_final?(target)
101
- return
102
- end
103
- # use reverse_each to check farther-up ancestors first, for better error messages. we could avoid this if we were on
104
- # the version of ruby that adds the optional argument to method_defined? that allows you to exclude ancestors.
123
+ #
124
+ # we assume that source_method_names has already been filtered to only include method
125
+ # names that were declared final at one point.
126
+ def self._check_final_ancestors(target, target_ancestors, source_method_names, source)
127
+ source_ancestors = nil
128
+ # use reverse_each to check farther-up ancestors first, for better error messages.
105
129
  target_ancestors.reverse_each do |ancestor|
130
+ final_methods = @modules_with_final.fetch(ancestor, nil)
131
+ # In this case, either ancestor didn't have any final methods anywhere in its
132
+ # ancestor chain, or ancestor did have final methods somewhere in its ancestor
133
+ # chain, but no final methods defined in ancestor itself. Either way, there
134
+ # are no final methods to check here, so we can move on to the next ancestor.
135
+ next unless final_methods
106
136
  source_method_names.each do |method_name|
107
- # the usage of method_owner_and_name_to_key(ancestor, method_name) instead of
108
- # method_to_key(ancestor.instance_method(method_name)) is not (just) an optimization, but also required for
109
- # correctness, since ancestor.method_defined?(method_name) may return true even if method_name is not defined
110
- # directly on ancestor but instead an ancestor of ancestor.
111
- if (ancestor.method_defined?(method_name) ||
112
- ancestor.private_method_defined?(method_name) ||
113
- ancestor.protected_method_defined?(method_name)) &&
114
- final_method?(method_owner_and_name_to_key(ancestor, method_name))
115
- definition_file, definition_line = T::Private::Methods.signature_for_method(ancestor.instance_method(method_name)).method.source_location
116
- is_redefined = target == ancestor
117
- caller_loc = caller_locations&.find {|l| !l.to_s.match?(%r{sorbet-runtime[^/]*/lib/}) }
118
- extra_info = "\n"
119
- if caller_loc
120
- extra_info = (is_redefined ? "Redefined" : "Overridden") + " here: #{caller_loc.path}:#{caller_loc.lineno}\n"
137
+ next unless final_methods.include?(method_name)
138
+
139
+ # If we get here, we are defining a method that some ancestor declared as
140
+ # final. however, we permit a final method to be defined multiple
141
+ # times if it is the same final method being defined each time.
142
+ if source
143
+ if !source_ancestors
144
+ source_ancestors = source.ancestors
145
+ # filter out things without actual final methods just to make sure that
146
+ # the below checks (which should be uncommon) go as quickly as possible.
147
+ source_ancestors.select! do |a|
148
+ @modules_with_final.fetch(a, nil)
149
+ end
121
150
  end
122
-
123
- error_message = "The method `#{method_name}` on #{ancestor} was declared as final and cannot be " +
124
- (is_redefined ? "redefined" : "overridden in #{target}")
125
- pretty_message = "#{error_message}\n" \
126
- "Made final here: #{definition_file}:#{definition_line}\n" \
127
- "#{extra_info}"
128
-
129
- begin
130
- raise pretty_message
131
- rescue => e
132
- # sig_validation_error_handler raises by default; on the off chance that
133
- # it doesn't raise, we need to ensure that the rest of signature building
134
- # sees a consistent state. This sig failed to validate, so we should get
135
- # rid of it. If we don't do this, errors of the form "You called sig
136
- # twice without declaring a method in between" will non-deterministically
137
- # crop up in tests.
138
- T::Private::DeclState.current.reset!
139
- T::Configuration.sig_validation_error_handler(e, {})
151
+ # final-ness means that there should be no more than one index for which
152
+ # the below block returns true.
153
+ defining_ancestor_idx = source_ancestors.index do |a|
154
+ @modules_with_final.fetch(a).include?(method_name)
140
155
  end
156
+ next if defining_ancestor_idx && source_ancestors[defining_ancestor_idx] == ancestor
141
157
  end
142
- end
143
- end
144
- end
145
158
 
146
- private_class_method def self.add_final_method(method_key)
147
- @final_methods.add(method_key)
148
- end
159
+ definition_file, definition_line = T::Private::Methods.signature_for_method(ancestor.instance_method(method_name)).method.source_location
160
+ is_redefined = target == ancestor
161
+ caller_loc = caller_locations&.find {|l| !l.to_s.match?(%r{sorbet-runtime[^/]*/lib/}) }
162
+ extra_info = "\n"
163
+ if caller_loc
164
+ extra_info = (is_redefined ? "Redefined" : "Overridden") + " here: #{caller_loc.path}:#{caller_loc.lineno}\n"
165
+ end
149
166
 
150
- private_class_method def self.final_method?(method_key)
151
- @final_methods.include?(method_key)
167
+ error_message = "The method `#{method_name}` on #{ancestor} was declared as final and cannot be " +
168
+ (is_redefined ? "redefined" : "overridden in #{target}")
169
+ pretty_message = "#{error_message}\n" \
170
+ "Made final here: #{definition_file}:#{definition_line}\n" \
171
+ "#{extra_info}"
172
+
173
+ begin
174
+ raise pretty_message
175
+ rescue => e
176
+ # sig_validation_error_handler raises by default; on the off chance that
177
+ # it doesn't raise, we need to ensure that the rest of signature building
178
+ # sees a consistent state. This sig failed to validate, so we should get
179
+ # rid of it. If we don't do this, errors of the form "You called sig
180
+ # twice without declaring a method in between" will non-deterministically
181
+ # crop up in tests.
182
+ T::Private::DeclState.current.reset!
183
+ T::Configuration.sig_validation_error_handler(e, {})
184
+ end
185
+ end
186
+ end
152
187
  end
153
188
 
154
- def self.add_module_with_final(mod)
155
- @modules_with_final.add(mod)
156
- @modules_with_final.add(mod.singleton_class)
189
+ def self.add_module_with_final_method(mod, method_name, is_singleton_method)
190
+ m = is_singleton_method ? mod.singleton_class : mod
191
+ methods = @modules_with_final[m]
192
+ if methods.nil?
193
+ methods = Set.new
194
+ @modules_with_final[m] = methods
195
+ end
196
+ methods.add(method_name)
157
197
  end
158
198
 
159
- private_class_method def self.module_with_final?(mod)
160
- @modules_with_final.include?(mod)
199
+ def self.note_module_deals_with_final(mod)
200
+ # Side-effectfully initialize the value if it's not already there
201
+ @modules_with_final[mod]
202
+ @modules_with_final[mod.singleton_class]
161
203
  end
162
204
 
163
205
  # Only public because it needs to get called below inside the replace_method blocks below.
@@ -172,11 +214,14 @@ module T::Private::Methods
172
214
  if T::Private::Final.final_module?(mod) && (current_declaration.nil? || !current_declaration.final)
173
215
  raise "#{mod} was declared as final but its method `#{method_name}` was not declared as final"
174
216
  end
175
- _check_final_ancestors(mod, mod.ancestors, [method_name])
217
+ # Don't compute mod.ancestors if we don't need to bother checking final-ness.
218
+ if @was_ever_final_names.include?(method_name) && @modules_with_final.include?(mod)
219
+ _check_final_ancestors(mod, mod.ancestors, [method_name], nil)
220
+ # We need to fetch the active declaration again, as _check_final_ancestors
221
+ # may have reset it (see the comment in that method for details).
222
+ current_declaration = T::Private::DeclState.current.active_declaration
223
+ end
176
224
 
177
- # We need to fetch the active declaration again, as _check_final_ancestors
178
- # may have reset it (see the comment in that method for details).
179
- current_declaration = T::Private::DeclState.current.active_declaration
180
225
  if current_declaration.nil?
181
226
  return
182
227
  end
@@ -198,41 +243,44 @@ module T::Private::Methods
198
243
  # which is called only on the *first* invocation.
199
244
  # This wrapper is very slow, so it will subsequently re-wrap with a much faster wrapper
200
245
  # (or unwrap back to the original method).
201
- new_method = nil
202
- T::Private::ClassUtils.replace_method(mod, method_name) do |*args, &blk|
203
- method_sig = T::Private::Methods.maybe_run_sig_block_for_method(new_method)
204
- method_sig ||= T::Private::Methods._handle_missing_method_signature(
205
- self,
206
- original_method,
207
- __callee__,
208
- )
209
-
210
- # Should be the same logic as CallValidation.wrap_method_if_needed but we
211
- # don't want that extra layer of indirection in the callstack
212
- if method_sig.mode == T::Private::Methods::Modes.abstract
213
- # We're in an interface method, keep going up the chain
214
- if defined?(super)
215
- super(*args, &blk)
246
+ key = method_owner_and_name_to_key(mod, method_name)
247
+ unless current_declaration.raw
248
+ T::Private::ClassUtils.replace_method(mod, method_name) do |*args, &blk|
249
+ method_sig = T::Private::Methods.maybe_run_sig_block_for_key(key)
250
+ method_sig ||= T::Private::Methods._handle_missing_method_signature(
251
+ self,
252
+ original_method,
253
+ __callee__,
254
+ )
255
+
256
+ # Should be the same logic as CallValidation.wrap_method_if_needed but we
257
+ # don't want that extra layer of indirection in the callstack
258
+ if method_sig.mode == T::Private::Methods::Modes.abstract
259
+ # We're in an interface method, keep going up the chain
260
+ if defined?(super)
261
+ super(*args, &blk)
262
+ else
263
+ raise NotImplementedError.new("The method `#{method_sig.method_name}` on #{mod} is declared as `abstract`. It does not have an implementation.")
264
+ end
265
+ # Note, this logic is duplicated (intentionally, for micro-perf) at `CallValidation.wrap_method_if_needed`,
266
+ # make sure to keep changes in sync.
267
+ elsif method_sig.check_level == :always || (method_sig.check_level == :tests && T::Private::RuntimeLevels.check_tests?)
268
+ CallValidation.validate_call(self, original_method, method_sig, args, blk)
269
+ elsif T::Configuration::AT_LEAST_RUBY_2_7
270
+ original_method.bind_call(self, *args, &blk)
216
271
  else
217
- raise NotImplementedError.new("The method `#{method_sig.method_name}` on #{mod} is declared as `abstract`. It does not have an implementation.")
272
+ original_method.bind(self).call(*args, &blk)
218
273
  end
219
- # Note, this logic is duplicated (intentionally, for micro-perf) at `CallValidation.wrap_method_if_needed`,
220
- # make sure to keep changes in sync.
221
- elsif method_sig.check_level == :always || (method_sig.check_level == :tests && T::Private::RuntimeLevels.check_tests?)
222
- CallValidation.validate_call(self, original_method, method_sig, args, blk)
223
- else
224
- original_method.bind(self).call(*args, &blk)
225
274
  end
226
275
  end
227
276
 
228
- new_method = mod.instance_method(method_name)
229
- key = method_to_key(new_method)
230
277
  @sig_wrappers[key] = sig_block
231
278
  if current_declaration.final
232
- add_final_method(key)
279
+ @was_ever_final_names.add(method_name)
233
280
  # use hook_mod, not mod, because for example, we want class C to be marked as having final if we def C.foo as
234
281
  # final. change this to mod to see some final_method tests fail.
235
- add_module_with_final(hook_mod)
282
+ note_module_deals_with_final(hook_mod)
283
+ add_module_with_final_method(hook_mod, method_name, is_singleton_method)
236
284
  end
237
285
  end
238
286
 
@@ -296,7 +344,7 @@ module T::Private::Methods
296
344
  end
297
345
 
298
346
  def self.run_builder(declaration_block)
299
- builder = DeclBuilder.new(declaration_block.mod)
347
+ builder = DeclBuilder.new(declaration_block.mod, declaration_block.raw)
300
348
  builder
301
349
  .instance_exec(&declaration_block.blk)
302
350
  .finalize!
@@ -327,6 +375,7 @@ module T::Private::Methods
327
375
  check_level: current_declaration.checked,
328
376
  on_failure: current_declaration.on_failure,
329
377
  override_allow_incompatible: current_declaration.override_allow_incompatible,
378
+ defined_raw: current_declaration.raw,
330
379
  )
331
380
 
332
381
  SignatureValidation.validate(signature)
@@ -364,7 +413,8 @@ module T::Private::Methods
364
413
  maybe_run_sig_block_for_key(method_to_key(method))
365
414
  end
366
415
 
367
- private_class_method def self.maybe_run_sig_block_for_key(key)
416
+ # Only public so that it can be accessed in the closure for _on_method_added
417
+ def self.maybe_run_sig_block_for_key(key)
368
418
  run_sig_block_for_key(key) if has_sig_block_for_key(key)
369
419
  end
370
420
 
@@ -408,13 +458,28 @@ module T::Private::Methods
408
458
 
409
459
  # the module target is adding the methods from the module source to itself. we need to check that for all instance
410
460
  # methods M on source, M is not defined on any of target's ancestors.
411
- def self._hook_impl(target, target_ancestors, source)
412
- if !module_with_final?(target) && !module_with_final?(source)
461
+ def self._hook_impl(target, singleton_class, source)
462
+ # we do not need to call add_was_ever_final here, because we have already marked
463
+ # any such methods when source was originally defined.
464
+ if !@modules_with_final.include?(target)
465
+ if !@modules_with_final.include?(source)
466
+ return
467
+ end
468
+ note_module_deals_with_final(target)
469
+ install_hooks(target)
413
470
  return
414
471
  end
415
- add_module_with_final(target)
416
- install_hooks(target)
417
- _check_final_ancestors(target, target_ancestors - source.ancestors, source.instance_methods)
472
+
473
+ methods = source.instance_methods
474
+ methods.select! do |method_name|
475
+ @was_ever_final_names.include?(method_name)
476
+ end
477
+ if methods.empty?
478
+ return
479
+ end
480
+
481
+ target_ancestors = singleton_class ? target.singleton_class.ancestors : target.ancestors
482
+ _check_final_ancestors(target, target_ancestors, methods, source)
418
483
  end
419
484
 
420
485
  def self.set_final_checks_on_hooks(enable)
@@ -428,15 +493,15 @@ module T::Private::Methods
428
493
  else
429
494
  old_included = T::Private::ClassUtils.replace_method(Module, :included) do |arg|
430
495
  old_included.bind(self).call(arg)
431
- ::T::Private::Methods._hook_impl(arg, arg.ancestors, self)
496
+ ::T::Private::Methods._hook_impl(arg, false, self)
432
497
  end
433
498
  old_extended = T::Private::ClassUtils.replace_method(Module, :extended) do |arg|
434
499
  old_extended.bind(self).call(arg)
435
- ::T::Private::Methods._hook_impl(arg, arg.singleton_class.ancestors, self)
500
+ ::T::Private::Methods._hook_impl(arg, true, self)
436
501
  end
437
502
  old_inherited = T::Private::ClassUtils.replace_method(Class, :inherited) do |arg|
438
503
  old_inherited.bind(self).call(arg)
439
- ::T::Private::Methods._hook_impl(arg, arg.ancestors, self)
504
+ ::T::Private::Methods._hook_impl(arg, false, self)
440
505
  end
441
506
  @old_hooks = [old_included, old_extended, old_inherited]
442
507
  end