sorbet-runtime 0.4.4442 → 0.4.4443
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 +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
|