sorbet-runtime 0.5.6341 → 0.5.6353
Sign up to get free protection for your applications and to get access to all the features.
- 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: '069891f68b6b2b9b365b40fec9b3dfb01f07c22a944c707d60ff3a8fd40450e3'
|
4
|
+
data.tar.gz: 2f44e74bfdb9d0dc36cb67baa58c4b3d8683ae85a0ce4433290247026edbb118
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e4128803eed737e960e4b7fc45f39d41e265a837f10594ed71d45afd15b23624c59fa79d1aa62d33080d063d7234b15ffd02f6f61a5014996aaf1801845dbbc7
|
7
|
+
data.tar.gz: 4125231ae4581a7f14c32a01151a0adbb51697065f8d8f8be4e4dcd4bc6a46ad09ae77e65b8b017aa738b997bf2c13e0a3912179991c3e797d3816ac4ca3cbc2
|
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.6353
|
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-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|