sorbet-runtime 0.0.1.pre.prealpha → 0.4.4253
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 +5 -5
- data/lib/sorbet-runtime.rb +100 -0
- data/lib/types/_types.rb +245 -0
- data/lib/types/abstract_utils.rb +50 -0
- data/lib/types/boolean.rb +8 -0
- data/lib/types/compatibility_patches.rb +37 -0
- data/lib/types/configuration.rb +368 -0
- data/lib/types/generic.rb +23 -0
- data/lib/types/helpers.rb +31 -0
- data/lib/types/interface_wrapper.rb +158 -0
- data/lib/types/private/abstract/data.rb +36 -0
- data/lib/types/private/abstract/declare.rb +39 -0
- data/lib/types/private/abstract/hooks.rb +43 -0
- data/lib/types/private/abstract/validate.rb +128 -0
- data/lib/types/private/casts.rb +22 -0
- data/lib/types/private/class_utils.rb +102 -0
- data/lib/types/private/decl_state.rb +18 -0
- data/lib/types/private/error_handler.rb +37 -0
- data/lib/types/private/methods/_methods.rb +344 -0
- data/lib/types/private/methods/call_validation.rb +1177 -0
- data/lib/types/private/methods/decl_builder.rb +275 -0
- data/lib/types/private/methods/modes.rb +18 -0
- data/lib/types/private/methods/signature.rb +196 -0
- data/lib/types/private/methods/signature_validation.rb +232 -0
- data/lib/types/private/mixins/mixins.rb +27 -0
- data/lib/types/private/runtime_levels.rb +41 -0
- data/lib/types/private/types/not_typed.rb +23 -0
- data/lib/types/private/types/string_holder.rb +26 -0
- data/lib/types/private/types/void.rb +33 -0
- data/lib/types/profile.rb +27 -0
- data/lib/types/props/_props.rb +165 -0
- data/lib/types/props/constructor.rb +20 -0
- data/lib/types/props/custom_type.rb +84 -0
- data/lib/types/props/decorator.rb +826 -0
- data/lib/types/props/errors.rb +8 -0
- data/lib/types/props/optional.rb +73 -0
- data/lib/types/props/plugin.rb +15 -0
- data/lib/types/props/pretty_printable.rb +106 -0
- data/lib/types/props/serializable.rb +376 -0
- data/lib/types/props/type_validation.rb +98 -0
- data/lib/types/props/utils.rb +49 -0
- data/lib/types/props/weak_constructor.rb +30 -0
- data/lib/types/runtime_profiled.rb +36 -0
- data/lib/types/sig.rb +28 -0
- data/lib/types/struct.rb +8 -0
- data/lib/types/types/base.rb +141 -0
- data/lib/types/types/class_of.rb +38 -0
- data/lib/types/types/enum.rb +42 -0
- data/lib/types/types/fixed_array.rb +60 -0
- data/lib/types/types/fixed_hash.rb +59 -0
- data/lib/types/types/intersection.rb +36 -0
- data/lib/types/types/noreturn.rb +25 -0
- data/lib/types/types/proc.rb +51 -0
- data/lib/types/types/self_type.rb +31 -0
- data/lib/types/types/simple.rb +33 -0
- data/lib/types/types/type_member.rb +7 -0
- data/lib/types/types/type_parameter.rb +23 -0
- data/lib/types/types/type_template.rb +7 -0
- data/lib/types/types/type_variable.rb +31 -0
- data/lib/types/types/typed_array.rb +20 -0
- data/lib/types/types/typed_enumerable.rb +141 -0
- data/lib/types/types/typed_enumerator.rb +22 -0
- data/lib/types/types/typed_hash.rb +29 -0
- data/lib/types/types/typed_range.rb +22 -0
- data/lib/types/types/typed_set.rb +22 -0
- data/lib/types/types/union.rb +59 -0
- data/lib/types/types/untyped.rb +25 -0
- data/lib/types/utils.rb +223 -0
- metadata +122 -15
@@ -0,0 +1,275 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Private::Methods
|
5
|
+
Declaration = Struct.new(:mod, :params, :returns, :bind, :mode, :checked, :finalized, :soft_notify, :override_allow_incompatible, :type_parameters, :generated)
|
6
|
+
|
7
|
+
class DeclBuilder
|
8
|
+
attr_reader :decl
|
9
|
+
|
10
|
+
class BuilderError < StandardError; end
|
11
|
+
|
12
|
+
private def check_live!
|
13
|
+
if decl.finalized
|
14
|
+
raise BuilderError.new("You can't modify a signature declaration after it has been used.")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(mod)
|
19
|
+
# TODO RUBYPLAT-1278 - with ruby 2.5, use kwargs here
|
20
|
+
@decl = Declaration.new(
|
21
|
+
mod,
|
22
|
+
ARG_NOT_PROVIDED, # params
|
23
|
+
ARG_NOT_PROVIDED, # returns
|
24
|
+
ARG_NOT_PROVIDED, # bind
|
25
|
+
Modes.standard, # mode
|
26
|
+
ARG_NOT_PROVIDED, # checked
|
27
|
+
false, # finalized
|
28
|
+
ARG_NOT_PROVIDED, # soft_notify
|
29
|
+
nil, # override_allow_incompatible
|
30
|
+
ARG_NOT_PROVIDED, # type_parameters
|
31
|
+
ARG_NOT_PROVIDED, # generated
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def params(params)
|
36
|
+
check_live!
|
37
|
+
if !decl.params.equal?(ARG_NOT_PROVIDED)
|
38
|
+
raise BuilderError.new("You can't call .params twice")
|
39
|
+
end
|
40
|
+
|
41
|
+
decl.params = params
|
42
|
+
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def returns(type)
|
47
|
+
check_live!
|
48
|
+
if decl.returns.is_a?(T::Private::Types::Void)
|
49
|
+
raise BuilderError.new("You can't call .returns after calling .void.")
|
50
|
+
end
|
51
|
+
if !decl.returns.equal?(ARG_NOT_PROVIDED)
|
52
|
+
raise BuilderError.new("You can't call .returns multiple times in a signature.")
|
53
|
+
end
|
54
|
+
|
55
|
+
decl.returns = type
|
56
|
+
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def void
|
61
|
+
check_live!
|
62
|
+
if !decl.returns.equal?(ARG_NOT_PROVIDED)
|
63
|
+
raise BuilderError.new("You can't call .void after calling .returns.")
|
64
|
+
end
|
65
|
+
|
66
|
+
decl.returns = T::Private::Types::Void.new
|
67
|
+
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def bind(type)
|
72
|
+
check_live!
|
73
|
+
if !decl.bind.equal?(ARG_NOT_PROVIDED)
|
74
|
+
raise BuilderError.new("You can't call .bind multiple times in a signature.")
|
75
|
+
end
|
76
|
+
|
77
|
+
decl.bind = type
|
78
|
+
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
def checked(level)
|
83
|
+
if T.unsafe(true)
|
84
|
+
raise "The .checked API is unstable, so we don't want it used until we redesign it. To change Sorbet's runtime behavior, see https://sorbet.org/docs/tconfiguration"
|
85
|
+
end
|
86
|
+
check_live!
|
87
|
+
|
88
|
+
if !decl.checked.equal?(ARG_NOT_PROVIDED)
|
89
|
+
raise BuilderError.new("You can't call .checked multiple times in a signature.")
|
90
|
+
end
|
91
|
+
if !decl.soft_notify.equal?(ARG_NOT_PROVIDED)
|
92
|
+
raise BuilderError.new("You can't use .checked with .soft.")
|
93
|
+
end
|
94
|
+
if !decl.generated.equal?(ARG_NOT_PROVIDED)
|
95
|
+
raise BuilderError.new("You can't use .checked with .generated.")
|
96
|
+
end
|
97
|
+
if !T::Private::RuntimeLevels::LEVELS.include?(level)
|
98
|
+
raise BuilderError.new("Invalid `checked` level '#{level}'. Use one of: #{T::Private::RuntimeLevels::LEVELS}.")
|
99
|
+
end
|
100
|
+
|
101
|
+
decl.checked = level
|
102
|
+
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
def soft(notify:)
|
107
|
+
if T.unsafe(true)
|
108
|
+
raise "The .soft API is unstable, so we don't want it used until we redesign it. To change Sorbet's runtime behavior, see https://sorbet.org/docs/tconfiguration"
|
109
|
+
end
|
110
|
+
check_live!
|
111
|
+
|
112
|
+
if !decl.soft_notify.equal?(ARG_NOT_PROVIDED)
|
113
|
+
raise BuilderError.new("You can't call .soft multiple times in a signature.")
|
114
|
+
end
|
115
|
+
if !decl.checked.equal?(ARG_NOT_PROVIDED)
|
116
|
+
raise BuilderError.new("You can't use .soft with .checked.")
|
117
|
+
end
|
118
|
+
if !decl.generated.equal?(ARG_NOT_PROVIDED)
|
119
|
+
raise BuilderError.new("You can't use .soft with .generated.")
|
120
|
+
end
|
121
|
+
|
122
|
+
# TODO consider validating that :notify is a project that sentry knows about,
|
123
|
+
# as per https://git.corp.stripe.com/stripe-internal/pay-server/blob/master/lib/event/job/sentry_job.rb#L125
|
124
|
+
if !notify || notify == ''
|
125
|
+
raise BuilderError.new("You can't provide an empty notify to .soft().")
|
126
|
+
end
|
127
|
+
|
128
|
+
decl.soft_notify = notify
|
129
|
+
|
130
|
+
self
|
131
|
+
end
|
132
|
+
|
133
|
+
def generated
|
134
|
+
if T.unsafe(true)
|
135
|
+
raise "The .generated API is unstable, so we don't want it used until we redesign it. To change Sorbet's runtime behavior, see https://sorbet.org/docs/tconfiguration"
|
136
|
+
end
|
137
|
+
check_live!
|
138
|
+
|
139
|
+
if !decl.generated.equal?(ARG_NOT_PROVIDED)
|
140
|
+
raise BuilderError.new("You can't call .generated multiple times in a signature.")
|
141
|
+
end
|
142
|
+
if !decl.checked.equal?(ARG_NOT_PROVIDED)
|
143
|
+
raise BuilderError.new("You can't use .generated with .checked.")
|
144
|
+
end
|
145
|
+
if !decl.soft_notify.equal?(ARG_NOT_PROVIDED)
|
146
|
+
raise BuilderError.new("You can't use .generated with .soft.")
|
147
|
+
end
|
148
|
+
|
149
|
+
decl.generated = true
|
150
|
+
|
151
|
+
self
|
152
|
+
end
|
153
|
+
|
154
|
+
def abstract
|
155
|
+
check_live!
|
156
|
+
|
157
|
+
case decl.mode
|
158
|
+
when Modes.standard
|
159
|
+
decl.mode = Modes.abstract
|
160
|
+
when Modes.abstract
|
161
|
+
raise BuilderError.new(".abstract cannot be repeated in a single signature")
|
162
|
+
else
|
163
|
+
raise BuilderError.new("`.abstract` cannot be combined with any of `.override`, `.implementation`, or "\
|
164
|
+
"`.overridable`.")
|
165
|
+
end
|
166
|
+
|
167
|
+
self
|
168
|
+
end
|
169
|
+
|
170
|
+
def override(allow_incompatible: false)
|
171
|
+
check_live!
|
172
|
+
|
173
|
+
case decl.mode
|
174
|
+
when Modes.standard
|
175
|
+
decl.mode = Modes.override
|
176
|
+
decl.override_allow_incompatible = allow_incompatible
|
177
|
+
when Modes.override
|
178
|
+
raise BuilderError.new(".override cannot be repeated in a single signature")
|
179
|
+
else
|
180
|
+
raise BuilderError.new("`.override` cannot be combined with any of `.abstract`, `.implementation`, or "\
|
181
|
+
"`.overridable`.")
|
182
|
+
end
|
183
|
+
|
184
|
+
self
|
185
|
+
end
|
186
|
+
|
187
|
+
def overridable
|
188
|
+
check_live!
|
189
|
+
|
190
|
+
case decl.mode
|
191
|
+
when Modes.abstract, Modes.override
|
192
|
+
raise BuilderError.new("`.overridable` cannot be combined with `.#{decl.mode}`")
|
193
|
+
when Modes.standard
|
194
|
+
decl.mode = Modes.overridable
|
195
|
+
when Modes.implementation
|
196
|
+
decl.mode = Modes.overridable_implementation
|
197
|
+
when Modes.overridable, Modes.overridable_implementation
|
198
|
+
raise BuilderError.new(".overridable cannot be repeated in a single signature")
|
199
|
+
end
|
200
|
+
|
201
|
+
self
|
202
|
+
end
|
203
|
+
|
204
|
+
def implementation
|
205
|
+
check_live!
|
206
|
+
|
207
|
+
case decl.mode
|
208
|
+
when Modes.abstract, Modes.override
|
209
|
+
raise BuilderError.new("`.implementation` cannot be combined with `.#{decl.mode}`")
|
210
|
+
when Modes.standard
|
211
|
+
decl.mode = Modes.implementation
|
212
|
+
when Modes.overridable
|
213
|
+
decl.mode = Modes.overridable_implementation
|
214
|
+
when Modes.implementation, Modes.overridable_implementation
|
215
|
+
raise BuilderError.new(".implementation cannot be repeated in a single signature")
|
216
|
+
end
|
217
|
+
|
218
|
+
self
|
219
|
+
end
|
220
|
+
|
221
|
+
# Declares valid type paramaters which can be used with `T.type_parameter` in
|
222
|
+
# this `sig`.
|
223
|
+
#
|
224
|
+
# This is used for generic methods. Example usage:
|
225
|
+
#
|
226
|
+
# sig do
|
227
|
+
# type_parameters(:U)
|
228
|
+
# .params(blk: T.proc.params(arg0: Elem).returns(T.type_parameter(:U)))
|
229
|
+
# .returns(T::Array[T.type_parameter(:U)])
|
230
|
+
# end
|
231
|
+
# def map(&blk); end
|
232
|
+
def type_parameters(*names)
|
233
|
+
check_live!
|
234
|
+
|
235
|
+
names.each do |name|
|
236
|
+
raise BuilderError.new("not a symbol: #{name}") unless name.is_a?(Symbol)
|
237
|
+
end
|
238
|
+
|
239
|
+
if !decl.type_parameters.equal?(ARG_NOT_PROVIDED)
|
240
|
+
raise BuilderError.new("You can't call .type_parameters multiple times in a signature.")
|
241
|
+
end
|
242
|
+
|
243
|
+
decl.type_parameters = names
|
244
|
+
|
245
|
+
self
|
246
|
+
end
|
247
|
+
|
248
|
+
def finalize!
|
249
|
+
check_live!
|
250
|
+
|
251
|
+
if decl.bind.equal?(ARG_NOT_PROVIDED)
|
252
|
+
decl.bind = nil
|
253
|
+
end
|
254
|
+
if decl.checked.equal?(ARG_NOT_PROVIDED)
|
255
|
+
decl.checked = :always
|
256
|
+
end
|
257
|
+
if decl.soft_notify.equal?(ARG_NOT_PROVIDED)
|
258
|
+
decl.soft_notify = nil
|
259
|
+
end
|
260
|
+
if decl.generated.equal?(ARG_NOT_PROVIDED)
|
261
|
+
decl.generated = false
|
262
|
+
end
|
263
|
+
if decl.params.equal?(ARG_NOT_PROVIDED)
|
264
|
+
decl.params = {}
|
265
|
+
end
|
266
|
+
if decl.type_parameters.equal?(ARG_NOT_PROVIDED)
|
267
|
+
decl.type_parameters = {}
|
268
|
+
end
|
269
|
+
|
270
|
+
decl.finalized = true
|
271
|
+
|
272
|
+
self
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Private::Methods::Modes
|
5
|
+
def self.standard; 'standard'; end
|
6
|
+
def self.abstract; 'abstract'; end
|
7
|
+
def self.overridable; 'overridable'; end
|
8
|
+
def self.implementation; 'implementation'; end
|
9
|
+
def self.override; 'override'; end
|
10
|
+
def self.overridable_implementation; 'overridable_implementation'; end
|
11
|
+
def self.untyped; 'untyped'; end
|
12
|
+
MODES = [self.standard, self.abstract, self.overridable, self.implementation, self.override, self.overridable_implementation, self.untyped]
|
13
|
+
|
14
|
+
IMPLEMENT_MODES = [self.implementation, self.overridable_implementation]
|
15
|
+
OVERRIDABLE_MODES = [self.override, self.overridable, self.overridable_implementation, self.untyped]
|
16
|
+
OVERRIDE_MODES = [self.override]
|
17
|
+
NON_OVERRIDE_MODES = MODES - OVERRIDE_MODES - IMPLEMENT_MODES
|
18
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
class T::Private::Methods::Signature
|
5
|
+
attr_reader :method, :method_name, :arg_types, :kwarg_types, :block_type, :block_name,
|
6
|
+
:rest_type, :rest_name, :keyrest_type, :keyrest_name, :bind,
|
7
|
+
:return_type, :mode, :req_arg_count, :req_kwarg_names, :has_rest, :has_keyrest,
|
8
|
+
:check_level, :generated, :parameters, :soft_notify, :override_allow_incompatible, :ever_failed
|
9
|
+
|
10
|
+
def self.new_untyped(method:, mode: T::Private::Methods::Modes.untyped, parameters: method.parameters)
|
11
|
+
# Using `Untyped` ensures we'll get an error if we ever try validation on these.
|
12
|
+
not_typed = T::Private::Types::NotTyped.new
|
13
|
+
raw_return_type = not_typed
|
14
|
+
raw_arg_types = parameters.map do |_param_kind, param_name|
|
15
|
+
[param_name, not_typed]
|
16
|
+
end.to_h
|
17
|
+
|
18
|
+
self.new(
|
19
|
+
method: method,
|
20
|
+
method_name: method.name,
|
21
|
+
raw_arg_types: raw_arg_types,
|
22
|
+
raw_return_type: raw_return_type,
|
23
|
+
bind: nil,
|
24
|
+
mode: mode,
|
25
|
+
check_level: :never,
|
26
|
+
parameters: parameters,
|
27
|
+
soft_notify: nil,
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def mark_failed
|
32
|
+
@ever_failed = true
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(method:, method_name:, raw_arg_types:, raw_return_type:, bind:, mode:, check_level:, parameters: method.parameters, soft_notify:, generated: false, override_allow_incompatible: false)
|
36
|
+
@method = method
|
37
|
+
@method_name = method_name
|
38
|
+
@arg_types = []
|
39
|
+
@kwarg_types = {}
|
40
|
+
@block_type = nil
|
41
|
+
@block_name = nil
|
42
|
+
@rest_type = nil
|
43
|
+
@rest_name = nil
|
44
|
+
@keyrest_type = nil
|
45
|
+
@keyrest_name = nil
|
46
|
+
@return_type = T::Utils.coerce(raw_return_type)
|
47
|
+
@bind = bind ? T::Utils.coerce(bind) : bind
|
48
|
+
@mode = mode
|
49
|
+
@check_level = check_level
|
50
|
+
@req_arg_count = 0
|
51
|
+
@req_kwarg_names = []
|
52
|
+
@has_rest = false
|
53
|
+
@has_keyrest = false
|
54
|
+
@parameters = parameters
|
55
|
+
@soft_notify = soft_notify
|
56
|
+
@override_allow_incompatible = override_allow_incompatible
|
57
|
+
@generated = generated
|
58
|
+
@ever_failed = false
|
59
|
+
|
60
|
+
param_names = parameters.map {|_, name| name}
|
61
|
+
declared_param_names = raw_arg_types.keys
|
62
|
+
missing_names = param_names - declared_param_names
|
63
|
+
extra_names = declared_param_names - param_names
|
64
|
+
if !missing_names.empty?
|
65
|
+
raise "The declaration for `#{method.name}` is missing parameter(s): #{missing_names.join(', ')}"
|
66
|
+
end
|
67
|
+
if !extra_names.empty?
|
68
|
+
raise "The declaration for `#{method.name}` has extra parameter(s): #{extra_names.join(', ')}"
|
69
|
+
end
|
70
|
+
|
71
|
+
parameters.zip(raw_arg_types) do |(param_kind, param_name), (type_name, raw_type)|
|
72
|
+
if type_name != param_name
|
73
|
+
hint = ""
|
74
|
+
# Ruby reorders params so that required keyword arguments
|
75
|
+
# always precede optional keyword arguments. We can't tell
|
76
|
+
# whether the culprit is the Ruby reordering or user error, so
|
77
|
+
# we error but include a note
|
78
|
+
if param_kind == :keyreq && parameters.any? {|k, _| k == :key}
|
79
|
+
hint = "\n\nNote: Any required keyword arguments must precede any optional keyword " \
|
80
|
+
"arguments. If your method declaration matches your `def`, try reordering any " \
|
81
|
+
"optional keyword parameters to the end of the method list."
|
82
|
+
end
|
83
|
+
|
84
|
+
raise "Parameter `#{type_name}` is declared out of order (declared as arg number " \
|
85
|
+
"#{declared_param_names.index(type_name) + 1}, defined in the method as arg number " \
|
86
|
+
"#{param_names.index(type_name) + 1}).#{hint}\nMethod: #{method_desc}"
|
87
|
+
end
|
88
|
+
|
89
|
+
type = T::Utils.coerce(raw_type)
|
90
|
+
|
91
|
+
case param_kind
|
92
|
+
when :req
|
93
|
+
if @arg_types.length > @req_arg_count
|
94
|
+
# Note that this is actually is supported by Ruby, but it would add complexity to
|
95
|
+
# support it here, and I'm happy to discourage its use anyway.
|
96
|
+
raise "Required params after optional params are not supported in method declarations. Method: #{method_desc}"
|
97
|
+
end
|
98
|
+
@arg_types << [param_name, type]
|
99
|
+
@req_arg_count += 1
|
100
|
+
when :opt
|
101
|
+
@arg_types << [param_name, type]
|
102
|
+
when :key, :keyreq
|
103
|
+
@kwarg_types[param_name] = type
|
104
|
+
if param_kind == :keyreq
|
105
|
+
@req_kwarg_names << param_name
|
106
|
+
end
|
107
|
+
when :block
|
108
|
+
@block_name = param_name
|
109
|
+
@block_type = type
|
110
|
+
when :rest
|
111
|
+
@has_rest = true
|
112
|
+
@rest_name = param_name
|
113
|
+
@rest_type = type
|
114
|
+
when :keyrest
|
115
|
+
@has_keyrest = true
|
116
|
+
@keyrest_name = param_name
|
117
|
+
@keyrest_type = type
|
118
|
+
else
|
119
|
+
raise "Unexpected param_kind: `#{param_kind}`. Method: #{method_desc}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def arg_count
|
125
|
+
@arg_types.length
|
126
|
+
end
|
127
|
+
|
128
|
+
def kwarg_names
|
129
|
+
@kwarg_types.keys
|
130
|
+
end
|
131
|
+
|
132
|
+
def owner
|
133
|
+
@method.owner
|
134
|
+
end
|
135
|
+
|
136
|
+
def dsl_method
|
137
|
+
"#{@mode}_method"
|
138
|
+
end
|
139
|
+
|
140
|
+
# @return [Hash] a mapping like {arg_name: [val, type], ...}, for only those args actually present.
|
141
|
+
def each_args_value_type(args)
|
142
|
+
# Manually split out args and kwargs based on ruby's behavior. Do not try to implement this by
|
143
|
+
# getting ruby to determine the kwargs for you (e.g., by defining this method to take *args and
|
144
|
+
# **kwargs). That won't work, because ruby's behavior for determining kwargs is dependent on the
|
145
|
+
# the other parameters in the method definition, and our method definition here doesn't (and
|
146
|
+
# can't) match the definition of the method we're validating. In addition, Ruby has a bug that
|
147
|
+
# causes forwarding **kwargs to do the wrong thing: see https://bugs.ruby-lang.org/issues/10708
|
148
|
+
# and https://bugs.ruby-lang.org/issues/11860.
|
149
|
+
if (args.length > @req_arg_count) && (!@kwarg_types.empty? || @has_keyrest) && args[-1].is_a?(Hash)
|
150
|
+
kwargs = args[-1]
|
151
|
+
args = args[0...-1]
|
152
|
+
else
|
153
|
+
kwargs = EMPTY_HASH
|
154
|
+
end
|
155
|
+
|
156
|
+
arg_types = @arg_types
|
157
|
+
|
158
|
+
if @has_rest
|
159
|
+
arg_types += [[@rest_name, @rest_type]] * (args.length - @arg_types.length)
|
160
|
+
|
161
|
+
elsif (args.length < @req_arg_count) || (args.length > @arg_types.length)
|
162
|
+
expected_str = @req_arg_count.to_s
|
163
|
+
if @arg_types.length != @req_arg_count
|
164
|
+
expected_str += "..#{@arg_types.length}"
|
165
|
+
end
|
166
|
+
raise ArgumentError.new("wrong number of arguments (given #{args.length}, expected #{expected_str})")
|
167
|
+
end
|
168
|
+
|
169
|
+
begin
|
170
|
+
it = 0
|
171
|
+
while it < args.length
|
172
|
+
yield arg_types[it][0], args[it], arg_types[it][1]
|
173
|
+
it += 1
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
kwargs.each do |name, val|
|
178
|
+
type = @kwarg_types[name]
|
179
|
+
if !type && @has_keyrest
|
180
|
+
type = @keyrest_type
|
181
|
+
end
|
182
|
+
yield name, val, type if type
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def method_desc
|
187
|
+
if @method.source_location
|
188
|
+
loc = @method.source_location.join(':')
|
189
|
+
else
|
190
|
+
loc = "<unknown location>"
|
191
|
+
end
|
192
|
+
"#{@method} at #{loc}"
|
193
|
+
end
|
194
|
+
|
195
|
+
EMPTY_HASH = {}.freeze
|
196
|
+
end
|