rigortype 0.0.1
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 +7 -0
- data/LICENSE +373 -0
- data/README.md +152 -0
- data/exe/rigor +9 -0
- data/lib/rigor/analysis/check_rules.rb +503 -0
- data/lib/rigor/analysis/diagnostic.rb +35 -0
- data/lib/rigor/analysis/fact_store.rb +133 -0
- data/lib/rigor/analysis/result.rb +29 -0
- data/lib/rigor/analysis/runner.rb +119 -0
- data/lib/rigor/ast/type_node.rb +41 -0
- data/lib/rigor/ast.rb +22 -0
- data/lib/rigor/cli/type_of_command.rb +160 -0
- data/lib/rigor/cli/type_of_renderer.rb +88 -0
- data/lib/rigor/cli/type_scan_command.rb +160 -0
- data/lib/rigor/cli/type_scan_renderer.rb +165 -0
- data/lib/rigor/cli/type_scan_report.rb +32 -0
- data/lib/rigor/cli.rb +195 -0
- data/lib/rigor/configuration.rb +49 -0
- data/lib/rigor/environment/class_registry.rb +141 -0
- data/lib/rigor/environment/rbs_hierarchy.rb +64 -0
- data/lib/rigor/environment/rbs_loader.rb +244 -0
- data/lib/rigor/environment.rb +177 -0
- data/lib/rigor/inference/acceptance.rb +444 -0
- data/lib/rigor/inference/block_parameter_binder.rb +198 -0
- data/lib/rigor/inference/closure_escape_analyzer.rb +191 -0
- data/lib/rigor/inference/coverage_scanner.rb +85 -0
- data/lib/rigor/inference/expression_typer.rb +831 -0
- data/lib/rigor/inference/fallback.rb +35 -0
- data/lib/rigor/inference/fallback_tracer.rb +64 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +102 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +169 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +421 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +336 -0
- data/lib/rigor/inference/method_dispatcher.rb +213 -0
- data/lib/rigor/inference/method_parameter_binder.rb +257 -0
- data/lib/rigor/inference/multi_target_binder.rb +143 -0
- data/lib/rigor/inference/narrowing.rb +1008 -0
- data/lib/rigor/inference/rbs_type_translator.rb +219 -0
- data/lib/rigor/inference/scope_indexer.rb +468 -0
- data/lib/rigor/inference/statement_evaluator.rb +1017 -0
- data/lib/rigor/rbs_extended.rb +98 -0
- data/lib/rigor/scope.rb +340 -0
- data/lib/rigor/source/node_locator.rb +104 -0
- data/lib/rigor/source/node_walker.rb +37 -0
- data/lib/rigor/source.rb +15 -0
- data/lib/rigor/testing.rb +65 -0
- data/lib/rigor/trinary.rb +108 -0
- data/lib/rigor/type/accepts_result.rb +109 -0
- data/lib/rigor/type/bot.rb +57 -0
- data/lib/rigor/type/combinator.rb +148 -0
- data/lib/rigor/type/constant.rb +90 -0
- data/lib/rigor/type/dynamic.rb +60 -0
- data/lib/rigor/type/hash_shape.rb +246 -0
- data/lib/rigor/type/nominal.rb +83 -0
- data/lib/rigor/type/singleton.rb +65 -0
- data/lib/rigor/type/top.rb +56 -0
- data/lib/rigor/type/tuple.rb +84 -0
- data/lib/rigor/type/union.rb +65 -0
- data/lib/rigor/type.rb +23 -0
- data/lib/rigor/version.rb +5 -0
- data/lib/rigor.rb +29 -0
- data/sig/rigor/analysis/fact_store.rbs +51 -0
- data/sig/rigor/ast.rbs +11 -0
- data/sig/rigor/environment.rbs +59 -0
- data/sig/rigor/inference.rbs +151 -0
- data/sig/rigor/rbs_extended.rbs +22 -0
- data/sig/rigor/scope.rbs +49 -0
- data/sig/rigor/source.rbs +20 -0
- data/sig/rigor/testing.rbs +9 -0
- data/sig/rigor/trinary.rbs +29 -0
- data/sig/rigor/type.rbs +171 -0
- data/sig/rigor.rbs +70 -0
- metadata +260 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../type"
|
|
4
|
+
require_relative "../rbs_type_translator"
|
|
5
|
+
require_relative "overload_selector"
|
|
6
|
+
|
|
7
|
+
module Rigor
|
|
8
|
+
module Inference
|
|
9
|
+
module MethodDispatcher
|
|
10
|
+
# Slice 4 dispatch tier that consults RBS method signatures.
|
|
11
|
+
# Sits behind {ConstantFolding}, so anything the constant folder
|
|
12
|
+
# already proves (e.g., `1 + 2 == 3`) keeps its full Constant
|
|
13
|
+
# precision; only the calls the folder cannot prove fall through
|
|
14
|
+
# to RBS.
|
|
15
|
+
#
|
|
16
|
+
# Phase 2b extends the dispatcher to recognise `Singleton[Foo]`
|
|
17
|
+
# receivers, routing those calls through `singleton_method`
|
|
18
|
+
# instead of `instance_method`. The constant `Foo` therefore now
|
|
19
|
+
# resolves to `Singleton[Foo]`, and `Foo.new` / `Foo.bar` look up
|
|
20
|
+
# the corresponding *class* methods.
|
|
21
|
+
#
|
|
22
|
+
# Phase 2c adds argument-typed overload selection: instead of
|
|
23
|
+
# always returning `method_types.first`, the dispatcher delegates
|
|
24
|
+
# to {OverloadSelector} which filters overloads by positional
|
|
25
|
+
# arity and consults `Rigor::Type#accepts` for each parameter.
|
|
26
|
+
# When no overload accepts the actual argument types, the
|
|
27
|
+
# selector falls back to the first overload so the existing
|
|
28
|
+
# phase-1/2b behavior is preserved.
|
|
29
|
+
#
|
|
30
|
+
# Phase 2d adds generics instantiation. Receivers carry an
|
|
31
|
+
# ordered `type_args` array on `Rigor::Type::Nominal`. The
|
|
32
|
+
# dispatcher zips the receiver's `type_args` against the class's
|
|
33
|
+
# declared type-parameter names (`Array` -> `[:Elem]`, `Hash` ->
|
|
34
|
+
# `[:K, :V]`, ...) to build a substitution map; that map is then
|
|
35
|
+
# threaded through {RbsTypeTranslator} so a return type like
|
|
36
|
+
# `::Array[Elem]` resolves to `Nominal["Array", [Integer]]`
|
|
37
|
+
# rather than degrading the variable to `Dynamic[Top]`. When
|
|
38
|
+
# arities mismatch (raw receiver, partial generics) the map is
|
|
39
|
+
# left empty and free variables degrade as before.
|
|
40
|
+
#
|
|
41
|
+
# Slice 5 phase 1 projects shape-carrying receivers onto their
|
|
42
|
+
# underlying nominal so the existing dispatch + substitution
|
|
43
|
+
# machinery works without duplication: `Tuple[Integer, String]`
|
|
44
|
+
# dispatches as `Array[Integer | String]`, and
|
|
45
|
+
# `HashShape{a: Integer}` dispatches as `Hash[Symbol, Integer]`.
|
|
46
|
+
# Tuple-aware refinements (e.g., `tuple[0]` returning the precise
|
|
47
|
+
# member) are deferred to Slice 5 phase 2.
|
|
48
|
+
#
|
|
49
|
+
# Remaining limitations:
|
|
50
|
+
#
|
|
51
|
+
# * `block_type:` is ignored; method types that constrain the
|
|
52
|
+
# block return type are not yet honored.
|
|
53
|
+
# * Keyword arguments are not threaded through call_arg_types,
|
|
54
|
+
# so overloads with required keywords are skipped (they cannot
|
|
55
|
+
# match the empty kwargs we send).
|
|
56
|
+
# * Method-level type parameters (e.g., `def foo[T]: (T) -> T`)
|
|
57
|
+
# are not bound; their variables remain `Dynamic[Top]` after
|
|
58
|
+
# substitution.
|
|
59
|
+
#
|
|
60
|
+
# See docs/adr/4-type-inference-engine.md for the broader plan.
|
|
61
|
+
# rubocop:disable Metrics/ModuleLength
|
|
62
|
+
module RbsDispatch
|
|
63
|
+
module_function
|
|
64
|
+
|
|
65
|
+
# @param receiver [Rigor::Type]
|
|
66
|
+
# @param method_name [Symbol]
|
|
67
|
+
# @param args [Array<Rigor::Type>]
|
|
68
|
+
# @param environment [Rigor::Environment]
|
|
69
|
+
# @param block_type [Rigor::Type, nil] inferred block return
|
|
70
|
+
# type, propagated from `MethodDispatcher.dispatch`. When
|
|
71
|
+
# non-nil, the selector prefers a block-bearing overload
|
|
72
|
+
# and binds the method-level type parameter that the
|
|
73
|
+
# block's return type references to `block_type` (Slice 6
|
|
74
|
+
# phase C sub-phase 2).
|
|
75
|
+
# @return [Rigor::Type, nil] inferred return type, or `nil`
|
|
76
|
+
# when no rule resolves (no class name, no method, dispatch
|
|
77
|
+
# on a Top/Dynamic[Top] receiver, etc.).
|
|
78
|
+
def try_dispatch(receiver:, method_name:, args:, environment:, block_type: nil)
|
|
79
|
+
return nil if environment.nil?
|
|
80
|
+
return nil unless environment.rbs_loader
|
|
81
|
+
|
|
82
|
+
dispatch_for(
|
|
83
|
+
receiver: receiver,
|
|
84
|
+
method_name: method_name,
|
|
85
|
+
args: args,
|
|
86
|
+
environment: environment,
|
|
87
|
+
block_type: block_type
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Slice 6 (Phase C sub-phase 1) probe: returns the positional
|
|
92
|
+
# block-parameter types declared by the receiving method's
|
|
93
|
+
# selected RBS overload, translated into `Rigor::Type`. Used
|
|
94
|
+
# by the StatementEvaluator to bind block parameter names
|
|
95
|
+
# before evaluating the block body.
|
|
96
|
+
#
|
|
97
|
+
# The probe shares the receiver descriptor / overload selector
|
|
98
|
+
# plumbing with `try_dispatch`; only the projection at the end
|
|
99
|
+
# differs (the block's positional params instead of the return
|
|
100
|
+
# type). Returns an empty array when:
|
|
101
|
+
#
|
|
102
|
+
# - the environment / RBS loader is missing,
|
|
103
|
+
# - the receiver does not project to a known class,
|
|
104
|
+
# - the method has no signature in RBS,
|
|
105
|
+
# - the selected overload has no `block:` clause, or
|
|
106
|
+
# - the block is `untyped` / `UntypedFunction` (no statically
|
|
107
|
+
# declared parameter types).
|
|
108
|
+
#
|
|
109
|
+
# This deliberately does NOT differentiate "no overload had a
|
|
110
|
+
# block" from "the block is untyped"; the binder treats both
|
|
111
|
+
# the same way (every parameter defaults to `Dynamic[Top]`).
|
|
112
|
+
# @return [Array<Rigor::Type>] positional block parameter types.
|
|
113
|
+
def block_param_types(receiver:, method_name:, args:, environment:)
|
|
114
|
+
return [] if environment.nil?
|
|
115
|
+
return [] unless environment.rbs_loader
|
|
116
|
+
|
|
117
|
+
probe_block_param_types(
|
|
118
|
+
receiver: receiver,
|
|
119
|
+
method_name: method_name,
|
|
120
|
+
args: args,
|
|
121
|
+
environment: environment
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# rubocop:disable Metrics/ClassLength
|
|
126
|
+
class << self
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
def dispatch_for(receiver:, method_name:, args:, environment:, block_type:)
|
|
130
|
+
args ||= []
|
|
131
|
+
case receiver
|
|
132
|
+
when Type::Union
|
|
133
|
+
dispatch_union(receiver, method_name, args, environment, block_type)
|
|
134
|
+
else
|
|
135
|
+
dispatch_one(receiver, method_name, args, environment, block_type)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def dispatch_union(receiver, method_name, args, environment, block_type)
|
|
140
|
+
results = receiver.members.map do |member|
|
|
141
|
+
dispatch_one(member, method_name, args, environment, block_type)
|
|
142
|
+
end
|
|
143
|
+
return nil if results.any?(&:nil?)
|
|
144
|
+
|
|
145
|
+
Type::Combinator.union(*results)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def dispatch_one(receiver, method_name, args, environment, block_type)
|
|
149
|
+
descriptor = receiver_descriptor(receiver)
|
|
150
|
+
return nil unless descriptor
|
|
151
|
+
|
|
152
|
+
class_name, kind, receiver_args = descriptor
|
|
153
|
+
method_definition = lookup_method(environment, class_name, kind, method_name)
|
|
154
|
+
return nil unless method_definition
|
|
155
|
+
|
|
156
|
+
type_vars = build_type_vars(environment, class_name, receiver_args)
|
|
157
|
+
translate_return_type(
|
|
158
|
+
method_definition,
|
|
159
|
+
class_name: class_name,
|
|
160
|
+
kind: kind,
|
|
161
|
+
args: args,
|
|
162
|
+
type_vars: type_vars,
|
|
163
|
+
block_type: block_type
|
|
164
|
+
)
|
|
165
|
+
rescue StandardError
|
|
166
|
+
# Defensive: if RBS' definition builder raises on a broken
|
|
167
|
+
# hierarchy (e.g., partially loaded user signatures), the
|
|
168
|
+
# dispatcher MUST stay fail-soft.
|
|
169
|
+
nil
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Maps a Rigor::Type receiver to a
|
|
173
|
+
# `[class_name, kind, type_args]` triple where `kind` is
|
|
174
|
+
# either `:instance` or `:singleton` and `type_args` carries
|
|
175
|
+
# the receiver's generic instantiation (empty for raw or
|
|
176
|
+
# singleton receivers, since `Singleton[Foo]` carries no
|
|
177
|
+
# generic args today). Returns nil when the receiver does
|
|
178
|
+
# not correspond to a single concrete class.
|
|
179
|
+
#
|
|
180
|
+
# Slice 5 phase 1 projects Tuple/HashShape receivers to
|
|
181
|
+
# their underlying Array/Hash nominal so dispatch reuses the
|
|
182
|
+
# generic-typed pipeline.
|
|
183
|
+
def receiver_descriptor(receiver)
|
|
184
|
+
case receiver
|
|
185
|
+
when Type::Constant
|
|
186
|
+
[receiver.value.class.name, :instance, []]
|
|
187
|
+
when Type::Nominal
|
|
188
|
+
[receiver.class_name, :instance, receiver.type_args]
|
|
189
|
+
when Type::Singleton
|
|
190
|
+
[receiver.class_name, :singleton, []]
|
|
191
|
+
when Type::Tuple
|
|
192
|
+
["Array", :instance, tuple_type_args(receiver)]
|
|
193
|
+
when Type::HashShape
|
|
194
|
+
["Hash", :instance, hash_shape_type_args(receiver)]
|
|
195
|
+
when Type::Dynamic
|
|
196
|
+
receiver_descriptor(receiver.static_facet)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def tuple_type_args(tuple)
|
|
201
|
+
return [] if tuple.elements.empty?
|
|
202
|
+
|
|
203
|
+
[Type::Combinator.union(*tuple.elements)]
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def hash_shape_type_args(shape)
|
|
207
|
+
return [] if shape.pairs.empty?
|
|
208
|
+
|
|
209
|
+
key_types = shape.pairs.keys.map { |k| Type::Combinator.constant_of(k) }
|
|
210
|
+
value_types = shape.pairs.values
|
|
211
|
+
[
|
|
212
|
+
Type::Combinator.union(*key_types),
|
|
213
|
+
Type::Combinator.union(*value_types)
|
|
214
|
+
]
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def lookup_method(environment, class_name, kind, method_name)
|
|
218
|
+
case kind
|
|
219
|
+
when :instance
|
|
220
|
+
environment.rbs_loader.instance_method(
|
|
221
|
+
class_name: class_name,
|
|
222
|
+
method_name: method_name
|
|
223
|
+
)
|
|
224
|
+
when :singleton
|
|
225
|
+
environment.rbs_loader.singleton_method(
|
|
226
|
+
class_name: class_name,
|
|
227
|
+
method_name: method_name
|
|
228
|
+
)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Slice 4 phase 2d substitution map. Zips the class's
|
|
233
|
+
# declared type-parameter names against the receiver's
|
|
234
|
+
# `type_args`. Returns an empty hash when either side is
|
|
235
|
+
# empty or when arities disagree -- in both cases free
|
|
236
|
+
# variables in the method's return type degrade to
|
|
237
|
+
# `Dynamic[Top]` per the translator's contract.
|
|
238
|
+
def build_type_vars(environment, class_name, receiver_args)
|
|
239
|
+
return {} if receiver_args.empty?
|
|
240
|
+
|
|
241
|
+
param_names = environment.rbs_loader.class_type_param_names(class_name)
|
|
242
|
+
return {} if param_names.empty?
|
|
243
|
+
return {} if param_names.size != receiver_args.size
|
|
244
|
+
|
|
245
|
+
param_names.zip(receiver_args).to_h
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# rubocop:disable Metrics/ParameterLists
|
|
249
|
+
def translate_return_type(method_definition, class_name:, kind:, args:, type_vars:, block_type:)
|
|
250
|
+
instance_type = Type::Combinator.nominal_of(class_name)
|
|
251
|
+
self_type =
|
|
252
|
+
case kind
|
|
253
|
+
when :singleton then Type::Combinator.singleton_of(class_name)
|
|
254
|
+
else instance_type
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
method_type = OverloadSelector.select(
|
|
258
|
+
method_definition,
|
|
259
|
+
arg_types: args,
|
|
260
|
+
self_type: self_type,
|
|
261
|
+
instance_type: instance_type,
|
|
262
|
+
type_vars: type_vars,
|
|
263
|
+
block_required: !block_type.nil?
|
|
264
|
+
)
|
|
265
|
+
return nil unless method_type
|
|
266
|
+
|
|
267
|
+
full_type_vars = compose_block_type_vars(method_type, type_vars, block_type)
|
|
268
|
+
|
|
269
|
+
RbsTypeTranslator.translate(
|
|
270
|
+
method_type.type.return_type,
|
|
271
|
+
self_type: self_type,
|
|
272
|
+
instance_type: instance_type,
|
|
273
|
+
type_vars: full_type_vars
|
|
274
|
+
)
|
|
275
|
+
end
|
|
276
|
+
# rubocop:enable Metrics/ParameterLists
|
|
277
|
+
|
|
278
|
+
# When a block type is supplied, locate the method-level
|
|
279
|
+
# type parameter that the selected overload's block return
|
|
280
|
+
# type references and bind it to `block_type`. The
|
|
281
|
+
# contribution layers on top of the receiver-derived
|
|
282
|
+
# `type_vars` so a method like
|
|
283
|
+
# `def map[U] { (Elem) -> U } -> Array[U]` resolves
|
|
284
|
+
# `Elem` from the receiver and `U` from the block return
|
|
285
|
+
# type at the same call site. Anything outside this exact
|
|
286
|
+
# shape (no block clause, an `untyped` block, a non-
|
|
287
|
+
# variable block return type, a variable not declared in
|
|
288
|
+
# `type_params`) returns the original `type_vars` so
|
|
289
|
+
# fallbacks stay consistent.
|
|
290
|
+
def compose_block_type_vars(method_type, type_vars, block_type)
|
|
291
|
+
return type_vars if block_type.nil?
|
|
292
|
+
|
|
293
|
+
block_var_name = method_type_block_return_variable(method_type)
|
|
294
|
+
return type_vars if block_var_name.nil?
|
|
295
|
+
|
|
296
|
+
type_vars.merge(block_var_name => block_type)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def method_type_block_return_variable(method_type)
|
|
300
|
+
return_variable = block_return_variable(method_type)
|
|
301
|
+
return nil if return_variable.nil?
|
|
302
|
+
|
|
303
|
+
params = method_type.respond_to?(:type_params) ? method_type.type_params : []
|
|
304
|
+
return nil if params.nil?
|
|
305
|
+
return nil unless params.any? { |tp| tp.name == return_variable.name }
|
|
306
|
+
|
|
307
|
+
return_variable.name
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def block_return_variable(method_type)
|
|
311
|
+
block = method_type.respond_to?(:block) ? method_type.block : nil
|
|
312
|
+
return nil if block.nil?
|
|
313
|
+
|
|
314
|
+
fun = block.type
|
|
315
|
+
return nil unless fun.respond_to?(:return_type)
|
|
316
|
+
|
|
317
|
+
return_type = fun.return_type
|
|
318
|
+
return_type.is_a?(RBS::Types::Variable) ? return_type : nil
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# ----- block parameter probe (Phase C sub-phase 1) -----
|
|
322
|
+
|
|
323
|
+
def probe_block_param_types(receiver:, method_name:, args:, environment:)
|
|
324
|
+
args ||= []
|
|
325
|
+
case receiver
|
|
326
|
+
when Type::Union then probe_block_param_types_union(receiver, method_name, args, environment)
|
|
327
|
+
else probe_block_param_types_one(receiver, method_name, args, environment)
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# For a union receiver we keep the conservative answer: only
|
|
332
|
+
# return block param types when every member resolves the
|
|
333
|
+
# same arity and types (otherwise the call sites would have
|
|
334
|
+
# to thread per-member binders, which the slice does not
|
|
335
|
+
# support yet). Mismatches degrade to the empty array so the
|
|
336
|
+
# binder defaults all params to Dynamic[Top].
|
|
337
|
+
def probe_block_param_types_union(receiver, method_name, args, environment)
|
|
338
|
+
results = receiver.members.map do |member|
|
|
339
|
+
probe_block_param_types_one(member, method_name, args, environment)
|
|
340
|
+
end
|
|
341
|
+
return [] if results.empty?
|
|
342
|
+
return [] unless results.all? { |r| r == results.first }
|
|
343
|
+
|
|
344
|
+
results.first
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def probe_block_param_types_one(receiver, method_name, args, environment)
|
|
348
|
+
descriptor = receiver_descriptor(receiver)
|
|
349
|
+
return [] unless descriptor
|
|
350
|
+
|
|
351
|
+
class_name, kind, receiver_args = descriptor
|
|
352
|
+
method_definition = lookup_method(environment, class_name, kind, method_name)
|
|
353
|
+
return [] unless method_definition
|
|
354
|
+
|
|
355
|
+
type_vars = build_type_vars(environment, class_name, receiver_args)
|
|
356
|
+
extract_block_param_types(
|
|
357
|
+
method_definition,
|
|
358
|
+
class_name: class_name,
|
|
359
|
+
kind: kind,
|
|
360
|
+
args: args,
|
|
361
|
+
type_vars: type_vars
|
|
362
|
+
)
|
|
363
|
+
rescue StandardError
|
|
364
|
+
[]
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def extract_block_param_types(method_definition, class_name:, kind:, args:, type_vars:)
|
|
368
|
+
instance_type = Type::Combinator.nominal_of(class_name)
|
|
369
|
+
self_type =
|
|
370
|
+
case kind
|
|
371
|
+
when :singleton then Type::Combinator.singleton_of(class_name)
|
|
372
|
+
else instance_type
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
method_type = OverloadSelector.select(
|
|
376
|
+
method_definition,
|
|
377
|
+
arg_types: args,
|
|
378
|
+
self_type: self_type,
|
|
379
|
+
instance_type: instance_type,
|
|
380
|
+
type_vars: type_vars,
|
|
381
|
+
block_required: true
|
|
382
|
+
)
|
|
383
|
+
return [] unless method_type
|
|
384
|
+
|
|
385
|
+
block = method_type.respond_to?(:block) ? method_type.block : nil
|
|
386
|
+
return [] unless block
|
|
387
|
+
|
|
388
|
+
translate_block_positional_params(
|
|
389
|
+
block,
|
|
390
|
+
self_type: self_type,
|
|
391
|
+
instance_type: instance_type,
|
|
392
|
+
type_vars: type_vars
|
|
393
|
+
)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# `RBS::Types::Block#type` is normally an `RBS::Types::Function`
|
|
397
|
+
# carrying the block's parameter list; some signatures use
|
|
398
|
+
# `RBS::Types::UntypedFunction` (a `(?)` block) which exposes
|
|
399
|
+
# no parameter types -- we treat it as "no information" and
|
|
400
|
+
# return an empty array so the binder defaults every slot.
|
|
401
|
+
def translate_block_positional_params(block, self_type:, instance_type:, type_vars:)
|
|
402
|
+
fun = block.type
|
|
403
|
+
return [] unless fun.respond_to?(:required_positionals)
|
|
404
|
+
|
|
405
|
+
params = fun.required_positionals + fun.optional_positionals
|
|
406
|
+
params.map do |param|
|
|
407
|
+
RbsTypeTranslator.translate(
|
|
408
|
+
param.type,
|
|
409
|
+
self_type: self_type,
|
|
410
|
+
instance_type: instance_type,
|
|
411
|
+
type_vars: type_vars
|
|
412
|
+
)
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
# rubocop:enable Metrics/ClassLength
|
|
417
|
+
end
|
|
418
|
+
# rubocop:enable Metrics/ModuleLength
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
end
|