sorbet-runtime 0.5.6336 → 0.5.6351
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/types/configuration.rb +16 -0
- data/lib/types/private/final.rb +0 -1
- data/lib/types/private/methods/_methods.rb +118 -98
- data/lib/types/props/decorator.rb +5 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d598bc133cb90dc719b9072e14eb5bc578c679694887ee98e657571d85a46d2a
|
4
|
+
data.tar.gz: 7c718728dfb154109f628a36566bf7fa85ca3d8df7f379a391344863e520fa63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 10c9194b02d89b13d546bcf3a9e2ca6e4e1d3d0b7225e1f4b3d17d7fb8f599a3e5f55815fd36997d86ad4834c531674befe7e224dcf8697b3f856c173812f78c
|
7
|
+
data.tar.gz: 184e4212a39731ae43f29a4120505c9d0f4b974d1a8ec9dba015b804f57c1ec17fceebefafd260aaca32fd08600cdf7f8a9b723248d5535e8397639ff520783d
|
data/lib/types/configuration.rb
CHANGED
@@ -433,6 +433,22 @@ module T::Configuration
|
|
433
433
|
@sensitivity_and_pii_handler
|
434
434
|
end
|
435
435
|
|
436
|
+
@redaction_handler = nil
|
437
|
+
# Set to a redaction handling function. This will be called when the
|
438
|
+
# `_redacted` version of a prop reader is used. By default this is set to
|
439
|
+
# `nil` and will raise an exception when the redacted version of a prop is
|
440
|
+
# accessed.
|
441
|
+
#
|
442
|
+
# @param [Lambda, Proc, nil] value Proc that converts a value into its
|
443
|
+
# redacted version according to the spec passed as the second argument.
|
444
|
+
def self.redaction_handler=(handler)
|
445
|
+
@redaction_handler = handler
|
446
|
+
end
|
447
|
+
|
448
|
+
def self.redaction_handler
|
449
|
+
@redaction_handler
|
450
|
+
end
|
451
|
+
|
436
452
|
# Set to a function which can get the 'owner' of a class. This is
|
437
453
|
# used in reporting deserialization errors
|
438
454
|
#
|
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,22 +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
9
|
# stores method names that were declared final without regard for where.
|
15
10
|
# enables early rejection of names that we know can't induce final method violations.
|
16
11
|
@was_ever_final_names = Set.new
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
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 }
|
25
24
|
# this stores the old [included, extended] hooks for Module and inherited hook for Class that we override when
|
26
25
|
# enabling final checks for when those hooks are called. the 'hooks' here don't have anything to do with the 'hooks'
|
27
26
|
# in installed_hooks.
|
@@ -99,80 +98,86 @@ module T::Private::Methods
|
|
99
98
|
# when target includes a module with instance methods source_method_names, ensure there is zero intersection between
|
100
99
|
# the final instance methods of target and source_method_names. so, for every m in source_method_names, check if there
|
101
100
|
# is already a method defined on one of target_ancestors with the same name that is final.
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
end
|
109
|
-
if source_method_names.empty?
|
110
|
-
return
|
111
|
-
end
|
112
|
-
# use reverse_each to check farther-up ancestors first, for better error messages. we could avoid this if we were on
|
113
|
-
# the version of ruby that adds the optional argument to method_defined? that allows you to exclude ancestors.
|
101
|
+
#
|
102
|
+
# we assume that source_method_names has already been filtered to only include method
|
103
|
+
# names that were declared final at one point.
|
104
|
+
def self._check_final_ancestors(target, target_ancestors, source_method_names, source)
|
105
|
+
source_ancestors = nil
|
106
|
+
# use reverse_each to check farther-up ancestors first, for better error messages.
|
114
107
|
target_ancestors.reverse_each do |ancestor|
|
115
|
-
|
108
|
+
final_methods = @modules_with_final.fetch(ancestor, nil)
|
109
|
+
# In this case, either ancestor didn't have any final methods anywhere in its
|
110
|
+
# ancestor chain, or ancestor did have final methods somewhere in its ancestor
|
111
|
+
# chain, but no final methods defined in ancestor itself. Either way, there
|
112
|
+
# are no final methods to check here, so we can move on to the next ancestor.
|
113
|
+
next unless final_methods
|
116
114
|
source_method_names.each do |method_name|
|
117
|
-
|
118
|
-
|
119
|
-
#
|
120
|
-
#
|
121
|
-
if
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
115
|
+
next unless final_methods.include?(method_name)
|
116
|
+
|
117
|
+
# If we get here, we are defining a method that some ancestor declared as
|
118
|
+
# final. however, we permit a final method to be defined multiple
|
119
|
+
# times if it is the same final method being defined each time.
|
120
|
+
if source
|
121
|
+
if !source_ancestors
|
122
|
+
source_ancestors = source.ancestors
|
123
|
+
# filter out things without actual final methods just to make sure that
|
124
|
+
# the below checks (which should be uncommon) go as quickly as possible.
|
125
|
+
source_ancestors.select! do |a|
|
126
|
+
@modules_with_final.fetch(a, nil)
|
127
|
+
end
|
128
128
|
end
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
"Made final here: #{definition_file}:#{definition_line}\n" \
|
134
|
-
"#{extra_info}"
|
135
|
-
|
136
|
-
begin
|
137
|
-
raise pretty_message
|
138
|
-
rescue => e
|
139
|
-
# sig_validation_error_handler raises by default; on the off chance that
|
140
|
-
# it doesn't raise, we need to ensure that the rest of signature building
|
141
|
-
# sees a consistent state. This sig failed to validate, so we should get
|
142
|
-
# rid of it. If we don't do this, errors of the form "You called sig
|
143
|
-
# twice without declaring a method in between" will non-deterministically
|
144
|
-
# crop up in tests.
|
145
|
-
T::Private::DeclState.current.reset!
|
146
|
-
T::Configuration.sig_validation_error_handler(e, {})
|
129
|
+
# final-ness means that there should be no more than one index for which
|
130
|
+
# the below block returns true.
|
131
|
+
defining_ancestor_idx = source_ancestors.index do |a|
|
132
|
+
@modules_with_final.fetch(a).include?(method_name)
|
147
133
|
end
|
134
|
+
next if defining_ancestor_idx && source_ancestors[defining_ancestor_idx] == ancestor
|
148
135
|
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
private_class_method def self.add_final_method(method_key)
|
154
|
-
@final_methods.add(method_key)
|
155
|
-
end
|
156
136
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
137
|
+
definition_file, definition_line = T::Private::Methods.signature_for_method(ancestor.instance_method(method_name)).method.source_location
|
138
|
+
is_redefined = target == ancestor
|
139
|
+
caller_loc = caller_locations&.find {|l| !l.to_s.match?(%r{sorbet-runtime[^/]*/lib/}) }
|
140
|
+
extra_info = "\n"
|
141
|
+
if caller_loc
|
142
|
+
extra_info = (is_redefined ? "Redefined" : "Overridden") + " here: #{caller_loc.path}:#{caller_loc.lineno}\n"
|
143
|
+
end
|
164
144
|
|
165
|
-
|
166
|
-
|
145
|
+
error_message = "The method `#{method_name}` on #{ancestor} was declared as final and cannot be " +
|
146
|
+
(is_redefined ? "redefined" : "overridden in #{target}")
|
147
|
+
pretty_message = "#{error_message}\n" \
|
148
|
+
"Made final here: #{definition_file}:#{definition_line}\n" \
|
149
|
+
"#{extra_info}"
|
150
|
+
|
151
|
+
begin
|
152
|
+
raise pretty_message
|
153
|
+
rescue => e
|
154
|
+
# sig_validation_error_handler raises by default; on the off chance that
|
155
|
+
# it doesn't raise, we need to ensure that the rest of signature building
|
156
|
+
# sees a consistent state. This sig failed to validate, so we should get
|
157
|
+
# rid of it. If we don't do this, errors of the form "You called sig
|
158
|
+
# twice without declaring a method in between" will non-deterministically
|
159
|
+
# crop up in tests.
|
160
|
+
T::Private::DeclState.current.reset!
|
161
|
+
T::Configuration.sig_validation_error_handler(e, {})
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
167
165
|
end
|
168
166
|
|
169
|
-
def self.
|
170
|
-
|
171
|
-
@modules_with_final
|
167
|
+
def self.add_module_with_final_method(mod, method_name, is_singleton_method)
|
168
|
+
m = is_singleton_method ? mod.singleton_class : mod
|
169
|
+
methods = @modules_with_final[m]
|
170
|
+
if methods.nil?
|
171
|
+
methods = Set.new
|
172
|
+
@modules_with_final[m] = methods
|
173
|
+
end
|
174
|
+
methods.add(method_name)
|
172
175
|
end
|
173
176
|
|
174
|
-
|
175
|
-
|
177
|
+
def self.note_module_deals_with_final(mod)
|
178
|
+
# Side-effectfully initialize the value if it's not already there
|
179
|
+
@modules_with_final[mod]
|
180
|
+
@modules_with_final[mod.singleton_class]
|
176
181
|
end
|
177
182
|
|
178
183
|
# Only public because it needs to get called below inside the replace_method blocks below.
|
@@ -187,11 +192,14 @@ module T::Private::Methods
|
|
187
192
|
if T::Private::Final.final_module?(mod) && (current_declaration.nil? || !current_declaration.final)
|
188
193
|
raise "#{mod} was declared as final but its method `#{method_name}` was not declared as final"
|
189
194
|
end
|
190
|
-
|
195
|
+
# Don't compute mod.ancestors if we don't need to bother checking final-ness.
|
196
|
+
if @was_ever_final_names.include?(method_name) && @modules_with_final.include?(mod)
|
197
|
+
_check_final_ancestors(mod, mod.ancestors, [method_name], nil)
|
198
|
+
# We need to fetch the active declaration again, as _check_final_ancestors
|
199
|
+
# may have reset it (see the comment in that method for details).
|
200
|
+
current_declaration = T::Private::DeclState.current.active_declaration
|
201
|
+
end
|
191
202
|
|
192
|
-
# We need to fetch the active declaration again, as _check_final_ancestors
|
193
|
-
# may have reset it (see the comment in that method for details).
|
194
|
-
current_declaration = T::Private::DeclState.current.active_declaration
|
195
203
|
if current_declaration.nil?
|
196
204
|
return
|
197
205
|
end
|
@@ -213,9 +221,9 @@ module T::Private::Methods
|
|
213
221
|
# which is called only on the *first* invocation.
|
214
222
|
# This wrapper is very slow, so it will subsequently re-wrap with a much faster wrapper
|
215
223
|
# (or unwrap back to the original method).
|
216
|
-
|
224
|
+
key = method_owner_and_name_to_key(mod, method_name)
|
217
225
|
T::Private::ClassUtils.replace_method(mod, method_name) do |*args, &blk|
|
218
|
-
method_sig = T::Private::Methods.
|
226
|
+
method_sig = T::Private::Methods.maybe_run_sig_block_for_key(key)
|
219
227
|
method_sig ||= T::Private::Methods._handle_missing_method_signature(
|
220
228
|
self,
|
221
229
|
original_method,
|
@@ -242,15 +250,13 @@ module T::Private::Methods
|
|
242
250
|
end
|
243
251
|
end
|
244
252
|
|
245
|
-
new_method = mod.instance_method(method_name)
|
246
|
-
key = method_to_key(new_method)
|
247
253
|
@sig_wrappers[key] = sig_block
|
248
254
|
if current_declaration.final
|
249
|
-
|
250
|
-
add_was_ever_final(method_name)
|
255
|
+
@was_ever_final_names.add(method_name)
|
251
256
|
# use hook_mod, not mod, because for example, we want class C to be marked as having final if we def C.foo as
|
252
257
|
# final. change this to mod to see some final_method tests fail.
|
253
|
-
|
258
|
+
note_module_deals_with_final(hook_mod)
|
259
|
+
add_module_with_final_method(hook_mod, method_name, is_singleton_method)
|
254
260
|
end
|
255
261
|
end
|
256
262
|
|
@@ -382,7 +388,8 @@ module T::Private::Methods
|
|
382
388
|
maybe_run_sig_block_for_key(method_to_key(method))
|
383
389
|
end
|
384
390
|
|
385
|
-
|
391
|
+
# Only public so that it can be accessed in the closure for _on_method_added
|
392
|
+
def self.maybe_run_sig_block_for_key(key)
|
386
393
|
run_sig_block_for_key(key) if has_sig_block_for_key(key)
|
387
394
|
end
|
388
395
|
|
@@ -426,15 +433,28 @@ module T::Private::Methods
|
|
426
433
|
|
427
434
|
# the module target is adding the methods from the module source to itself. we need to check that for all instance
|
428
435
|
# methods M on source, M is not defined on any of target's ancestors.
|
429
|
-
def self._hook_impl(target,
|
430
|
-
if !module_with_final?(target) && !module_with_final?(source)
|
431
|
-
return
|
432
|
-
end
|
436
|
+
def self._hook_impl(target, singleton_class, source)
|
433
437
|
# we do not need to call add_was_ever_final here, because we have already marked
|
434
438
|
# any such methods when source was originally defined.
|
435
|
-
|
436
|
-
|
437
|
-
|
439
|
+
if !@modules_with_final.include?(target)
|
440
|
+
if !@modules_with_final.include?(source)
|
441
|
+
return
|
442
|
+
end
|
443
|
+
note_module_deals_with_final(target)
|
444
|
+
install_hooks(target)
|
445
|
+
return
|
446
|
+
end
|
447
|
+
|
448
|
+
methods = source.instance_methods
|
449
|
+
methods.select! do |method_name|
|
450
|
+
@was_ever_final_names.include?(method_name)
|
451
|
+
end
|
452
|
+
if methods.empty?
|
453
|
+
return
|
454
|
+
end
|
455
|
+
|
456
|
+
target_ancestors = singleton_class ? target.singleton_class.ancestors : target.ancestors
|
457
|
+
_check_final_ancestors(target, target_ancestors, methods, source)
|
438
458
|
end
|
439
459
|
|
440
460
|
def self.set_final_checks_on_hooks(enable)
|
@@ -448,15 +468,15 @@ module T::Private::Methods
|
|
448
468
|
else
|
449
469
|
old_included = T::Private::ClassUtils.replace_method(Module, :included) do |arg|
|
450
470
|
old_included.bind(self).call(arg)
|
451
|
-
::T::Private::Methods._hook_impl(arg,
|
471
|
+
::T::Private::Methods._hook_impl(arg, false, self)
|
452
472
|
end
|
453
473
|
old_extended = T::Private::ClassUtils.replace_method(Module, :extended) do |arg|
|
454
474
|
old_extended.bind(self).call(arg)
|
455
|
-
::T::Private::Methods._hook_impl(arg,
|
475
|
+
::T::Private::Methods._hook_impl(arg, true, self)
|
456
476
|
end
|
457
477
|
old_inherited = T::Private::ClassUtils.replace_method(Class, :inherited) do |arg|
|
458
478
|
old_inherited.bind(self).call(arg)
|
459
|
-
::T::Private::Methods._hook_impl(arg,
|
479
|
+
::T::Private::Methods._hook_impl(arg, false, self)
|
460
480
|
end
|
461
481
|
@old_hooks = [old_included, old_extended, old_inherited]
|
462
482
|
end
|
@@ -449,8 +449,11 @@ class T::Props::Decorator
|
|
449
449
|
|
450
450
|
@class.send(:define_method, redacted_method) do
|
451
451
|
value = self.public_send(prop_name)
|
452
|
-
|
453
|
-
|
452
|
+
handler = T::Configuration.redaction_handler
|
453
|
+
if !handler
|
454
|
+
raise "Using `redaction:` on a prop requires specifying `T::Configuration.redaction_handler`"
|
455
|
+
end
|
456
|
+
handler.call(value, redaction)
|
454
457
|
end
|
455
458
|
end
|
456
459
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sorbet-runtime
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.6351
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stripe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-03-
|
11
|
+
date: 2021-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|