sorbet-runtime 0.5.6338 → 0.5.6352
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 +98 -90
- 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: 749c737c99c9607d2b7c8dca07174fc9c29c7310022992388106585f071010e4
|
4
|
+
data.tar.gz: e9288fd72206f3ea8e51147ddc98976eb2cab320b191c36b83833dfcc416aee4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bfeba8bf0a258bab60cf9a86e924fea90e072ecc2c57963aeede61e0f40f2b260ded8d80916e672eb82310b89d7b3a4f02e2c0fb2ebf9e4c1efaa0bd888d0227
|
7
|
+
data.tar.gz: 0a38952b492811ac25b9b4aed0f91adb1dce7b6c2764350e3d4ed4164a2ba7bcb48ce63366c428106062b8f394c323c4c01698718ba89d4961ef45e286ee6284
|
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.
|
@@ -102,71 +101,83 @@ module T::Private::Methods
|
|
102
101
|
#
|
103
102
|
# we assume that source_method_names has already been filtered to only include method
|
104
103
|
# names that were declared final at one point.
|
105
|
-
def self._check_final_ancestors(target, target_ancestors, source_method_names)
|
106
|
-
|
107
|
-
#
|
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.
|
108
107
|
target_ancestors.reverse_each do |ancestor|
|
109
|
-
|
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
|
110
114
|
source_method_names.each do |method_name|
|
111
|
-
|
112
|
-
|
113
|
-
#
|
114
|
-
#
|
115
|
-
if
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
122
128
|
end
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
"Made final here: #{definition_file}:#{definition_line}\n" \
|
128
|
-
"#{extra_info}"
|
129
|
-
|
130
|
-
begin
|
131
|
-
raise pretty_message
|
132
|
-
rescue => e
|
133
|
-
# sig_validation_error_handler raises by default; on the off chance that
|
134
|
-
# it doesn't raise, we need to ensure that the rest of signature building
|
135
|
-
# sees a consistent state. This sig failed to validate, so we should get
|
136
|
-
# rid of it. If we don't do this, errors of the form "You called sig
|
137
|
-
# twice without declaring a method in between" will non-deterministically
|
138
|
-
# crop up in tests.
|
139
|
-
T::Private::DeclState.current.reset!
|
140
|
-
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)
|
141
133
|
end
|
134
|
+
next if defining_ancestor_idx && source_ancestors[defining_ancestor_idx] == ancestor
|
142
135
|
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
136
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
private_class_method def self.add_was_ever_final(method_name)
|
156
|
-
@was_ever_final_names.add(method_name)
|
157
|
-
end
|
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
|
158
144
|
|
159
|
-
|
160
|
-
|
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
|
161
165
|
end
|
162
166
|
|
163
|
-
def self.
|
164
|
-
|
165
|
-
@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)
|
166
175
|
end
|
167
176
|
|
168
|
-
|
169
|
-
|
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]
|
170
181
|
end
|
171
182
|
|
172
183
|
# Only public because it needs to get called below inside the replace_method blocks below.
|
@@ -182,13 +193,13 @@ module T::Private::Methods
|
|
182
193
|
raise "#{mod} was declared as final but its method `#{method_name}` was not declared as final"
|
183
194
|
end
|
184
195
|
# Don't compute mod.ancestors if we don't need to bother checking final-ness.
|
185
|
-
if
|
186
|
-
_check_final_ancestors(mod, mod.ancestors, [method_name])
|
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
|
187
201
|
end
|
188
202
|
|
189
|
-
# We need to fetch the active declaration again, as _check_final_ancestors
|
190
|
-
# may have reset it (see the comment in that method for details).
|
191
|
-
current_declaration = T::Private::DeclState.current.active_declaration
|
192
203
|
if current_declaration.nil?
|
193
204
|
return
|
194
205
|
end
|
@@ -210,9 +221,9 @@ module T::Private::Methods
|
|
210
221
|
# which is called only on the *first* invocation.
|
211
222
|
# This wrapper is very slow, so it will subsequently re-wrap with a much faster wrapper
|
212
223
|
# (or unwrap back to the original method).
|
213
|
-
|
224
|
+
key = method_owner_and_name_to_key(mod, method_name)
|
214
225
|
T::Private::ClassUtils.replace_method(mod, method_name) do |*args, &blk|
|
215
|
-
method_sig = T::Private::Methods.
|
226
|
+
method_sig = T::Private::Methods.maybe_run_sig_block_for_key(key)
|
216
227
|
method_sig ||= T::Private::Methods._handle_missing_method_signature(
|
217
228
|
self,
|
218
229
|
original_method,
|
@@ -239,15 +250,13 @@ module T::Private::Methods
|
|
239
250
|
end
|
240
251
|
end
|
241
252
|
|
242
|
-
new_method = mod.instance_method(method_name)
|
243
|
-
key = method_to_key(new_method)
|
244
253
|
@sig_wrappers[key] = sig_block
|
245
254
|
if current_declaration.final
|
246
|
-
|
247
|
-
add_was_ever_final(method_name)
|
255
|
+
@was_ever_final_names.add(method_name)
|
248
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
|
249
257
|
# final. change this to mod to see some final_method tests fail.
|
250
|
-
|
258
|
+
note_module_deals_with_final(hook_mod)
|
259
|
+
add_module_with_final_method(hook_mod, method_name, is_singleton_method)
|
251
260
|
end
|
252
261
|
end
|
253
262
|
|
@@ -379,7 +388,8 @@ module T::Private::Methods
|
|
379
388
|
maybe_run_sig_block_for_key(method_to_key(method))
|
380
389
|
end
|
381
390
|
|
382
|
-
|
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)
|
383
393
|
run_sig_block_for_key(key) if has_sig_block_for_key(key)
|
384
394
|
end
|
385
395
|
|
@@ -424,29 +434,27 @@ module T::Private::Methods
|
|
424
434
|
# the module target is adding the methods from the module source to itself. we need to check that for all instance
|
425
435
|
# methods M on source, M is not defined on any of target's ancestors.
|
426
436
|
def self._hook_impl(target, singleton_class, source)
|
427
|
-
target_was_final = module_with_final?(target)
|
428
|
-
if !target_was_final && !module_with_final?(source)
|
429
|
-
return
|
430
|
-
end
|
431
437
|
# we do not need to call add_was_ever_final here, because we have already marked
|
432
438
|
# any such methods when source was originally defined.
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
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)
|
437
445
|
return
|
438
446
|
end
|
439
447
|
|
440
448
|
methods = source.instance_methods
|
441
449
|
methods.select! do |method_name|
|
442
|
-
|
450
|
+
@was_ever_final_names.include?(method_name)
|
443
451
|
end
|
444
452
|
if methods.empty?
|
445
453
|
return
|
446
454
|
end
|
447
455
|
|
448
456
|
target_ancestors = singleton_class ? target.singleton_class.ancestors : target.ancestors
|
449
|
-
_check_final_ancestors(target, target_ancestors
|
457
|
+
_check_final_ancestors(target, target_ancestors, methods, source)
|
450
458
|
end
|
451
459
|
|
452
460
|
def self.set_final_checks_on_hooks(enable)
|
@@ -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.6352
|
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-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|