sorbet-runtime 0.4.4301 → 0.4.4446
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/_types.rb +6 -12
- data/lib/types/boolean.rb +1 -1
- data/lib/types/compatibility_patches.rb +1 -0
- data/lib/types/configuration.rb +82 -63
- data/lib/types/generic.rb +2 -2
- data/lib/types/private/abstract/declare.rb +3 -4
- data/lib/types/private/class_utils.rb +7 -3
- data/lib/types/private/decl_state.rb +1 -0
- data/lib/types/private/methods/_methods.rb +114 -20
- data/lib/types/private/methods/call_validation.rb +20 -9
- data/lib/types/private/methods/decl_builder.rb +21 -32
- data/lib/types/private/methods/signature.rb +4 -4
- data/lib/types/private/methods/signature_validation.rb +1 -1
- data/lib/types/private/runtime_levels.rb +15 -0
- data/lib/types/props/constructor.rb +1 -1
- data/lib/types/props/decorator.rb +28 -22
- data/lib/types/props/errors.rb +1 -1
- data/lib/types/props/optional.rb +13 -1
- data/lib/types/props/pretty_printable.rb +1 -0
- data/lib/types/props/serializable.rb +49 -40
- data/lib/types/props/utils.rb +15 -1
- data/lib/types/runtime_profiled.rb +12 -24
- data/lib/types/sig.rb +12 -10
- data/lib/types/types/type_member.rb +1 -1
- data/lib/types/types/type_template.rb +1 -1
- data/lib/types/utils.rb +0 -40
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4a800d5e7728111e31a78225eaf32a47c00e3fed
|
|
4
|
+
data.tar.gz: e8546195bf3b79b86484089e66a78966d8cbc9ef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2cc3a61f53c8073bcbc091bb5a244059714de8ef38d4f101f5d4d626ab928cfd546a504ce6ef77d1a972b824d0fbfaffdbf11f6fd7e85b6ad8f238131fee3d2
|
|
7
|
+
data.tar.gz: 8db326b0147134aabba33d5500065d5438ed2d362fc86f90ac36c8d53318c228439ace3c4ccb194ec35d8d42991a26a63d0c2ee433867ddbd98ad9710f8abcf6
|
data/lib/types/_types.rb
CHANGED
|
@@ -176,21 +176,15 @@ module T
|
|
|
176
176
|
# Intended to be used to promise sorbet that a given nilable value happens
|
|
177
177
|
# to contain a non-nil value at this point.
|
|
178
178
|
#
|
|
179
|
-
# sig {params(arg: T.nilable(A)
|
|
180
|
-
def self.must(arg
|
|
179
|
+
# sig {params(arg: T.nilable(A)).returns(A)}
|
|
180
|
+
def self.must(arg)
|
|
181
|
+
return arg if arg
|
|
182
|
+
return arg if arg == false
|
|
183
|
+
|
|
181
184
|
begin
|
|
182
|
-
|
|
183
|
-
if !T.unsafe(msg).is_a?(String)
|
|
184
|
-
raise TypeError.new("T.must expects a string as second argument")
|
|
185
|
-
end
|
|
186
|
-
else
|
|
187
|
-
msg = "Passed `nil` into T.must"
|
|
188
|
-
end
|
|
189
|
-
raise TypeError.new(msg) if arg.nil?
|
|
190
|
-
arg
|
|
185
|
+
raise TypeError.new("Passed `nil` into T.must")
|
|
191
186
|
rescue TypeError => e # raise into rescue to ensure e.backtrace is populated
|
|
192
187
|
T::Private::ErrorHandler.handle_inline_type_error(e)
|
|
193
|
-
arg
|
|
194
188
|
end
|
|
195
189
|
end
|
|
196
190
|
|
data/lib/types/boolean.rb
CHANGED
data/lib/types/configuration.rb
CHANGED
|
@@ -2,6 +2,59 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module T::Configuration
|
|
5
|
+
# Announces to Sorbet that we are currently in a test environment, so it
|
|
6
|
+
# should treat any sigs which are marked `.checked(:tests)` as if they were
|
|
7
|
+
# just a normal sig.
|
|
8
|
+
#
|
|
9
|
+
# If this method is not called, sigs marked `.checked(:tests)` will not be
|
|
10
|
+
# checked. In fact, such methods won't even be wrapped--the runtime will put
|
|
11
|
+
# back the original method.
|
|
12
|
+
#
|
|
13
|
+
# Note: Due to the way sigs are evaluated and methods are wrapped, this
|
|
14
|
+
# method MUST be called before any code calls `sig`. This method raises if
|
|
15
|
+
# it has been called too late.
|
|
16
|
+
def self.enable_checking_for_sigs_marked_checked_tests
|
|
17
|
+
T::Private::RuntimeLevels.enable_checking_in_tests
|
|
18
|
+
end
|
|
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
|
+
|
|
45
|
+
# Configure the default checked level for a sig with no explicit `.checked`
|
|
46
|
+
# builder. When unset, the default checked level is `:always`.
|
|
47
|
+
#
|
|
48
|
+
# Note: setting this option is potentially dangerous! Sorbet can't check all
|
|
49
|
+
# code statically. The runtime checks complement the checks that Sorbet does
|
|
50
|
+
# statically, so that methods don't have to guard themselves from being
|
|
51
|
+
# called incorrectly by untyped code.
|
|
52
|
+
#
|
|
53
|
+
# @param [:never, :tests, :always] default_checked_level
|
|
54
|
+
def self.default_checked_level=(default_checked_level)
|
|
55
|
+
T::Private::RuntimeLevels.default_checked_level = default_checked_level
|
|
56
|
+
end
|
|
57
|
+
|
|
5
58
|
# Set a handler to handle `TypeError`s raised by any in-line type assertions,
|
|
6
59
|
# including `T.must`, `T.let`, `T.cast`, and `T.assert_type!`.
|
|
7
60
|
#
|
|
@@ -84,11 +137,10 @@ module T::Configuration
|
|
|
84
137
|
# successfully built, but the built sig is incompatible with other sigs in
|
|
85
138
|
# some way.
|
|
86
139
|
#
|
|
87
|
-
# By default, sig validation errors cause an exception to be raised.
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
# behavior when a method signature's build fails.
|
|
140
|
+
# By default, sig validation errors cause an exception to be raised.
|
|
141
|
+
# Setting sig_validation_error_handler to an object that implements :call
|
|
142
|
+
# (e.g. proc or lambda) allows users to customize the behavior when a method
|
|
143
|
+
# signature's build fails.
|
|
92
144
|
#
|
|
93
145
|
# @param [Lambda, Proc, Object, nil] value Proc that handles the error (pass
|
|
94
146
|
# nil to reset to default behavior)
|
|
@@ -116,24 +168,7 @@ module T::Configuration
|
|
|
116
168
|
end
|
|
117
169
|
|
|
118
170
|
private_class_method def self.sig_validation_error_handler_default(error, opts)
|
|
119
|
-
|
|
120
|
-
bad_method = opts[:method]
|
|
121
|
-
if !opts[:declaration].generated
|
|
122
|
-
super_signature = opts[:super_signature]
|
|
123
|
-
raise error if !super_signature&.generated
|
|
124
|
-
bad_method = super_signature.method
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
method_file, method_line = bad_method.source_location
|
|
128
|
-
T::Configuration.log_info_handler(
|
|
129
|
-
"SIG-DECLARE-FAILED",
|
|
130
|
-
{
|
|
131
|
-
definition_file: method_file,
|
|
132
|
-
definition_line: method_line,
|
|
133
|
-
kind: "Delete",
|
|
134
|
-
message: error.message,
|
|
135
|
-
},
|
|
136
|
-
)
|
|
171
|
+
raise error
|
|
137
172
|
end
|
|
138
173
|
|
|
139
174
|
def self.sig_validation_error_handler(error, opts)
|
|
@@ -147,11 +182,9 @@ module T::Configuration
|
|
|
147
182
|
# Set a handler for type errors that result from calling a method.
|
|
148
183
|
#
|
|
149
184
|
# By default, errors from calling a method cause an exception to be raised.
|
|
150
|
-
#
|
|
151
|
-
#
|
|
152
|
-
#
|
|
153
|
-
# behavior when a method is called with invalid parameters, or returns an
|
|
154
|
-
# invalid value.
|
|
185
|
+
# Setting call_validation_error_handler to an object that implements :call
|
|
186
|
+
# (e.g. proc or lambda) allows users to customize the behavior when a method
|
|
187
|
+
# is called with invalid parameters, or returns an invalid value.
|
|
155
188
|
#
|
|
156
189
|
# @param [Lambda, Proc, Object, nil] value Proc that handles the error
|
|
157
190
|
# report (pass nil to reset to default behavior)
|
|
@@ -180,41 +213,7 @@ module T::Configuration
|
|
|
180
213
|
end
|
|
181
214
|
|
|
182
215
|
private_class_method def self.call_validation_error_handler_default(signature, opts)
|
|
183
|
-
|
|
184
|
-
location = opts[:location]
|
|
185
|
-
suffix = "Caller: #{location.path}:#{location.lineno}\n" \
|
|
186
|
-
"Definition: #{method_file}:#{method_line}"
|
|
187
|
-
|
|
188
|
-
error_message = "#{opts[:kind]}#{opts[:name] ? " '#{opts[:name]}'" : ''}: #{opts[:message]}\n#{suffix}"
|
|
189
|
-
|
|
190
|
-
if signature.generated
|
|
191
|
-
got = opts[:value].class
|
|
192
|
-
got = T.unsafe(T::Enumerable[T.untyped]).describe_obj(opts[:value]) if got < Enumerable
|
|
193
|
-
T::Configuration.log_info_handler(
|
|
194
|
-
"SIG-CHECK-FAILED",
|
|
195
|
-
{
|
|
196
|
-
caller_file: location.path,
|
|
197
|
-
caller_line: location.lineno,
|
|
198
|
-
definition_file: method_file,
|
|
199
|
-
definition_line: method_line,
|
|
200
|
-
kind: opts[:kind],
|
|
201
|
-
name: opts[:name],
|
|
202
|
-
expected: opts[:type].name,
|
|
203
|
-
got: got,
|
|
204
|
-
},
|
|
205
|
-
)
|
|
206
|
-
elsif signature.soft_notify
|
|
207
|
-
T::Configuration.soft_assert_handler(
|
|
208
|
-
"TypeError: #{error_message}",
|
|
209
|
-
{notify: signature.soft_notify}
|
|
210
|
-
)
|
|
211
|
-
else
|
|
212
|
-
begin
|
|
213
|
-
raise TypeError.new(error_message)
|
|
214
|
-
rescue TypeError => e # raise into rescue to ensure e.backtrace is populated
|
|
215
|
-
T::Private::ErrorHandler.handle_inline_type_error(e)
|
|
216
|
-
end
|
|
217
|
-
end
|
|
216
|
+
raise TypeError.new(opts[:pretty_message])
|
|
218
217
|
end
|
|
219
218
|
|
|
220
219
|
def self.call_validation_error_handler(signature, opts)
|
|
@@ -359,6 +358,26 @@ module T::Configuration
|
|
|
359
358
|
@scalar_types || @default_scalar_types
|
|
360
359
|
end
|
|
361
360
|
|
|
361
|
+
# Temporarily disable ruby warnings while executing the given block. This is
|
|
362
|
+
# useful when doing something that would normally cause a warning to be
|
|
363
|
+
# emitted in Ruby verbose mode ($VERBOSE = true).
|
|
364
|
+
#
|
|
365
|
+
# @yield
|
|
366
|
+
#
|
|
367
|
+
def self.without_ruby_warnings
|
|
368
|
+
if $VERBOSE
|
|
369
|
+
begin
|
|
370
|
+
original_verbose = $VERBOSE
|
|
371
|
+
$VERBOSE = false
|
|
372
|
+
yield
|
|
373
|
+
ensure
|
|
374
|
+
$VERBOSE = original_verbose
|
|
375
|
+
end
|
|
376
|
+
else
|
|
377
|
+
yield
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
362
381
|
|
|
363
382
|
private_class_method def self.validate_lambda_given!(value)
|
|
364
383
|
if !value.nil? && !value.respond_to?(:call)
|
data/lib/types/generic.rb
CHANGED
|
@@ -13,11 +13,11 @@ module T::Generic
|
|
|
13
13
|
self
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def type_member(variance=:invariant, fixed: nil)
|
|
16
|
+
def type_member(variance=:invariant, fixed: nil, lower: T.untyped, upper: BasicObject)
|
|
17
17
|
T::Types::TypeMember.new(variance) # rubocop:disable PrisonGuard/UseOpusTypesShortcut
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def type_template(variance=:invariant, fixed: nil)
|
|
20
|
+
def type_template(variance=:invariant, fixed: nil, lower: T.untyped, upper: BasicObject)
|
|
21
21
|
T::Types::TypeTemplate.new(variance) # rubocop:disable PrisonGuard/UseOpusTypesShortcut
|
|
22
22
|
end
|
|
23
23
|
end
|
|
@@ -18,10 +18,6 @@ module T::Private::Abstract::Declare
|
|
|
18
18
|
mod.extend(T::InterfaceWrapper::Helpers)
|
|
19
19
|
|
|
20
20
|
if mod.is_a?(Class)
|
|
21
|
-
if mod < T::Struct
|
|
22
|
-
raise "#{mod.name} is a subclass of T::Struct and cannot be declared abstract"
|
|
23
|
-
end
|
|
24
|
-
|
|
25
21
|
if type == :interface
|
|
26
22
|
# Since `interface!` is just `abstract!` with some extra validation, we could technically
|
|
27
23
|
# allow this, but it's unclear there are good use cases, and it might be confusing.
|
|
@@ -32,6 +28,9 @@ module T::Private::Abstract::Declare
|
|
|
32
28
|
raise "You must call `abstract!` *before* defining an initialize method"
|
|
33
29
|
end
|
|
34
30
|
|
|
31
|
+
# Don't need to silence warnings via without_ruby_warnings when calling
|
|
32
|
+
# define_method because of the guard above
|
|
33
|
+
|
|
35
34
|
mod.send(:define_method, :initialize) do |*args, &blk|
|
|
36
35
|
if self.class == mod
|
|
37
36
|
raise "#{mod} is declared as abstract; it cannot be instantiated"
|
|
@@ -32,7 +32,9 @@ module T::Private::ClassUtils
|
|
|
32
32
|
|
|
33
33
|
if @overwritten
|
|
34
34
|
# The original method was overwritten. Overwrite again to restore it.
|
|
35
|
-
|
|
35
|
+
T::Configuration.without_ruby_warnings do
|
|
36
|
+
@mod.send(:define_method, @old_method.name, @old_method) # rubocop:disable PrisonGuard/UsePublicSend
|
|
37
|
+
end
|
|
36
38
|
else
|
|
37
39
|
# The original method was in an ancestor. Restore it by removing the overriding method.
|
|
38
40
|
@mod.send(:remove_method, @old_method.name) # rubocop:disable PrisonGuard/UsePublicSend
|
|
@@ -93,8 +95,10 @@ module T::Private::ClassUtils
|
|
|
93
95
|
end
|
|
94
96
|
|
|
95
97
|
overwritten = original_owner == mod
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
T::Configuration.without_ruby_warnings do
|
|
99
|
+
mod.send(:define_method, name, &blk) # rubocop:disable PrisonGuard/UsePublicSend
|
|
100
|
+
mod.send(original_visibility, name) # rubocop:disable PrisonGuard/UsePublicSend
|
|
101
|
+
end
|
|
98
102
|
new_method = mod.instance_method(name)
|
|
99
103
|
|
|
100
104
|
ReplacedMethod.new(mod, original_method, new_method, overwritten, original_visibility)
|
|
@@ -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
|
|
@@ -43,8 +56,8 @@ module T::Private::Methods
|
|
|
43
56
|
if decl.returns == ARG_NOT_PROVIDED
|
|
44
57
|
raise "Procs must specify a return type"
|
|
45
58
|
end
|
|
46
|
-
if decl.
|
|
47
|
-
raise "Procs cannot use .
|
|
59
|
+
if decl.on_failure != ARG_NOT_PROVIDED
|
|
60
|
+
raise "Procs cannot use .on_failure"
|
|
48
61
|
end
|
|
49
62
|
|
|
50
63
|
if decl.params == ARG_NOT_PROVIDED
|
|
@@ -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
|
|
@@ -224,7 +283,7 @@ module T::Private::Methods
|
|
|
224
283
|
bind: current_declaration.bind,
|
|
225
284
|
mode: current_declaration.mode,
|
|
226
285
|
check_level: current_declaration.checked,
|
|
227
|
-
|
|
286
|
+
on_failure: current_declaration.on_failure,
|
|
228
287
|
override_allow_incompatible: current_declaration.override_allow_incompatible,
|
|
229
288
|
generated: current_declaration.generated,
|
|
230
289
|
)
|
|
@@ -300,21 +359,51 @@ 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
|
|
306
397
|
|
|
307
398
|
if mod.singleton_class?
|
|
308
399
|
install_singleton_method_added_hook(mod)
|
|
309
|
-
install_singleton_method_added_hook(mod.singleton_class)
|
|
310
400
|
else
|
|
311
401
|
original_method = T::Private::ClassUtils.replace_method(mod.singleton_class, :method_added) do |name|
|
|
312
402
|
T::Private::Methods._on_method_added(self, name)
|
|
313
403
|
original_method.bind(self).call(name)
|
|
314
404
|
end
|
|
315
|
-
|
|
316
|
-
install_singleton_method_added_hook(mod.singleton_class)
|
|
317
405
|
end
|
|
406
|
+
install_singleton_method_added_hook(mod.singleton_class)
|
|
318
407
|
end
|
|
319
408
|
|
|
320
409
|
private_class_method def self.install_singleton_method_added_hook(singleton_klass)
|
|
@@ -332,8 +421,13 @@ module T::Private::Methods
|
|
|
332
421
|
original_singleton_method.bind(attached).call(:singleton_method_added)
|
|
333
422
|
end
|
|
334
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
|
+
|
|
335
429
|
private_class_method def self.method_to_key(method)
|
|
336
|
-
|
|
430
|
+
method_owner_and_name_to_key(method.owner, method.name)
|
|
337
431
|
end
|
|
338
432
|
|
|
339
433
|
private_class_method def self.key_to_method(key)
|
|
@@ -35,9 +35,11 @@ module T::Private::Methods::CallValidation
|
|
|
35
35
|
elsif method_sig.check_level == :always || (method_sig.check_level == :tests && T::Private::RuntimeLevels.check_tests?)
|
|
36
36
|
create_validator_method(mod, original_method, method_sig, original_visibility)
|
|
37
37
|
else
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
T::Configuration.without_ruby_warnings do
|
|
39
|
+
# get all the shims out of the way and put back the original method
|
|
40
|
+
mod.send(:define_method, method_sig.method_name, original_method)
|
|
41
|
+
mod.send(original_visibility, method_sig.method_name)
|
|
42
|
+
end
|
|
41
43
|
end
|
|
42
44
|
# Return the newly created method (or the original one if we didn't replace it)
|
|
43
45
|
mod.instance_method(method_sig.method_name)
|
|
@@ -161,18 +163,21 @@ module T::Private::Methods::CallValidation
|
|
|
161
163
|
end
|
|
162
164
|
|
|
163
165
|
def self.create_validator_method(mod, original_method, method_sig, original_visibility)
|
|
166
|
+
T::Private::DeclState.current.skip_next_on_method_added = true
|
|
164
167
|
has_fixed_arity = method_sig.kwarg_types.empty? && !method_sig.has_rest && !method_sig.has_keyrest &&
|
|
165
168
|
original_method.parameters.all? {|(kind, _name)| kind == :req}
|
|
166
169
|
all_args_are_simple = method_sig.arg_types.all? {|_name, type| type.is_a?(T::Types::Simple)}
|
|
167
170
|
has_simple_method_types = all_args_are_simple && method_sig.return_type.is_a?(T::Types::Simple)
|
|
168
171
|
has_simple_procedure_types = all_args_are_simple && method_sig.return_type.is_a?(T::Private::Types::Void)
|
|
169
172
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
T::Configuration.without_ruby_warnings do
|
|
174
|
+
if has_fixed_arity && has_simple_method_types && method_sig.arg_types.length < 5 && is_allowed_to_have_fast_path
|
|
175
|
+
create_validator_method_fast(mod, original_method, method_sig)
|
|
176
|
+
elsif has_fixed_arity && has_simple_procedure_types && method_sig.arg_types.length < 5 && is_allowed_to_have_fast_path
|
|
177
|
+
create_validator_procedure_fast(mod, original_method, method_sig)
|
|
178
|
+
else
|
|
179
|
+
create_validator_slow(mod, original_method, method_sig)
|
|
180
|
+
end
|
|
176
181
|
end
|
|
177
182
|
mod.send(original_visibility, method_sig.method_name)
|
|
178
183
|
end
|
|
@@ -1150,10 +1155,16 @@ module T::Private::Methods::CallValidation
|
|
|
1150
1155
|
def self.report_error(method_sig, error_message, kind, name, type, value, caller_offset: 0)
|
|
1151
1156
|
method_sig.mark_failed
|
|
1152
1157
|
caller_loc = T.must(caller_locations(3 + caller_offset, 1))[0]
|
|
1158
|
+
definition_file, definition_line = method_sig.method.source_location
|
|
1159
|
+
|
|
1160
|
+
pretty_message = "#{kind}#{name ? " '#{name}'" : ''}: #{error_message}\n" \
|
|
1161
|
+
"Caller: #{caller_loc.path}:#{caller_loc.lineno}\n" \
|
|
1162
|
+
"Definition: #{definition_file}:#{definition_line}"
|
|
1153
1163
|
|
|
1154
1164
|
T::Private::ErrorHandler.handle_call_validation_error(
|
|
1155
1165
|
method_sig,
|
|
1156
1166
|
message: error_message,
|
|
1167
|
+
pretty_message: pretty_message,
|
|
1157
1168
|
kind: kind,
|
|
1158
1169
|
name: name,
|
|
1159
1170
|
type: type,
|