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 +4 -4
- data/lib/sorbet-runtime.rb +0 -1
- data/lib/types/_types.rb +19 -0
- data/lib/types/configuration.rb +56 -1
- data/lib/types/non_forcing_constants.rb +11 -3
- data/lib/types/private/final.rb +0 -1
- data/lib/types/private/methods/_methods.rb +176 -111
- data/lib/types/private/methods/call_validation.rb +54 -982
- data/lib/types/private/methods/call_validation_2_6.rb +1203 -0
- data/lib/types/private/methods/call_validation_2_7.rb +1203 -0
- data/lib/types/private/methods/decl_builder.rb +3 -2
- data/lib/types/private/methods/signature.rb +4 -2
- data/lib/types/private/methods/signature_validation.rb +3 -3
- data/lib/types/props/decorator.rb +5 -2
- data/lib/types/props/generated_code_validation.rb +11 -2
- data/lib/types/props/has_lazily_specialized_methods.rb +43 -0
- data/lib/types/props/private/deserializer_generator.rb +4 -9
- data/lib/types/props/serializable.rb +32 -2
- data/lib/types/sig.rb +1 -1
- data/lib/types/types/fixed_hash.rb +10 -2
- data/lib/types/types/simple.rb +18 -3
- data/lib/types/types/typed_enumerable.rb +5 -1
- data/lib/types/types/union.rb +2 -2
- data/lib/types/utils.rb +12 -15
- metadata +5 -4
- data/lib/types/profile.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd98656f464dabae597c1f8e68368144f0cce86a1ea33e74978355a7079e0394
|
4
|
+
data.tar.gz: 33c17eba9561f989af082c9e09a70f58b63a9bda6b5074cc1615f066ecbba644
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9fac1efbc3e5ab2d1403a3983907faa40bb0c330cc26c9a1f513984320e3a3ca697718cf1393731b37e42d0368a0b34f5db62c9fea350d68d3052e1dee549bf
|
7
|
+
data.tar.gz: 2cb2d5baa9f0176f4787fb3017aefcfbbb4bcfad5a33ecb598c63b01b1618acc45a6b88fe1e2e7564387c51cd16c6f2bdfe69f63d0449cf7c3fe8bbf2623a213
|
data/lib/sorbet-runtime.rb
CHANGED
@@ -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
|
data/lib/types/configuration.rb
CHANGED
@@ -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
|
-
|
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
|
|
data/lib/types/private/final.rb
CHANGED
@@ -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
|
-
#
|
10
|
-
#
|
11
|
-
|
12
|
-
#
|
13
|
-
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# a
|
20
|
-
#
|
21
|
-
|
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.
|
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
|
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
|
-
|
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
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
#
|
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
|
-
|
108
|
-
|
109
|
-
#
|
110
|
-
#
|
111
|
-
if
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
124
|
-
|
125
|
-
|
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
|
-
|
147
|
-
|
148
|
-
|
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
|
-
|
151
|
-
|
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.
|
155
|
-
|
156
|
-
@modules_with_final
|
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
|
-
|
160
|
-
|
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
|
-
|
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
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
super
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
412
|
-
|
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
|
-
|
416
|
-
|
417
|
-
|
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,
|
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,
|
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,
|
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
|