sorbet-runtime 0.4.4442 → 0.4.4443
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 +25 -0
- data/lib/types/private/decl_state.rb +1 -0
- data/lib/types/private/methods/_methods.rb +110 -14
- data/lib/types/private/methods/call_validation.rb +1 -0
- data/lib/types/sig.rb +6 -6
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f71f4ecf6f6c24f0dd4ba065d1f911e5ca4f3477
|
4
|
+
data.tar.gz: 2992a0f425abcba10f414389cfb23469af9eca1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4bcdd49035d1e279c21225ad88becc369ad63a5ce046e05587ecba1b6c0f8c4424e28eb2060726248168d6a0c6daee042dd84388e9321682f72e7a003d83f41a
|
7
|
+
data.tar.gz: f5c7844214396806c60bcbee562e72c87b5cb73a352b5593e8758d7b8b8c4fcfb91787475518bab58b28e03ef5b4c19a393c408ffcbb693e730237ea7384900e
|
data/lib/types/configuration.rb
CHANGED
@@ -17,6 +17,31 @@ module T::Configuration
|
|
17
17
|
T::Private::RuntimeLevels.enable_checking_in_tests
|
18
18
|
end
|
19
19
|
|
20
|
+
# Announce to Sorbet that we would like the final checks to be enabled when
|
21
|
+
# including and extending modules. Iff this is not called, then the following
|
22
|
+
# example will not raise an error.
|
23
|
+
#
|
24
|
+
# ```ruby
|
25
|
+
# module M
|
26
|
+
# extend T::Sig
|
27
|
+
# sig(:final) {void}
|
28
|
+
# def foo; end
|
29
|
+
# end
|
30
|
+
# class C
|
31
|
+
# include M
|
32
|
+
# def foo; end
|
33
|
+
# end
|
34
|
+
# ```
|
35
|
+
def self.enable_final_checks_for_include_extend
|
36
|
+
T::Private::Methods.set_final_checks_for_include_extend(true)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Undo the effects of a previous call to
|
40
|
+
# `enable_final_checks_for_include_extend`.
|
41
|
+
def self.reset_final_checks_for_include_extend
|
42
|
+
T::Private::Methods.set_final_checks_for_include_extend(false)
|
43
|
+
end
|
44
|
+
|
20
45
|
# Configure the default checked level for a sig with no explicit `.checked`
|
21
46
|
# builder. When unset, the default checked level is `:always`.
|
22
47
|
#
|
@@ -6,23 +6,36 @@ 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
|
+
# if a module directly defines a final method, or includes, extends, or inherits from a module which does so, then it
|
15
|
+
# should be in this set.
|
16
|
+
@modules_with_final = Set.new
|
17
|
+
@old_included_extended = nil
|
9
18
|
|
10
19
|
ARG_NOT_PROVIDED = Object.new
|
11
20
|
PROC_TYPE = Object.new
|
12
21
|
|
13
|
-
DeclarationBlock = Struct.new(:mod, :loc, :blk)
|
22
|
+
DeclarationBlock = Struct.new(:mod, :loc, :blk, :final)
|
14
23
|
|
15
|
-
def self.declare_sig(mod, &blk)
|
24
|
+
def self.declare_sig(mod, arg, &blk)
|
16
25
|
install_hooks(mod)
|
17
26
|
|
18
27
|
if T::Private::DeclState.current.active_declaration
|
19
|
-
T::Private::DeclState.current.
|
28
|
+
T::Private::DeclState.current.reset!
|
20
29
|
raise "You called sig twice without declaring a method inbetween"
|
21
30
|
end
|
22
31
|
|
32
|
+
if !arg.nil? && arg != :final
|
33
|
+
raise "Invalid argument to `sig`: #{arg}"
|
34
|
+
end
|
35
|
+
|
23
36
|
loc = caller_locations(2, 1).first
|
24
37
|
|
25
|
-
T::Private::DeclState.current.active_declaration = DeclarationBlock.new(mod, loc, blk)
|
38
|
+
T::Private::DeclState.current.active_declaration = DeclarationBlock.new(mod, loc, blk, arg == :final)
|
26
39
|
|
27
40
|
nil
|
28
41
|
end
|
@@ -106,15 +119,60 @@ module T::Private::Methods
|
|
106
119
|
@signatures_by_method[key]
|
107
120
|
end
|
108
121
|
|
122
|
+
# when target includes a module with instance methods source_method_names, ensure there is zero intersection between
|
123
|
+
# the final instance methods of target and source_method_names. so, for every m in source_method_names, check if there
|
124
|
+
# is already a method defined on one of target_ancestors with the same name that is final.
|
125
|
+
def self._check_final_ancestors(target, target_ancestors, source_method_names)
|
126
|
+
# use reverse_each to check farther-up ancestors first, for better error messages. we could avoid this if we were on
|
127
|
+
# the version of ruby that adds the optional argument to method_defined? that allows you to exclude ancestors.
|
128
|
+
target_ancestors.reverse_each do |ancestor|
|
129
|
+
source_method_names.each do |method_name|
|
130
|
+
# the usage of method_owner_and_name_to_key(ancestor, method_name) instead of
|
131
|
+
# method_to_key(ancestor.instance_method(method_name)) is not (just) an optimization, but also required for
|
132
|
+
# correctness, since ancestor.method_defined?(method_name) may return true even if method_name is not defined
|
133
|
+
# directly on ancestor but instead an ancestor of ancestor.
|
134
|
+
if ancestor.method_defined?(method_name) && final_method?(method_owner_and_name_to_key(ancestor, method_name))
|
135
|
+
raise(
|
136
|
+
"`#{ancestor.name}##{method_name}` was declared as final and cannot be " +
|
137
|
+
(target == ancestor ? "redefined" : "overridden in `#{target.name}`")
|
138
|
+
)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
private_class_method def self.add_final_method(method_key)
|
145
|
+
@final_methods.add(method_key)
|
146
|
+
end
|
147
|
+
|
148
|
+
private_class_method def self.final_method?(method_key)
|
149
|
+
@final_methods.include?(method_key)
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.add_module_with_final(mod)
|
153
|
+
@modules_with_final.add(mod)
|
154
|
+
end
|
155
|
+
|
156
|
+
private_class_method def self.module_with_final?(mod)
|
157
|
+
@modules_with_final.include?(mod)
|
158
|
+
end
|
159
|
+
|
109
160
|
# Only public because it needs to get called below inside the replace_method blocks below.
|
110
161
|
def self._on_method_added(hook_mod, method_name, is_singleton_method: false)
|
111
|
-
|
112
|
-
|
113
|
-
|
162
|
+
if T::Private::DeclState.current.skip_next_on_method_added
|
163
|
+
T::Private::DeclState.current.skip_next_on_method_added = false
|
164
|
+
return
|
165
|
+
end
|
114
166
|
|
167
|
+
current_declaration = T::Private::DeclState.current.active_declaration
|
115
168
|
mod = is_singleton_method ? hook_mod.singleton_class : hook_mod
|
116
169
|
original_method = mod.instance_method(method_name)
|
117
170
|
|
171
|
+
_check_final_ancestors(mod, mod.ancestors, [method_name])
|
172
|
+
|
173
|
+
return if current_declaration.nil?
|
174
|
+
T::Private::DeclState.current.reset!
|
175
|
+
|
118
176
|
sig_block = lambda do
|
119
177
|
T::Private::Methods.run_sig(hook_mod, method_name, original_method, current_declaration)
|
120
178
|
end
|
@@ -161,15 +219,16 @@ module T::Private::Methods
|
|
161
219
|
end
|
162
220
|
|
163
221
|
new_method = mod.instance_method(method_name)
|
164
|
-
|
222
|
+
key = method_to_key(new_method)
|
223
|
+
@sig_wrappers[key] = sig_block
|
224
|
+
if current_declaration.final
|
225
|
+
add_final_method(key)
|
226
|
+
add_module_with_final(mod)
|
227
|
+
end
|
165
228
|
end
|
166
229
|
|
167
230
|
def self.sig_error(loc, message)
|
168
|
-
raise(
|
169
|
-
ArgumentError.new(
|
170
|
-
"#{loc.path}:#{loc.lineno}: Error interpreting `sig`:\n #{message}\n\n"
|
171
|
-
)
|
172
|
-
)
|
231
|
+
raise(ArgumentError.new("#{loc.path}:#{loc.lineno}: Error interpreting `sig`:\n #{message}\n\n"))
|
173
232
|
end
|
174
233
|
|
175
234
|
# Executes the `sig` block, and converts the resulting Declaration
|
@@ -300,6 +359,38 @@ module T::Private::Methods
|
|
300
359
|
end
|
301
360
|
end
|
302
361
|
|
362
|
+
# the module target is adding the methods from the module source to itself. we need to check that for all instance
|
363
|
+
# methods M on source, M is not defined on any of target's ancestors.
|
364
|
+
def self._included_extended_impl(target, target_ancestors, source)
|
365
|
+
if !module_with_final?(target) && !module_with_final?(source)
|
366
|
+
return
|
367
|
+
end
|
368
|
+
add_module_with_final(target)
|
369
|
+
install_hooks(target)
|
370
|
+
_check_final_ancestors(target, target_ancestors - source.ancestors, source.instance_methods)
|
371
|
+
end
|
372
|
+
|
373
|
+
def self.set_final_checks_for_include_extend(enable)
|
374
|
+
is_enabled = @old_included_extended != nil
|
375
|
+
if enable == is_enabled
|
376
|
+
return
|
377
|
+
end
|
378
|
+
if is_enabled
|
379
|
+
@old_included_extended.each(&:restore)
|
380
|
+
@old_included_extended = nil
|
381
|
+
else
|
382
|
+
old_included = T::Private::ClassUtils.replace_method(Module, :included) do |arg|
|
383
|
+
old_included.bind(self).call(arg)
|
384
|
+
::T::Private::Methods._included_extended_impl(arg, arg.ancestors, self)
|
385
|
+
end
|
386
|
+
old_extended = T::Private::ClassUtils.replace_method(Module, :extended) do |arg|
|
387
|
+
old_extended.bind(self).call(arg)
|
388
|
+
::T::Private::Methods._included_extended_impl(arg, arg.singleton_class.ancestors, self)
|
389
|
+
end
|
390
|
+
@old_included_extended = [old_included, old_extended]
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
303
394
|
def self.install_hooks(mod)
|
304
395
|
return if @installed_hooks.include?(mod)
|
305
396
|
@installed_hooks << mod
|
@@ -330,8 +421,13 @@ module T::Private::Methods
|
|
330
421
|
original_singleton_method.bind(attached).call(:singleton_method_added)
|
331
422
|
end
|
332
423
|
|
424
|
+
# use this directly if you don't want/need to box up the method into an object to pass to method_to_key.
|
425
|
+
private_class_method def self.method_owner_and_name_to_key(owner, name)
|
426
|
+
"#{owner.object_id}##{name}"
|
427
|
+
end
|
428
|
+
|
333
429
|
private_class_method def self.method_to_key(method)
|
334
|
-
|
430
|
+
method_owner_and_name_to_key(method.owner, method.name)
|
335
431
|
end
|
336
432
|
|
337
433
|
private_class_method def self.key_to_method(key)
|
@@ -163,6 +163,7 @@ module T::Private::Methods::CallValidation
|
|
163
163
|
end
|
164
164
|
|
165
165
|
def self.create_validator_method(mod, original_method, method_sig, original_visibility)
|
166
|
+
T::Private::DeclState.current.skip_next_on_method_added = true
|
166
167
|
has_fixed_arity = method_sig.kwarg_types.empty? && !method_sig.has_rest && !method_sig.has_keyrest &&
|
167
168
|
original_method.parameters.all? {|(kind, _name)| kind == :req}
|
168
169
|
all_args_are_simple = method_sig.arg_types.all? {|_name, type| type.is_a?(T::Types::Simple)}
|
data/lib/types/sig.rb
CHANGED
@@ -7,15 +7,15 @@ module T::Sig
|
|
7
7
|
module WithoutRuntime
|
8
8
|
# At runtime, does nothing, but statically it is treated exactly the same
|
9
9
|
# as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
|
10
|
-
def self.sig(&blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
|
10
|
+
def self.sig(arg=nil, &blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
|
11
11
|
|
12
12
|
original_verbose = $VERBOSE
|
13
13
|
$VERBOSE = false
|
14
14
|
|
15
15
|
# At runtime, does nothing, but statically it is treated exactly the same
|
16
16
|
# as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
|
17
|
-
T::Sig::WithoutRuntime.sig {params(blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
|
18
|
-
def self.sig(&blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride, Lint/DuplicateMethods
|
17
|
+
T::Sig::WithoutRuntime.sig {params(arg: T.nilable(Symbol), blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
|
18
|
+
def self.sig(arg=nil, &blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride, Lint/DuplicateMethods
|
19
19
|
|
20
20
|
$VERBOSE = original_verbose
|
21
21
|
end
|
@@ -23,8 +23,8 @@ module T::Sig
|
|
23
23
|
# Declares a method with type signatures and/or
|
24
24
|
# abstract/override/... helpers. See the documentation URL on
|
25
25
|
# {T::Helpers}
|
26
|
-
T::Sig::WithoutRuntime.sig {params(blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
|
27
|
-
def sig(&blk) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
|
28
|
-
T::Private::Methods.declare_sig(self, &blk)
|
26
|
+
T::Sig::WithoutRuntime.sig {params(arg: T.nilable(Symbol), blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
|
27
|
+
def sig(arg=nil, &blk) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
|
28
|
+
T::Private::Methods.declare_sig(self, arg, &blk)
|
29
29
|
end
|
30
30
|
end
|